gtk2/testsuite/gtk/shortcuts.c
Matthias Clasen 0a96a483c6 gdk: Redo key events
Add all of the keyboard translation results in the key event,
so we can translate the keyboard state at the time the event
is created, and avoid doing state translation at match time.

We actually need to carry two sets of translation results,
since we ignore CapsLock when matching accelerators, in
gdk_event_matches().

At the same time, drop the scancode field - it is only ever
set on win32, and is basically unused in GTK.

Update all callers.
2020-04-06 15:13:54 -04:00

498 lines
15 KiB
C

#include <gtk/gtk.h>
typedef struct _GdkEventAny GdkEventAny;
typedef struct _GdkEventKey GdkEventKey;
struct _GdkEventAny
{
int ref_count;
GdkEventType type;
GdkSurface *surface;
guint32 time;
guint16 flags;
guint pointer_emulated : 1;
guint touch_emulating : 1;
guint scroll_is_stop : 1;
guint key_is_modifier : 1;
guint focus_in : 1;
GdkDevice *device;
GdkDevice *source_device;
};
typedef struct {
guint keyval;
GdkModifierType consumed;
guint layout;
guint level;
} GdkTranslatedKey;
struct _GdkEventKey
{
GdkEventAny any;
GdkModifierType state;
guint32 keycode;
GdkTranslatedKey translated[2];
};
static GdkEvent *
gdk_event_key_new (GdkEventType type,
GdkSurface *surface,
GdkDevice *device,
GdkDevice *source_device,
guint32 time,
guint keycode,
GdkModifierType state,
gboolean is_modifier,
GdkTranslatedKey *translated,
GdkTranslatedKey *no_lock);
static GdkEvent *
gdk_event_key_new (GdkEventType type,
GdkSurface *surface,
GdkDevice *device,
GdkDevice *source_device,
guint32 time,
guint keycode,
GdkModifierType state,
gboolean is_modifier,
GdkTranslatedKey *translated,
GdkTranslatedKey *no_lock)
{
GdkEventKey *event;
g_return_val_if_fail (type == GDK_KEY_PRESS ||
type == GDK_KEY_RELEASE, NULL);
event = g_new0 (GdkEventKey, 1);
event->any.ref_count = 1;
event->any.type = type;
event->any.time = time;
event->any.surface = g_object_ref (surface);
event->any.device = g_object_ref (device);
event->any.source_device = g_object_ref (source_device);
event->keycode = keycode;
event->state = state;
event->any.key_is_modifier = is_modifier;
event->translated[0] = *translated;
event->translated[1] = *no_lock;
return (GdkEvent *)event;
}
static void
test_trigger_basic (void)
{
GtkShortcutTrigger *trigger;
trigger = gtk_never_trigger_get ();
trigger = gtk_keyval_trigger_new (GDK_KEY_a, GDK_CONTROL_MASK);
g_assert_cmpint (gtk_keyval_trigger_get_keyval (GTK_KEYVAL_TRIGGER (trigger)), ==, GDK_KEY_a);
g_assert_cmpint (gtk_keyval_trigger_get_modifiers (GTK_KEYVAL_TRIGGER (trigger)), ==, GDK_CONTROL_MASK);
g_object_unref (trigger);
trigger = gtk_mnemonic_trigger_new (GDK_KEY_u);
g_assert_cmpint (gtk_mnemonic_trigger_get_keyval (GTK_MNEMONIC_TRIGGER (trigger)), ==, GDK_KEY_u);
g_object_unref (trigger);
}
static void
test_trigger_equal (void)
{
GtkShortcutTrigger *trigger1, *trigger2, *trigger3, *trigger4;
GtkShortcutTrigger *trigger5, *trigger6, *trigger1a, *trigger2a;
trigger1 = gtk_keyval_trigger_new ('u', GDK_CONTROL_MASK);
trigger2 = g_object_ref (gtk_never_trigger_get ());
trigger3 = gtk_alternative_trigger_new (g_object_ref (trigger1), g_object_ref (trigger2));
trigger4 = gtk_alternative_trigger_new (g_object_ref (trigger2), g_object_ref (trigger1));
trigger5 = gtk_keyval_trigger_new ('u', GDK_SHIFT_MASK);
trigger6 = gtk_mnemonic_trigger_new ('u');
trigger1a = gtk_keyval_trigger_new ('u', GDK_CONTROL_MASK);
trigger2a = g_object_ref (gtk_never_trigger_get ());
g_assert_true (gtk_shortcut_trigger_equal (trigger1, trigger1));
g_assert_true (gtk_shortcut_trigger_equal (trigger2, trigger2));
g_assert_true (gtk_shortcut_trigger_equal (trigger3, trigger3));
g_assert_true (gtk_shortcut_trigger_equal (trigger4, trigger4));
g_assert_true (gtk_shortcut_trigger_equal (trigger5, trigger5));
g_assert_true (gtk_shortcut_trigger_equal (trigger6, trigger6));
g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger2));
g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger3));
g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger4));
g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger5));
g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger6));
g_assert_false (gtk_shortcut_trigger_equal (trigger2, trigger3));
g_assert_false (gtk_shortcut_trigger_equal (trigger2, trigger4));
g_assert_false (gtk_shortcut_trigger_equal (trigger2, trigger5));
g_assert_false (gtk_shortcut_trigger_equal (trigger2, trigger6));
g_assert_false (gtk_shortcut_trigger_equal (trigger3, trigger4));
g_assert_false (gtk_shortcut_trigger_equal (trigger3, trigger5));
g_assert_false (gtk_shortcut_trigger_equal (trigger3, trigger6));
g_assert_false (gtk_shortcut_trigger_equal (trigger4, trigger5));
g_assert_false (gtk_shortcut_trigger_equal (trigger4, trigger6));
g_assert_false (gtk_shortcut_trigger_equal (trigger5, trigger6));
g_assert_true (gtk_shortcut_trigger_equal (trigger1, trigger1a));
g_assert_true (gtk_shortcut_trigger_equal (trigger2, trigger2a));
g_object_unref (trigger1);
g_object_unref (trigger2);
g_object_unref (trigger3);
g_object_unref (trigger4);
g_object_unref (trigger5);
g_object_unref (trigger6);
g_object_unref (trigger1a);
g_object_unref (trigger2a);
}
static void
test_trigger_parse_never (void)
{
GtkShortcutTrigger *trigger;
trigger = gtk_shortcut_trigger_parse_string ("never");
g_assert_true (GTK_IS_NEVER_TRIGGER (trigger));
g_object_unref (trigger);
}
static void
test_trigger_parse_keyval (void)
{
const struct
{
const char *str;
GdkModifierType modifiers;
guint keyval;
int trigger_type;
} tests[] = {
{ "<Primary><Alt>z", GDK_CONTROL_MASK | GDK_ALT_MASK, 'z' },
{ "<Control>U", GDK_CONTROL_MASK, 'u' },
{ "<Hyper>x", GDK_HYPER_MASK, 'x' },
{ "<Meta>y", GDK_META_MASK, 'y' },
{ "KP_7", 0, GDK_KEY_KP_7 },
{ "<Shift>exclam", GDK_SHIFT_MASK, '!' },
};
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
{
g_test_message ("Checking: '%s'", tests[i].str);
GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i].str);
g_assert_true (GTK_IS_KEYVAL_TRIGGER (trigger));
g_assert_cmpint (gtk_keyval_trigger_get_modifiers (GTK_KEYVAL_TRIGGER (trigger)),
==,
tests[i].modifiers);
g_assert_cmpuint (gtk_keyval_trigger_get_keyval (GTK_KEYVAL_TRIGGER (trigger)),
==,
tests[i].keyval);
g_object_unref (trigger);
}
}
static void
test_trigger_parse_mnemonic (void)
{
struct
{
const char *str;
guint keyval;
} tests[] = {
{ "_A", GDK_KEY_a },
{ "_s", GDK_KEY_s },
};
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
{
g_test_message ("Checking: '%s'", tests[i].str);
GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i].str);
g_assert_true (GTK_IS_MNEMONIC_TRIGGER (trigger));
g_assert_cmpuint (gtk_mnemonic_trigger_get_keyval (GTK_MNEMONIC_TRIGGER (trigger)),
==,
tests[i].keyval);
g_object_unref (trigger);
}
}
static void
test_trigger_parse_alternative (void)
{
enum
{
TRIGGER_NEVER,
TRIGGER_KEYVAL,
TRIGGER_MNEMONIC,
TRIGGER_ALTERNATIVE
};
const struct
{
const char *str;
int first;
int second;
} tests[] = {
{ "U|<Primary>U", TRIGGER_KEYVAL, TRIGGER_KEYVAL },
{ "_U|<Shift>u", TRIGGER_MNEMONIC, TRIGGER_KEYVAL },
{ "x|_x|<Primary>x", TRIGGER_KEYVAL, TRIGGER_ALTERNATIVE },
};
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
{
g_test_message ("Checking: '%s'", tests[i].str);
GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i].str);
g_assert_true (GTK_IS_ALTERNATIVE_TRIGGER (trigger));
GtkShortcutTrigger *t1 = gtk_alternative_trigger_get_first (GTK_ALTERNATIVE_TRIGGER (trigger));
switch (tests[i].first)
{
case TRIGGER_NEVER:
g_assert_true (GTK_IS_NEVER_TRIGGER (t1));
break;
case TRIGGER_KEYVAL:
g_assert_true (GTK_IS_KEYVAL_TRIGGER (t1));
break;
case TRIGGER_MNEMONIC:
g_assert_true (GTK_IS_MNEMONIC_TRIGGER (t1));
break;
case TRIGGER_ALTERNATIVE:
g_assert_true (GTK_IS_ALTERNATIVE_TRIGGER (t1));
break;
default:
g_assert_not_reached ();
break;
}
GtkShortcutTrigger *t2 = gtk_alternative_trigger_get_second (GTK_ALTERNATIVE_TRIGGER (trigger));
switch (tests[i].second)
{
case TRIGGER_NEVER:
g_assert_true (GTK_IS_NEVER_TRIGGER (t2));
break;
case TRIGGER_KEYVAL:
g_assert_true (GTK_IS_KEYVAL_TRIGGER (t2));
break;
case TRIGGER_MNEMONIC:
g_assert_true (GTK_IS_MNEMONIC_TRIGGER (t2));
break;
case TRIGGER_ALTERNATIVE:
g_assert_true (GTK_IS_ALTERNATIVE_TRIGGER (t2));
break;
default:
g_assert_not_reached ();
break;
}
g_object_unref (trigger);
}
}
static void
test_trigger_parse_invalid (void)
{
const char *tests[] = {
"<never>",
"Never",
"Foo",
"<Foo>Nyaa",
"never|",
"|never",
};
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
{
g_test_message ("Checking: '%s'", tests[i]);
GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i]);
g_assert_null (trigger);
}
}
static void
test_trigger_trigger (void)
{
GtkShortcutTrigger *trigger[4];
GdkDisplay *display;
GdkSurface *surface;
GdkDevice *device;
GdkEvent *event;
struct {
guint keyval;
GdkModifierType state;
gboolean mnemonic;
GdkKeyMatch result[4];
} tests[] = {
{ GDK_KEY_a, GDK_CONTROL_MASK, FALSE, { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT } },
{ GDK_KEY_a, GDK_CONTROL_MASK, TRUE, { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT } },
{ GDK_KEY_a, GDK_SHIFT_MASK, FALSE, { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE } },
{ GDK_KEY_a, GDK_SHIFT_MASK, TRUE, { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE } },
{ GDK_KEY_u, GDK_SHIFT_MASK, FALSE, { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE } },
{ GDK_KEY_u, GDK_SHIFT_MASK, TRUE, { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT, GDK_KEY_MATCH_EXACT } },
};
int i, j;
trigger[0] = g_object_ref (gtk_never_trigger_get ());
trigger[1] = gtk_keyval_trigger_new (GDK_KEY_a, GDK_CONTROL_MASK);
trigger[2] = gtk_mnemonic_trigger_new (GDK_KEY_u);
trigger[3] = gtk_alternative_trigger_new (g_object_ref (trigger[1]),
g_object_ref (trigger[2]));
display = gdk_display_get_default ();
device = gdk_seat_get_keyboard (gdk_display_get_default_seat (display));
surface = gdk_surface_new_toplevel (display, 100, 100);
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
GdkKeymapKey *keys;
int n_keys;
GdkTranslatedKey translated;
if (!gdk_display_map_keyval (display, tests[i].keyval, &keys, &n_keys))
continue;
translated.keyval = tests[i].keyval;
translated.consumed = 0;
translated.layout = keys[0].group;
translated.level = keys[0].level;
event = gdk_event_key_new (GDK_KEY_PRESS,
surface,
device,
device,
GDK_CURRENT_TIME,
keys[0].keycode,
tests[i].state,
FALSE,
&translated,
&translated);
for (j = 0; j < 4; j++)
{
g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger[j], event, tests[i].mnemonic), ==, tests[i].result[j]);
}
gdk_event_unref (event);
}
g_object_unref (surface);
g_object_unref (trigger[0]);
g_object_unref (trigger[1]);
g_object_unref (trigger[2]);
g_object_unref (trigger[3]);
}
static gboolean
callback (GtkWidget *widget,
GVariant *args,
gpointer user_data)
{
int *callback_count = user_data;
*callback_count += 1;
return TRUE;
}
static void
test_action_basic (void)
{
GtkShortcutAction *action;
action = gtk_signal_action_new ("activate");
g_assert_cmpstr (gtk_signal_action_get_signal_name (GTK_SIGNAL_ACTION (action)), ==, "activate");
g_object_unref (action);
action = gtk_named_action_new ("text.undo");
g_assert_cmpstr (gtk_named_action_get_action_name (GTK_NAMED_ACTION (action)), ==, "text.undo");
g_object_unref (action);
}
static void
test_action_activate (void)
{
GtkShortcutAction *action;
GtkWidget *widget;
int callback_count;
widget = gtk_label_new ("");
g_object_ref_sink (widget);
action = gtk_nothing_action_get ();
g_assert_false (gtk_shortcut_action_activate (action, 0, widget, NULL));
callback_count = 0;
action = gtk_callback_action_new (callback, &callback_count, NULL);
g_assert_true (gtk_shortcut_action_activate (action, 0, widget, NULL));
g_assert_cmpint (callback_count, ==, 1);
g_object_unref (action);
g_object_unref (widget);
}
static void
test_action_parse (void)
{
GtkShortcutAction *action;
action = gtk_shortcut_action_parse_string ("nothing");
g_assert_true (GTK_IS_NOTHING_ACTION (action));
g_object_unref (action);
action = gtk_shortcut_action_parse_string ("activate");
g_assert_true (GTK_IS_ACTIVATE_ACTION (action));
g_object_unref (action);
action = gtk_shortcut_action_parse_string ("mnemonic-activate");
g_assert_true (GTK_IS_MNEMONIC_ACTION (action));
g_object_unref (action);
action = gtk_shortcut_action_parse_string ("action(win.dark)");
g_assert_true (GTK_IS_NAMED_ACTION (action));
g_object_unref (action);
action = gtk_shortcut_action_parse_string ("signal(frob)");
g_assert_true (GTK_IS_SIGNAL_ACTION (action));
g_object_unref (action);
}
int
main (int argc, char *argv[])
{
gtk_test_init (&argc, &argv);
g_test_add_func ("/shortcuts/trigger/basic", test_trigger_basic);
g_test_add_func ("/shortcuts/trigger/equal", test_trigger_equal);
g_test_add_func ("/shortcuts/trigger/parse/never", test_trigger_parse_never);
g_test_add_func ("/shortcuts/trigger/parse/keyval", test_trigger_parse_keyval);
g_test_add_func ("/shortcuts/trigger/parse/mnemonic", test_trigger_parse_mnemonic);
g_test_add_func ("/shortcuts/trigger/parse/alternative", test_trigger_parse_alternative);
g_test_add_func ("/shortcuts/trigger/parse/invalid", test_trigger_parse_invalid);
g_test_add_func ("/shortcuts/trigger/trigger", test_trigger_trigger);
g_test_add_func ("/shortcuts/action/basic", test_action_basic);
g_test_add_func ("/shortcuts/action/activate", test_action_activate);
g_test_add_func ("/shortcuts/action/parse", test_action_parse);
return g_test_run ();
}