1 diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
2 index e6c516c..f3fa26a 100644
5 @@ -65,6 +65,11 @@ typedef struct _GdkEventWindowState GdkEventWindowState;
6 typedef struct _GdkEventSetting GdkEventSetting;
7 typedef struct _GdkEventGrabBroken GdkEventGrabBroken;
9 +/* OS X specific gesture events */
10 +typedef struct _GdkEventGestureMagnify GdkEventGestureMagnify;
11 +typedef struct _GdkEventGestureRotate GdkEventGestureRotate;
12 +typedef struct _GdkEventGestureSwipe GdkEventGestureSwipe;
14 typedef union _GdkEvent GdkEvent;
16 typedef void (*GdkEventFunc) (GdkEvent *event,
17 @@ -152,6 +157,9 @@ typedef enum
18 GDK_OWNER_CHANGE = 34,
21 + GDK_GESTURE_MAGNIFY = 37,
22 + GDK_GESTURE_ROTATE = 38,
23 + GDK_GESTURE_SWIPE = 39,
24 GDK_EVENT_LAST /* helper variable for decls */
27 @@ -488,6 +496,52 @@ struct _GdkEventDND {
28 gshort x_root, y_root;
31 +/* Event types for OS X gestures */
33 +struct _GdkEventGestureMagnify
42 + gdouble magnification;
44 + gdouble x_root, y_root;
47 +struct _GdkEventGestureRotate
58 + gdouble x_root, y_root;
61 +struct _GdkEventGestureSwipe
73 + gdouble x_root, y_root;
80 @@ -511,6 +565,9 @@ union _GdkEvent
81 GdkEventWindowState window_state;
82 GdkEventSetting setting;
83 GdkEventGrabBroken grab_broken;
84 + GdkEventGestureMagnify magnify;
85 + GdkEventGestureRotate rotate;
86 + GdkEventGestureSwipe swipe;
89 GType gdk_event_get_type (void) G_GNUC_CONST;
90 diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
91 index d20b424..fb89451 100644
94 @@ -9813,6 +9813,9 @@ static const guint type_masks[] = {
95 0, /* GDK_OWNER_CHANGE = 34 */
96 0, /* GDK_GRAB_BROKEN = 35 */
97 0, /* GDK_DAMAGE = 36 */
98 + 0, /* GDK_GESTURE_MAGNIFY = 37 */
99 + 0, /* GDK_GESTURE_ROTATE = 38 */
100 + 0, /* GDK_GESTURE_SWIPE = 39 */
102 G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
104 diff --git a/gdk/quartz/GdkQuartzView.c b/gdk/quartz/GdkQuartzView.c
105 index 2c897fb..cdae2f1 100644
106 --- a/gdk/quartz/GdkQuartzView.c
107 +++ b/gdk/quartz/GdkQuartzView.c
109 [self updateTrackingRect];
112 +/* Handle OS X gesture events. The Apple documentation is explicit
113 + * that these events should not be captured through a tracking loop (which
114 + * is how we handle usual event handling), so we fallback to overriding
115 + * NSResponder methods.
118 +-(void)magnifyWithEvent:(NSEvent *)event
120 + GdkEvent *gdk_event;
122 + gdk_event = _gdk_quartz_events_create_magnify_event (event);
124 + _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
127 +-(void)rotateWithEvent:(NSEvent *)event
129 + GdkEvent *gdk_event;
131 + gdk_event = _gdk_quartz_events_create_rotate_event (event);
133 + _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
136 +-(void)swipeWithEvent:(NSEvent *)event
138 + GdkEvent *gdk_event;
140 + gdk_event = _gdk_quartz_events_create_swipe_event (event);
142 + _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
146 diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
147 index 9478c16..128c794 100644
148 --- a/gdk/quartz/gdkevents-quartz.c
149 +++ b/gdk/quartz/gdkevents-quartz.c
150 @@ -374,6 +374,11 @@ get_event_mask_from_ns_event (NSEvent *nsevent)
152 return GDK_LEAVE_NOTIFY_MASK;
154 + case NSEventTypeMagnify:
155 + case NSEventTypeRotate:
156 + case NSEventTypeSwipe:
160 g_assert_not_reached ();
162 @@ -752,6 +757,11 @@ find_window_for_ns_event (NSEvent *nsevent,
166 + case NSEventTypeMagnify:
167 + case NSEventTypeRotate:
168 + case NSEventTypeSwipe:
172 /* Ignore everything else. */
174 @@ -1011,6 +1021,139 @@ fill_key_event (GdkWindow *window,
180 +find_window_for_ns_gesture_event (NSEvent *nsevent,
186 + GdkDisplay *display = _gdk_display;
187 + GdkWindow *toplevel_window = NULL, *pointer_window = NULL;
188 + gdouble found_x, found_y;
191 + toplevel_window = find_window_for_ns_event (nsevent, &x, &y, x_root, y_root);
192 + if (!toplevel_window)
195 + if (toplevel_window == display->pointer_info.toplevel_under_pointer)
196 + pointer_window = _gdk_window_find_descendant_at (toplevel_window,
198 + &found_x, &found_y);
200 + pointer_window = NULL;
205 + return pointer_window;
210 +_gdk_quartz_events_create_magnify_event (NSEvent *nsevent)
213 + GdkWindow *window = NULL;
214 + gint x, y, x_root, y_root;
216 + window = find_window_for_ns_gesture_event (nsevent,
223 + event = gdk_event_new (GDK_GESTURE_MAGNIFY);
225 + event->any.type = GDK_GESTURE_MAGNIFY;
226 + event->magnify.window = window;
227 + event->magnify.time = get_time_from_ns_event (nsevent);
228 + event->magnify.x = x;
229 + event->magnify.y = y;
230 + event->magnify.x_root = x_root;
231 + event->magnify.y_root = y_root;
232 + event->magnify.magnification = [nsevent magnification];
233 + event->magnify.state = get_keyboard_modifiers_from_ns_event (nsevent) |
234 + _gdk_quartz_events_get_current_mouse_modifiers ();
235 + event->magnify.device = _gdk_display->core_pointer;
237 + fixup_event (event);
243 +_gdk_quartz_events_create_rotate_event (NSEvent *nsevent)
247 + gint x, y, x_root, y_root;
249 + window = find_window_for_ns_gesture_event (nsevent,
256 + event = gdk_event_new (GDK_GESTURE_ROTATE);
258 + event->any.type = GDK_GESTURE_ROTATE;
259 + event->rotate.window = window;
260 + event->rotate.time = get_time_from_ns_event (nsevent);
261 + event->rotate.x = x;
262 + event->rotate.y = y;
263 + event->rotate.x_root = x_root;
264 + event->rotate.y_root = y_root;
265 + event->rotate.rotation = [nsevent rotation];
266 + event->rotate.state = get_keyboard_modifiers_from_ns_event (nsevent) |
267 + _gdk_quartz_events_get_current_mouse_modifiers ();
268 + event->rotate.device = _gdk_display->core_pointer;
270 + fixup_event (event);
276 +_gdk_quartz_events_create_swipe_event (NSEvent *nsevent)
280 + gint x, y, x_root, y_root;
282 + window = find_window_for_ns_gesture_event (nsevent,
289 + event = gdk_event_new (GDK_GESTURE_SWIPE);
291 + event->any.type = GDK_GESTURE_SWIPE;
292 + event->swipe.window = window;
293 + event->swipe.time = get_time_from_ns_event (nsevent);
294 + event->swipe.x = x;
295 + event->swipe.y = y;
296 + event->swipe.x_root = x_root;
297 + event->swipe.y_root = y_root;
298 + event->swipe.delta_x = [nsevent deltaX];
299 + event->swipe.delta_y = [nsevent deltaY];
300 + event->swipe.state = get_keyboard_modifiers_from_ns_event (nsevent) |
301 + _gdk_quartz_events_get_current_mouse_modifiers ();
302 + event->swipe.device = _gdk_display->core_pointer;
304 + fixup_event (event);
312 synthesize_crossing_event (GdkWindow *window,
314 @@ -1392,6 +1535,13 @@ gdk_event_translate (GdkEvent *event,
318 + /* Gesture events are handled through GdkQuartzView, because the
319 + * Apple documentation states very clearly that such events should
320 + * not be handled through a tracking loop (like GDK uses).
321 + * Experiments show that gesture events do not get through our
322 + * tracking loop either.
326 /* Ignore everything elsee. */
328 diff --git a/gdk/quartz/gdkglobals-quartz.c b/gdk/quartz/gdkglobals-quartz.c
329 index 53c6d5e..6d01b59 100644
330 --- a/gdk/quartz/gdkglobals-quartz.c
331 +++ b/gdk/quartz/gdkglobals-quartz.c
332 @@ -41,3 +41,9 @@ gdk_quartz_osx_version (void)
338 +gdk_quartz_supports_gesture_events (void)
342 diff --git a/gdk/quartz/gdkprivate-quartz.h b/gdk/quartz/gdkprivate-quartz.h
343 index c1b3083..03a3857 100644
344 --- a/gdk/quartz/gdkprivate-quartz.h
345 +++ b/gdk/quartz/gdkprivate-quartz.h
346 @@ -179,6 +179,10 @@ GdkModifierType _gdk_quartz_events_get_current_mouse_modifiers (void);
347 void _gdk_quartz_events_send_enter_notify_event (GdkWindow *window);
348 void _gdk_quartz_events_break_all_grabs (guint32 time);
350 +GdkEvent *_gdk_quartz_events_create_magnify_event (NSEvent *event);
351 +GdkEvent *_gdk_quartz_events_create_rotate_event (NSEvent *event);
352 +GdkEvent *_gdk_quartz_events_create_swipe_event (NSEvent *event);
355 gboolean _gdk_quartz_event_loop_check_pending (void);
356 NSEvent * _gdk_quartz_event_loop_get_pending (void);
357 diff --git a/gdk/quartz/gdkquartz.h b/gdk/quartz/gdkquartz.h
358 index 742d651..f79d04a 100644
359 --- a/gdk/quartz/gdkquartz.h
360 +++ b/gdk/quartz/gdkquartz.h
361 @@ -57,6 +57,7 @@ NSImage *gdk_quartz_pixbuf_to_ns_image_libgtk_only (GdkPixbuf
362 id gdk_quartz_drag_context_get_dragging_info_libgtk_only (GdkDragContext *context);
363 NSEvent *gdk_quartz_event_get_nsevent (GdkEvent *event);
364 GdkOSXVersion gdk_quartz_osx_version (void);
365 +gboolean gdk_quartz_supports_gesture_events (void);
369 diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
370 index 5a88679..db77675 100644
373 @@ -1636,6 +1636,9 @@ gtk_main_do_event (GdkEvent *event)
374 case GDK_WINDOW_STATE:
375 case GDK_GRAB_BROKEN:
377 + case GDK_GESTURE_MAGNIFY:
378 + case GDK_GESTURE_ROTATE:
379 + case GDK_GESTURE_SWIPE:
380 if (!_gtk_widget_captured_event (event_widget, event))
381 gtk_widget_event (event_widget, event);
383 diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
384 index 9a43d27..2d11e09 100644
385 --- a/gtk/gtkwidget.c
386 +++ b/gtk/gtkwidget.c
387 @@ -197,6 +197,9 @@ enum {
391 + GESTURE_MAGNIFY_EVENT,
392 + GESTURE_ROTATE_EVENT,
393 + GESTURE_SWIPE_EVENT,
397 @@ -2242,6 +2245,70 @@ gtk_widget_class_init (GtkWidgetClass *klass)
398 _gtk_marshal_BOOLEAN__BOXED,
400 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
402 + * GtkWidget::gesture-magnify-event:
403 + * @widget: the object which received the signal
404 + * @event: the #GdkEventGestureMagnify event
406 + * Emitted when a magnify event is received on @widget's window.
408 + * Returns: %TRUE to stop other handlers from being invoked for the event.
409 + * %FALSE to propagate the event further.
411 + * Since: 2.24 Xamarin specific.
413 + widget_signals[GESTURE_MAGNIFY_EVENT] =
414 + g_signal_new (I_("gesture-magnify-event"),
415 + G_TYPE_FROM_CLASS (gobject_class),
418 + _gtk_boolean_handled_accumulator, NULL,
419 + _gtk_marshal_BOOLEAN__BOXED,
421 + GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
423 + * GtkWidget::gesture-rotate-event:
424 + * @widget: the object which received the signal
425 + * @event: the #GdkEventGestureRotate event
427 + * Emitted when a rotation event is received on @widget's window.
429 + * Returns: %TRUE to stop other handlers from being invoked for the event.
430 + * %FALSE to propagate the event further.
432 + * Since: 2.24 Xamarin specific.
434 + widget_signals[GESTURE_ROTATE_EVENT] =
435 + g_signal_new (I_("gesture-rotate-event"),
436 + G_TYPE_FROM_CLASS (gobject_class),
439 + _gtk_boolean_handled_accumulator, NULL,
440 + _gtk_marshal_BOOLEAN__BOXED,
442 + GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
444 + * GtkWidget::gesture-swipe-event:
445 + * @widget: the object which received the signal
446 + * @event: the #GdkEventGestureSwipe event
448 + * Emitted when a swipe event is received on @widget's window.
450 + * Returns: %TRUE to stop other handlers from being invoked for the event.
451 + * %FALSE to propagate the event further.
453 + * Since: 2.24 Xamarin specific.
455 + widget_signals[GESTURE_SWIPE_EVENT] =
456 + g_signal_new (I_("gesture-swipe-event"),
457 + G_TYPE_FROM_CLASS (gobject_class),
460 + _gtk_boolean_handled_accumulator, NULL,
461 + _gtk_marshal_BOOLEAN__BOXED,
463 + GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
466 * GtkWidget::grab-broken-event:
467 * @widget: the object which received the signal
468 @@ -4975,6 +5042,15 @@ gtk_widget_event_internal (GtkWidget *widget,
470 signal_num = DAMAGE_EVENT;
472 + case GDK_GESTURE_MAGNIFY:
473 + signal_num = GESTURE_MAGNIFY_EVENT;
475 + case GDK_GESTURE_ROTATE:
476 + signal_num = GESTURE_ROTATE_EVENT;
478 + case GDK_GESTURE_SWIPE:
479 + signal_num = GESTURE_SWIPE_EVENT;
482 g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
484 diff --git a/tests/Makefile.am b/tests/Makefile.am
485 index 3888826..68a0a79 100644
486 --- a/tests/Makefile.am
487 +++ b/tests/Makefile.am
488 @@ -88,7 +88,8 @@ noinst_PROGRAMS = $(TEST_PROGS) \
497 noinst_PROGRAMS += autotestkeywords
498 @@ -165,6 +166,7 @@ testgrouping_DEPENDENCIES = $(TEST_DEPS)
499 testtooltips_DEPENDENCIES = $(TEST_DEPS)
500 testvolumebutton_DEPENDENCIES = $(TEST_DEPS)
501 testwindows_DEPENDENCIES = $(TEST_DEPS)
502 +testgestures_DEPENDENCIES = $(TEST_DEPS)
504 testentrycompletion_SOURCES = \
506 @@ -273,6 +275,9 @@ testoffscreenwindow_SOURCES = \
507 testwindows_SOURCES = \
510 +testgestures_SOURCES = \
516 diff --git a/tests/testgestures.c b/tests/testgestures.c
518 index 0000000..ae3ab99
520 +++ b/tests/testgestures.c
522 +#include <gtk/gtk.h>
537 + gboolean increasing;
544 +handle_expose_event (GtkWidget *widget,
545 + GdkEventExpose *expose,
546 + gpointer user_data)
549 + int center_x, center_y;
550 + RectangleInfo *rect = (RectangleInfo *)user_data;
552 + cr = gdk_cairo_create (widget->window);
557 + cairo_rectangle (cr, 0, 0,
558 + widget->allocation.width,
559 + widget->allocation.height);
560 + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
563 + cairo_restore (cr);
566 + center_x = (widget->allocation.width - rect->width) / 2;
567 + center_y = (widget->allocation.height - rect->height) / 2;
569 + if (rect->progress != 0.0f)
571 + cairo_translate (cr, rect->offset_x * rect->progress,
572 + rect->offset_y * rect->progress);
577 + cairo_translate (cr, widget->allocation.width / 2,
578 + widget->allocation.height / 2);
579 + cairo_rotate (cr, rect->angle * M_PI / 180.0);
580 + cairo_translate (cr, -widget->allocation.width / 2,
581 + -widget->allocation.height / 2);
584 + cairo_rectangle (cr,
585 + center_x, center_y,
586 + rect->width, rect->height);
587 + cairo_set_source_rgb (cr, 0.9, 0.0, 0.0);
590 + cairo_rectangle (cr,
591 + center_x, center_y,
592 + rect->width, rect->height);
593 + cairo_set_source_rgba (cr, 0.9, 0.0, 0.0, 0.3);
596 + cairo_restore (cr);
599 + cairo_destroy (cr);
605 +handle_gesture_magnify_event (GtkWidget *widget,
606 + GdkEventGestureMagnify *magnify,
607 + gpointer user_data)
609 + RectangleInfo *rect = (RectangleInfo *)user_data;
611 + rect->width += rect->width * magnify->magnification;
612 + if (rect->width < 5)
615 + rect->height += rect->height * magnify->magnification;
616 + if (rect->height < 5)
619 + gtk_widget_queue_draw (widget);
625 +handle_gesture_rotate_event (GtkWidget *widget,
626 + GdkEventGestureRotate *rotate,
627 + gpointer user_data)
629 + RectangleInfo *rect = (RectangleInfo *)user_data;
631 + rect->angle -= rotate->rotation;
633 + gtk_widget_queue_draw (widget);
640 +bounce_timeout (gpointer user_data)
642 + gboolean retval = TRUE;
643 + RectangleInfo *rect = (RectangleInfo *)user_data;
645 + if (rect->increasing)
646 + rect->progress += 0.10f;
648 + rect->progress -= 0.10f;
650 + if (rect->progress > 1.0f)
652 + rect->progress = 0.90f;
653 + rect->increasing = FALSE;
655 + else if (rect->progress <= 0.0f)
657 + rect->progress = 0.0f;
661 + gtk_widget_queue_draw (rect->widget);
667 +bounce (RectangleInfo *rect,
671 + if (rect->progress != 0.0f)
674 + rect->progress = 0.10f;
675 + rect->increasing = TRUE;
676 + rect->offset_x = offset_x;
677 + rect->offset_y = offset_y;
678 + gtk_widget_queue_draw (rect->widget);
680 + gdk_threads_add_timeout (25, bounce_timeout, rect);
684 +handle_gesture_swipe_event (GtkWidget *widget,
685 + GdkEventGestureSwipe *swipe,
686 + gpointer user_data)
688 + int offset_x = 150, offset_y = 150;
689 + RectangleInfo *rect = (RectangleInfo *)user_data;
691 + offset_x *= -1.0 * swipe->delta_x;
692 + offset_y *= -1.0 * swipe->delta_y;
694 + bounce (rect, offset_x, offset_y);
700 +main (int argc, char **argv)
703 + GtkWidget *drawing_area;
704 + RectangleInfo rect;
706 + gtk_init (&argc, &argv);
709 + rect.height = 40.0;
711 + rect.progress = 0.0f;
713 + window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
714 + gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
715 + g_signal_connect (window, "delete-event",
716 + G_CALLBACK (gtk_main_quit), NULL);
718 + drawing_area = gtk_drawing_area_new ();
719 + rect.widget = drawing_area;
720 + g_signal_connect (drawing_area, "expose-event",
721 + G_CALLBACK (handle_expose_event), &rect);
722 + g_signal_connect (drawing_area, "gesture-magnify-event",
723 + G_CALLBACK (handle_gesture_magnify_event), &rect);
724 + g_signal_connect (drawing_area, "gesture-rotate-event",
725 + G_CALLBACK (handle_gesture_rotate_event), &rect);
726 + g_signal_connect (drawing_area, "gesture-swipe-event",
727 + G_CALLBACK (handle_gesture_swipe_event), &rect);
728 + gtk_container_add (GTK_CONTAINER (window), drawing_area);
730 + gtk_widget_show_all (window);