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.
This commit is contained in:
Erik S. V. Jansson 2016-06-27 19:12:07 +02:00
parent 30111efc6d
commit 8f6da9008e
3 changed files with 31 additions and 0 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);
}