I am using arch linux and a basic cpp xlib custom window manager. However, every time I right click to open the context menu it just flickers and disappears. I cannot use it at all. I also cannot use top drop down menus (file, edit, about, ect.) on any application. Is there anything in Xlib which I have to look out for to ensure I may use the context menus normally?
This is the case in every application I have tried. Only clue I have is in brave it occasionally displays the following message:
XGetWindowAttributes failed for window [WINDOW_ID]
The following simplified example also has this issue:
int main()
{
display = XOpenDisplay(nullptr);
root = DefaultRootWindow(display);
XSelectInput(display, root, SubstructureRedirectMask | SubstructureNotifyMask | StructureNotifyMask);
XGrabServer(display);
Window returned_root;
Window returned_parent;
Window* top_level_windows;
unsigned int num_top_level_windows;
XQueryTree(display, root, &returned_root, &returned_parent, &top_level_windows, &num_top_level_windows);
for(unsigned int i = 0; i < num_top_level_windows; ++i)
{
Frame(top_level_windows[i], true);
}
XFree(top_level_windows);
XUngrabServer(display);
for(;;)
{
XEvent event;
XNextEvent(display, &event);
switch (event.type)
{
case MapRequest:
{
Frame(event.xmaprequest.window, false);
XMapWindow(display, event.xmaprequest.window);
break;
}
case ButtonPress:
XRaiseWindow(display, event.xbutton.window);
break;
}
}
return true;
}
void Frame(Window window, bool created_before_manager)
{
//Retrieve attributes of window to frame
XWindowAttributes attr = {0};
XGetWindowAttributes(display, window, &attr);
//If window was created before window manager started, we should frame it only if it is visible and does not set override_redirect
if(created_before_manager && (attr.override_redirect || attr.map_state != IsViewable))
{
return;
}
//Create frame
Window frame = XCreateSimpleWindow(display, root, attr.x, attr.y, attr.width, attr.height, 5, 0xff0000, 0xffffff);
XReparentWindow(display, window, frame, 0, 0);
XMapWindow(display, frame);
XGrabButton(display, Button1Mask, Mod1Mask, window, None, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
}
To be clear it also works with a super simple example such as:
int main()
{
Display* display = XOpenDisplay(nullptr);
for(;;) {}
return true;
}
The reason I believe the window manager is at fault is because this issue only occurs after I run the window manager.
I expected this to work out of the box. I have not found any information on context menus needing special treatment. They do have the override_redirect flag set to true, so I do not frame them. I cannot find information on any other special treatment required.
It is necessary to make sure the client window has input. I had the input set to whatever was clicked (frame, title bar, or client) because it worked fine as far as normal input is concerned. However, the context menus will only work if you make sure the input is set to the client window directly.
Related
I'm trying to open a gtk file dialog window with a GLFW window.
Now since GLFW is a pretty low level API it only exposes the X11 window and display, because it just creates a window without any GUI stuff.
The problem I'm having is that gtk_file_chooser_dialog_new() expects a parent window to be passed on, but since I only have an X11 handle I'm not quite sure how to create a GTK handle from it.
I followed this tutorial which resulted in the following code:
glfwSetKeyCallback(windowHandle1, [](GLFWwindow *window, int keyCode, int scanCode, int action, int mods) {
if (action == GLFW_PRESS)
{
if (keyCode == GLFW_KEY_O && mods == (GLFW_MOD_SHIFT | GLFW_MOD_CONTROL))
{
GtkWidget *dialog;
GtkFileChooserAction fileAction = GTK_FILE_CHOOSER_ACTION_OPEN;
gint res;
// Window x11Window = glfwGetX11Window(window);
// Display *x11Display = glfwGetX11Display();
int argc = 0;
gtk_init(&argc, nullptr); // TODO: don't do this every time
dialog = gtk_file_chooser_dialog_new("Open File",
nullptr, // should be _GtkWindow of the GLFWwindow
fileAction,
_("_Cancel"),
GTK_RESPONSE_CANCEL,
_("_Open"),
GTK_RESPONSE_ACCEPT,
nullptr);
res = gtk_dialog_run(GTK_DIALOG(dialog));
if (res == GTK_RESPONSE_ACCEPT)
{
char *filename;
GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
filename = gtk_file_chooser_get_filename(chooser);
std::cout << filename << std::endl;
g_free(filename);
}
gtk_widget_destroy(dialog);
std::cout << "destroyed file dialog" << std::endl;
}
}
});
This opens an open file dialog, but because I didn't specify a parent window the main window can still be focused, and another problem is that the dialog doesn't close for some reason even though I call gtk_widget_destroy(dialog).
I already took a look at this post, but the only answer seems to be getting the xid of the file dialog window, which is not what I want to do.
This google search result doesn't seem to help either, as it creates a completely new gdk (not gtk) window on the default display.
I've got the same problem and found hackish way to fix this. Null parent is not really problem here, but lack of event dispatching, so I've added:
gtk_widget_destroy(dialog);
while (g_main_context_iteration(nullptr, false));
I'm struggling to get X11 redrawing events right in a cross platform C++ application.
Everything works great on Windows (I just issue some InvalidateRect calls when I need the window to redraw), but on Linux I keep having redrawing stuttering (probably when I send too many redraw events as follows)
event.type = Expose;
event.xexpose.window = window;
XSendEvent(display, window, False, ExposureMask, &event);
the same happens when I resize the window.
This is the code I'm using
void Window::redraw() { // Called by any control which needs redrawing
XEvent event;
memset(&event, 0, sizeof(event));
event.type = Expose;
event.xexpose.display = display;
XSendEvent(display, window, False, ExposureMask, &event);
}
void Window::resize(int width, int height) {
this->Width = width;
this->Height = height;
}
bool Window::wndProc(XEvent *evt) {
switch (evt->type) {
case Expose: {
if (evt->xexpose.count == 0) { // handle last one only
if (Width != Bitmap.width() || Height != Bitmap.height())
Bitmap.resize(Width, Height);
Renderer.drawOnBitmap(Bitmap);
this->paint();
}
return true;
} break;
case ConfigureNotify: {
this->resize(evt->xconfigure.width, evt->xconfigure.height);
redraw();
return true;
} break;
}
}
void Window::paint() {
XImage image;
sk_bzero(&image, sizeof(image));
// .. boilerplate to initialize XImage...
XInitImage(&image);
XPutImage(display, window, fGc, &image,
0, 0,
0, 0,
Width, Height);
}
I tried several approaches to solve this issue including:
Having a 200 ms delay between every redraw event sending (stuttering still happening.. resize still issues a LOT of events)
Having a secondary thread issuing a redraw call every 50ms (this saturates X11's buffer immediately and crashes the whole thing)
Unfortunately I have animated controls which call redraw() anytime they need another redrawing of part of the window (I handle painting regions of the window separately).
How can I solve stuttering during resizing and too many redrawing events while ensuring my animated controls remain smooth?
I am having some issues with GLFW's window creation. I am wanting to have a program capable of toggling between windowed and fullscreen mode. To do this in GLFW 2.7.8 one must first destroy the active window, then create a new one. I read that version 3.0 has support for multiple windows, but it is still in development.
I have provided my own function to handle keyboard input. Using the initial 400 by 400 window, the program functions as expected; it will enter fullscreen on f or F, will exit when the escape key is pressed, and will complain when anything else is pressed.
However, when fullscreen mode is entered, the window becomes unresponsive with regards to my provided keyboard function. It will continue to run through the loop in main() and will respond to something like the glfwGetKey(GLFW_KEY_ESC) test. Regardless of if I have the mouse cursor enabled or not, the cursor does not appear.
Again, the fullscreen window is created and the KeyboardCallback function returns back into the main loop. I wish to understand why the fullscreen window is not working with my keyboard function, and why it is not displaying properly.
I am not drawing anything to the window, as I am trying to get some experience with various window abstraction libraries specifically. Drawing a simple triangle did nothing to solve he problem, and the fullscreen window remains black.
My code is as follows:
#include <stdio.h>
#include <stdlib.h>
// glfw32 - 2.7.8
#include <GL\glfw.h>
#pragma comment (lib, "GLFW.lib")
#pragma comment (lib, "opengl32.lib")
using namespace std;
// constants and globals
const int WINDOW_WIDTH=400, WINDOW_HEIGHT=400;
static bool fullscreen=false;
// function declarations
void GLFWCALL KeyboardCallback(int key, int action);
void FatalError(const char* msg) {
fprintf(stderr, "ERROR: Failure in \"%s\"\n", msg);
exit(1);
}
int main() {
// ititialize GLFW
glfwInit();
glfwEnable(GLFW_MOUSE_CURSOR);
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3);
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 3);
// initial window, 400x400 with 32-bit depth buffer in windowed mode
glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 0,0,0,0, 32, 0, GLFW_WINDOW);
glfwSetWindowTitle("Simple Title");
glfwSetWindowPos(0, 0);
// set custom keyboard callback
glfwSetKeyCallback(KeyboardCallback);
while (true) { // loop until exit
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers();
// debug
//printf("Looping...\n");
if ( glfwGetKey(GLFW_KEY_ESC) ) {break;}
}
glfwTerminate();
printf("\nHave a nice day\n");
return 0;
}
void GLFWCALL KeyboardCallback(int key, int action) {
//printf("In keyboard function\n");
if (action) { // if key DOWN,
switch(key) { // do something...
case 'f':
case 'F': {
fullscreen = !fullscreen;
printf("Toggle Fullscreen: %s\n", (fullscreen ? "On" : "Off"));
glfwCloseWindow();
if (! glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 0,0,0,0, 32, 0,
fullscreen ? GLFW_FULLSCREEN : GLFW_WINDOW)) {
FatalError("toggle fullscreen");
}
glfwSetWindowTitle("New Title");
break;
}
case GLFW_KEY_ESC: {
printf("\nGoodbye cruel world...\n");
glfwTerminate();
exit(0);
}
default: {
printf("Key not implemented: %c\n", key);
break;
}
}
}
printf("Exiting keyboard function\n");
}
I tried James' approach from here but to no effect. Are there GLFW flags I am forgetting to set?
EDIT------- 5/20
I had a thought. Perhaps my callback was being unregistered when the window is destroyed. Turns out that re-registering my function when the new window is created made the window responsive.
While this solves my original problem, I now face a new one. I cannot render to the new window properly. I can insert glClear( GL_COLOR_BUFFER_BIT ); into my main loop, which will successfully set a background colour for the new window. For some reason it does not interact with my arrays and buffers to draw the image.
Code with attempted drawing
EDIT------- 5/21
I converted my code to GLFW 3.0.0. The window management is much cleaner (albeit more complex) and I do not have the rendering issue. Probably because I explicitly have to state the current context to use.
I would still like to solve the rendering issue, both out of curiosity and for if/when I return to 2.7.
Right now the event queue of GLFW is not pumped. You must either set the GLFW_AUTO_POLL_EVENTS option so that glfwSwapBuffers pumps the event queue, or you call glfwPollEvents at the start of a main event loop iteration.
I'm working on my first game in Allegro 5, I've got the title menu rendering as such, however I want to add clickable text in the menu. How would I make it so that, when you hover over the text you can click it? I'm thinking having a for statement checking the pixels would be very bad for performance, here's what I have so far:
#include <allegro5\allegro.h>
#include <allegro5\allegro_image.h>
#include <allegro5\allegro_primitives.h>
const int width = 1280;
const int height = 720;
int main(void)
{
al_init();
al_init_primitives_addon();
al_init_image_addon();
ALLEGRO_DISPLAY *display = al_create_display(width, height);
ALLEGRO_BITMAP *title = al_load_bitmap("titlemenu.bmp");
al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_bitmap(title, 0, 0, 0);
al_flip_display();
al_rest(3.0);
al_destroy_display(display);
return 0;
}
I'm using codeblocks on windows XP SP3
To do it "properly," you'd need to use some sort of GUI library. But you can easily create a clickable section of the screen by hardcoding some rectangle's coordinates.
First you'll need to set up your event handling:
ALLEGRO_EVENT_QUEUE *queue;
queue = al_create_event_queue();
al_install_keyboard();
al_register_event_source(queue, al_get_keyboard_event_source());
Without getting into the specifics of event handling (it's an entire topic of its own), here's the relevant bit:
int selection = 0;
while (!selection)
{
ALLEGRO_EVENT event;
al_wait_for_event(queue, &event);
if (event.type == ALLEGRO_EVENT_KEY_UP)
{
if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
selection = MYGAME_QUIT;
}
else if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)
{
if (event.mouse.x >= MYGAME_MENU_X1 && event.mouse.x < MYGAME_MENU_X2 &&
event.mouse.y >= MYGAME_MENU_Y1 && event.mouse.y < MYGAME_MENU_Y2)
{
selection = MYGAME_OPTION1;
}
}
}
There are many ways to improve upon this example... This is just to get you started.
You should carefully read through the documentation regarding event handling and examine the bundled examples and check out the wiki for more information.
PS: Use forward slashes when using file paths, as they are cross platform:
#include <allegro5/allegro.h>
The above isn't true;
You can add the keyboard to your game and then you have 2 options for collision detection, pixel perfect and bounding box, both covered in the allegro 5 wiki. The tick here to create the best menu IMO is to create a loop and timer for the menu, then list the keyboard, mouse, and timer events seperatley. Next make some if statements so the mouse or keyboard event only triggers when you actually click it, this is needed so you can scroll through a menu with both mouse and keypad, or you could just make it so the mouse wont affect the screen until after you click, but former looks better, at least IMO.
So I'm in a situation where I need to know when a top level window gets created. I'm working at the Xlib/Xt level and on a Window Manager that doesn't support the EWMH specification. My idea is to hook into the root window's SubstructureNotify events. But things are not as simple as just that.
The problem is that not every CreateNotify event corresponds to the creation of a [b]top level[/b] window. So what I think I need to do is test the window I get from the event somehow to confirm that it is a top level window. I've got close, but some spurious windows still make it through my net. For example, in a GTK application if you have a dropdown box and you click it, a new window is created that I can't figure out how to catch and ignore. Such a window is troublesomely indistinguishable from a typical top level application window.
Here's what I have so far:
// I am omiting (tons of) cleanup code and where I set the display and toplevel variables.
Display* display;
Widget toplevel;
bool has_name(Window window)
{
XTextProperty data = XTextProperty ();
return (!XGetWMName (display, window, &data));
}
bool has_client_leader(Window window)
{
unsigned long nitems = 0;
unsigned char* data = 0;
Atom actual_type;
int actual_format;
unsigned long bytes;
// WM_CLIENT_LEADER is an interned Atom for the WM_CLIENT_LEADER property
int status = XGetWindowProperty (display, window, WM_CLIENT_LEADER, 0L, (~0L), False,
AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes, &data);
if (status != Success || acutal_type == None) return false;
Window* leader = reinterpret_cast<Window*> (data);
return (*leader != 0);
}
bool has_class(Window window)
{
XClassHint data = XClassHint ();
return (!GetClassHint (display, window, &data));
}
void handle_event(Widget widget, XtPointer, XEvent* event, Boolean*)
{
if (event->type != CreateNotify) return;
Window w = event->xcreatewindow.window;
// confirm window has a name
if (!has_name (w)) return;
// confirm window is a client window
Window client = XmuClientWindow (display, w);
if (!client || client != w) return;
// confirm window has a client leader that is not 0x0
if (!has_client_leader (client)) return;
// confirm window has a class
if (!has_class (client)) return;
// The window has passed all our checks!
// Go on to do stuff with the window ...
}
int main(int argc, char* argv[])
{
// ...
// Setting up the event handler for SubstructureNotify on root window
Window root_window = XDefaultRootWindow (display);
Widget dummy = XtCreateWidget ("dummy", coreWidgetClass, toplevel, 0, 0);
XtRegisterDrawable (display, root_window, dummy);
XSelectInput (display, root_window, SubstructureNotifyMask);
XtAddRawEventHandler (dummy, SubstructureNotifyMask, False, handle_event, 0);
// ...
}
A long shot, but does anyone have any ideas I could try? I can't think of much else I can really do here.
I assume you're familiar with the ICCCM and its long-winded discussion.
Have you checked the WM_TRANSIENT_FOR property?