Fixed bug 1990 - focus/keyboard events not generated correctly for multiple windows
authorSam Lantinga <slouken@libsdl.org>
Sun, 03 Nov 2013 09:55:27 -0800
changeset 79020c2e2b91eeea
parent 7901 ae8600f0916a
child 7903 4d3e6191c807
Fixed bug 1990 - focus/keyboard events not generated correctly for multiple windows

Mai Lavelle

I've recently tried to create multiple windows and process key events for them, and found that key events weren't being generated for most of the windows. After some investigating I've observed the following effects. All but the most recently created window experience these effects...

- a focus lost event is generated immediately after the focus gained event, even tho window still has focus
- key events report window id 0 rather than the id of the window which has focus, SDL thinks no window has focus?
- giving focus to a non SDL window and then selecting an SDL window causes events to be generated as expected, but only until focus changes again

Focus change events are queued and delayed (200 ticks) before they are dispatched. The problem occurs when a focus out and focus in event are received on the same tick. When these delayed events are dispatched they will be sent in the order determined by the window list rather than the order in which they are received.

The focus out dispatch is implemented by calling SDL_SetKeyboardFocus(NULL). This will remove focus from any window, regardless of whether it is the one originally targeted by the X11 event.

Since SDL_SetKeyboardFocus() will always dispatch a focus lost event as needed, the easiest solution is simply to only call SDL_SetKeyboardFocus(NULL) when SDL_GetKeyboardFocus() matches the target window.
src/video/x11/SDL_x11events.c
     1.1 --- a/src/video/x11/SDL_x11events.c	Sun Nov 03 09:42:23 2013 -0800
     1.2 +++ b/src/video/x11/SDL_x11events.c	Sun Nov 03 09:55:27 2013 -0800
     1.3 @@ -245,7 +245,13 @@
     1.4  #ifdef DEBUG_XEVENTS
     1.5      printf("window %p: Dispatching FocusOut\n", data);
     1.6  #endif
     1.7 -    SDL_SetKeyboardFocus(NULL);
     1.8 +    /* If another window has already processed a focus in, then don't try to
     1.9 +     * remove focus here.  Doing so will incorrectly remove focus from that
    1.10 +     * window, and the focus lost event for this window will have already
    1.11 +     * been dispatched anyway. */
    1.12 +    if (data->window == SDL_GetKeyboardFocus()) {
    1.13 +        SDL_SetKeyboardFocus(NULL);
    1.14 +    }
    1.15  #ifdef X_HAVE_UTF8_STRING
    1.16      if (data->ic) {
    1.17          X11_XUnsetICFocus(data->ic);