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.
This commit is contained in:
Matthias Clasen 2020-04-04 17:51:32 -04:00
parent 1149502767
commit e8330c5eec
7 changed files with 191 additions and 141 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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;