From 79285bd63ee9834c2e77ff9639d14a4cd6b7c3c8 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sat, 17 Sep 2022 18:53:00 +0100 Subject: [PATCH] gdkevents: Don't ignore modifiers that are not currently active The X11 backend can mark modifiers like Shift as consumed even if they aren't actually active, which seems to be something to do with making shortcuts like `plus` and `plus` work as intended regardless of whether the plus symbol is obtained by pressing Shift and a key (like `+/=` on American, British or French keyboards) or not (like `*/+` on German keyboards). However, this can go badly wrong when the modifier is *not* pressed. For example, terminals normally have separate bindings for `c` (send SIGINT) and `c` (copy). If we disregard the consumed modifiers completely, when the X11 backend marks Shift as consumed, pressing Ctrl+c would send SIGINT *and* copy to the clipboard, which is not what was intended. By masking out the members of `consumed` that are not in `state`, we get the same interpretation for X11 and Wayland, and ensure that keyboard shortcuts that explicitly mention Shift can only be triggered while holding Shift. It continues to be possible to trigger keyboard shortcuts that do not explicitly mention Shift (such as `plus`) while holding Shift, if the backend reports Shift as having been consumed in order to generate the plus keysym. Resolves: https://gitlab.gnome.org/GNOME/gtk/-/issues/5095 Bug-Debian: https://bugs.debian.org/1016927 Signed-off-by: Simon McVittie --- gdk/gdkevents.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 4b61715575..0daf475c3f 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -1773,7 +1773,7 @@ gdk_key_event_matches (GdkEvent *event, guint ev_keyval; int layout; int level; - GdkModifierType consumed_modifiers; + GdkModifierType ignored_modifiers; GdkModifierType shift_group_mask; gboolean group_mod_is_accel_mod = FALSE; const GdkModifierType mask = GDK_CONTROL_MASK | @@ -1792,7 +1792,23 @@ gdk_key_event_matches (GdkEvent *event, ev_keyval = self->translated[1].keyval; layout = self->translated[1].layout; level = self->translated[1].level; - consumed_modifiers = self->translated[1].consumed; + + /* + * If a modifier is currently active (e.g. Shift is pressed) and was marked + * as consumed, we ignore it for the purposes of matching shortcuts. + * For example, when Ctrl+Shift+[plus/equals key] is translated into + * Ctrl+plus on a keyboard where Shift+equals is the plus sign, we want + * shortcuts for either plus or plus to match. + * (See https://bugzilla.gnome.org/show_bug.cgi?id=100439) + * + * If a modifier is *not* currently active, the X11 backend can sometimes + * mark it as consumed where the Wayland and Windows backends do not. + * In this case, we still want to pay attention to its state. + * For example, when Ctrl+x is translated into Ctrl+x, we only want to + * trigger shortcuts for x, not for x. + * (See https://gitlab.gnome.org/GNOME/gtk/-/issues/5095) + */ + ignored_modifiers = (self->translated[1].consumed & state); /* if the group-toggling modifier is part of the default accel mod * mask, and it is active, disable it for matching @@ -1804,7 +1820,7 @@ gdk_key_event_matches (GdkEvent *event, if (mask & shift_group_mask) group_mod_is_accel_mod = TRUE; - if ((modifiers & ~consumed_modifiers & mask) == (state & ~consumed_modifiers & mask)) + if ((modifiers & ~ignored_modifiers & mask) == (state & ~ignored_modifiers & mask)) { /* modifier match */ GdkKeymapKey *keys;