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