1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2012, One Laptop Per Child.
3 * Copyright (C) 2014, Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author(s): Carlos Garnacho <carlosg@gnome.org>
19 */
20
21 /**
22 * SECTION:gtkgesture
23 * @Short_description: Base class for gestures
24 * @Title: GtkGesture
25 * @See_also: #GtkEventController, #GtkGestureSingle
26 *
27 * #GtkGesture is the base object for gesture recognition, although this
28 * object is quite generalized to serve as a base for multi-touch gestures,
29 * it is suitable to implement single-touch and pointer-based gestures (using
30 * the special %NULL #GdkEventSequence value for these).
31 *
32 * The number of touches that a #GtkGesture need to be recognized is controlled
33 * by the #GtkGesture:n-points property, if a gesture is keeping track of less
34 * or more than that number of sequences, it won't check wether the gesture
35 * is recognized.
36 *
37 * As soon as the gesture has the expected number of touches, the gesture will
38 * run the #GtkGesture::check signal regularly on input events until the gesture
39 * is recognized, the criteria to consider a gesture as "recognized" is left to
40 * #GtkGesture subclasses.
41 *
42 * A recognized gesture will then emit the following signals:
43 * - #GtkGesture::begin when the gesture is recognized.
44 * - A number of #GtkGesture::update, whenever an input event is processed.
45 * - #GtkGesture::end when the gesture is no longer recognized.
46 *
47 * ## Event propagation
48 *
49 * In order to receive events, a gesture needs to either set a propagation phase
50 * through gtk_event_controller_set_propagation_phase(), or feed those manually
51 * through gtk_event_controller_handle_event().
52 *
53 * In the capture phase, events are propagated from the toplevel down to the
54 * target widget, and gestures that are attached to containers above the widget
55 * get a chance to interact with the event before it reaches the target.
56 *
57 * After the capture phase, GTK+ emits the traditional #GtkWidget::button-press-event,
58 * #GtkWidget::button-release-event, #GtkWidget::touch-event, etc signals. Gestures
59 * with the %GTK_PHASE_TARGET phase are fed events from the default #GtkWidget::event
60 * handlers.
61 *
62 * In the bubble phase, events are propagated up from the target widget to the
63 * toplevel, and gestures that are attached to containers above the widget get
64 * a chance to interact with events that have not been handled yet.
65 *
66 * ## States of a sequence # {#touch-sequence-states}
67 *
68 * Whenever input interaction happens, a single event may trigger a cascade of
69 * #GtkGestures, both across the parents of the widget receiving the event and
70 * in parallel within an individual widget. It is a responsibility of the
71 * widgets using those gestures to set the state of touch sequences accordingly
72 * in order to enable cooperation of gestures around the #GdkEventSequences
73 * triggering those.
74 *
75 * Within a widget, gestures can be grouped through gtk_gesture_group(),
76 * grouped gestures synchronize the state of sequences, so calling
77 * gtk_gesture_set_sequence_state() on one will effectively propagate
78 * the state throughout the group.
79 *
80 * By default, all sequences start out in the #GTK_EVENT_SEQUENCE_NONE state,
81 * sequences in this state trigger the gesture event handler, but event
82 * propagation will continue unstopped by gestures.
83 *
84 * If a sequence enters into the #GTK_EVENT_SEQUENCE_DENIED state, the gesture
85 * group will effectively ignore the sequence, letting events go unstopped
86 * through the gesture, but the "slot" will still remain occupied while
87 * the touch is active.
88 *
89 * If a sequence enters in the #GTK_EVENT_SEQUENCE_CLAIMED state, the gesture
90 * group will grab all interaction on the sequence, by:
91 * - Setting the same sequence to #GTK_EVENT_SEQUENCE_DENIED on every other gesture
92 * group within the widget, and every gesture on parent widgets in the propagation
93 * chain.
94 * - calling #GtkGesture::cancel on every gesture in widgets underneath in the
95 * propagation chain.
96 * - Stopping event propagation after the gesture group handles the event.
97 *
98 * Note: if a sequence is set early to #GTK_EVENT_SEQUENCE_CLAIMED on
99 * #GDK_TOUCH_BEGIN/#GDK_BUTTON_PRESS (so those events are captured before
100 * reaching the event widget, this implies #GTK_PHASE_CAPTURE), one similar
101 * event will emulated if the sequence changes to #GTK_EVENT_SEQUENCE_DENIED.
102 * This way event coherence is preserved before event propagation is unstopped
103 * again.
104 *
105 * Sequence states can't be changed freely, see gtk_gesture_set_sequence_state()
106 * to know about the possible lifetimes of a #GdkEventSequence.
107 *
108 * ## Touchpad gestures
109 *
110 * On the platforms that support it, #GtkGesture will handle transparently
111 * touchpad gesture events. The only precautions users of #GtkGesture should do
112 * to enable this support are:
113 * - Enabling %GDK_TOUCHPAD_GESTURE_MASK on their #GdkWindows
114 * - If the gesture has %GTK_PHASE_NONE, ensuring events of type
115 * %GDK_TOUCHPAD_SWIPE and %GDK_TOUCHPAD_PINCH are handled by the #GtkGesture
116 */
117
118 #include "config.h"
119 #include "gtkgesture.h"
120 #include "gtkwidgetprivate.h"
121 #include "gtkeventcontrollerprivate.h"
122 #include "gtkgestureprivate.h"
123 #include "gtktypebuiltins.h"
124 #include "gtkprivate.h"
125 #include "gtkmain.h"
126 #include "gtkintl.h"
127 #include "gtkmarshalers.h"
128
129 typedef struct _GtkGesturePrivate GtkGesturePrivate;
130 typedef struct _PointData PointData;
131
132 enum {
133 PROP_N_POINTS = 1,
134 PROP_WINDOW
135 };
136
137 enum {
138 BEGIN,
139 END,
140 UPDATE,
141 CANCEL,
142 SEQUENCE_STATE_CHANGED,
143 N_SIGNALS
144 };
145
146 struct _PointData
147 {
148 GdkEvent *event;
149 gdouble widget_x;
150 gdouble widget_y;
151
152 /* Acummulators for touchpad events */
153 gdouble accum_dx;
154 gdouble accum_dy;
155
156 guint press_handled : 1;
157 guint state : 2;
158 };
159
160 struct _GtkGesturePrivate
161 {
162 GHashTable *points;
163 GdkEventSequence *last_sequence;
164 GdkWindow *user_window;
165 GdkWindow *window;
166 GdkDevice *device;
167 GList *group_link;
168 guint n_points;
169 guint recognized : 1;
170 guint touchpad : 1;
171 };
172
173 static guint signals[N_SIGNALS] = { 0 };
174
175 #define BUTTONS_MASK (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)
176
177 #define EVENT_IS_TOUCHPAD_GESTURE(e) ((e)->type == GDK_TOUCHPAD_SWIPE || \
178 (e)->type == GDK_TOUCHPAD_PINCH)
179
180 GList * _gtk_gesture_get_group_link (GtkGesture *gesture);
181
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(GtkGesture,gtk_gesture,GTK_TYPE_EVENT_CONTROLLER)182 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkGesture, gtk_gesture, GTK_TYPE_EVENT_CONTROLLER)
183
184 static void
185 gtk_gesture_get_property (GObject *object,
186 guint prop_id,
187 GValue *value,
188 GParamSpec *pspec)
189 {
190 GtkGesturePrivate *priv = gtk_gesture_get_instance_private (GTK_GESTURE (object));
191
192 switch (prop_id)
193 {
194 case PROP_N_POINTS:
195 g_value_set_uint (value, priv->n_points);
196 break;
197 case PROP_WINDOW:
198 g_value_set_object (value, priv->user_window);
199 break;
200 default:
201 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
202 }
203 }
204
205 static void
gtk_gesture_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)206 gtk_gesture_set_property (GObject *object,
207 guint prop_id,
208 const GValue *value,
209 GParamSpec *pspec)
210 {
211 GtkGesturePrivate *priv = gtk_gesture_get_instance_private (GTK_GESTURE (object));
212
213 switch (prop_id)
214 {
215 case PROP_N_POINTS:
216 priv->n_points = g_value_get_uint (value);
217 break;
218 case PROP_WINDOW:
219 gtk_gesture_set_window (GTK_GESTURE (object),
220 g_value_get_object (value));
221 break;
222 default:
223 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224 }
225 }
226
227 static void
gtk_gesture_finalize(GObject * object)228 gtk_gesture_finalize (GObject *object)
229 {
230 GtkGesture *gesture = GTK_GESTURE (object);
231 GtkGesturePrivate *priv = gtk_gesture_get_instance_private (gesture);
232
233 gtk_gesture_ungroup (gesture);
234 g_list_free (priv->group_link);
235
236 g_hash_table_destroy (priv->points);
237
238 G_OBJECT_CLASS (gtk_gesture_parent_class)->finalize (object);
239 }
240
241 static guint
_gtk_gesture_get_n_touchpad_points(GtkGesture * gesture,gboolean only_active)242 _gtk_gesture_get_n_touchpad_points (GtkGesture *gesture,
243 gboolean only_active)
244 {
245 GtkGesturePrivate *priv;
246 PointData *data;
247
248 priv = gtk_gesture_get_instance_private (gesture);
249
250 if (!priv->touchpad)
251 return 0;
252
253 data = g_hash_table_lookup (priv->points, NULL);
254
255 if (!data)
256 return 0;
257
258 if (only_active &&
259 (data->state == GTK_EVENT_SEQUENCE_DENIED ||
260 (data->event->type == GDK_TOUCHPAD_SWIPE &&
261 data->event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_END) ||
262 (data->event->type == GDK_TOUCHPAD_PINCH &&
263 data->event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_END)))
264 return 0;
265
266 switch (data->event->type)
267 {
268 case GDK_TOUCHPAD_SWIPE:
269 return data->event->touchpad_swipe.n_fingers;
270 case GDK_TOUCHPAD_PINCH:
271 return data->event->touchpad_pinch.n_fingers;
272 default:
273 return 0;
274 }
275 }
276
277 static guint
_gtk_gesture_get_n_touch_points(GtkGesture * gesture,gboolean only_active)278 _gtk_gesture_get_n_touch_points (GtkGesture *gesture,
279 gboolean only_active)
280 {
281 GtkGesturePrivate *priv;
282 GHashTableIter iter;
283 guint n_points = 0;
284 PointData *data;
285
286 priv = gtk_gesture_get_instance_private (gesture);
287 g_hash_table_iter_init (&iter, priv->points);
288
289 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
290 {
291 if (only_active &&
292 (data->state == GTK_EVENT_SEQUENCE_DENIED ||
293 data->event->type == GDK_TOUCH_END ||
294 data->event->type == GDK_BUTTON_RELEASE))
295 continue;
296
297 n_points++;
298 }
299
300 return n_points;
301 }
302
303 static guint
_gtk_gesture_get_n_physical_points(GtkGesture * gesture,gboolean only_active)304 _gtk_gesture_get_n_physical_points (GtkGesture *gesture,
305 gboolean only_active)
306 {
307 GtkGesturePrivate *priv;
308
309 priv = gtk_gesture_get_instance_private (gesture);
310
311 if (priv->touchpad)
312 return _gtk_gesture_get_n_touchpad_points (gesture, only_active);
313 else
314 return _gtk_gesture_get_n_touch_points (gesture, only_active);
315 }
316
317 static gboolean
gtk_gesture_check_impl(GtkGesture * gesture)318 gtk_gesture_check_impl (GtkGesture *gesture)
319 {
320 GtkGesturePrivate *priv;
321 guint n_points;
322
323 priv = gtk_gesture_get_instance_private (gesture);
324 n_points = _gtk_gesture_get_n_physical_points (gesture, TRUE);
325
326 return n_points == priv->n_points;
327 }
328
329 static void
_gtk_gesture_set_recognized(GtkGesture * gesture,gboolean recognized,GdkEventSequence * sequence)330 _gtk_gesture_set_recognized (GtkGesture *gesture,
331 gboolean recognized,
332 GdkEventSequence *sequence)
333 {
334 GtkGesturePrivate *priv;
335
336 priv = gtk_gesture_get_instance_private (gesture);
337
338 if (priv->recognized == recognized)
339 return;
340
341 priv->recognized = recognized;
342
343 if (recognized)
344 g_signal_emit (gesture, signals[BEGIN], 0, sequence);
345 else
346 g_signal_emit (gesture, signals[END], 0, sequence);
347 }
348
349 static gboolean
_gtk_gesture_do_check(GtkGesture * gesture)350 _gtk_gesture_do_check (GtkGesture *gesture)
351 {
352 GtkGestureClass *gesture_class;
353 gboolean retval = FALSE;
354
355 gesture_class = GTK_GESTURE_GET_CLASS (gesture);
356
357 if (!gesture_class->check)
358 return retval;
359
360 retval = gesture_class->check (gesture);
361 return retval;
362 }
363
364 static gboolean
_gtk_gesture_has_matching_touchpoints(GtkGesture * gesture)365 _gtk_gesture_has_matching_touchpoints (GtkGesture *gesture)
366 {
367 GtkGesturePrivate *priv = gtk_gesture_get_instance_private (gesture);
368 guint active_n_points, current_n_points;
369
370 current_n_points = _gtk_gesture_get_n_physical_points (gesture, FALSE);
371 active_n_points = _gtk_gesture_get_n_physical_points (gesture, TRUE);
372
373 return (active_n_points == priv->n_points &&
374 current_n_points == priv->n_points);
375 }
376
377 static gboolean
_gtk_gesture_check_recognized(GtkGesture * gesture,GdkEventSequence * sequence)378 _gtk_gesture_check_recognized (GtkGesture *gesture,
379 GdkEventSequence *sequence)
380 {
381 GtkGesturePrivate *priv = gtk_gesture_get_instance_private (gesture);
382 gboolean has_matching_touchpoints;
383
384 has_matching_touchpoints = _gtk_gesture_has_matching_touchpoints (gesture);
385
386 if (priv->recognized && !has_matching_touchpoints)
387 _gtk_gesture_set_recognized (gesture, FALSE, sequence);
388 else if (!priv->recognized && has_matching_touchpoints &&
389 _gtk_gesture_do_check (gesture))
390 _gtk_gesture_set_recognized (gesture, TRUE, sequence);
391
392 return priv->recognized;
393 }
394
395 /* Finds the first window pertaining to the controller's widget */
396 static GdkWindow *
_find_widget_window(GtkGesture * gesture,GdkWindow * window)397 _find_widget_window (GtkGesture *gesture,
398 GdkWindow *window)
399 {
400 GtkWidget *widget, *window_widget;
401
402 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
403
404 while (window && !gdk_window_is_destroyed (window))
405 {
406 gdk_window_get_user_data (window, (gpointer*) &window_widget);
407
408 if (window_widget == widget ||
409 gtk_widget_get_window (widget) == window)
410 return window;
411
412 window = gdk_window_get_effective_parent (window);
413 }
414
415 return NULL;
416 }
417
418 static void
_update_touchpad_deltas(PointData * data)419 _update_touchpad_deltas (PointData *data)
420 {
421 GdkEvent *event = data->event;
422
423 if (!event)
424 return;
425
426 if (event->type == GDK_TOUCHPAD_SWIPE)
427 {
428 if (event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN)
429 data->accum_dx = data->accum_dy = 0;
430 else if (event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE)
431 {
432 data->accum_dx += event->touchpad_swipe.dx;
433 data->accum_dy += event->touchpad_swipe.dy;
434 }
435 }
436 else if (event->type == GDK_TOUCHPAD_PINCH)
437 {
438 if (event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN)
439 data->accum_dx = data->accum_dy = 0;
440 else if (event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE)
441 {
442 data->accum_dx += event->touchpad_pinch.dx;
443 data->accum_dy += event->touchpad_pinch.dy;
444 }
445 }
446 }
447
448 static void
_get_event_coordinates(PointData * data,gdouble * x,gdouble * y)449 _get_event_coordinates (PointData *data,
450 gdouble *x,
451 gdouble *y)
452 {
453 gdouble event_x, event_y;
454
455 g_assert (data->event != NULL);
456
457 gdk_event_get_coords (data->event, &event_x, &event_y);
458 event_x += data->accum_dx;
459 event_y += data->accum_dy;
460
461 if (x)
462 *x = event_x;
463 if (y)
464 *y = event_y;
465 }
466
467 static void
_update_widget_coordinates(GtkGesture * gesture,PointData * data)468 _update_widget_coordinates (GtkGesture *gesture,
469 PointData *data)
470 {
471 GdkWindow *window, *event_widget_window;
472 GtkWidget *event_widget, *widget;
473 GtkAllocation allocation;
474 gdouble event_x, event_y;
475 gint wx, wy, x, y;
476
477 event_widget = gtk_get_event_widget (data->event);
478
479 if (!event_widget)
480 return;
481
482 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
483 event_widget_window = gtk_widget_get_window (event_widget);
484 _get_event_coordinates (data, &event_x, &event_y);
485 window = data->event->any.window;
486
487 while (window && window != event_widget_window)
488 {
489 gdk_window_get_position (window, &wx, &wy);
490 event_x += wx;
491 event_y += wy;
492 window = gdk_window_get_effective_parent (window);
493 }
494
495 if (!window)
496 return;
497
498 if (!gtk_widget_get_has_window (event_widget))
499 {
500 gtk_widget_get_allocation (event_widget, &allocation);
501 event_x -= allocation.x;
502 event_y -= allocation.y;
503 }
504
505 gtk_widget_translate_coordinates (event_widget, widget,
506 event_x, event_y, &x, &y);
507 data->widget_x = x;
508 data->widget_y = y;
509 }
510
511 static GtkEventSequenceState
gtk_gesture_get_group_state(GtkGesture * gesture,GdkEventSequence * sequence)512 gtk_gesture_get_group_state (GtkGesture *gesture,
513 GdkEventSequence *sequence)
514 {
515 GtkEventSequenceState state = GTK_EVENT_SEQUENCE_NONE;
516 GList *group_elem;
517
518 group_elem = g_list_first (_gtk_gesture_get_group_link (gesture));
519
520 for (; group_elem; group_elem = group_elem->next)
521 {
522 if (group_elem->data == gesture)
523 continue;
524 if (!gtk_gesture_handles_sequence (group_elem->data, sequence))
525 continue;
526
527 state = gtk_gesture_get_sequence_state (group_elem->data, sequence);
528 break;
529 }
530
531 return state;
532 }
533
534 static gboolean
_gtk_gesture_update_point(GtkGesture * gesture,const GdkEvent * event,gboolean add)535 _gtk_gesture_update_point (GtkGesture *gesture,
536 const GdkEvent *event,
537 gboolean add)
538 {
539 GdkEventSequence *sequence;
540 GdkWindow *widget_window;
541 GtkGesturePrivate *priv;
542 GdkDevice *device;
543 gboolean existed, touchpad;
544 PointData *data;
545
546 if (!gdk_event_get_coords (event, NULL, NULL))
547 return FALSE;
548
549 device = gdk_event_get_device (event);
550
551 if (!device)
552 return FALSE;
553
554 priv = gtk_gesture_get_instance_private (gesture);
555 widget_window = _find_widget_window (gesture, event->any.window);
556
557 if (!widget_window)
558 widget_window = event->any.window;
559
560 touchpad = EVENT_IS_TOUCHPAD_GESTURE (event);
561
562 if (add)
563 {
564 /* If the event happens with the wrong device, or
565 * on the wrong window, ignore.
566 */
567 if (priv->device && priv->device != device)
568 return FALSE;
569 if (priv->window && priv->window != widget_window)
570 return FALSE;
571 if (priv->user_window && priv->user_window != widget_window)
572 return FALSE;
573
574 /* Make touchpad and touchscreen gestures mutually exclusive */
575 if (touchpad && g_hash_table_size (priv->points) > 0)
576 return FALSE;
577 else if (!touchpad && priv->touchpad)
578 return FALSE;
579 }
580 else if (!priv->device || !priv->window)
581 return FALSE;
582
583 sequence = gdk_event_get_event_sequence (event);
584 existed = g_hash_table_lookup_extended (priv->points, sequence,
585 NULL, (gpointer *) &data);
586 if (!existed)
587 {
588 if (!add)
589 return FALSE;
590
591 if (g_hash_table_size (priv->points) == 0)
592 {
593 priv->window = widget_window;
594 priv->device = device;
595 priv->touchpad = touchpad;
596 }
597
598 data = g_new0 (PointData, 1);
599 g_hash_table_insert (priv->points, sequence, data);
600 }
601
602 if (data->event)
603 gdk_event_free (data->event);
604
605 data->event = gdk_event_copy (event);
606 _update_touchpad_deltas (data);
607 _update_widget_coordinates (gesture, data);
608
609 if (!existed)
610 {
611 GtkEventSequenceState state;
612
613 /* Deny the sequence right away if the expected
614 * number of points is exceeded, so this sequence
615 * can be tracked with gtk_gesture_handles_sequence().
616 *
617 * Otherwise, make the sequence inherit the same state
618 * from other gestures in the same group.
619 */
620 if (_gtk_gesture_get_n_physical_points (gesture, FALSE) > priv->n_points)
621 state = GTK_EVENT_SEQUENCE_DENIED;
622 else
623 state = gtk_gesture_get_group_state (gesture, sequence);
624
625 gtk_gesture_set_sequence_state (gesture, sequence, state);
626 }
627
628 return TRUE;
629 }
630
631 static void
_gtk_gesture_check_empty(GtkGesture * gesture)632 _gtk_gesture_check_empty (GtkGesture *gesture)
633 {
634 GtkGesturePrivate *priv;
635
636 priv = gtk_gesture_get_instance_private (gesture);
637
638 if (g_hash_table_size (priv->points) == 0)
639 {
640 priv->window = NULL;
641 priv->device = NULL;
642 priv->touchpad = FALSE;
643 }
644 }
645
646 static void
_gtk_gesture_remove_point(GtkGesture * gesture,const GdkEvent * event)647 _gtk_gesture_remove_point (GtkGesture *gesture,
648 const GdkEvent *event)
649 {
650 GdkEventSequence *sequence;
651 GtkGesturePrivate *priv;
652 GdkDevice *device;
653
654 sequence = gdk_event_get_event_sequence (event);
655 device = gdk_event_get_device (event);
656 priv = gtk_gesture_get_instance_private (gesture);
657
658 if (priv->device != device)
659 return;
660
661 g_hash_table_remove (priv->points, sequence);
662 _gtk_gesture_check_empty (gesture);
663 }
664
665 static void
_gtk_gesture_cancel_all(GtkGesture * gesture)666 _gtk_gesture_cancel_all (GtkGesture *gesture)
667 {
668 GdkEventSequence *sequence;
669 GtkGesturePrivate *priv;
670 GHashTableIter iter;
671
672 priv = gtk_gesture_get_instance_private (gesture);
673 g_hash_table_iter_init (&iter, priv->points);
674
675 while (g_hash_table_iter_next (&iter, (gpointer*) &sequence, NULL))
676 {
677 g_signal_emit (gesture, signals[CANCEL], 0, sequence);
678 g_hash_table_iter_remove (&iter);
679 _gtk_gesture_check_recognized (gesture, sequence);
680 }
681
682 _gtk_gesture_check_empty (gesture);
683 }
684
685 static gboolean
gesture_within_window(GtkGesture * gesture,GdkWindow * parent)686 gesture_within_window (GtkGesture *gesture,
687 GdkWindow *parent)
688 {
689 GdkWindow *window;
690 GtkWidget *widget;
691
692 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
693 window = gtk_widget_get_window (widget);
694
695 while (window)
696 {
697 if (window == parent)
698 return TRUE;
699
700 window = gdk_window_get_effective_parent (window);
701 }
702
703 return FALSE;
704 }
705
706 static gboolean
gtk_gesture_filter_event(GtkEventController * controller,const GdkEvent * event)707 gtk_gesture_filter_event (GtkEventController *controller,
708 const GdkEvent *event)
709 {
710 /* Even though GtkGesture handles these events, we want
711 * touchpad gestures disabled by default, it will be
712 * subclasses which punch the holes in for the events
713 * they can possibly handle.
714 */
715 return EVENT_IS_TOUCHPAD_GESTURE (event);
716 }
717
718 static gboolean
gtk_gesture_handle_event(GtkEventController * controller,const GdkEvent * event)719 gtk_gesture_handle_event (GtkEventController *controller,
720 const GdkEvent *event)
721 {
722 GtkGesture *gesture = GTK_GESTURE (controller);
723 GdkEventSequence *sequence;
724 GtkGesturePrivate *priv;
725 GdkDevice *source_device;
726 gboolean was_recognized;
727
728 source_device = gdk_event_get_source_device (event);
729
730 if (!source_device)
731 return FALSE;
732
733 priv = gtk_gesture_get_instance_private (gesture);
734 sequence = gdk_event_get_event_sequence (event);
735 was_recognized = gtk_gesture_is_recognized (gesture);
736
737 if (gtk_gesture_get_sequence_state (gesture, sequence) != GTK_EVENT_SEQUENCE_DENIED)
738 priv->last_sequence = sequence;
739
740 if (event->type == GDK_BUTTON_PRESS ||
741 event->type == GDK_TOUCH_BEGIN ||
742 (event->type == GDK_TOUCHPAD_SWIPE &&
743 event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN) ||
744 (event->type == GDK_TOUCHPAD_PINCH &&
745 event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN))
746 {
747 if (_gtk_gesture_update_point (gesture, event, TRUE))
748 {
749 gboolean triggered_recognition;
750
751 triggered_recognition =
752 !was_recognized && _gtk_gesture_has_matching_touchpoints (gesture);
753
754 if (_gtk_gesture_check_recognized (gesture, sequence))
755 {
756 PointData *data;
757
758 data = g_hash_table_lookup (priv->points, sequence);
759
760 /* If the sequence was claimed early, the press event will be consumed */
761 if (gtk_gesture_get_sequence_state (gesture, sequence) == GTK_EVENT_SEQUENCE_CLAIMED)
762 data->press_handled = TRUE;
763 }
764 else if (triggered_recognition && g_hash_table_size (priv->points) == 0)
765 {
766 /* Recognition was triggered, but the gesture reset during
767 * ::begin emission. Still, recognition was strictly triggered,
768 * so the event should be consumed.
769 */
770 return TRUE;
771 }
772 }
773 }
774 else if (event->type == GDK_BUTTON_RELEASE ||
775 event->type == GDK_TOUCH_END ||
776 (event->type == GDK_TOUCHPAD_SWIPE &&
777 event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_END) ||
778 (event->type == GDK_TOUCHPAD_PINCH &&
779 event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_END))
780 {
781 if (_gtk_gesture_update_point (gesture, event, FALSE))
782 {
783 if (was_recognized &&
784 _gtk_gesture_check_recognized (gesture, sequence))
785 g_signal_emit (gesture, signals[UPDATE], 0, sequence);
786
787 _gtk_gesture_remove_point (gesture, event);
788 }
789 }
790 else if (event->type == GDK_MOTION_NOTIFY ||
791 event->type == GDK_TOUCH_UPDATE ||
792 (event->type == GDK_TOUCHPAD_SWIPE &&
793 event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE) ||
794 (event->type == GDK_TOUCHPAD_PINCH &&
795 event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE))
796 {
797 if (event->type == GDK_MOTION_NOTIFY)
798 {
799 if ((event->motion.state & BUTTONS_MASK) == 0)
800 return FALSE;
801
802 if (event->motion.is_hint)
803 gdk_event_request_motions (&event->motion);
804 }
805
806 if (_gtk_gesture_update_point (gesture, event, FALSE) &&
807 _gtk_gesture_check_recognized (gesture, sequence))
808 g_signal_emit (gesture, signals[UPDATE], 0, sequence);
809 }
810 else if (event->type == GDK_TOUCH_CANCEL)
811 {
812 if (!priv->touchpad)
813 _gtk_gesture_cancel_sequence (gesture, sequence);
814 }
815 else if ((event->type == GDK_TOUCHPAD_SWIPE &&
816 event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_CANCEL) ||
817 (event->type == GDK_TOUCHPAD_PINCH &&
818 event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_CANCEL))
819 {
820 if (priv->touchpad)
821 _gtk_gesture_cancel_sequence (gesture, sequence);
822 }
823 else if (event->type == GDK_GRAB_BROKEN)
824 {
825 if (!event->grab_broken.grab_window ||
826 !gesture_within_window (gesture, event->grab_broken.grab_window))
827 _gtk_gesture_cancel_all (gesture);
828
829 return FALSE;
830 }
831 else
832 {
833 /* Unhandled event */
834 return FALSE;
835 }
836
837 if (gtk_gesture_get_sequence_state (gesture, sequence) != GTK_EVENT_SEQUENCE_CLAIMED)
838 return FALSE;
839
840 return priv->recognized;
841 }
842
843 static void
gtk_gesture_reset(GtkEventController * controller)844 gtk_gesture_reset (GtkEventController *controller)
845 {
846 _gtk_gesture_cancel_all (GTK_GESTURE (controller));
847 }
848
849 static void
gtk_gesture_class_init(GtkGestureClass * klass)850 gtk_gesture_class_init (GtkGestureClass *klass)
851 {
852 GObjectClass *object_class = G_OBJECT_CLASS (klass);
853 GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
854
855 object_class->get_property = gtk_gesture_get_property;
856 object_class->set_property = gtk_gesture_set_property;
857 object_class->finalize = gtk_gesture_finalize;
858
859 controller_class->filter_event = gtk_gesture_filter_event;
860 controller_class->handle_event = gtk_gesture_handle_event;
861 controller_class->reset = gtk_gesture_reset;
862
863 klass->check = gtk_gesture_check_impl;
864
865 /**
866 * GtkGesture:n-points:
867 *
868 * The number of touch points that trigger recognition on this gesture,
869 *
870 *
871 * Since: 3.14
872 */
873 g_object_class_install_property (object_class,
874 PROP_N_POINTS,
875 g_param_spec_uint ("n-points",
876 P_("Number of points"),
877 P_("Number of points needed "
878 "to trigger the gesture"),
879 1, G_MAXUINT, 1,
880 GTK_PARAM_READWRITE |
881 G_PARAM_CONSTRUCT_ONLY));
882 /**
883 * GtkGesture:window:
884 *
885 * If non-%NULL, the gesture will only listen for events that happen on
886 * this #GdkWindow, or a child of it.
887 *
888 * Since: 3.14
889 */
890 g_object_class_install_property (object_class,
891 PROP_WINDOW,
892 g_param_spec_object ("window",
893 P_("GdkWindow to receive events about"),
894 P_("GdkWindow to receive events about"),
895 GDK_TYPE_WINDOW,
896 GTK_PARAM_READWRITE));
897 /**
898 * GtkGesture::begin:
899 * @gesture: the object which received the signal
900 * @sequence: (nullable): the #GdkEventSequence that made the gesture to be recognized
901 *
902 * This signal is emitted when the gesture is recognized. This means the
903 * number of touch sequences matches #GtkGesture:n-points, and the #GtkGesture::check
904 * handler(s) returned #TRUE.
905 *
906 * Note: These conditions may also happen when an extra touch (eg. a third touch
907 * on a 2-touches gesture) is lifted, in that situation @sequence won't pertain
908 * to the current set of active touches, so don't rely on this being true.
909 *
910 * Since: 3.14
911 */
912 signals[BEGIN] =
913 g_signal_new (I_("begin"),
914 G_TYPE_FROM_CLASS (klass),
915 G_SIGNAL_RUN_LAST,
916 G_STRUCT_OFFSET (GtkGestureClass, begin),
917 NULL, NULL, NULL,
918 G_TYPE_NONE, 1, GDK_TYPE_EVENT_SEQUENCE);
919 /**
920 * GtkGesture::end:
921 * @gesture: the object which received the signal
922 * @sequence: (nullable): the #GdkEventSequence that made gesture recognition to finish
923 *
924 * This signal is emitted when @gesture either stopped recognizing the event
925 * sequences as something to be handled (the #GtkGesture::check handler returned
926 * %FALSE), or the number of touch sequences became higher or lower than
927 * #GtkGesture:n-points.
928 *
929 * Note: @sequence might not pertain to the group of sequences that were
930 * previously triggering recognition on @gesture (ie. a just pressed touch
931 * sequence that exceeds #GtkGesture:n-points). This situation may be detected
932 * by checking through gtk_gesture_handles_sequence().
933 *
934 * Since: 3.14
935 */
936 signals[END] =
937 g_signal_new (I_("end"),
938 G_TYPE_FROM_CLASS (klass),
939 G_SIGNAL_RUN_LAST,
940 G_STRUCT_OFFSET (GtkGestureClass, end),
941 NULL, NULL, NULL,
942 G_TYPE_NONE, 1, GDK_TYPE_EVENT_SEQUENCE);
943 /**
944 * GtkGesture::update:
945 * @gesture: the object which received the signal
946 * @sequence: (nullable): the #GdkEventSequence that was updated
947 *
948 * This signal is emitted whenever an event is handled while the gesture is
949 * recognized. @sequence is guaranteed to pertain to the set of active touches.
950 *
951 * Since: 3.14
952 */
953 signals[UPDATE] =
954 g_signal_new (I_("update"),
955 G_TYPE_FROM_CLASS (klass),
956 G_SIGNAL_RUN_LAST,
957 G_STRUCT_OFFSET (GtkGestureClass, update),
958 NULL, NULL, NULL,
959 G_TYPE_NONE, 1, GDK_TYPE_EVENT_SEQUENCE);
960 /**
961 * GtkGesture::cancel:
962 * @gesture: the object which received the signal
963 * @sequence: (nullable): the #GdkEventSequence that was cancelled
964 *
965 * This signal is emitted whenever a sequence is cancelled. This usually
966 * happens on active touches when gtk_event_controller_reset() is called
967 * on @gesture (manually, due to grabs...), or the individual @sequence
968 * was claimed by parent widgets' controllers (see gtk_gesture_set_sequence_state()).
969 *
970 * @gesture must forget everything about @sequence as a reaction to this signal.
971 *
972 * Since: 3.14
973 */
974 signals[CANCEL] =
975 g_signal_new (I_("cancel"),
976 G_TYPE_FROM_CLASS (klass),
977 G_SIGNAL_RUN_LAST,
978 G_STRUCT_OFFSET (GtkGestureClass, cancel),
979 NULL, NULL, NULL,
980 G_TYPE_NONE, 1, GDK_TYPE_EVENT_SEQUENCE);
981 /**
982 * GtkGesture::sequence-state-changed:
983 * @gesture: the object which received the signal
984 * @sequence: (nullable): the #GdkEventSequence that was cancelled
985 * @state: the new sequence state
986 *
987 * This signal is emitted whenever a sequence state changes. See
988 * gtk_gesture_set_sequence_state() to know more about the expectable
989 * sequence lifetimes.
990 *
991 * Since: 3.14
992 */
993 signals[SEQUENCE_STATE_CHANGED] =
994 g_signal_new (I_("sequence-state-changed"),
995 G_TYPE_FROM_CLASS (klass),
996 G_SIGNAL_RUN_LAST,
997 G_STRUCT_OFFSET (GtkGestureClass, sequence_state_changed),
998 NULL, NULL,
999 _gtk_marshal_VOID__BOXED_ENUM,
1000 G_TYPE_NONE, 2, GDK_TYPE_EVENT_SEQUENCE,
1001 GTK_TYPE_EVENT_SEQUENCE_STATE);
1002 g_signal_set_va_marshaller (signals[SEQUENCE_STATE_CHANGED],
1003 G_TYPE_FROM_CLASS (klass),
1004 _gtk_marshal_VOID__BOXED_ENUMv);
1005 }
1006
1007 static void
free_point_data(gpointer data)1008 free_point_data (gpointer data)
1009 {
1010 PointData *point = data;
1011
1012 if (point->event)
1013 gdk_event_free (point->event);
1014
1015 g_free (point);
1016 }
1017
1018 static void
gtk_gesture_init(GtkGesture * gesture)1019 gtk_gesture_init (GtkGesture *gesture)
1020 {
1021 GtkGesturePrivate *priv;
1022
1023 priv = gtk_gesture_get_instance_private (gesture);
1024 priv->points = g_hash_table_new_full (NULL, NULL, NULL,
1025 (GDestroyNotify) free_point_data);
1026 gtk_event_controller_set_event_mask (GTK_EVENT_CONTROLLER (gesture),
1027 GDK_TOUCH_MASK |
1028 GDK_TOUCHPAD_GESTURE_MASK);
1029
1030 priv->group_link = g_list_prepend (NULL, gesture);
1031 }
1032
1033 /**
1034 * gtk_gesture_get_device:
1035 * @gesture: a #GtkGesture
1036 *
1037 * Returns the master #GdkDevice that is currently operating
1038 * on @gesture, or %NULL if the gesture is not being interacted.
1039 *
1040 * Returns: (nullable) (transfer none): a #GdkDevice, or %NULL
1041 *
1042 * Since: 3.14
1043 **/
1044 GdkDevice *
gtk_gesture_get_device(GtkGesture * gesture)1045 gtk_gesture_get_device (GtkGesture *gesture)
1046 {
1047 GtkGesturePrivate *priv;
1048
1049 g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
1050
1051 priv = gtk_gesture_get_instance_private (gesture);
1052
1053 return priv->device;
1054 }
1055
1056 /**
1057 * gtk_gesture_get_sequence_state:
1058 * @gesture: a #GtkGesture
1059 * @sequence: a #GdkEventSequence
1060 *
1061 * Returns the @sequence state, as seen by @gesture.
1062 *
1063 * Returns: The sequence state in @gesture
1064 *
1065 * Since: 3.14
1066 **/
1067 GtkEventSequenceState
gtk_gesture_get_sequence_state(GtkGesture * gesture,GdkEventSequence * sequence)1068 gtk_gesture_get_sequence_state (GtkGesture *gesture,
1069 GdkEventSequence *sequence)
1070 {
1071 GtkGesturePrivate *priv;
1072 PointData *data;
1073
1074 g_return_val_if_fail (GTK_IS_GESTURE (gesture),
1075 GTK_EVENT_SEQUENCE_NONE);
1076
1077 priv = gtk_gesture_get_instance_private (gesture);
1078 data = g_hash_table_lookup (priv->points, sequence);
1079
1080 if (!data)
1081 return GTK_EVENT_SEQUENCE_NONE;
1082
1083 return data->state;
1084 }
1085
1086 /**
1087 * gtk_gesture_set_sequence_state:
1088 * @gesture: a #GtkGesture
1089 * @sequence: a #GdkEventSequence
1090 * @state: the sequence state
1091 *
1092 * Sets the state of @sequence in @gesture. Sequences start
1093 * in state #GTK_EVENT_SEQUENCE_NONE, and whenever they change
1094 * state, they can never go back to that state. Likewise,
1095 * sequences in state #GTK_EVENT_SEQUENCE_DENIED cannot turn
1096 * back to a not denied state. With these rules, the lifetime
1097 * of an event sequence is constrained to the next four:
1098 *
1099 * * None
1100 * * None → Denied
1101 * * None → Claimed
1102 * * None → Claimed → Denied
1103 *
1104 * Note: Due to event handling ordering, it may be unsafe to
1105 * set the state on another gesture within a #GtkGesture::begin
1106 * signal handler, as the callback might be executed before
1107 * the other gesture knows about the sequence. A safe way to
1108 * perform this could be:
1109 *
1110 * |[
1111 * static void
1112 * first_gesture_begin_cb (GtkGesture *first_gesture,
1113 * GdkEventSequence *sequence,
1114 * gpointer user_data)
1115 * {
1116 * gtk_gesture_set_sequence_state (first_gesture, sequence, GTK_EVENT_SEQUENCE_CLAIMED);
1117 * gtk_gesture_set_sequence_state (second_gesture, sequence, GTK_EVENT_SEQUENCE_DENIED);
1118 * }
1119 *
1120 * static void
1121 * second_gesture_begin_cb (GtkGesture *second_gesture,
1122 * GdkEventSequence *sequence,
1123 * gpointer user_data)
1124 * {
1125 * if (gtk_gesture_get_sequence_state (first_gesture, sequence) == GTK_EVENT_SEQUENCE_CLAIMED)
1126 * gtk_gesture_set_sequence_state (second_gesture, sequence, GTK_EVENT_SEQUENCE_DENIED);
1127 * }
1128 * ]|
1129 *
1130 * If both gestures are in the same group, just set the state on
1131 * the gesture emitting the event, the sequence will be already
1132 * be initialized to the group's global state when the second
1133 * gesture processes the event.
1134 *
1135 * Returns: %TRUE if @sequence is handled by @gesture,
1136 * and the state is changed successfully
1137 *
1138 * Since: 3.14
1139 **/
1140 gboolean
gtk_gesture_set_sequence_state(GtkGesture * gesture,GdkEventSequence * sequence,GtkEventSequenceState state)1141 gtk_gesture_set_sequence_state (GtkGesture *gesture,
1142 GdkEventSequence *sequence,
1143 GtkEventSequenceState state)
1144 {
1145 GtkGesturePrivate *priv;
1146 PointData *data;
1147
1148 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1149 g_return_val_if_fail (state >= GTK_EVENT_SEQUENCE_NONE &&
1150 state <= GTK_EVENT_SEQUENCE_DENIED, FALSE);
1151
1152 priv = gtk_gesture_get_instance_private (gesture);
1153 data = g_hash_table_lookup (priv->points, sequence);
1154
1155 if (!data)
1156 return FALSE;
1157
1158 if (data->state == state)
1159 return FALSE;
1160
1161 /* denied sequences remain denied */
1162 if (data->state == GTK_EVENT_SEQUENCE_DENIED)
1163 return FALSE;
1164
1165 /* Sequences can't go from claimed/denied to none */
1166 if (state == GTK_EVENT_SEQUENCE_NONE &&
1167 data->state != GTK_EVENT_SEQUENCE_NONE)
1168 return FALSE;
1169
1170 data->state = state;
1171 g_signal_emit (gesture, signals[SEQUENCE_STATE_CHANGED], 0,
1172 sequence, state);
1173
1174 if (state == GTK_EVENT_SEQUENCE_DENIED)
1175 _gtk_gesture_check_recognized (gesture, sequence);
1176
1177 return TRUE;
1178 }
1179
1180 /**
1181 * gtk_gesture_set_state:
1182 * @gesture: a #GtkGesture
1183 * @state: the sequence state
1184 *
1185 * Sets the state of all sequences that @gesture is currently
1186 * interacting with. See gtk_gesture_set_sequence_state()
1187 * for more details on sequence states.
1188 *
1189 * Returns: %TRUE if the state of at least one sequence
1190 * was changed successfully
1191 *
1192 * Since: 3.14
1193 */
1194 gboolean
gtk_gesture_set_state(GtkGesture * gesture,GtkEventSequenceState state)1195 gtk_gesture_set_state (GtkGesture *gesture,
1196 GtkEventSequenceState state)
1197 {
1198 gboolean handled = FALSE;
1199 GtkGesturePrivate *priv;
1200 GList *sequences, *l;
1201
1202 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1203 g_return_val_if_fail (state >= GTK_EVENT_SEQUENCE_NONE &&
1204 state <= GTK_EVENT_SEQUENCE_DENIED, FALSE);
1205
1206 priv = gtk_gesture_get_instance_private (gesture);
1207 sequences = g_hash_table_get_keys (priv->points);
1208
1209 for (l = sequences; l; l = l->next)
1210 handled |= gtk_gesture_set_sequence_state (gesture, l->data, state);
1211
1212 g_list_free (sequences);
1213
1214 return handled;
1215 }
1216
1217 /**
1218 * gtk_gesture_get_sequences:
1219 * @gesture: a #GtkGesture
1220 *
1221 * Returns the list of #GdkEventSequences currently being interpreted
1222 * by @gesture.
1223 *
1224 * Returns: (transfer container) (element-type GdkEventSequence): A list
1225 * of #GdkEventSequences, the list elements are owned by GTK+
1226 * and must not be freed or modified, the list itself must be deleted
1227 * through g_list_free()
1228 *
1229 * Since: 3.14
1230 **/
1231 GList *
gtk_gesture_get_sequences(GtkGesture * gesture)1232 gtk_gesture_get_sequences (GtkGesture *gesture)
1233 {
1234 GdkEventSequence *sequence;
1235 GtkGesturePrivate *priv;
1236 GList *sequences = NULL;
1237 GHashTableIter iter;
1238 PointData *data;
1239
1240 g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
1241
1242 priv = gtk_gesture_get_instance_private (gesture);
1243 g_hash_table_iter_init (&iter, priv->points);
1244
1245 while (g_hash_table_iter_next (&iter, (gpointer *) &sequence, (gpointer *) &data))
1246 {
1247 if (data->state == GTK_EVENT_SEQUENCE_DENIED)
1248 continue;
1249 if (data->event->type == GDK_TOUCH_END ||
1250 data->event->type == GDK_BUTTON_RELEASE)
1251 continue;
1252
1253 sequences = g_list_prepend (sequences, sequence);
1254 }
1255
1256 return sequences;
1257 }
1258
1259 /**
1260 * gtk_gesture_get_last_updated_sequence:
1261 * @gesture: a #GtkGesture
1262 *
1263 * Returns the #GdkEventSequence that was last updated on @gesture.
1264 *
1265 * Returns: (transfer none) (nullable): The last updated sequence
1266 *
1267 * Since: 3.14
1268 **/
1269 GdkEventSequence *
gtk_gesture_get_last_updated_sequence(GtkGesture * gesture)1270 gtk_gesture_get_last_updated_sequence (GtkGesture *gesture)
1271 {
1272 GtkGesturePrivate *priv;
1273
1274 g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
1275
1276 priv = gtk_gesture_get_instance_private (gesture);
1277
1278 return priv->last_sequence;
1279 }
1280
1281 /**
1282 * gtk_gesture_get_last_event:
1283 * @gesture: a #GtkGesture
1284 * @sequence: (nullable): a #GdkEventSequence
1285 *
1286 * Returns the last event that was processed for @sequence.
1287 *
1288 * Note that the returned pointer is only valid as long as the @sequence
1289 * is still interpreted by the @gesture. If in doubt, you should make
1290 * a copy of the event.
1291 *
1292 * Returns: (transfer none) (nullable): The last event from @sequence
1293 **/
1294 const GdkEvent *
gtk_gesture_get_last_event(GtkGesture * gesture,GdkEventSequence * sequence)1295 gtk_gesture_get_last_event (GtkGesture *gesture,
1296 GdkEventSequence *sequence)
1297 {
1298 GtkGesturePrivate *priv;
1299 PointData *data;
1300
1301 g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
1302
1303 priv = gtk_gesture_get_instance_private (gesture);
1304 data = g_hash_table_lookup (priv->points, sequence);
1305
1306 if (!data)
1307 return NULL;
1308
1309 return data->event;
1310 }
1311
1312 /**
1313 * gtk_gesture_get_point:
1314 * @gesture: a #GtkGesture
1315 * @sequence: (allow-none): a #GdkEventSequence, or %NULL for pointer events
1316 * @x: (out) (allow-none): return location for X axis of the sequence coordinates
1317 * @y: (out) (allow-none): return location for Y axis of the sequence coordinates
1318 *
1319 * If @sequence is currently being interpreted by @gesture, this
1320 * function returns %TRUE and fills in @x and @y with the last coordinates
1321 * stored for that event sequence. The coordinates are always relative to the
1322 * widget allocation.
1323 *
1324 * Returns: %TRUE if @sequence is currently interpreted
1325 *
1326 * Since: 3.14
1327 **/
1328 gboolean
gtk_gesture_get_point(GtkGesture * gesture,GdkEventSequence * sequence,gdouble * x,gdouble * y)1329 gtk_gesture_get_point (GtkGesture *gesture,
1330 GdkEventSequence *sequence,
1331 gdouble *x,
1332 gdouble *y)
1333 {
1334 GtkGesturePrivate *priv;
1335 PointData *data;
1336
1337 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1338
1339 priv = gtk_gesture_get_instance_private (gesture);
1340
1341 if (!g_hash_table_lookup_extended (priv->points, sequence,
1342 NULL, (gpointer *) &data))
1343 return FALSE;
1344
1345 if (x)
1346 *x = data->widget_x;
1347 if (y)
1348 *y = data->widget_y;
1349
1350 return TRUE;
1351 }
1352
1353 gboolean
_gtk_gesture_get_last_update_time(GtkGesture * gesture,GdkEventSequence * sequence,guint32 * evtime)1354 _gtk_gesture_get_last_update_time (GtkGesture *gesture,
1355 GdkEventSequence *sequence,
1356 guint32 *evtime)
1357 {
1358 GtkGesturePrivate *priv;
1359 PointData *data;
1360
1361 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1362
1363 priv = gtk_gesture_get_instance_private (gesture);
1364
1365 if (!g_hash_table_lookup_extended (priv->points, sequence,
1366 NULL, (gpointer *) &data))
1367 return FALSE;
1368
1369 if (evtime)
1370 *evtime = gdk_event_get_time (data->event);
1371
1372 return TRUE;
1373 };
1374
1375 /**
1376 * gtk_gesture_get_bounding_box:
1377 * @gesture: a #GtkGesture
1378 * @rect: (out): bounding box containing all active touches.
1379 *
1380 * If there are touch sequences being currently handled by @gesture,
1381 * this function returns %TRUE and fills in @rect with the bounding
1382 * box containing all active touches. Otherwise, %FALSE will be
1383 * returned.
1384 *
1385 * Note: This function will yield unexpected results on touchpad
1386 * gestures. Since there is no correlation between physical and
1387 * pixel distances, these will look as if constrained in an
1388 * infinitely small area, @rect width and height will thus be 0
1389 * regardless of the number of touchpoints.
1390 *
1391 * Returns: %TRUE if there are active touches, %FALSE otherwise
1392 *
1393 * Since: 3.14
1394 **/
1395 gboolean
gtk_gesture_get_bounding_box(GtkGesture * gesture,GdkRectangle * rect)1396 gtk_gesture_get_bounding_box (GtkGesture *gesture,
1397 GdkRectangle *rect)
1398 {
1399 GtkGesturePrivate *priv;
1400 gdouble x1, y1, x2, y2;
1401 GHashTableIter iter;
1402 guint n_points = 0;
1403 PointData *data;
1404
1405 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1406 g_return_val_if_fail (rect != NULL, FALSE);
1407
1408 priv = gtk_gesture_get_instance_private (gesture);
1409 x1 = y1 = G_MAXDOUBLE;
1410 x2 = y2 = -G_MAXDOUBLE;
1411
1412 g_hash_table_iter_init (&iter, priv->points);
1413
1414 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
1415 {
1416 gdouble x, y;
1417
1418 if (data->state == GTK_EVENT_SEQUENCE_DENIED)
1419 continue;
1420 if (data->event->type == GDK_TOUCH_END ||
1421 data->event->type == GDK_BUTTON_RELEASE)
1422 continue;
1423
1424 gdk_event_get_coords (data->event, &x, &y);
1425 n_points++;
1426 x1 = MIN (x1, x);
1427 y1 = MIN (y1, y);
1428 x2 = MAX (x2, x);
1429 y2 = MAX (y2, y);
1430 }
1431
1432 if (n_points == 0)
1433 return FALSE;
1434
1435 rect->x = x1;
1436 rect->y = y1;
1437 rect->width = x2 - x1;
1438 rect->height = y2 - y1;
1439
1440 return TRUE;
1441 }
1442
1443
1444 /**
1445 * gtk_gesture_get_bounding_box_center:
1446 * @gesture: a #GtkGesture
1447 * @x: (out): X coordinate for the bounding box center
1448 * @y: (out): Y coordinate for the bounding box center
1449 *
1450 * If there are touch sequences being currently handled by @gesture,
1451 * this function returns %TRUE and fills in @x and @y with the center
1452 * of the bounding box containing all active touches. Otherwise, %FALSE
1453 * will be returned.
1454 *
1455 * Returns: %FALSE if no active touches are present, %TRUE otherwise
1456 *
1457 * Since: 3.14
1458 **/
1459 gboolean
gtk_gesture_get_bounding_box_center(GtkGesture * gesture,gdouble * x,gdouble * y)1460 gtk_gesture_get_bounding_box_center (GtkGesture *gesture,
1461 gdouble *x,
1462 gdouble *y)
1463 {
1464 const GdkEvent *last_event;
1465 GdkRectangle rect;
1466 GdkEventSequence *sequence;
1467
1468 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1469 g_return_val_if_fail (x != NULL && y != NULL, FALSE);
1470
1471 sequence = gtk_gesture_get_last_updated_sequence (gesture);
1472 last_event = gtk_gesture_get_last_event (gesture, sequence);
1473
1474 if (EVENT_IS_TOUCHPAD_GESTURE (last_event))
1475 return gtk_gesture_get_point (gesture, sequence, x, y);
1476 else if (!gtk_gesture_get_bounding_box (gesture, &rect))
1477 return FALSE;
1478
1479 *x = rect.x + rect.width / 2;
1480 *y = rect.y + rect.height / 2;
1481 return TRUE;
1482 }
1483
1484 /**
1485 * gtk_gesture_is_active:
1486 * @gesture: a #GtkGesture
1487 *
1488 * Returns %TRUE if the gesture is currently active.
1489 * A gesture is active meanwhile there are touch sequences
1490 * interacting with it.
1491 *
1492 * Returns: %TRUE if gesture is active
1493 *
1494 * Since: 3.14
1495 **/
1496 gboolean
gtk_gesture_is_active(GtkGesture * gesture)1497 gtk_gesture_is_active (GtkGesture *gesture)
1498 {
1499 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1500
1501 return _gtk_gesture_get_n_physical_points (gesture, TRUE) != 0;
1502 }
1503
1504 /**
1505 * gtk_gesture_is_recognized:
1506 * @gesture: a #GtkGesture
1507 *
1508 * Returns %TRUE if the gesture is currently recognized.
1509 * A gesture is recognized if there are as many interacting
1510 * touch sequences as required by @gesture, and #GtkGesture::check
1511 * returned %TRUE for the sequences being currently interpreted.
1512 *
1513 * Returns: %TRUE if gesture is recognized
1514 *
1515 * Since: 3.14
1516 **/
1517 gboolean
gtk_gesture_is_recognized(GtkGesture * gesture)1518 gtk_gesture_is_recognized (GtkGesture *gesture)
1519 {
1520 GtkGesturePrivate *priv;
1521
1522 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1523
1524 priv = gtk_gesture_get_instance_private (gesture);
1525
1526 return priv->recognized;
1527 }
1528
1529 gboolean
_gtk_gesture_check(GtkGesture * gesture)1530 _gtk_gesture_check (GtkGesture *gesture)
1531 {
1532 GtkGesturePrivate *priv;
1533
1534 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1535
1536 priv = gtk_gesture_get_instance_private (gesture);
1537
1538 return _gtk_gesture_check_recognized (gesture, priv->last_sequence);
1539 }
1540
1541 /**
1542 * gtk_gesture_handles_sequence:
1543 * @gesture: a #GtkGesture
1544 * @sequence: (nullable): a #GdkEventSequence or %NULL
1545 *
1546 * Returns %TRUE if @gesture is currently handling events corresponding to
1547 * @sequence.
1548 *
1549 * Returns: %TRUE if @gesture is handling @sequence, %FALSE otherwise
1550 *
1551 * Since: 3.14
1552 **/
1553 gboolean
gtk_gesture_handles_sequence(GtkGesture * gesture,GdkEventSequence * sequence)1554 gtk_gesture_handles_sequence (GtkGesture *gesture,
1555 GdkEventSequence *sequence)
1556 {
1557 GtkGesturePrivate *priv;
1558 PointData *data;
1559
1560 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1561
1562 priv = gtk_gesture_get_instance_private (gesture);
1563 data = g_hash_table_lookup (priv->points, sequence);
1564
1565 if (!data)
1566 return FALSE;
1567
1568 if (data->state == GTK_EVENT_SEQUENCE_DENIED)
1569 return FALSE;
1570
1571 return TRUE;
1572 }
1573
1574 gboolean
_gtk_gesture_cancel_sequence(GtkGesture * gesture,GdkEventSequence * sequence)1575 _gtk_gesture_cancel_sequence (GtkGesture *gesture,
1576 GdkEventSequence *sequence)
1577 {
1578 GtkGesturePrivate *priv;
1579 PointData *data;
1580
1581 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1582
1583 priv = gtk_gesture_get_instance_private (gesture);
1584 data = g_hash_table_lookup (priv->points, sequence);
1585
1586 if (!data)
1587 return FALSE;
1588
1589 g_signal_emit (gesture, signals[CANCEL], 0, sequence);
1590 _gtk_gesture_remove_point (gesture, data->event);
1591 _gtk_gesture_check_recognized (gesture, sequence);
1592
1593 return TRUE;
1594 }
1595
1596 /**
1597 * gtk_gesture_get_window:
1598 * @gesture: a #GtkGesture
1599 *
1600 * Returns the user-defined window that receives the events
1601 * handled by @gesture. See gtk_gesture_set_window() for more
1602 * information.
1603 *
1604 * Returns: (nullable) (transfer none): the user defined window, or %NULL if none
1605 *
1606 * Since: 3.14
1607 **/
1608 GdkWindow *
gtk_gesture_get_window(GtkGesture * gesture)1609 gtk_gesture_get_window (GtkGesture *gesture)
1610 {
1611 GtkGesturePrivate *priv;
1612
1613 g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
1614
1615 priv = gtk_gesture_get_instance_private (gesture);
1616
1617 return priv->user_window;
1618 }
1619
1620 /**
1621 * gtk_gesture_set_window:
1622 * @gesture: a #GtkGesture
1623 * @window: (allow-none): a #GdkWindow, or %NULL
1624 *
1625 * Sets a specific window to receive events about, so @gesture
1626 * will effectively handle only events targeting @window, or
1627 * a child of it. @window must pertain to gtk_event_controller_get_widget().
1628 *
1629 * Since: 3.14
1630 **/
1631 void
gtk_gesture_set_window(GtkGesture * gesture,GdkWindow * window)1632 gtk_gesture_set_window (GtkGesture *gesture,
1633 GdkWindow *window)
1634 {
1635 GtkGesturePrivate *priv;
1636
1637 g_return_if_fail (GTK_IS_GESTURE (gesture));
1638 g_return_if_fail (!window || GDK_IS_WINDOW (window));
1639
1640 priv = gtk_gesture_get_instance_private (gesture);
1641
1642 if (window)
1643 {
1644 GtkWidget *window_widget;
1645
1646 gdk_window_get_user_data (window, (gpointer*) &window_widget);
1647 g_return_if_fail (window_widget ==
1648 gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
1649 }
1650
1651 if (priv->user_window == window)
1652 return;
1653
1654 priv->user_window = window;
1655 g_object_notify (G_OBJECT (gesture), "window");
1656 }
1657
1658 GList *
_gtk_gesture_get_group_link(GtkGesture * gesture)1659 _gtk_gesture_get_group_link (GtkGesture *gesture)
1660 {
1661 GtkGesturePrivate *priv;
1662
1663 priv = gtk_gesture_get_instance_private (gesture);
1664
1665 return priv->group_link;
1666 }
1667
1668 /**
1669 * gtk_gesture_group:
1670 * @gesture: a #GtkGesture
1671 * @group_gesture: #GtkGesture to group @gesture with
1672 *
1673 * Adds @gesture to the same group than @group_gesture. Gestures
1674 * are by default isolated in their own groups.
1675 *
1676 * When gestures are grouped, the state of #GdkEventSequences
1677 * is kept in sync for all of those, so calling gtk_gesture_set_sequence_state(),
1678 * on one will transfer the same value to the others.
1679 *
1680 * Groups also perform an "implicit grabbing" of sequences, if a
1681 * #GdkEventSequence state is set to #GTK_EVENT_SEQUENCE_CLAIMED on one group,
1682 * every other gesture group attached to the same #GtkWidget will switch the
1683 * state for that sequence to #GTK_EVENT_SEQUENCE_DENIED.
1684 *
1685 * Since: 3.14
1686 **/
1687 void
gtk_gesture_group(GtkGesture * gesture,GtkGesture * group_gesture)1688 gtk_gesture_group (GtkGesture *gesture,
1689 GtkGesture *group_gesture)
1690 {
1691 GList *link, *group_link, *next;
1692
1693 g_return_if_fail (GTK_IS_GESTURE (gesture));
1694 g_return_if_fail (GTK_IS_GESTURE (group_gesture));
1695 g_return_if_fail (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (group_gesture)) ==
1696 gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
1697
1698 link = _gtk_gesture_get_group_link (gesture);
1699
1700 if (link->prev || link->next)
1701 {
1702 if (gtk_gesture_is_grouped_with (gesture, group_gesture))
1703 return;
1704 gtk_gesture_ungroup (gesture);
1705 }
1706
1707 group_link = _gtk_gesture_get_group_link (group_gesture);
1708 next = group_link->next;
1709
1710 /* Rewire link so it's inserted after the group_gesture elem */
1711 link->prev = group_link;
1712 link->next = next;
1713 group_link->next = link;
1714 if (next)
1715 next->prev = link;
1716 }
1717
1718 /**
1719 * gtk_gesture_ungroup:
1720 * @gesture: a #GtkGesture
1721 *
1722 * Separates @gesture into an isolated group.
1723 *
1724 * Since: 3.14
1725 **/
1726 void
gtk_gesture_ungroup(GtkGesture * gesture)1727 gtk_gesture_ungroup (GtkGesture *gesture)
1728 {
1729 GList *link, *prev, *next;
1730
1731 g_return_if_fail (GTK_IS_GESTURE (gesture));
1732
1733 link = _gtk_gesture_get_group_link (gesture);
1734 prev = link->prev;
1735 next = link->next;
1736
1737 /* Detach link from the group chain */
1738 if (prev)
1739 prev->next = next;
1740 if (next)
1741 next->prev = prev;
1742
1743 link->next = link->prev = NULL;
1744 }
1745
1746 /**
1747 * gtk_gesture_get_group:
1748 * @gesture: a #GtkGesture
1749 *
1750 * Returns all gestures in the group of @gesture
1751 *
1752 * Returns: (element-type GtkGesture) (transfer container): The list
1753 * of #GtkGestures, free with g_list_free()
1754 *
1755 * Since: 3.14
1756 **/
1757 GList *
gtk_gesture_get_group(GtkGesture * gesture)1758 gtk_gesture_get_group (GtkGesture *gesture)
1759 {
1760 GList *link;
1761
1762 g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
1763
1764 link = _gtk_gesture_get_group_link (gesture);
1765
1766 return g_list_copy (g_list_first (link));
1767 }
1768
1769 /**
1770 * gtk_gesture_is_grouped_with:
1771 * @gesture: a #GtkGesture
1772 * @other: another #GtkGesture
1773 *
1774 * Returns %TRUE if both gestures pertain to the same group.
1775 *
1776 * Returns: whether the gestures are grouped
1777 *
1778 * Since: 3.14
1779 **/
1780 gboolean
gtk_gesture_is_grouped_with(GtkGesture * gesture,GtkGesture * other)1781 gtk_gesture_is_grouped_with (GtkGesture *gesture,
1782 GtkGesture *other)
1783 {
1784 GList *link;
1785
1786 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1787 g_return_val_if_fail (GTK_IS_GESTURE (other), FALSE);
1788
1789 link = _gtk_gesture_get_group_link (gesture);
1790 link = g_list_first (link);
1791
1792 return g_list_find (link, other) != NULL;
1793 }
1794
1795 gboolean
_gtk_gesture_handled_sequence_press(GtkGesture * gesture,GdkEventSequence * sequence)1796 _gtk_gesture_handled_sequence_press (GtkGesture *gesture,
1797 GdkEventSequence *sequence)
1798 {
1799 GtkGesturePrivate *priv;
1800 PointData *data;
1801
1802 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1803
1804 priv = gtk_gesture_get_instance_private (gesture);
1805 data = g_hash_table_lookup (priv->points, sequence);
1806
1807 if (!data)
1808 return FALSE;
1809
1810 return data->press_handled;
1811 }
1812
1813 gboolean
_gtk_gesture_get_pointer_emulating_sequence(GtkGesture * gesture,GdkEventSequence ** sequence)1814 _gtk_gesture_get_pointer_emulating_sequence (GtkGesture *gesture,
1815 GdkEventSequence **sequence)
1816 {
1817 GtkGesturePrivate *priv;
1818 GdkEventSequence *seq;
1819 GHashTableIter iter;
1820 PointData *data;
1821
1822 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
1823
1824 priv = gtk_gesture_get_instance_private (gesture);
1825 g_hash_table_iter_init (&iter, priv->points);
1826
1827 while (g_hash_table_iter_next (&iter, (gpointer*) &seq, (gpointer*) &data))
1828 {
1829 switch (data->event->type)
1830 {
1831 case GDK_TOUCH_BEGIN:
1832 case GDK_TOUCH_UPDATE:
1833 case GDK_TOUCH_END:
1834 if (!data->event->touch.emulating_pointer)
1835 continue;
1836 /* Fall through */
1837 case GDK_BUTTON_PRESS:
1838 case GDK_BUTTON_RELEASE:
1839 case GDK_MOTION_NOTIFY:
1840 *sequence = seq;
1841 return TRUE;
1842 default:
1843 break;
1844 }
1845 }
1846
1847 return FALSE;
1848 }
1849