Commit 2bfe81e4 authored by Arkadiusz Hiler's avatar Arkadiusz Hiler Committed by Alexandre Julliard

winex11.drv: Send missed KEYUP events on KeymapNotify.

Full focus lost / focus gained events on the Windows side are not feasible for X11's FocusIn/FocusOut events generated by keyboard grabs (see XGrabKeyboard()) that are used for example for Atl+Tab handling. Using them would degrade user's experience by causing the window to minimize or flash multiple times depending on a game/window manager combo. Because of that the programs may miss on some KEYUP events that happen during the grab, and since there are no focus changes on the Windows side the state doesn't get resynced. This change attempts to improve user experience by syncing any missed key release events that happened while the window haven't had focus on the X11 side. There's no syncing of key presses as those are more problematic because of window manager quirks, e.g. on KDE it may end up syncing the Tab press portion of Alt+Tab. Luckily missing key events for keys that were pressed and not released while the WM had the keyboard grab is not nearly as confusing as stuck keys. For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal and many other games that end up with stuck Alt after Alt+Tabbing.
parent 6631e6bc
...@@ -775,6 +775,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) ...@@ -775,6 +775,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev )
if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window(); if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window();
if (hwnd == NtUserGetDesktopWindow()) return FALSE; if (hwnd == NtUserGetDesktopWindow()) return FALSE;
x11drv_thread_data()->keymapnotify_hwnd = hwnd;
/* when keyboard grab is released, re-apply the cursor clipping rect */ /* when keyboard grab is released, re-apply the cursor clipping rect */
was_grabbed = keyboard_grabbed; was_grabbed = keyboard_grabbed;
keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed;
......
...@@ -1192,11 +1192,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) ...@@ -1192,11 +1192,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event )
int i, j; int i, j;
BYTE keystate[256]; BYTE keystate[256];
WORD vkey; WORD vkey;
DWORD flags;
KeyCode keycode;
HWND keymapnotify_hwnd;
BOOL changed = FALSE; BOOL changed = FALSE;
struct { struct {
WORD vkey; WORD vkey;
WORD scan;
WORD pressed; WORD pressed;
} keys[256]; } keys[256];
struct x11drv_thread_data *thread_data = x11drv_thread_data();
keymapnotify_hwnd = thread_data->keymapnotify_hwnd;
thread_data->keymapnotify_hwnd = NULL;
if (!get_async_key_state( keystate )) return FALSE; if (!get_async_key_state( keystate )) return FALSE;
...@@ -1211,11 +1219,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) ...@@ -1211,11 +1219,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event )
{ {
for (j = 0; j < 8; j++) for (j = 0; j < 8; j++)
{ {
vkey = keyc2vkey[(i * 8) + j]; keycode = (i * 8) + j;
vkey = keyc2vkey[keycode];
/* If multiple keys map to the same vkey, we want to report it as /* If multiple keys map to the same vkey, we want to report it as
* pressed iff any of them are pressed. */ * pressed iff any of them are pressed. */
if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; if (!keys[vkey & 0xff].vkey)
{
keys[vkey & 0xff].vkey = vkey;
keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff;
}
if (event->xkeymap.key_vector[i] & (1<<j)) keys[vkey & 0xff].pressed = TRUE; if (event->xkeymap.key_vector[i] & (1<<j)) keys[vkey & 0xff].pressed = TRUE;
} }
} }
...@@ -1227,6 +1241,31 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) ...@@ -1227,6 +1241,31 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event )
TRACE( "Adjusting state for vkey %#.2x. State before %#.2x\n", TRACE( "Adjusting state for vkey %#.2x. State before %#.2x\n",
keys[vkey].vkey, keystate[vkey]); keys[vkey].vkey, keystate[vkey]);
/* This KeymapNotify follows a FocusIn(mode=NotifyUngrab) event,
* which is caused by a keyboard grab being released.
* See XGrabKeyboard().
*
* We might have missed some key press/release events while the
* keyboard was grabbed, but keyboard grab doesn't generate focus
* lost / focus gained events on the Windows side, so the affected
* program is not aware that it needs to resync the keyboard state.
*
* This, for example, may cause Alt being stuck after using Alt+Tab.
*
* To work around that problem we sync any possible key releases.
*
* Syncing key presses is not feasible as window managers differ in
* event sequences, e.g. KDE performs two keyboard grabs for
* Alt+Tab, which would sync the Tab press.
*/
if (keymapnotify_hwnd && !keys[vkey].pressed)
{
TRACE( "Sending KEYUP for a modifier %#.2x\n", vkey);
flags = KEYEVENTF_KEYUP;
if (keys[vkey].vkey & 0x1000) flags |= KEYEVENTF_EXTENDEDKEY;
X11DRV_send_keyboard_input( keymapnotify_hwnd, vkey, keys[vkey].scan, flags, NtGetTickCount() );
}
update_key_state( keystate, vkey, keys[vkey].pressed ); update_key_state( keystate, vkey, keys[vkey].pressed );
changed = TRUE; changed = TRUE;
} }
......
...@@ -1611,6 +1611,8 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev ) ...@@ -1611,6 +1611,8 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
TRACE( "hwnd %p/%lx pos %d,%d detail %d\n", hwnd, event->window, event->x, event->y, event->detail ); TRACE( "hwnd %p/%lx pos %d,%d detail %d\n", hwnd, event->window, event->x, event->y, event->detail );
x11drv_thread_data()->keymapnotify_hwnd = hwnd;
if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE;
/* simulate a mouse motion event */ /* simulate a mouse motion event */
......
...@@ -382,6 +382,7 @@ struct x11drv_thread_data ...@@ -382,6 +382,7 @@ struct x11drv_thread_data
XEvent *current_event; /* event currently being processed */ XEvent *current_event; /* event currently being processed */
HWND grab_hwnd; /* window that currently grabs the mouse */ HWND grab_hwnd; /* window that currently grabs the mouse */
HWND last_focus; /* last window that had focus */ HWND last_focus; /* last window that had focus */
HWND keymapnotify_hwnd; /* window that should receive modifier release events */
XIM xim; /* input method */ XIM xim; /* input method */
HWND last_xic_hwnd; /* last xic window */ HWND last_xic_hwnd; /* last xic window */
XFontSet font_set; /* international text drawing font set */ XFontSet font_set; /* international text drawing font set */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment