From 8f6da9008ee5cfd27eb937edd5ac44629d2856f8 Mon Sep 17 00:00:00 2001 From: "Erik S. V. Jansson" Date: Mon, 27 Jun 2016 19:12:07 +0200 Subject: [PATCH] X11: Fixed XSetInputFocus before VisibilityNotify Should resolve glfw/glfw#789 issue, where calls to glfwCreateWindow might fail on non-reparenting window managers (e.g. dwm and xmonad) since they don't set their visibility flag, right after XMapWindow. Attempting to print 'map_state' right before XSetInputFocus results either in IsUnmapped or IsUnviewable, on the tested WM (dwm 6.1-3). Which gives BadMatch errors on XSetInputFocus as shown in Xlib doc. Hasn't been tested on any additional X11 window managers as of yet. --- README.md | 1 + src/x11_platform.h | 1 + src/x11_window.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/README.md b/README.md index f488584af..de09a7fca 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ does not find Doxygen, the documentation will not be generated. ## Changelog + - [X11] Bugfix: Didn't wait until window was visible before setting focus (#789) - Bugfix: Single compilation unit builds failed due to naming conflicts (#783) - Bugfix: The range checks for `glfwSetCursorPos` used the wrong minimum (#773) - [Win32] Bugfix: `glfwSetClipboardString` created an unneccessary intermediate diff --git a/src/x11_platform.h b/src/x11_platform.h index 7b4602545..b48968f64 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -112,6 +112,7 @@ typedef struct _GLFWwindowX11 XIC ic; GLFWbool overrideRedirect; + GLFWbool visuallyMapped; // Cached position and size used to filter out duplicate events int width, height; diff --git a/src/x11_window.c b/src/x11_window.c index 1bcab263d..36d915f41 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -604,6 +604,7 @@ static GLFWbool createWindow(_GLFWwindow* window, _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); + window->x11.visuallyMapped = GLFW_FALSE; // Assume window isn't mapped yet. return GLFW_TRUE; } @@ -1365,6 +1366,29 @@ static void processEvent(XEvent *event) return; } + case VisibilityNotify: + { + int visibilityState = event->xvisibility.state; + // Some window managers (mostly non-reparenting + // like dwm, xmonad, ratpoision, openbox, cwm), + // don't set the visibility flag after mapping, + // a pre-condition for e.g. XSetInputFocus()... + // leads to BadMatch error as described in X11. + // Most of these will however notify with event + // VisibilityFullyObscured on visibility unset. + if (visibilityState != VisibilityFullyObscured) + window->x11.visuallyMapped = GLFW_TRUE; + return; + } + + case UnmapNotify: + { + // Should be the only case when a window + // becomes completely visually unmapped. + window->x11.visuallyMapped = GLFW_FALSE; + return; + } + case FocusIn: { if (window->cursorMode == GLFW_CURSOR_DISABLED) @@ -1925,6 +1949,11 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window) else { XRaiseWindow(_glfw.x11.display, window->x11.handle); + // Requested window might not have been completely initialized + // by the window manager at this point, need to wait until the + // window has both been mapped and also set a visibility flag. + // Wait for VisibilityNotify X11 event before setting a focus. + while (!window->x11.visuallyMapped) _glfwPlatformWaitEvents(); XSetInputFocus(_glfw.x11.display, window->x11.handle, RevertToParent, CurrentTime); }