]> Pileus Git - ~andy/gtk/blob - gtk/tests/crossingevents.c
Some more documentation fixes
[~andy/gtk] / gtk / tests / crossingevents.c
1 /*
2  * crossingevents.c: A test for crossing events
3  *
4  * Copyright (C) 2008 Cody Russell
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <gtk/gtk.h>
21 #include <string.h>
22
23 typedef struct {
24   GtkWidget *window;
25   GtkWidget *eventbox;
26   GtkWidget *frame;
27   GtkWidget *button;
28   GtkWidget *check;
29   gboolean   events_connected;
30   GQueue    *queue;
31 } CrossingTest;
32
33 typedef struct {
34   gboolean entered;
35   gchar *name;
36   gboolean synthesized;
37   GdkCrossingMode mode;
38   GdkNotifyType detail;
39 } CrossingEventData;
40
41 #define SLEEP_DURATION    100
42
43 void start_events (CrossingTest *test);
44 void stop_events (CrossingTest *test);
45
46 static gboolean
47 sleep_timeout_cb (gpointer data)
48 {
49   gtk_main_quit ();
50   return FALSE;
51 }
52
53 static void
54 sleep_in_main_loop (double fraction)
55 {
56   /* process all pending idles and events */
57   while (g_main_context_pending (NULL))
58     g_main_context_iteration (NULL, FALSE);
59   /* sleeping probably isn't strictly necessary here */
60   gdk_threads_add_timeout_full (G_MAXINT, fraction * SLEEP_DURATION, sleep_timeout_cb, NULL, NULL);
61   gtk_main ();
62   /* process any pending idles or events that arrived during sleep */
63   while (g_main_context_pending (NULL))
64     g_main_context_iteration (NULL, FALSE);
65 }
66
67 void
68 set_cursor (GtkWidget *widget)
69 {
70   int x, y, w, h;
71
72   gdk_window_get_origin (widget->window, &x, &y);
73
74   x += widget->allocation.x;
75   y += widget->allocation.y;
76   w = widget->allocation.width;
77   h = widget->allocation.height;
78
79   gdk_display_warp_pointer (gtk_widget_get_display (widget),
80                             gtk_widget_get_screen (widget),
81                             x + w / 2,
82                             y + h / 2);
83
84   sleep_in_main_loop (0.5);
85 }
86
87 static gboolean
88 on_enter (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
89 {
90   CrossingTest *test = (CrossingTest*)user_data;
91
92   CrossingEventData *evt = g_slice_new0 (CrossingEventData);
93   evt->entered = TRUE;
94   evt->name = g_strdup (gtk_widget_get_name (widget));
95   evt->synthesized = event->send_event;
96   evt->mode = event->mode;
97   evt->detail = event->detail;
98
99   if (!test->queue)
100     test->queue = g_queue_new ();
101
102   g_queue_push_tail (test->queue, evt);
103
104   return FALSE;
105 }
106
107 static gboolean
108 on_leave (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
109 {
110   CrossingTest *test = (CrossingTest*)user_data;
111
112   CrossingEventData *evt = g_slice_new0 (CrossingEventData);
113   evt->entered = FALSE;
114   evt->name = g_strdup (gtk_widget_get_name (widget));
115   evt->synthesized = event->send_event;
116   evt->mode = event->mode;
117   evt->detail = event->detail;
118
119   if (!test->queue)
120     test->queue = g_queue_new ();
121
122   g_queue_push_tail (test->queue, evt);
123
124   return FALSE;
125 }
126
127 static void
128 on_check_toggled (GtkWidget *toggle, GtkWidget *button)
129 {
130   gtk_widget_set_sensitive (button, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)));
131 }
132
133 static void
134 sensitivity_setup (CrossingTest *test,
135                    gconstpointer user_data)
136 {
137   GtkWidget *frame;
138
139   test->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
140   gtk_widget_set_name (test->window, "W");
141   frame = gtk_frame_new ("Crossing Events");
142   test->eventbox = gtk_event_box_new ();
143   gtk_widget_set_name (test->eventbox, "E");
144
145   GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 10);
146   gtk_container_add (GTK_CONTAINER (test->window), frame);
147   gtk_container_add (GTK_CONTAINER (frame), test->eventbox);
148   gtk_container_add (GTK_CONTAINER (test->eventbox), vbox);
149
150   test->button = gtk_button_new_with_label ("Click me!");
151   gtk_widget_set_name (test->button, "B");
152   gtk_box_pack_start (GTK_BOX (vbox), test->button, FALSE, TRUE, 0);
153
154   test->check = gtk_check_button_new_with_label ("Sensitive?");
155   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), TRUE);
156   g_signal_connect (G_OBJECT (test->check),
157                     "toggled", G_CALLBACK (on_check_toggled), test->button);
158   gtk_widget_set_name (test->check, "C");
159   gtk_box_pack_start (GTK_BOX (vbox), test->check, FALSE, TRUE, 0);
160
161   gtk_widget_show_all (test->window);
162
163   gtk_window_move (GTK_WINDOW (test->window), 0, 0);
164
165   sleep_in_main_loop (0.5);
166 }
167
168 static void
169 sensitivity_teardown (CrossingTest *test,
170                       gconstpointer user_data)
171 {
172   stop_events (test);
173   gtk_widget_destroy (test->window);
174
175   if (test->queue != NULL)
176     {
177       g_queue_clear (test->queue);
178       test->queue = NULL;
179     }
180 }
181
182 void
183 start_events (CrossingTest *test)
184 {
185   if (!test->events_connected)
186     {
187       g_object_connect (G_OBJECT (test->window),
188                         "signal::destroy", gtk_main_quit, NULL,
189                         "signal::enter-notify-event", on_enter, test,
190                         "signal::leave-notify-event", on_leave, test,
191                         NULL);
192       g_object_connect (G_OBJECT (test->eventbox),
193                         "signal::enter-notify-event", on_enter, test,
194                         "signal::leave-notify-event", on_leave, test,
195                         NULL);
196       g_object_connect (G_OBJECT (test->button),
197                         "signal::enter-notify-event", on_enter, test,
198                         "signal::leave-notify-event", on_leave, test,
199                         NULL);
200       g_object_connect (G_OBJECT (test->check),
201                         "signal::enter-notify-event", on_enter, test,
202                         "signal::leave-notify-event", on_leave, test,
203                         NULL);
204       test->events_connected = TRUE;
205     }
206
207   sleep_in_main_loop (0.5);
208 }
209
210 void
211 stop_events (CrossingTest *test)
212 {
213   if (test->events_connected)
214     {
215       g_object_disconnect (G_OBJECT (test->window),
216                            "any_signal", gtk_main_quit, NULL,
217                            "any_signal", on_enter, test,
218                            "any_signal", on_leave, test,
219                            NULL);
220       g_object_disconnect (G_OBJECT (test->eventbox),
221                            "any_signal", on_enter, test,
222                            "any_signal", on_leave, test,
223                            NULL);
224       g_object_disconnect (G_OBJECT (test->button),
225                            "any_signal", on_enter, test,
226                            "any_signal", on_leave, test,
227                            NULL);
228       g_object_disconnect (G_OBJECT (test->check),
229                            "any_signal", G_CALLBACK (on_check_toggled), test->button,
230                            "any_signal", on_enter, test,
231                            "any_signal", on_leave, test,
232                            NULL);
233       test->events_connected = FALSE;
234     }
235 }
236
237 void
238 move_cursor_away (CrossingTest *test)
239 {
240   gdk_display_warp_pointer (gtk_widget_get_display (test->window),
241                             gtk_widget_get_screen (test->window),
242                             1000, -1000);
243
244   sleep_in_main_loop (0.5);
245 }
246
247 void
248 check_event (CrossingTest *test,
249              const gchar *name,
250              gboolean entered,
251              gboolean synthesized,
252              GdkCrossingMode mode,
253              GdkNotifyType detail)
254 {
255   CrossingEventData *evt;
256
257   g_assert (test->queue != NULL);
258
259   evt = g_queue_pop_head (test->queue);
260
261   g_assert (evt->entered == entered);
262   g_assert (strcmp (evt->name, name) == 0);
263   g_assert (evt->synthesized == synthesized);
264   g_assert (evt->mode == mode);
265
266   if (evt->detail != detail)
267     g_print ("%s %s event, detail %d, expected detail %d\n", 
268              synthesized ? "synthesized" : "native",
269              entered ? "enter" : "leave",
270              evt->detail, detail);
271  
272   g_assert (evt->detail == detail);
273 }
274
275 /* Verify crossing events when moving into and out of a sensitive widget */
276 static void
277 cursor_on_sensitive (CrossingTest *test,
278                      gconstpointer user_data)
279 {
280   move_cursor_away (test);
281
282   start_events (test);
283
284   set_cursor (test->button);
285
286   check_event (test,
287                "W",
288                TRUE,
289                FALSE,  /* native */
290                GDK_CROSSING_NORMAL,
291                GDK_NOTIFY_NONLINEAR_VIRTUAL);
292
293   check_event (test,
294                "E",
295                TRUE,
296                FALSE,  /* native */
297                GDK_CROSSING_NORMAL,
298                GDK_NOTIFY_NONLINEAR_VIRTUAL);
299
300   check_event (test,
301                "B",
302                TRUE,
303                FALSE,  /* native */
304                GDK_CROSSING_NORMAL,
305                GDK_NOTIFY_NONLINEAR);
306
307   g_assert (g_queue_is_empty (test->queue));
308
309   move_cursor_away (test);
310
311   check_event (test,
312                "B",
313                FALSE,
314                FALSE,  /* native */
315                GDK_CROSSING_NORMAL,
316                GDK_NOTIFY_NONLINEAR);
317
318   check_event (test,
319                "E",
320                FALSE,
321                FALSE,  /* native */
322                GDK_CROSSING_NORMAL,
323                GDK_NOTIFY_NONLINEAR_VIRTUAL);
324
325   check_event (test,
326                "W",
327                FALSE,
328                FALSE,  /* native */
329                GDK_CROSSING_NORMAL,
330                GDK_NOTIFY_NONLINEAR_VIRTUAL);
331
332   g_assert (g_queue_is_empty (test->queue));
333
334   stop_events (test);
335 }
336
337 static void
338 change_sensitive_to_insensitive (CrossingTest *test,
339                                  gconstpointer user_data)
340 {
341   move_cursor_away (test);
342   set_cursor (test->button);
343
344   start_events (test);
345
346   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
347
348   check_event (test,
349                "B",
350                FALSE,
351                TRUE,  /* synthesized */
352                GDK_CROSSING_STATE_CHANGED,
353                GDK_NOTIFY_ANCESTOR);
354
355   check_event (test,
356                "E",
357                FALSE,
358                TRUE,  /* synthesized */
359                GDK_CROSSING_STATE_CHANGED,
360                GDK_NOTIFY_VIRTUAL);
361
362   check_event (test,
363                "W",
364                FALSE,
365                TRUE,  /* synthesized */
366                GDK_CROSSING_STATE_CHANGED,
367                GDK_NOTIFY_VIRTUAL);
368
369   g_assert (g_queue_is_empty (test->queue));
370
371   stop_events (test);
372 }
373
374 static void
375 change_insensitive_to_sensitive (CrossingTest *test,
376                                  gconstpointer user_data)
377 {
378   move_cursor_away (test);
379   set_cursor (test->button);
380   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
381
382   start_events (test);
383
384   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), TRUE);
385
386   check_event (test,
387                "W",
388                TRUE,
389                TRUE,  /* synthesized */
390                GDK_CROSSING_STATE_CHANGED,
391                GDK_NOTIFY_VIRTUAL);
392
393   check_event (test,
394                "E",
395                TRUE,
396                TRUE,  /* synthesized */
397                GDK_CROSSING_STATE_CHANGED,
398                GDK_NOTIFY_VIRTUAL);
399
400   check_event (test,
401                "B",
402                TRUE,
403                TRUE,  /* synthesized */
404                GDK_CROSSING_STATE_CHANGED,
405                GDK_NOTIFY_ANCESTOR);
406
407   g_assert (g_queue_is_empty (test->queue));
408
409   stop_events (test);
410 }
411
412 static void
413 cursor_from_insensitive_to_sensitive (CrossingTest *test,
414                                       gconstpointer user_data)
415 {
416   set_cursor (test->button);
417   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
418
419   start_events (test);
420
421   set_cursor (test->check);
422
423   check_event (test,
424                "C",
425                TRUE,
426                FALSE,  /* native */
427                GDK_CROSSING_NORMAL,
428                GDK_NOTIFY_NONLINEAR);
429
430   g_assert (g_queue_is_empty (test->queue));
431
432   stop_events (test);
433 }
434
435 static void
436 cursor_from_sensitive_to_insensitive (CrossingTest *test,
437                                       gconstpointer user_data)
438 {
439   set_cursor (test->check);
440   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
441
442   start_events (test);
443
444   set_cursor (test->button);
445
446   check_event (test,
447                "C",
448                FALSE,
449                FALSE,  /* native */
450                GDK_CROSSING_NORMAL,
451                GDK_NOTIFY_NONLINEAR);
452
453   g_assert (g_queue_is_empty (test->queue));
454
455   stop_events (test);
456 }
457
458 static void
459 add_gtk_grab (CrossingTest *test,
460               gconstpointer user_data)
461 {
462   set_cursor (test->button);
463
464   start_events (test);
465
466   gtk_grab_add (test->check);
467
468   check_event (test,
469                "B",
470                FALSE,
471                TRUE,   /* synthesized */
472                GDK_CROSSING_GTK_GRAB,
473                GDK_NOTIFY_ANCESTOR);
474
475   check_event (test,
476                "E",
477                FALSE,
478                TRUE,   /* synthesized */
479                GDK_CROSSING_GTK_GRAB,
480                GDK_NOTIFY_ANCESTOR);
481
482   check_event (test,
483                "W",
484                FALSE,
485                TRUE,   /* synthesized */
486                GDK_CROSSING_GTK_GRAB,
487                GDK_NOTIFY_ANCESTOR);
488
489   g_assert (g_queue_is_empty (test->queue));
490
491   stop_events (test);
492 }
493
494 static void
495 remove_gtk_grab (CrossingTest *test,
496                  gconstpointer user_data)
497 {
498   set_cursor (test->button);
499
500   gtk_grab_add (test->check);
501
502   start_events (test);
503
504   gtk_grab_remove (test->check);
505
506   check_event (test,
507                "B",
508                TRUE,
509                TRUE,   /* synthesized */
510                GDK_CROSSING_GTK_UNGRAB,
511                GDK_NOTIFY_ANCESTOR);
512
513   check_event (test,
514                "E",
515                TRUE,
516                TRUE,   /* synthesized */
517                GDK_CROSSING_GTK_UNGRAB,
518                GDK_NOTIFY_ANCESTOR);
519
520   check_event (test,
521                "W",
522                TRUE,
523                TRUE,   /* synthesized */
524                GDK_CROSSING_GTK_UNGRAB,
525                GDK_NOTIFY_ANCESTOR);
526
527   g_assert (g_queue_is_empty (test->queue));
528
529   stop_events (test);
530 }
531
532 static void
533 cursor_from_shadowed_to_unshadowed (CrossingTest *test,
534                                     gconstpointer user_data)
535 {
536   set_cursor (test->button);
537
538   gtk_grab_add (test->check);
539
540   start_events (test);
541
542   set_cursor (test->check);
543
544   check_event (test,
545                "C",
546                FALSE,
547                FALSE,   /* native */
548                GDK_CROSSING_NORMAL,
549                GDK_NOTIFY_NONLINEAR);
550
551   check_event (test,
552                "C",
553                TRUE,
554                FALSE,   /* native */
555                GDK_CROSSING_NORMAL,
556                GDK_NOTIFY_NONLINEAR);
557
558   g_assert (g_queue_is_empty (test->queue));
559
560   stop_events (test);
561 }
562
563 static void
564 cursor_from_unshadowed_to_shadowed (CrossingTest *test,
565                                     gconstpointer user_data)
566 {
567   set_cursor (test->check);
568
569   gtk_grab_add (test->check);
570
571   start_events (test);
572
573   set_cursor (test->button);
574
575   check_event (test,
576                "C",
577                FALSE,
578                FALSE,   /* native */
579                GDK_CROSSING_NORMAL,
580                GDK_NOTIFY_NONLINEAR);
581
582   check_event (test,
583                "C",
584                TRUE,
585                FALSE,   /* native */
586                GDK_CROSSING_NORMAL,
587                GDK_NOTIFY_NONLINEAR);
588
589   g_assert (g_queue_is_empty (test->queue));
590
591   stop_events (test);
592 }
593
594 int
595 main (int    argc,
596       char **argv)
597 {
598   gtk_test_init (&argc, &argv, NULL);
599
600   g_test_add ("/crossings/cursor-on-sensitive", CrossingTest, NULL,
601               sensitivity_setup, cursor_on_sensitive, sensitivity_teardown);
602
603   g_test_add ("/crossings/change-sensitive-to-insensitive", CrossingTest, NULL,
604               sensitivity_setup, change_sensitive_to_insensitive, sensitivity_teardown);
605
606   g_test_add ("/crossings/cursor-from-insensitive-to-sensitive", CrossingTest, NULL,
607               sensitivity_setup, cursor_from_insensitive_to_sensitive, sensitivity_teardown);
608
609   g_test_add ("/crossings/cursor-from-sensitive-to-insensitive", CrossingTest, NULL,
610               sensitivity_setup, cursor_from_sensitive_to_insensitive, sensitivity_teardown);
611
612   g_test_add ("/crossings/change-insensitive-to-sensitive", CrossingTest, NULL,
613               sensitivity_setup, change_insensitive_to_sensitive, sensitivity_teardown);
614
615   g_test_add ("/crossings/add-gtk-grab", CrossingTest, NULL,
616               sensitivity_setup, add_gtk_grab, sensitivity_teardown);
617
618   g_test_add ("/crossings/remove-gtk-grab", CrossingTest, NULL,
619               sensitivity_setup, remove_gtk_grab, sensitivity_teardown);
620
621   g_test_add ("/crossings/cursor-from-shadowed-to-unshadowed", CrossingTest, NULL,
622               sensitivity_setup, cursor_from_shadowed_to_unshadowed, sensitivity_teardown);
623
624   g_test_add ("/crossings/cursor-from-unshadowed-to-shadowed", CrossingTest, NULL,
625               sensitivity_setup, cursor_from_unshadowed_to_shadowed, sensitivity_teardown);
626
627   return g_test_run ();
628 }