1 #include <gtk/gtk.h>
2 
3 #define GTK_COMPILATION
4 #include "gdk/gdkeventsprivate.h"
5 
6 static GdkEvent *
key_event_new(GdkEventType event_type,GdkSurface * surface,GdkDevice * device,GdkDevice * source_device,guint32 time_,guint keycode,GdkModifierType state,gboolean is_modifier,GdkTranslatedKey * translated,GdkTranslatedKey * no_lock)7 key_event_new (GdkEventType      event_type,
8                GdkSurface       *surface,
9                GdkDevice        *device,
10                GdkDevice        *source_device,
11                guint32           time_,
12                guint             keycode,
13                GdkModifierType   state,
14                gboolean          is_modifier,
15                GdkTranslatedKey *translated,
16                GdkTranslatedKey *no_lock)
17 {
18   GdkKeyEvent *key_event = (GdkKeyEvent *) g_type_create_instance (GDK_TYPE_KEY_EVENT);
19   GdkEvent *event = (GdkEvent *) key_event;
20 
21   event->event_type = event_type;
22   event->surface = g_object_ref (surface);
23   event->device = g_object_ref (device);
24   event->time = time_;
25 
26   key_event->keycode = keycode;
27   key_event->state = state;
28   key_event->key_is_modifier = is_modifier;
29   key_event->translated[0] = *translated;
30   key_event->translated[1] = *no_lock;
31 
32   return event;
33 }
34 
35 static void
test_trigger_basic(void)36 test_trigger_basic (void)
37 {
38   GtkShortcutTrigger *trigger;
39 
40   trigger = gtk_never_trigger_get ();
41 
42   trigger = gtk_keyval_trigger_new (GDK_KEY_a, GDK_CONTROL_MASK);
43   g_assert_cmpint (gtk_keyval_trigger_get_keyval (GTK_KEYVAL_TRIGGER (trigger)), ==, GDK_KEY_a);
44   g_assert_cmpint (gtk_keyval_trigger_get_modifiers (GTK_KEYVAL_TRIGGER (trigger)), ==, GDK_CONTROL_MASK);
45   g_object_unref (trigger);
46 
47   trigger = gtk_mnemonic_trigger_new (GDK_KEY_u);
48   g_assert_cmpint (gtk_mnemonic_trigger_get_keyval (GTK_MNEMONIC_TRIGGER (trigger)), ==, GDK_KEY_u);
49   g_object_unref (trigger);
50 }
51 
52 static void
test_trigger_equal(void)53 test_trigger_equal (void)
54 {
55   GtkShortcutTrigger *trigger1, *trigger2, *trigger3, *trigger4;
56   GtkShortcutTrigger *trigger5, *trigger6, *trigger1a, *trigger2a;
57 
58   trigger1 = gtk_keyval_trigger_new ('u', GDK_CONTROL_MASK);
59   trigger2 = g_object_ref (gtk_never_trigger_get ());
60   trigger3 = gtk_alternative_trigger_new (g_object_ref (trigger1), g_object_ref (trigger2));
61   trigger4 = gtk_alternative_trigger_new (g_object_ref (trigger2), g_object_ref (trigger1));
62   trigger5 = gtk_keyval_trigger_new ('u', GDK_SHIFT_MASK);
63   trigger6 = gtk_mnemonic_trigger_new ('u');
64 
65   trigger1a = gtk_keyval_trigger_new ('u', GDK_CONTROL_MASK);
66   trigger2a = g_object_ref (gtk_never_trigger_get ());
67 
68   g_assert_true (gtk_shortcut_trigger_equal (trigger1, trigger1));
69   g_assert_true (gtk_shortcut_trigger_equal (trigger2, trigger2));
70   g_assert_true (gtk_shortcut_trigger_equal (trigger3, trigger3));
71   g_assert_true (gtk_shortcut_trigger_equal (trigger4, trigger4));
72   g_assert_true (gtk_shortcut_trigger_equal (trigger5, trigger5));
73   g_assert_true (gtk_shortcut_trigger_equal (trigger6, trigger6));
74 
75   g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger2));
76   g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger3));
77   g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger4));
78   g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger5));
79   g_assert_false (gtk_shortcut_trigger_equal (trigger1, trigger6));
80 
81   g_assert_false (gtk_shortcut_trigger_equal (trigger2, trigger3));
82   g_assert_false (gtk_shortcut_trigger_equal (trigger2, trigger4));
83   g_assert_false (gtk_shortcut_trigger_equal (trigger2, trigger5));
84   g_assert_false (gtk_shortcut_trigger_equal (trigger2, trigger6));
85 
86   g_assert_false (gtk_shortcut_trigger_equal (trigger3, trigger4));
87   g_assert_false (gtk_shortcut_trigger_equal (trigger3, trigger5));
88   g_assert_false (gtk_shortcut_trigger_equal (trigger3, trigger6));
89 
90   g_assert_false (gtk_shortcut_trigger_equal (trigger4, trigger5));
91   g_assert_false (gtk_shortcut_trigger_equal (trigger4, trigger6));
92 
93   g_assert_false (gtk_shortcut_trigger_equal (trigger5, trigger6));
94 
95   g_assert_true (gtk_shortcut_trigger_equal (trigger1, trigger1a));
96   g_assert_true (gtk_shortcut_trigger_equal (trigger2, trigger2a));
97 
98   g_object_unref (trigger1);
99   g_object_unref (trigger2);
100   g_object_unref (trigger3);
101   g_object_unref (trigger4);
102   g_object_unref (trigger5);
103   g_object_unref (trigger6);
104   g_object_unref (trigger1a);
105   g_object_unref (trigger2a);
106 }
107 
108 static void
test_trigger_parse_never(void)109 test_trigger_parse_never (void)
110 {
111   GtkShortcutTrigger *trigger;
112 
113   trigger = gtk_shortcut_trigger_parse_string ("never");
114   g_assert_true (GTK_IS_NEVER_TRIGGER (trigger));
115 
116   g_object_unref (trigger);
117 }
118 
119 static void
test_trigger_parse_keyval(void)120 test_trigger_parse_keyval (void)
121 {
122   const struct
123   {
124     const char *str;
125     GdkModifierType modifiers;
126     guint keyval;
127     int trigger_type;
128   } tests[] = {
129     { "<Primary><Alt>z", GDK_CONTROL_MASK | GDK_ALT_MASK, 'z' },
130     { "<Control>U", GDK_CONTROL_MASK, 'u' },
131     { "<Hyper>x", GDK_HYPER_MASK, 'x' },
132     { "<Meta>y", GDK_META_MASK, 'y' },
133     { "KP_7", 0, GDK_KEY_KP_7 },
134     { "<Shift>exclam", GDK_SHIFT_MASK, '!' },
135   };
136 
137   for (int i = 0; i < G_N_ELEMENTS (tests); i++)
138     {
139       g_test_message ("Checking: '%s'", tests[i].str);
140 
141       GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i].str);
142 
143       g_assert_true (GTK_IS_KEYVAL_TRIGGER (trigger));
144       g_assert_cmpint (gtk_keyval_trigger_get_modifiers (GTK_KEYVAL_TRIGGER (trigger)),
145                        ==,
146                        tests[i].modifiers);
147       g_assert_cmpuint (gtk_keyval_trigger_get_keyval (GTK_KEYVAL_TRIGGER (trigger)),
148                         ==,
149                         tests[i].keyval);
150       g_object_unref (trigger);
151     }
152 }
153 
154 static void
test_trigger_parse_mnemonic(void)155 test_trigger_parse_mnemonic (void)
156 {
157   struct
158   {
159     const char *str;
160     guint keyval;
161   } tests[] = {
162     { "_A", GDK_KEY_a },
163     { "_s", GDK_KEY_s },
164   };
165 
166   for (int i = 0; i < G_N_ELEMENTS (tests); i++)
167     {
168       g_test_message ("Checking: '%s'", tests[i].str);
169 
170       GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i].str);
171 
172       g_assert_true (GTK_IS_MNEMONIC_TRIGGER (trigger));
173       g_assert_cmpuint (gtk_mnemonic_trigger_get_keyval (GTK_MNEMONIC_TRIGGER (trigger)),
174                         ==,
175                         tests[i].keyval);
176       g_object_unref (trigger);
177     }
178 }
179 
180 static void
test_trigger_parse_alternative(void)181 test_trigger_parse_alternative (void)
182 {
183   enum
184   {
185     TRIGGER_NEVER,
186     TRIGGER_KEYVAL,
187     TRIGGER_MNEMONIC,
188     TRIGGER_ALTERNATIVE
189   };
190 
191   const struct
192   {
193     const char *str;
194     int first;
195     int second;
196   } tests[] = {
197     { "U|<Primary>U", TRIGGER_KEYVAL, TRIGGER_KEYVAL },
198     { "_U|<Shift>u", TRIGGER_MNEMONIC, TRIGGER_KEYVAL },
199     { "x|_x|<Primary>x", TRIGGER_KEYVAL, TRIGGER_ALTERNATIVE },
200   };
201 
202   for (int i = 0; i < G_N_ELEMENTS (tests); i++)
203     {
204       g_test_message ("Checking: '%s'", tests[i].str);
205 
206       GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i].str);
207 
208       g_assert_true (GTK_IS_ALTERNATIVE_TRIGGER (trigger));
209 
210       GtkShortcutTrigger *t1 = gtk_alternative_trigger_get_first (GTK_ALTERNATIVE_TRIGGER (trigger));
211 
212       switch (tests[i].first)
213         {
214         case TRIGGER_NEVER:
215           g_assert_true (GTK_IS_NEVER_TRIGGER (t1));
216           break;
217 
218         case TRIGGER_KEYVAL:
219           g_assert_true (GTK_IS_KEYVAL_TRIGGER (t1));
220           break;
221 
222         case TRIGGER_MNEMONIC:
223           g_assert_true (GTK_IS_MNEMONIC_TRIGGER (t1));
224           break;
225 
226         case TRIGGER_ALTERNATIVE:
227           g_assert_true (GTK_IS_ALTERNATIVE_TRIGGER (t1));
228           break;
229 
230         default:
231           g_assert_not_reached ();
232           break;
233         }
234 
235       GtkShortcutTrigger *t2 = gtk_alternative_trigger_get_second (GTK_ALTERNATIVE_TRIGGER (trigger));
236 
237       switch (tests[i].second)
238         {
239         case TRIGGER_NEVER:
240           g_assert_true (GTK_IS_NEVER_TRIGGER (t2));
241           break;
242 
243         case TRIGGER_KEYVAL:
244           g_assert_true (GTK_IS_KEYVAL_TRIGGER (t2));
245           break;
246 
247         case TRIGGER_MNEMONIC:
248           g_assert_true (GTK_IS_MNEMONIC_TRIGGER (t2));
249           break;
250 
251         case TRIGGER_ALTERNATIVE:
252           g_assert_true (GTK_IS_ALTERNATIVE_TRIGGER (t2));
253           break;
254 
255         default:
256           g_assert_not_reached ();
257           break;
258         }
259 
260       g_object_unref (trigger);
261     }
262 }
263 
264 static void
test_trigger_parse_invalid(void)265 test_trigger_parse_invalid (void)
266 {
267   const char *tests[] = {
268     "<never>",
269     "Never",
270     "Foo",
271     "<Foo>Nyaa",
272     "never|",
273     "|never",
274   };
275 
276   for (int i = 0; i < G_N_ELEMENTS (tests); i++)
277     {
278       g_test_message ("Checking: '%s'", tests[i]);
279 
280       GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i]);
281 
282       g_assert_null (trigger);
283     }
284 }
285 
286 static void
test_trigger_trigger(void)287 test_trigger_trigger (void)
288 {
289   GtkShortcutTrigger *trigger[4];
290   GdkDisplay *display;
291   GdkSeat *seat;
292   GdkSurface *surface;
293   GdkDevice *device;
294   GdkEvent *event;
295   struct {
296     guint keyval;
297     GdkModifierType state;
298     gboolean mnemonic;
299     GdkKeyMatch result[4];
300   } tests[] = {
301     { GDK_KEY_a, GDK_CONTROL_MASK, FALSE, { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT } },
302     { GDK_KEY_a, GDK_CONTROL_MASK, TRUE,  { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT } },
303     { GDK_KEY_a, GDK_SHIFT_MASK,   FALSE, { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE } },
304     { GDK_KEY_a, GDK_SHIFT_MASK,   TRUE,  { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE } },
305     { GDK_KEY_u, GDK_SHIFT_MASK,   FALSE, { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE } },
306     { GDK_KEY_u, GDK_SHIFT_MASK,   TRUE,  { GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_NONE, GDK_KEY_MATCH_EXACT, GDK_KEY_MATCH_EXACT } },
307   };
308   int i, j;
309 
310   display = gdk_display_get_default ();
311   seat = gdk_display_get_default_seat (display);
312   if (!seat)
313     {
314       g_test_skip ("Display has no seat");
315       return;
316     }
317 
318   trigger[0] = g_object_ref (gtk_never_trigger_get ());
319   trigger[1] = gtk_keyval_trigger_new (GDK_KEY_a, GDK_CONTROL_MASK);
320   trigger[2] = gtk_mnemonic_trigger_new (GDK_KEY_u);
321   trigger[3] = gtk_alternative_trigger_new (g_object_ref (trigger[1]),
322                                             g_object_ref (trigger[2]));
323 
324   device = gdk_seat_get_keyboard (seat);
325   surface = gdk_surface_new_toplevel (display);
326 
327   for (i = 0; i < G_N_ELEMENTS (tests); i++)
328     {
329       GdkKeymapKey *keys;
330       int n_keys;
331       GdkTranslatedKey translated;
332 
333       if (!gdk_display_map_keyval (display, tests[i].keyval, &keys, &n_keys))
334         continue;
335 
336       translated.keyval = tests[i].keyval;
337       translated.consumed = 0;
338       translated.layout = keys[0].group;
339       translated.level = keys[0].level;
340       event = key_event_new (GDK_KEY_PRESS,
341                              surface,
342                              device,
343                              device,
344                              GDK_CURRENT_TIME,
345                              keys[0].keycode,
346                              tests[i].state,
347                              FALSE,
348                              &translated,
349                              &translated);
350       for (j = 0; j < 4; j++)
351         {
352           g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger[j], event, tests[i].mnemonic), ==, tests[i].result[j]);
353         }
354 
355       gdk_event_unref (event);
356 
357       g_free (keys);
358     }
359 
360   gdk_surface_destroy (surface);
361   g_object_unref (surface);
362 
363   g_object_unref (trigger[0]);
364   g_object_unref (trigger[1]);
365   g_object_unref (trigger[2]);
366   g_object_unref (trigger[3]);
367 }
368 
369 static gboolean
callback(GtkWidget * widget,GVariant * args,gpointer user_data)370 callback (GtkWidget *widget,
371           GVariant  *args,
372           gpointer   user_data)
373 {
374   int *callback_count = user_data;
375   *callback_count += 1;
376   return TRUE;
377 }
378 
379 static void
test_action_basic(void)380 test_action_basic (void)
381 {
382   GtkShortcutAction *action;
383 
384   action = gtk_signal_action_new ("activate");
385   g_assert_cmpstr (gtk_signal_action_get_signal_name (GTK_SIGNAL_ACTION (action)), ==, "activate");
386   g_object_unref (action);
387 
388   action = gtk_named_action_new ("text.undo");
389   g_assert_cmpstr (gtk_named_action_get_action_name (GTK_NAMED_ACTION (action)), ==, "text.undo");
390   g_object_unref (action);
391 }
392 
393 static void
test_action_activate(void)394 test_action_activate (void)
395 {
396   GtkShortcutAction *action;
397   GtkWidget *widget;
398   int callback_count;
399 
400   widget = gtk_label_new ("");
401   g_object_ref_sink (widget);
402 
403   action = gtk_nothing_action_get ();
404   g_assert_false (gtk_shortcut_action_activate (action, 0, widget, NULL));
405 
406   callback_count = 0;
407   action = gtk_callback_action_new (callback, &callback_count, NULL);
408   g_assert_true (gtk_shortcut_action_activate (action, 0, widget, NULL));
409   g_assert_cmpint (callback_count, ==, 1);
410   g_object_unref (action);
411 
412   g_object_unref (widget);
413 }
414 
415 static void
test_action_parse(void)416 test_action_parse (void)
417 {
418   GtkShortcutAction *action;
419 
420   action = gtk_shortcut_action_parse_string ("nothing");
421   g_assert_true (GTK_IS_NOTHING_ACTION (action));
422   g_object_unref (action);
423 
424   action = gtk_shortcut_action_parse_string ("activate");
425   g_assert_true (GTK_IS_ACTIVATE_ACTION (action));
426   g_object_unref (action);
427 
428   action = gtk_shortcut_action_parse_string ("mnemonic-activate");
429   g_assert_true (GTK_IS_MNEMONIC_ACTION (action));
430   g_object_unref (action);
431 
432   action = gtk_shortcut_action_parse_string ("action(win.dark)");
433   g_assert_true (GTK_IS_NAMED_ACTION (action));
434   g_object_unref (action);
435 
436   action = gtk_shortcut_action_parse_string ("signal(frob)");
437   g_assert_true (GTK_IS_SIGNAL_ACTION (action));
438   g_object_unref (action);
439 }
440 
441 int
main(int argc,char * argv[])442 main (int argc, char *argv[])
443 {
444   gtk_test_init (&argc, &argv);
445 
446   g_test_add_func ("/shortcuts/trigger/basic", test_trigger_basic);
447   g_test_add_func ("/shortcuts/trigger/equal", test_trigger_equal);
448   g_test_add_func ("/shortcuts/trigger/parse/never", test_trigger_parse_never);
449   g_test_add_func ("/shortcuts/trigger/parse/keyval", test_trigger_parse_keyval);
450   g_test_add_func ("/shortcuts/trigger/parse/mnemonic", test_trigger_parse_mnemonic);
451   g_test_add_func ("/shortcuts/trigger/parse/alternative", test_trigger_parse_alternative);
452   g_test_add_func ("/shortcuts/trigger/parse/invalid", test_trigger_parse_invalid);
453   g_test_add_func ("/shortcuts/trigger/trigger", test_trigger_trigger);
454   g_test_add_func ("/shortcuts/action/basic", test_action_basic);
455   g_test_add_func ("/shortcuts/action/activate", test_action_activate);
456   g_test_add_func ("/shortcuts/action/parse", test_action_parse);
457 
458   return g_test_run ();
459 }
460