From e8330c5eec5067d760973bd2628660771c95f142 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 4 Apr 2020 17:51:32 -0400 Subject: [PATCH] Add gdk_event_matches Move the elaborate key event matching code from GtkShortcutTrigger to GdkEvent, which greatly reduces the amount of keymap api use outside of GDK. --- docs/reference/gtk/gtk4-sections.txt | 1 - gdk/gdkevents.c | 156 +++++++++++++++++++++++++++ gdk/gdkevents.h | 11 ++ gtk/gtkshortcutcontroller.c | 6 +- gtk/gtkshortcuttrigger.c | 123 +++------------------ gtk/gtkshortcuttrigger.h | 21 +--- testsuite/gtk/shortcuts.c | 14 +-- 7 files changed, 191 insertions(+), 141 deletions(-) diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index e944c9256f..9b085828f1 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -5960,7 +5960,6 @@ gtk_event_controller_motion_get_type GtkShortcutTrigger GtkNeverTrigger -GtkShortcutTriggerMatch gtk_shortcut_trigger_trigger gtk_shortcut_trigger_hash gtk_shortcut_trigger_equal diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 097198ef29..1d53d9c30b 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -2110,3 +2110,159 @@ gdk_grab_broken_event_get_grab_surface (GdkEvent *event) return event->grab_broken.grab_surface; } + +static gboolean +translate_keyboard_accel_state (GdkKeymap *keymap, + guint hardware_keycode, + GdkModifierType state, + gint group, + guint *keyval, + gint *effective_group, + gint *level, + GdkModifierType *consumed_modifiers) +{ + GdkModifierType mask; + GdkModifierType shift_group_mask; + gboolean group_mask_disabled = FALSE; + gboolean retval; + + mask = gdk_keymap_get_modifier_mask (keymap, + GDK_MODIFIER_INTENT_DEFAULT_MOD_MASK); + + /* if the group-toggling modifier is part of the accel mod mask, and + * it is active, disable it for matching + */ + shift_group_mask = gdk_keymap_get_modifier_mask (keymap, + GDK_MODIFIER_INTENT_SHIFT_GROUP); + if (mask & state & shift_group_mask) + { + state &= ~shift_group_mask; + group = 0; + group_mask_disabled = TRUE; + } + + retval = gdk_keymap_translate_keyboard_state (keymap, + hardware_keycode, state, group, + keyval, + effective_group, level, + consumed_modifiers); + + /* add back the group mask, we want to match against the modifier, + * but not against the keyval from its group + */ + if (group_mask_disabled) + { + if (effective_group) + *effective_group = 1; + + if (consumed_modifiers) + *consumed_modifiers &= ~shift_group_mask; + } + + return retval; +} + + +GdkEventMatch +gdk_event_matches (GdkEvent *event, + guint match_keyval, + GdkModifierType match_modifiers) +{ + guint keycode; + GdkModifierType state; + GdkModifierType mask; + int group; + GdkKeymap *keymap; + guint keyval; + int effective_group; + int level; + GdkModifierType consumed_modifiers; + GdkModifierType shift_group_mask; + gboolean group_mod_is_accel_mod = FALSE; + const GdkModifierType xmods = GDK_MOD2_MASK|GDK_MOD3_MASK|GDK_MOD4_MASK|GDK_MOD5_MASK; + const GdkModifierType vmods = GDK_SUPER_MASK|GDK_HYPER_MASK|GDK_META_MASK; + GdkModifierType modifiers; + + if (gdk_event_get_event_type (event) != GDK_KEY_PRESS) + return GDK_EVENT_MATCH_NONE; + + keycode = gdk_key_event_get_keycode (event); + state = gdk_event_get_modifier_state (event); + group = gdk_key_event_get_group (event); + keymap = gdk_display_get_keymap (gdk_event_get_display (event)); + + mask = gdk_keymap_get_modifier_mask (keymap, + GDK_MODIFIER_INTENT_DEFAULT_MOD_MASK); + + /* We don't want Caps_Lock to affect keybinding lookups. + */ + state &= ~GDK_LOCK_MASK; + + translate_keyboard_accel_state (keymap, + keycode, state, group, + &keyval, + &effective_group, &level, + &consumed_modifiers); + + /* if the group-toggling modifier is part of the default accel mod + * mask, and it is active, disable it for matching + */ + shift_group_mask = gdk_keymap_get_modifier_mask (keymap, + GDK_MODIFIER_INTENT_SHIFT_GROUP); + if (mask & shift_group_mask) + group_mod_is_accel_mod = TRUE; + + gdk_keymap_map_virtual_modifiers (keymap, &mask); + gdk_keymap_add_virtual_modifiers (keymap, &state); + + modifiers = match_modifiers; + if (gdk_keymap_map_virtual_modifiers (keymap, &modifiers) && + ((modifiers & ~consumed_modifiers & mask & ~vmods) == (state & ~consumed_modifiers & mask & ~vmods) || + (modifiers & ~consumed_modifiers & mask & ~xmods) == (state & ~consumed_modifiers & mask & ~xmods))) + { + /* modifier match */ + GdkKeymapKey *keys; + int n_keys; + int i; + guint key; + + /* Shift gets consumed and applied for the event, + * so apply it to our keyval to match + */ + key = match_keyval; + if (match_modifiers & GDK_SHIFT_MASK) + { + if (key == GDK_KEY_Tab) + key = GDK_KEY_ISO_Left_Tab; + else + key = gdk_keyval_to_upper (key); + } + + if (keyval == key && /* exact match */ + (!group_mod_is_accel_mod || + (state & shift_group_mask) == (match_modifiers & shift_group_mask))) + return GDK_EVENT_MATCH_EXACT; + + gdk_keymap_get_entries_for_keyval (keymap, match_keyval, &keys, &n_keys); + + for (i = 0; i < n_keys; i++) + { + if (keys[i].keycode == keycode && + keys[i].level == level && + /* Only match for group if it's an accel mod */ + (!group_mod_is_accel_mod || + keys[i].group == effective_group)) + { + /* partial match */ + g_free (keys); + + return GDK_EVENT_MATCH_PARTIAL; + } + } + + g_free (keys); + } + + + return GDK_EVENT_MATCH_NONE; +} diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h index 888ca9e5c5..944047d946 100644 --- a/gdk/gdkevents.h +++ b/gdk/gdkevents.h @@ -428,6 +428,17 @@ gboolean gdk_events_get_center (GdkEvent *event1, double *x, double *y); +typedef enum { + GDK_EVENT_MATCH_NONE, + GDK_EVENT_MATCH_PARTIAL, + GDK_EVENT_MATCH_EXACT +} GdkEventMatch; + +GDK_AVAILABLE_IN_ALL +GdkEventMatch gdk_event_matches (GdkEvent *event, + guint keyval, + GdkModifierType modifiers); + G_END_DECLS #endif /* __GDK_EVENTS_H__ */ diff --git a/gtk/gtkshortcutcontroller.c b/gtk/gtkshortcutcontroller.c index 8fa081f977..9a817ac286 100644 --- a/gtk/gtkshortcutcontroller.c +++ b/gtk/gtkshortcutcontroller.c @@ -313,16 +313,16 @@ gtk_shortcut_controller_run_controllers (GtkEventController *controller, switch (gtk_shortcut_trigger_trigger (gtk_shortcut_get_trigger (shortcut), event, enable_mnemonics)) { - case GTK_SHORTCUT_TRIGGER_MATCH_PARTIAL: + case GDK_EVENT_MATCH_PARTIAL: if (!has_exact) break; G_GNUC_FALLTHROUGH; - case GTK_SHORTCUT_TRIGGER_MATCH_NONE: + case GDK_EVENT_MATCH_NONE: g_object_unref (shortcut); continue; - case GTK_SHORTCUT_TRIGGER_MATCH_EXACT: + case GDK_EVENT_MATCH_EXACT: if (!has_exact) { g_slist_free_full (shortcuts, shortcut_data_free); diff --git a/gtk/gtkshortcuttrigger.c b/gtk/gtkshortcuttrigger.c index 722a41fafd..ad58a14c80 100644 --- a/gtk/gtkshortcuttrigger.c +++ b/gtk/gtkshortcuttrigger.c @@ -59,8 +59,7 @@ struct _GtkShortcutTriggerClass { GObjectClass parent_class; - GtkShortcutTriggerMatch - (* trigger) (GtkShortcutTrigger *trigger, + GdkEventMatch (* trigger) (GtkShortcutTrigger *trigger, GdkEvent *event, gboolean enable_mnemonics); guint (* hash) (GtkShortcutTrigger *trigger); @@ -97,12 +96,12 @@ gtk_shortcut_trigger_init (GtkShortcutTrigger *self) * * Returns: Whether the event triggered the shortcut **/ -GtkShortcutTriggerMatch +GdkEventMatch gtk_shortcut_trigger_trigger (GtkShortcutTrigger *self, GdkEvent *event, gboolean enable_mnemonics) { - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (self), GTK_SHORTCUT_TRIGGER_MATCH_NONE); + g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (self), GDK_EVENT_MATCH_NONE); return GTK_SHORTCUT_TRIGGER_GET_CLASS (self)->trigger (self, event, enable_mnemonics); } @@ -410,12 +409,12 @@ gtk_never_trigger_finalize (GObject *gobject) G_OBJECT_CLASS (gtk_never_trigger_parent_class)->finalize (gobject); } -static GtkShortcutTriggerMatch +static GdkEventMatch gtk_never_trigger_trigger (GtkShortcutTrigger *trigger, GdkEvent *event, gboolean enable_mnemonics) { - return GTK_SHORTCUT_TRIGGER_MATCH_NONE; + return GDK_EVENT_MATCH_NONE; } static guint @@ -510,110 +509,14 @@ enum static GParamSpec *keyval_props[KEYVAL_N_PROPS]; -static GtkShortcutTriggerMatch +static GdkEventMatch gtk_keyval_trigger_trigger (GtkShortcutTrigger *trigger, GdkEvent *event, gboolean enable_mnemonics) { GtkKeyvalTrigger *self = GTK_KEYVAL_TRIGGER (trigger); - guint keycode; - GdkModifierType state; - GdkModifierType mask; - int group; - GdkKeymap *keymap; - guint keyval; - int effective_group; - int level; - GdkModifierType consumed_modifiers; - GdkModifierType shift_group_mask; - gboolean group_mod_is_accel_mod = FALSE; - const GdkModifierType xmods = GDK_MOD2_MASK|GDK_MOD3_MASK|GDK_MOD4_MASK|GDK_MOD5_MASK; - const GdkModifierType vmods = GDK_SUPER_MASK|GDK_HYPER_MASK|GDK_META_MASK; - GdkModifierType modifiers; - if (gdk_event_get_event_type (event) != GDK_KEY_PRESS) - return GTK_SHORTCUT_TRIGGER_MATCH_NONE; - - mask = gtk_accelerator_get_default_mod_mask (); - - keycode = gdk_key_event_get_keycode (event); - state = gdk_event_get_modifier_state (event); - group = gdk_key_event_get_group (event); - keymap = gdk_display_get_keymap (gdk_event_get_display (event)); - - /* We don't want Caps_Lock to affect keybinding lookups. - */ - state &= ~GDK_LOCK_MASK; - - _gtk_translate_keyboard_accel_state (keymap, - keycode, state, mask, group, - &keyval, - &effective_group, &level, - &consumed_modifiers); - - /* if the group-toggling modifier is part of the default accel mod - * mask, and it is active, disable it for matching - */ - shift_group_mask = gdk_keymap_get_modifier_mask (keymap, - GDK_MODIFIER_INTENT_SHIFT_GROUP); - if (mask & shift_group_mask) - group_mod_is_accel_mod = TRUE; - - gdk_keymap_map_virtual_modifiers (keymap, &mask); - gdk_keymap_add_virtual_modifiers (keymap, &state); - - modifiers = self->modifiers; - if (gdk_keymap_map_virtual_modifiers (keymap, &modifiers) && - ((modifiers & ~consumed_modifiers & mask & ~vmods) == (state & ~consumed_modifiers & mask & ~vmods) || - (modifiers & ~consumed_modifiers & mask & ~xmods) == (state & ~consumed_modifiers & mask & ~xmods))) - { - /* modifier match */ - GdkKeymapKey *keys; - int n_keys; - int i; - guint key; - - /* Shift gets consumed and applied for the event, - * so apply it to our keyval to match - */ - key = self->keyval; - if (self->modifiers & GDK_SHIFT_MASK) - { - if (key == GDK_KEY_Tab) - key = GDK_KEY_ISO_Left_Tab; - else - key = gdk_keyval_to_upper (key); - } - - if (keyval == key && /* exact match */ - (!group_mod_is_accel_mod || - (state & shift_group_mask) == (self->modifiers & shift_group_mask))) - return GTK_SHORTCUT_TRIGGER_MATCH_EXACT; - - gdk_keymap_get_entries_for_keyval (keymap, - self->keyval, - &keys, &n_keys); - - for (i = 0; i < n_keys; i++) - { - if (keys[i].keycode == keycode && - keys[i].level == level && - /* Only match for group if it's an accel mod */ - (!group_mod_is_accel_mod || - keys[i].group == effective_group)) - { - /* partial match */ - g_free (keys); - - return GTK_SHORTCUT_TRIGGER_MATCH_PARTIAL; - } - } - - g_free (keys); - } - - - return GTK_SHORTCUT_TRIGGER_MATCH_NONE; + return gdk_event_matches (event, self->keyval, self->modifiers); } static guint @@ -849,7 +752,7 @@ enum static GParamSpec *mnemonic_props[MNEMONIC_N_PROPS]; -static GtkShortcutTriggerMatch +static GdkEventMatch gtk_mnemonic_trigger_trigger (GtkShortcutTrigger *trigger, GdkEvent *event, gboolean enable_mnemonics) @@ -858,10 +761,10 @@ gtk_mnemonic_trigger_trigger (GtkShortcutTrigger *trigger, guint keyval; if (!enable_mnemonics) - return GTK_SHORTCUT_TRIGGER_MATCH_NONE; + return GDK_EVENT_MATCH_NONE; if (gdk_event_get_event_type (event) != GDK_KEY_PRESS) - return GTK_SHORTCUT_TRIGGER_MATCH_NONE; + return GDK_EVENT_MATCH_NONE; /* XXX: This needs to deal with groups */ keyval = gdk_key_event_get_keyval (event); @@ -872,9 +775,9 @@ gtk_mnemonic_trigger_trigger (GtkShortcutTrigger *trigger, keyval = gdk_keyval_to_lower (keyval); if (keyval != self->keyval) - return GTK_SHORTCUT_TRIGGER_MATCH_NONE; + return GDK_EVENT_MATCH_NONE; - return GTK_SHORTCUT_TRIGGER_MATCH_EXACT; + return GDK_EVENT_MATCH_EXACT; } static guint @@ -1085,7 +988,7 @@ gtk_alternative_trigger_dispose (GObject *gobject) G_OBJECT_CLASS (gtk_alternative_trigger_parent_class)->dispose (gobject); } -static GtkShortcutTriggerMatch +static GdkEventMatch gtk_alternative_trigger_trigger (GtkShortcutTrigger *trigger, GdkEvent *event, gboolean enable_mnemonics) diff --git a/gtk/gtkshortcuttrigger.h b/gtk/gtkshortcuttrigger.h index eba7bde4f4..36f092339d 100644 --- a/gtk/gtkshortcuttrigger.h +++ b/gtk/gtkshortcuttrigger.h @@ -36,25 +36,6 @@ G_BEGIN_DECLS * A trigger for a key shortcut. */ -/** - * GtkShortcutTriggerMatch: - * @GTK_SHORTCUT_TRIGGER_MATCH_NONE: The key event does not - * match the trigger - * @GTK_SHORTCUT_TRIGGER_MATCH_PARTIAL: The key event matches - * the trigger if keyboard state (specifically, the currently - * active group) is ignored - * @GTK_SHORTCUT_TRIGGER_MATCH_EXACT: The key event matches - * the trigger - * - * The possible return values from gtk_shortcut_trigger_trigger() - * describe if a key event triggers a shortcut. - */ -typedef enum { - GTK_SHORTCUT_TRIGGER_MATCH_NONE, - GTK_SHORTCUT_TRIGGER_MATCH_PARTIAL, - GTK_SHORTCUT_TRIGGER_MATCH_EXACT, -} GtkShortcutTriggerMatch; - GDK_AVAILABLE_IN_ALL GDK_DECLARE_INTERNAL_TYPE (GtkShortcutTrigger, gtk_shortcut_trigger, GTK, SHORTCUT_TRIGGER, GObject) @@ -84,7 +65,7 @@ gint gtk_shortcut_trigger_compare (gconstpointer gconstpointer trigger2); GDK_AVAILABLE_IN_ALL -GtkShortcutTriggerMatch gtk_shortcut_trigger_trigger (GtkShortcutTrigger *self, +GdkEventMatch gtk_shortcut_trigger_trigger (GtkShortcutTrigger *self, GdkEvent *event, gboolean enable_mnemonics); diff --git a/testsuite/gtk/shortcuts.c b/testsuite/gtk/shortcuts.c index 916a8f0424..174d3aba87 100644 --- a/testsuite/gtk/shortcuts.c +++ b/testsuite/gtk/shortcuts.c @@ -340,14 +340,14 @@ test_trigger_trigger (void) guint keyval; GdkModifierType state; gboolean mnemonic; - GtkShortcutTriggerMatch result[4]; + GdkEventMatch result[4]; } tests[] = { - { GDK_KEY_a, GDK_CONTROL_MASK, FALSE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_EXACT, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_EXACT } }, - { GDK_KEY_a, GDK_CONTROL_MASK, TRUE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_EXACT, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_EXACT } }, - { GDK_KEY_a, GDK_SHIFT_MASK, FALSE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE } }, - { GDK_KEY_a, GDK_SHIFT_MASK, TRUE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE } }, - { GDK_KEY_u, GDK_SHIFT_MASK, FALSE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE } }, - { GDK_KEY_u, GDK_SHIFT_MASK, TRUE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_EXACT, GTK_SHORTCUT_TRIGGER_MATCH_EXACT } }, + { GDK_KEY_a, GDK_CONTROL_MASK, FALSE, { GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_EXACT, GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_EXACT } }, + { GDK_KEY_a, GDK_CONTROL_MASK, TRUE, { GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_EXACT, GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_EXACT } }, + { GDK_KEY_a, GDK_SHIFT_MASK, FALSE, { GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE } }, + { GDK_KEY_a, GDK_SHIFT_MASK, TRUE, { GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE } }, + { GDK_KEY_u, GDK_SHIFT_MASK, FALSE, { GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE } }, + { GDK_KEY_u, GDK_SHIFT_MASK, TRUE, { GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_NONE, GDK_EVENT_MATCH_EXACT, GDK_EVENT_MATCH_EXACT } }, }; int i, j;