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

source code of gtk/gtk/gtkgesture.c