diff -urN gtk-3.24.52-orig/gdk/x11/gdkdevicemanager-core-x11.c gtk-3.24.52/gdk/x11/gdkdevicemanager-core-x11.c --- gtk-3.24.52-orig/gdk/x11/gdkdevicemanager-core-x11.c 2026-03-23 11:24:49 +0900 +++ gtk-3.24.52/gdk/x11/gdkdevicemanager-core-x11.c 2026-07-03 08:39:57 +0900 @@ -347,6 +347,7 @@ XEvent *xevent) { GdkWindowImplX11 *impl; + GdkToplevelX11 *toplevel; GdkX11DeviceManagerCore *device_manager; GdkWindow *window; gboolean return_val; @@ -391,6 +392,8 @@ } } + toplevel = window ? _gdk_x11_window_get_toplevel (window) : NULL; + /* We do a "manual" conversion of the XEvent to a * GdkEvent. The structures are mostly the same so * the conversion is fairly straightforward. We also @@ -408,6 +411,15 @@ return_val = FALSE; break; } + + /* See notes on repeat handling in KeyRelease. */ + if (toplevel && xevent->xkey.keycode < 256) + { + int byte = xevent->xkey.keycode >> 3; + unsigned int mask = 1 << (xevent->xkey.keycode & 7); + toplevel->valid_repeat_keys[byte] |= mask; + } + translate_key_event (display, device_manager, event, xevent); set_user_time (window, event); break; @@ -422,6 +434,12 @@ /* Emulate detectable auto-repeat by checking to see * if the next event is a key press with the same * keycode and timestamp, and if so, ignoring the event. + * + * Also detect repeating keys which have not been pressed since we + * last gained focus and suppress repeat presses of those keys. + * Among other benefits, this prevents misdirected input attacks + * where an attacker causes a window (such as a file chooser dialog) + * to open while the user has a relevant key (such as Enter) pressed. */ if (!display_x11->have_xkb_autorepeat && XPending (xevent->xkey.display)) @@ -434,6 +452,20 @@ next_event.xkey.keycode == xevent->xkey.keycode && next_event.xkey.time == xevent->xkey.time) { + /* Note that the keycode is an 8-bit value in the + * X11 protocol, so this range test should always pass, + * but we play it safe when indexing memory. + */ + if (toplevel && xevent->xkey.keycode < 256) + { + int byte = xevent->xkey.keycode >> 3; + unsigned int mask = 1 << (xevent->xkey.keycode & 7); + if (!(toplevel->valid_repeat_keys[byte] & mask)) + { + /* This is guaranteed to return the same event. */ + XNextEvent (xevent->xkey.display, &next_event); + } + } return_val = FALSE; break; } @@ -918,6 +950,9 @@ gdk_event_put (event); gdk_event_free (event); + + memset (toplevel->valid_repeat_keys, 0, + sizeof(toplevel->valid_repeat_keys)); } } diff -urN gtk-3.24.52-orig/gdk/x11/gdkdevicemanager-xi2.c gtk-3.24.52/gdk/x11/gdkdevicemanager-xi2.c --- gtk-3.24.52-orig/gdk/x11/gdkdevicemanager-xi2.c 2026-03-23 11:24:49 +0900 +++ gtk-3.24.52/gdk/x11/gdkdevicemanager-xi2.c 2026-07-03 08:43:03 +0900 @@ -1507,6 +1507,7 @@ gboolean return_val = TRUE; GdkWindow *window; GdkWindowImplX11 *impl; + GdkToplevelX11 *toplevel; int scale; XIEvent *ev; @@ -1536,6 +1537,8 @@ scale = impl->window_scale; } + toplevel = window ? _gdk_x11_window_get_toplevel (window) : NULL; + if (ev->evtype == XI_Motion || ev->evtype == XI_ButtonRelease) { @@ -1578,6 +1581,24 @@ xev->sourceid, xev->detail)); + /* Suppress repeat of keys held across a focus change. + * See notes in gdkdevicemanager-core-x11.c for details. + */ + if (toplevel && ev->evtype == XI_KeyPress && xev->detail < 256) + { + int byte = xev->detail >> 3; + unsigned int mask = 1 << (xev->detail & 7); + if (xev->flags & XIKeyRepeat) + { + if (!(toplevel->valid_repeat_keys[byte] & mask)) + return FALSE; + } + else + { + toplevel->valid_repeat_keys[byte] |= mask; + } + } + event->key.type = xev->evtype == XI_KeyPress ? GDK_KEY_PRESS : GDK_KEY_RELEASE; event->key.window = window; diff -urN gtk-3.24.52-orig/gdk/x11/gdkdisplay-x11.c gtk-3.24.52/gdk/x11/gdkdisplay-x11.c --- gtk-3.24.52-orig/gdk/x11/gdkdisplay-x11.c 2026-03-23 11:24:49 +0900 +++ gtk-3.24.52/gdk/x11/gdkdisplay-x11.c 2026-07-03 06:58:44 +0900 @@ -1770,15 +1770,21 @@ XkbAllStateComponentsMask, XkbModifierStateMask|XkbGroupStateMask); + /* Disable XKB detectable autorepeat so we can suppress repeating + * keys on focus change. (Ironically, the XKB implementation of + * "detectable" autorepeat makes repeat presses _un_detectable + * across an input focus change.) See notes at KeyRelease + * handling in gdk_x11_device_manager_core_translate_event(). + */ XkbSetDetectableAutoRepeat (display_x11->xdisplay, - True, + False, &detectable_autorepeat_supported); GDK_NOTE (MISC, g_message ("Detectable autorepeat %s.", detectable_autorepeat_supported ? - "supported" : "not supported")); + "supported (not used)" : "not supported")); - display_x11->have_xkb_autorepeat = detectable_autorepeat_supported; + display_x11->have_xkb_autorepeat = FALSE; } } } diff -urN gtk-3.24.52-orig/gdk/x11/gdkwindow-x11.h gtk-3.24.52/gdk/x11/gdkwindow-x11.h --- gtk-3.24.52-orig/gdk/x11/gdkwindow-x11.h 2026-03-23 11:24:49 +0900 +++ gtk-3.24.52/gdk/x11/gdkwindow-x11.h 2026-07-03 06:46:18 +0900 @@ -169,6 +169,9 @@ /* Constrained edge information */ guint edge_constraints; + /* Bitmap of keys which have been pressed since the last FocusIn event */ + guint8 valid_repeat_keys[256/8]; + #ifdef HAVE_XSYNC XID update_counter; XID extended_update_counter;