3bc316c1a70fd8d0fddee5cdcf81912614a8a8ce
[mono.git] / bockbuild / mac-sdk / patches / gtk-gestures.patch
1 diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
2 index e6c516c..f3fa26a 100644
3 --- a/gdk/gdkevents.h
4 +++ b/gdk/gdkevents.h
5 @@ -65,6 +65,11 @@ typedef struct _GdkEventWindowState GdkEventWindowState;
6  typedef struct _GdkEventSetting     GdkEventSetting;
7  typedef struct _GdkEventGrabBroken  GdkEventGrabBroken;
8
9 +/* OS X specific gesture events */
10 +typedef struct _GdkEventGestureMagnify GdkEventGestureMagnify;
11 +typedef struct _GdkEventGestureRotate  GdkEventGestureRotate;
12 +typedef struct _GdkEventGestureSwipe   GdkEventGestureSwipe;
13 +
14  typedef union  _GdkEvent           GdkEvent;
15
16  typedef void (*GdkEventFunc) (GdkEvent *event,
17 @@ -152,6 +157,9 @@ typedef enum
18    GDK_OWNER_CHANGE      = 34,
19    GDK_GRAB_BROKEN       = 35,
20    GDK_DAMAGE            = 36,
21 +  GDK_GESTURE_MAGNIFY   = 37,
22 +  GDK_GESTURE_ROTATE    = 38,
23 +  GDK_GESTURE_SWIPE     = 39,
24    GDK_EVENT_LAST        /* helper variable for decls */
25  } GdkEventType;
26
27 @@ -488,6 +496,52 @@ struct _GdkEventDND {
28    gshort x_root, y_root;
29  };
30
31 +/* Event types for OS X gestures */
32 +
33 +struct _GdkEventGestureMagnify
34 +{
35 +  GdkEventType type;
36 +  GdkWindow *window;
37 +  gint8 send_event;
38 +  guint32 time;
39 +  gdouble x;
40 +  gdouble y;
41 +  guint state;
42 +  gdouble magnification;
43 +  GdkDevice *device;
44 +  gdouble x_root, y_root;
45 +};
46 +
47 +struct _GdkEventGestureRotate
48 +{
49 +  GdkEventType type;
50 +  GdkWindow *window;
51 +  gint8 send_event;
52 +  guint32 time;
53 +  gdouble x;
54 +  gdouble y;
55 +  guint state;
56 +  gdouble rotation;
57 +  GdkDevice *device;
58 +  gdouble x_root, y_root;
59 +};
60 +
61 +struct _GdkEventGestureSwipe
62 +{
63 +  GdkEventType type;
64 +  GdkWindow *window;
65 +  gint8 send_event;
66 +  guint32 time;
67 +  gdouble x;
68 +  gdouble y;
69 +  guint state;
70 +  gdouble delta_x;
71 +  gdouble delta_y;
72 +  GdkDevice *device;
73 +  gdouble x_root, y_root;
74 +};
75 +
76 +
77  union _GdkEvent
78  {
79    GdkEventType             type;
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;
87  };
88
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
92 --- a/gdk/gdkwindow.c
93 +++ b/gdk/gdkwindow.c
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 */
101  };
102  G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
103
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
108 @@ -190,4 +190,37 @@
109      [self updateTrackingRect];
110  }
111
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.
116 + */
117 +
118 +-(void)magnifyWithEvent:(NSEvent *)event
119 +{
120 +  GdkEvent *gdk_event;
121 +
122 +  gdk_event = _gdk_quartz_events_create_magnify_event (event);
123 +  if (gdk_event)
124 +    _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
125 +}
126 +
127 +-(void)rotateWithEvent:(NSEvent *)event
128 +{
129 +  GdkEvent *gdk_event;
130 +
131 +  gdk_event = _gdk_quartz_events_create_rotate_event (event);
132 +  if (gdk_event)
133 +    _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
134 +}
135 +
136 +-(void)swipeWithEvent:(NSEvent *)event
137 +{
138 +  GdkEvent *gdk_event;
139 +
140 +  gdk_event = _gdk_quartz_events_create_swipe_event (event);
141 +  if (gdk_event)
142 +    _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
143 +}
144 +
145  @end
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)
151      case NSMouseExited:
152        return GDK_LEAVE_NOTIFY_MASK;
153
154 +    case NSEventTypeMagnify:
155 +    case NSEventTypeRotate:
156 +    case NSEventTypeSwipe:
157 +      return 0;
158 +
159      default:
160        g_assert_not_reached ();
161      }
162 @@ -752,6 +757,11 @@ find_window_for_ns_event (NSEvent *nsevent,
163
164        return toplevel;
165
166 +    case NSEventTypeMagnify:
167 +    case NSEventTypeRotate:
168 +    case NSEventTypeSwipe:
169 +      return toplevel;
170 +
171      default:
172        /* Ignore everything else. */
173        break;
174 @@ -1011,6 +1021,139 @@ fill_key_event (GdkWindow    *window,
175           event->key.keyval));
176  }
177
178 +
179 +static GdkWindow *
180 +find_window_for_ns_gesture_event (NSEvent *nsevent,
181 +                                  gint    *_x,
182 +                                  gint    *_y,
183 +                                  gint    *x_root,
184 +                                  gint    *y_root)
185 +{
186 +  GdkDisplay *display = _gdk_display;
187 +  GdkWindow *toplevel_window = NULL, *pointer_window = NULL;
188 +  gdouble found_x, found_y;
189 +  gint x, y;
190 +
191 +  toplevel_window = find_window_for_ns_event (nsevent, &x, &y, x_root, y_root);
192 +  if (!toplevel_window)
193 +    return NULL;
194 +
195 +  if (toplevel_window == display->pointer_info.toplevel_under_pointer)
196 +    pointer_window = _gdk_window_find_descendant_at (toplevel_window,
197 +                                                     x, y,
198 +                                                     &found_x, &found_y);
199 +  else
200 +    pointer_window = NULL;
201 +
202 +  *_x = found_x;
203 +  *_y = found_y;
204 +
205 +  return pointer_window;
206 +}
207 +
208 +
209 +GdkEvent *
210 +_gdk_quartz_events_create_magnify_event (NSEvent *nsevent)
211 +{
212 +  GdkEvent *event;
213 +  GdkWindow *window = NULL;
214 +  gint x, y, x_root, y_root;
215 +
216 +  window = find_window_for_ns_gesture_event (nsevent,
217 +                                             &x, &y,
218 +                                             &x_root, &y_root);
219 +
220 +  if (!window)
221 +    return NULL;
222 +
223 +  event = gdk_event_new (GDK_GESTURE_MAGNIFY);
224 +
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;
236 +
237 +  fixup_event (event);
238 +
239 +  return event;
240 +}
241 +
242 +GdkEvent *
243 +_gdk_quartz_events_create_rotate_event (NSEvent *nsevent)
244 +{
245 +  GdkEvent *event;
246 +  GdkWindow *window;
247 +  gint x, y, x_root, y_root;
248 +
249 +  window = find_window_for_ns_gesture_event (nsevent,
250 +                                             &x, &y,
251 +                                             &x_root, &y_root);
252 +
253 +  if (!window)
254 +    return NULL;
255 +
256 +  event = gdk_event_new (GDK_GESTURE_ROTATE);
257 +
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;
269 +
270 +  fixup_event (event);
271 +
272 +  return event;
273 +}
274 +
275 +GdkEvent *
276 +_gdk_quartz_events_create_swipe_event (NSEvent *nsevent)
277 +{
278 +  GdkEvent *event;
279 +  GdkWindow *window;
280 +  gint x, y, x_root, y_root;
281 +
282 +  window = find_window_for_ns_gesture_event (nsevent,
283 +                                             &x, &y,
284 +                                             &x_root, &y_root);
285 +
286 +  if (!window)
287 +    return NULL;
288 +
289 +  event = gdk_event_new (GDK_GESTURE_SWIPE);
290 +
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;
303 +
304 +  fixup_event (event);
305 +
306 +  return event;
307 +}
308 +
309 +
310 +
311  static gboolean
312  synthesize_crossing_event (GdkWindow *window,
313                             GdkEvent  *event,
314 @@ -1392,6 +1535,13 @@ gdk_event_translate (GdkEvent *event,
315        }
316        break;
317
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.
323 +     */
324 +
325      default:
326        /* Ignore everything elsee. */
327        return_val = FALSE;
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)
333    else
334      return minor;
335  }
336 +
337 +gboolean
338 +gdk_quartz_supports_gesture_events (void)
339 +{
340 +  return TRUE;
341 +}
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);
349
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);
353 +
354  /* Event loop */
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);
366
367  G_END_DECLS
368
369 diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
370 index 5a88679..db77675 100644
371 --- a/gtk/gtkmain.c
372 +++ b/gtk/gtkmain.c
373 @@ -1636,6 +1636,9 @@ gtk_main_do_event (GdkEvent *event)
374      case GDK_WINDOW_STATE:
375      case GDK_GRAB_BROKEN:
376      case GDK_DAMAGE:
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);
382        break;
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 {
388    KEYNAV_FAILED,
389    DRAG_FAILED,
390    DAMAGE_EVENT,
391 +  GESTURE_MAGNIFY_EVENT,
392 +  GESTURE_ROTATE_EVENT,
393 +  GESTURE_SWIPE_EVENT,
394    LAST_SIGNAL
395  };
396
397 @@ -2242,6 +2245,70 @@ gtk_widget_class_init (GtkWidgetClass *klass)
398                   _gtk_marshal_BOOLEAN__BOXED,
399                   G_TYPE_BOOLEAN, 1,
400                   GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
401 +  /**
402 +   * GtkWidget::gesture-magnify-event:
403 +   * @widget: the object which received the signal
404 +   * @event: the #GdkEventGestureMagnify event
405 +   *
406 +   * Emitted when a magnify event is received on @widget's window.
407 +   *
408 +   * Returns: %TRUE to stop other handlers from being invoked for the event.
409 +   *   %FALSE to propagate the event further.
410 +   *
411 +   * Since: 2.24 Xamarin specific.
412 +   */
413 +  widget_signals[GESTURE_MAGNIFY_EVENT] =
414 +    g_signal_new (I_("gesture-magnify-event"),
415 +                 G_TYPE_FROM_CLASS (gobject_class),
416 +                 G_SIGNAL_RUN_LAST,
417 +                  0,
418 +                 _gtk_boolean_handled_accumulator, NULL,
419 +                 _gtk_marshal_BOOLEAN__BOXED,
420 +                 G_TYPE_BOOLEAN, 1,
421 +                 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
422 +  /**
423 +   * GtkWidget::gesture-rotate-event:
424 +   * @widget: the object which received the signal
425 +   * @event: the #GdkEventGestureRotate event
426 +   *
427 +   * Emitted when a rotation event is received on @widget's window.
428 +   *
429 +   * Returns: %TRUE to stop other handlers from being invoked for the event.
430 +   *   %FALSE to propagate the event further.
431 +   *
432 +   * Since: 2.24 Xamarin specific.
433 +   */
434 +  widget_signals[GESTURE_ROTATE_EVENT] =
435 +    g_signal_new (I_("gesture-rotate-event"),
436 +                 G_TYPE_FROM_CLASS (gobject_class),
437 +                 G_SIGNAL_RUN_LAST,
438 +                  0,
439 +                 _gtk_boolean_handled_accumulator, NULL,
440 +                 _gtk_marshal_BOOLEAN__BOXED,
441 +                 G_TYPE_BOOLEAN, 1,
442 +                 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
443 +  /**
444 +   * GtkWidget::gesture-swipe-event:
445 +   * @widget: the object which received the signal
446 +   * @event: the #GdkEventGestureSwipe event
447 +   *
448 +   * Emitted when a swipe event is received on @widget's window.
449 +   *
450 +   * Returns: %TRUE to stop other handlers from being invoked for the event.
451 +   *   %FALSE to propagate the event further.
452 +   *
453 +   * Since: 2.24 Xamarin specific.
454 +   */
455 +  widget_signals[GESTURE_SWIPE_EVENT] =
456 +    g_signal_new (I_("gesture-swipe-event"),
457 +                 G_TYPE_FROM_CLASS (gobject_class),
458 +                 G_SIGNAL_RUN_LAST,
459 +                  0,
460 +                 _gtk_boolean_handled_accumulator, NULL,
461 +                 _gtk_marshal_BOOLEAN__BOXED,
462 +                 G_TYPE_BOOLEAN, 1,
463 +                 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
464 +
465  /**
466     * GtkWidget::grab-broken-event:
467     * @widget: the object which received the signal
468 @@ -4975,6 +5042,15 @@ gtk_widget_event_internal (GtkWidget *widget,
469         case GDK_DAMAGE:
470           signal_num = DAMAGE_EVENT;
471           break;
472 +        case GDK_GESTURE_MAGNIFY:
473 +          signal_num = GESTURE_MAGNIFY_EVENT;
474 +          break;
475 +        case GDK_GESTURE_ROTATE:
476 +          signal_num = GESTURE_ROTATE_EVENT;
477 +          break;
478 +        case GDK_GESTURE_SWIPE:
479 +          signal_num = GESTURE_SWIPE_EVENT;
480 +          break;
481         default:
482           g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
483           signal_num = -1;
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)      \
489         testactions                     \
490         testgrouping                    \
491         testtooltips                    \
492 -       testvolumebutton
493 +       testvolumebutton                \
494 +       testgestures
495
496  if HAVE_CXX
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)
503
504  testentrycompletion_SOURCES =  \
505         prop-editor.c           \
506 @@ -273,6 +275,9 @@ testoffscreenwindow_SOURCES =       \
507  testwindows_SOURCES =  \
508         testwindows.c
509
510 +testgestures_SOURCES =         \
511 +       testgestures.c
512 +
513  EXTRA_DIST +=                  \
514         prop-editor.h           \
515         testgtk.1               \
516 diff --git a/tests/testgestures.c b/tests/testgestures.c
517 new file mode 100644
518 index 0000000..ae3ab99
519 --- /dev/null
520 +++ b/tests/testgestures.c
521 @@ -0,0 +1,214 @@
522 +#include <gtk/gtk.h>
523 +#include <math.h>
524 +
525 +
526 +typedef struct
527 +{
528 +  gdouble width;
529 +  gdouble height;
530 +
531 +  gdouble angle;
532 +
533 +  GtkWidget *widget;
534 +  gint offset_x;
535 +  gint offset_y;
536 +  gdouble progress;
537 +  gboolean increasing;
538 +}
539 +RectangleInfo;
540 +
541 +
542 +
543 +static gboolean
544 +handle_expose_event (GtkWidget      *widget,
545 +                     GdkEventExpose *expose,
546 +                     gpointer        user_data)
547 +{
548 +  cairo_t *cr;
549 +  int center_x, center_y;
550 +  RectangleInfo *rect = (RectangleInfo *)user_data;
551 +
552 +  cr = gdk_cairo_create (widget->window);
553 +
554 +  cairo_save (cr);
555 +
556 +  /* Background */
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);
561 +  cairo_fill (cr);
562 +
563 +  cairo_restore (cr);
564 +
565 +  /* Rectangle */
566 +  center_x = (widget->allocation.width - rect->width) / 2;
567 +  center_y = (widget->allocation.height - rect->height) / 2;
568 +
569 +  if (rect->progress != 0.0f)
570 +    {
571 +      cairo_translate (cr, rect->offset_x * rect->progress,
572 +                       rect->offset_y * rect->progress);
573 +    }
574 +
575 +  cairo_save (cr);
576 +
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);
582 +
583 +
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);
588 +  cairo_stroke (cr);
589 +
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);
594 +  cairo_fill (cr);
595 +
596 +  cairo_restore (cr);
597 +
598 +
599 +  cairo_destroy (cr);
600 +
601 +  return FALSE;
602 +}
603 +
604 +static gboolean
605 +handle_gesture_magnify_event (GtkWidget              *widget,
606 +                              GdkEventGestureMagnify *magnify,
607 +                              gpointer                user_data)
608 +{
609 +  RectangleInfo *rect = (RectangleInfo *)user_data;
610 +
611 +  rect->width += rect->width * magnify->magnification;
612 +  if (rect->width < 5)
613 +    rect->width = 5;
614 +
615 +  rect->height += rect->height * magnify->magnification;
616 +  if (rect->height < 5)
617 +    rect->height = 5;
618 +
619 +  gtk_widget_queue_draw (widget);
620 +
621 +  return TRUE;
622 +}
623 +
624 +static gboolean
625 +handle_gesture_rotate_event (GtkWidget             *widget,
626 +                             GdkEventGestureRotate *rotate,
627 +                             gpointer               user_data)
628 +{
629 +  RectangleInfo *rect = (RectangleInfo *)user_data;
630 +
631 +  rect->angle -= rotate->rotation;
632 +
633 +  gtk_widget_queue_draw (widget);
634 +
635 +  return TRUE;
636 +}
637 +
638 +
639 +static gboolean
640 +bounce_timeout (gpointer user_data)
641 +{
642 +  gboolean retval = TRUE;
643 +  RectangleInfo *rect = (RectangleInfo *)user_data;
644 +
645 +  if (rect->increasing)
646 +    rect->progress += 0.10f;
647 +  else
648 +    rect->progress -= 0.10f;
649 +
650 +  if (rect->progress > 1.0f)
651 +    {
652 +      rect->progress = 0.90f;
653 +      rect->increasing = FALSE;
654 +    }
655 +  else if (rect->progress <= 0.0f)
656 +    {
657 +      rect->progress = 0.0f;
658 +      retval = FALSE;
659 +    }
660 +
661 +  gtk_widget_queue_draw (rect->widget);
662 +
663 +  return retval;
664 +}
665 +
666 +static void
667 +bounce (RectangleInfo *rect,
668 +        int            offset_x,
669 +        int            offset_y)
670 +{
671 +  if (rect->progress != 0.0f)
672 +    return;
673 +
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);
679 +
680 +  gdk_threads_add_timeout (25, bounce_timeout, rect);
681 +}
682 +
683 +static gboolean
684 +handle_gesture_swipe_event (GtkWidget             *widget,
685 +                            GdkEventGestureSwipe  *swipe,
686 +                            gpointer               user_data)
687 +{
688 +  int offset_x = 150, offset_y = 150;
689 +  RectangleInfo *rect = (RectangleInfo *)user_data;
690 +
691 +  offset_x *= -1.0 * swipe->delta_x;
692 +  offset_y *= -1.0 * swipe->delta_y;
693 +
694 +  bounce (rect, offset_x, offset_y);
695 +
696 +  return TRUE;
697 +}
698 +
699 +int
700 +main (int argc, char **argv)
701 +{
702 +  GtkWidget *window;
703 +  GtkWidget *drawing_area;
704 +  RectangleInfo rect;
705 +
706 +  gtk_init (&argc, &argv);
707 +
708 +  rect.width = 40.0;
709 +  rect.height = 40.0;
710 +  rect.angle = 0.0;
711 +  rect.progress = 0.0f;
712 +
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);
717 +
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);
729 +
730 +  gtk_widget_show_all (window);
731 +
732 +  gtk_main ();
733 +
734 +  return 0;
735 +}