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 * GtkGestureSingle:
23 *
24 * `GtkGestureSingle` is a `GtkGestures` subclass optimized for singe-touch
25 * and mouse gestures.
26 *
27 * Under interaction, these gestures stick to the first interacting sequence,
28 * which is accessible through [method@Gtk.GestureSingle.get_current_sequence]
29 * while the gesture is being interacted with.
30 *
31 * By default gestures react to both %GDK_BUTTON_PRIMARY and touch events.
32 * [method@Gtk.GestureSingle.set_touch_only] can be used to change the
33 * touch behavior. Callers may also specify a different mouse button number
34 * to interact with through [method@Gtk.GestureSingle.set_button], or react
35 * to any mouse button by setting it to 0. While the gesture is active, the
36 * button being currently pressed can be known through
37 * [method@Gtk.GestureSingle.get_current_button].
38 */
39
40#include "config.h"
41#include "gtkgesturesingle.h"
42#include "gtkgesturesingleprivate.h"
43#include "gtkprivate.h"
44#include "gtkintl.h"
45#include "gtkdebug.h"
46
47typedef struct _GtkGestureSinglePrivate GtkGestureSinglePrivate;
48
49struct _GtkGestureSinglePrivate
50{
51 GdkEventSequence *current_sequence;
52 guint button;
53 guint current_button;
54 guint touch_only : 1;
55 guint exclusive : 1;
56};
57
58enum {
59 PROP_TOUCH_ONLY = 1,
60 PROP_EXCLUSIVE,
61 PROP_BUTTON,
62 LAST_PROP
63};
64
65static GParamSpec *properties[LAST_PROP] = { NULL, };
66
67G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureSingle, gtk_gesture_single, GTK_TYPE_GESTURE)
68
69static void
70gtk_gesture_single_get_property (GObject *object,
71 guint prop_id,
72 GValue *value,
73 GParamSpec *pspec)
74{
75 GtkGestureSinglePrivate *priv;
76
77 priv = gtk_gesture_single_get_instance_private (GTK_GESTURE_SINGLE (object));
78
79 switch (prop_id)
80 {
81 case PROP_TOUCH_ONLY:
82 g_value_set_boolean (value, v_boolean: priv->touch_only);
83 break;
84 case PROP_EXCLUSIVE:
85 g_value_set_boolean (value, v_boolean: priv->exclusive);
86 break;
87 case PROP_BUTTON:
88 g_value_set_uint (value, v_uint: priv->button);
89 break;
90 default:
91 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
92 }
93}
94
95static void
96gtk_gesture_single_set_property (GObject *object,
97 guint prop_id,
98 const GValue *value,
99 GParamSpec *pspec)
100{
101 switch (prop_id)
102 {
103 case PROP_TOUCH_ONLY:
104 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (object),
105 touch_only: g_value_get_boolean (value));
106 break;
107 case PROP_EXCLUSIVE:
108 gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (object),
109 exclusive: g_value_get_boolean (value));
110 break;
111 case PROP_BUTTON:
112 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (object),
113 button: g_value_get_uint (value));
114 break;
115 default:
116 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117 }
118}
119
120static void
121gtk_gesture_single_cancel (GtkGesture *gesture,
122 GdkEventSequence *sequence)
123{
124 GtkGestureSinglePrivate *priv;
125
126 priv = gtk_gesture_single_get_instance_private (GTK_GESTURE_SINGLE (gesture));
127
128 if (sequence == priv->current_sequence)
129 priv->current_button = 0;
130}
131
132static gboolean
133gtk_gesture_single_handle_event (GtkEventController *controller,
134 GdkEvent *event,
135 double x,
136 double y)
137{
138 GdkEventSequence *sequence = NULL;
139 GtkGestureSinglePrivate *priv;
140 GdkDevice *source_device;
141 GdkInputSource source;
142 guint button = 0, state, i;
143 gboolean retval, test_touchscreen = FALSE;
144 GdkEventType event_type;
145
146 source_device = gdk_event_get_device (event);
147
148 if (!source_device)
149 return FALSE;
150
151 priv = gtk_gesture_single_get_instance_private (GTK_GESTURE_SINGLE (controller));
152 source = gdk_device_get_source (device: source_device);
153
154 if (source != GDK_SOURCE_TOUCHSCREEN)
155 test_touchscreen = gtk_simulate_touchscreen ();
156
157 event_type = gdk_event_get_event_type (event);
158
159 switch ((guint) event_type)
160 {
161 case GDK_TOUCH_BEGIN:
162 case GDK_TOUCH_END:
163 case GDK_TOUCH_UPDATE:
164 if (priv->exclusive && !gdk_touch_event_get_emulating_pointer (event))
165 return FALSE;
166
167 sequence = gdk_event_get_event_sequence (event);
168 button = 1;
169 break;
170 case GDK_BUTTON_PRESS:
171 case GDK_BUTTON_RELEASE:
172 if (priv->touch_only && !test_touchscreen && source != GDK_SOURCE_TOUCHSCREEN)
173 return FALSE;
174
175 button = gdk_button_event_get_button (event);
176 break;
177 case GDK_MOTION_NOTIFY:
178 if (!gtk_gesture_handles_sequence (GTK_GESTURE (controller), sequence))
179 return FALSE;
180 if (priv->touch_only && !test_touchscreen && source != GDK_SOURCE_TOUCHSCREEN)
181 return FALSE;
182 state = gdk_event_get_modifier_state (event);
183
184 if (priv->current_button > 0 && priv->current_button <= 5 &&
185 (state & (GDK_BUTTON1_MASK << (priv->current_button - 1))))
186 button = priv->current_button;
187 else if (priv->current_button == 0)
188 {
189 /* No current button, find out from the mask */
190 for (i = 0; i < 3; i++)
191 {
192 if ((state & (GDK_BUTTON1_MASK << i)) == 0)
193 continue;
194 button = i + 1;
195 break;
196 }
197 }
198
199 break;
200 case GDK_TOUCHPAD_HOLD:
201 if (gdk_touchpad_event_get_n_fingers (event) == 1)
202 return FALSE;
203 G_GNUC_FALLTHROUGH;
204 case GDK_TOUCH_CANCEL:
205 case GDK_GRAB_BROKEN:
206 case GDK_TOUCHPAD_SWIPE:
207 return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_single_parent_class)->handle_event (controller, event, x, y);
208 break;
209 default:
210 return FALSE;
211 }
212
213 if (button == 0 ||
214 (priv->button != 0 && priv->button != button) ||
215 (priv->current_button != 0 && priv->current_button != button))
216 {
217 if (gtk_gesture_is_active (GTK_GESTURE (controller)))
218 gtk_event_controller_reset (controller);
219 return FALSE;
220 }
221
222 if (event_type == GDK_BUTTON_PRESS || event_type == GDK_TOUCH_BEGIN ||
223 event_type == GDK_MOTION_NOTIFY || event_type == GDK_TOUCH_UPDATE)
224 {
225 if (!gtk_gesture_is_active (GTK_GESTURE (controller)))
226 priv->current_sequence = sequence;
227
228 priv->current_button = button;
229 }
230
231 retval = GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_single_parent_class)->handle_event (controller, event, x, y);
232
233 if (sequence == priv->current_sequence &&
234 (event_type == GDK_BUTTON_RELEASE || event_type == GDK_TOUCH_END))
235 priv->current_button = 0;
236 else if (priv->current_sequence == sequence &&
237 !gtk_gesture_handles_sequence (GTK_GESTURE (controller), sequence))
238 {
239 if (button == priv->current_button && event_type == GDK_BUTTON_PRESS)
240 priv->current_button = 0;
241 else if (sequence == priv->current_sequence && event_type == GDK_TOUCH_BEGIN)
242 priv->current_sequence = NULL;
243 }
244
245 return retval;
246}
247
248static void
249gtk_gesture_single_class_init (GtkGestureSingleClass *klass)
250{
251 GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
252 GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
253 GObjectClass *object_class = G_OBJECT_CLASS (klass);
254
255 object_class->get_property = gtk_gesture_single_get_property;
256 object_class->set_property = gtk_gesture_single_set_property;
257
258 controller_class->handle_event = gtk_gesture_single_handle_event;
259
260 gesture_class->cancel = gtk_gesture_single_cancel;
261
262 /**
263 * GtkGestureSingle:touch-only: (attributes org.gtk.Property.get=gtk_gesture_single_get_touch_only org.gtk.Property.set=gtk_gesture_single_set_touch_only)
264 *
265 * Whether the gesture handles only touch events.
266 */
267 properties[PROP_TOUCH_ONLY] =
268 g_param_spec_boolean (name: "touch-only",
269 P_("Handle only touch events"),
270 P_("Whether the gesture handles only touch events"),
271 FALSE,
272 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
273
274 /**
275 * GtkGestureSingle:exclusive: (attributes org.gtk.Property.get=gtk_gesture_single_get_exclusive org.gtk.Property.set=gtk_gesture_single_set_exclusive)
276 *
277 * Whether the gesture is exclusive.
278 *
279 * Exclusive gestures only listen to pointer and pointer emulated events.
280 */
281 properties[PROP_EXCLUSIVE] =
282 g_param_spec_boolean (name: "exclusive",
283 P_("Whether the gesture is exclusive"),
284 P_("Whether the gesture is exclusive"),
285 FALSE,
286 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
287
288 /**
289 * GtkGestureSingle:button: (attributes org.gtk.Property.get=gtk_gesture_single_get_button org.gtk.Property.set=gtk_gesture_single_set_button)
290 *
291 * Mouse button number to listen to, or 0 to listen for any button.
292 */
293 properties[PROP_BUTTON] =
294 g_param_spec_uint (name: "button",
295 P_("Button number"),
296 P_("Button number to listen to"),
297 minimum: 0, G_MAXUINT,
298 GDK_BUTTON_PRIMARY,
299 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
300
301 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: properties);
302}
303
304static void
305gtk_gesture_single_init (GtkGestureSingle *gesture)
306{
307 GtkGestureSinglePrivate *priv;
308
309 priv = gtk_gesture_single_get_instance_private (self: gesture);
310 priv->touch_only = FALSE;
311 priv->button = GDK_BUTTON_PRIMARY;
312}
313
314/**
315 * gtk_gesture_single_get_touch_only: (attributes org.gtk.Method.get_property=touch-only)
316 * @gesture: a `GtkGestureSingle`
317 *
318 * Returns %TRUE if the gesture is only triggered by touch events.
319 *
320 * Returns: %TRUE if the gesture only handles touch events
321 */
322gboolean
323gtk_gesture_single_get_touch_only (GtkGestureSingle *gesture)
324{
325 GtkGestureSinglePrivate *priv;
326
327 g_return_val_if_fail (GTK_IS_GESTURE_SINGLE (gesture), FALSE);
328
329 priv = gtk_gesture_single_get_instance_private (self: gesture);
330
331 return priv->touch_only;
332}
333
334/**
335 * gtk_gesture_single_set_touch_only: (attributes org.gtk.Method.set_property=touch-only)
336 * @gesture: a `GtkGestureSingle`
337 * @touch_only: whether @gesture handles only touch events
338 *
339 * Sets whether to handle only touch events.
340 *
341 * If @touch_only is %TRUE, @gesture will only handle events of type
342 * %GDK_TOUCH_BEGIN, %GDK_TOUCH_UPDATE or %GDK_TOUCH_END. If %FALSE,
343 * mouse events will be handled too.
344 */
345void
346gtk_gesture_single_set_touch_only (GtkGestureSingle *gesture,
347 gboolean touch_only)
348{
349 GtkGestureSinglePrivate *priv;
350
351 g_return_if_fail (GTK_IS_GESTURE_SINGLE (gesture));
352
353 touch_only = touch_only != FALSE;
354 priv = gtk_gesture_single_get_instance_private (self: gesture);
355
356 if (priv->touch_only == touch_only)
357 return;
358
359 priv->touch_only = touch_only;
360 g_object_notify_by_pspec (G_OBJECT (gesture), pspec: properties[PROP_TOUCH_ONLY]);
361}
362
363/**
364 * gtk_gesture_single_get_exclusive: (attributes org.gtk.Method.get_property=exclusive)
365 * @gesture: a `GtkGestureSingle`
366 *
367 * Gets whether a gesture is exclusive.
368 *
369 * For more information, see [method@Gtk.GestureSingle.set_exclusive].
370 *
371 * Returns: Whether the gesture is exclusive
372 */
373gboolean
374gtk_gesture_single_get_exclusive (GtkGestureSingle *gesture)
375{
376 GtkGestureSinglePrivate *priv;
377
378 g_return_val_if_fail (GTK_IS_GESTURE_SINGLE (gesture), FALSE);
379
380 priv = gtk_gesture_single_get_instance_private (self: gesture);
381
382 return priv->exclusive;
383}
384
385/**
386 * gtk_gesture_single_set_exclusive: (attributes org.gtk.Method.set_property=exclusive)
387 * @gesture: a `GtkGestureSingle`
388 * @exclusive: %TRUE to make @gesture exclusive
389 *
390 * Sets whether @gesture is exclusive.
391 *
392 * An exclusive gesture will only handle pointer and "pointer emulated"
393 * touch events, so at any given time, there is only one sequence able
394 * to interact with those.
395 */
396void
397gtk_gesture_single_set_exclusive (GtkGestureSingle *gesture,
398 gboolean exclusive)
399{
400 GtkGestureSinglePrivate *priv;
401
402 g_return_if_fail (GTK_IS_GESTURE_SINGLE (gesture));
403
404 exclusive = exclusive != FALSE;
405 priv = gtk_gesture_single_get_instance_private (self: gesture);
406
407 if (priv->exclusive == exclusive)
408 return;
409
410 priv->exclusive = exclusive;
411 g_object_notify_by_pspec (G_OBJECT (gesture), pspec: properties[PROP_EXCLUSIVE]);
412}
413
414/**
415 * gtk_gesture_single_get_button: (attributes org.gtk.Method.get_property=button)
416 * @gesture: a `GtkGestureSingle`
417 *
418 * Returns the button number @gesture listens for.
419 *
420 * If this is 0, the gesture reacts to any button press.
421 *
422 * Returns: The button number, or 0 for any button
423 */
424guint
425gtk_gesture_single_get_button (GtkGestureSingle *gesture)
426{
427 GtkGestureSinglePrivate *priv;
428
429 g_return_val_if_fail (GTK_IS_GESTURE_SINGLE (gesture), 0);
430
431 priv = gtk_gesture_single_get_instance_private (self: gesture);
432
433 return priv->button;
434}
435
436/**
437 * gtk_gesture_single_set_button: (attributes org.gtk.Method.set_property=button)
438 * @gesture: a `GtkGestureSingle`
439 * @button: button number to listen to, or 0 for any button
440 *
441 * Sets the button number @gesture listens to.
442 *
443 * If non-0, every button press from a different button
444 * number will be ignored. Touch events implicitly match
445 * with button 1.
446 */
447void
448gtk_gesture_single_set_button (GtkGestureSingle *gesture,
449 guint button)
450{
451 GtkGestureSinglePrivate *priv;
452
453 g_return_if_fail (GTK_IS_GESTURE_SINGLE (gesture));
454
455 priv = gtk_gesture_single_get_instance_private (self: gesture);
456
457 if (priv->button == button)
458 return;
459
460 priv->button = button;
461 g_object_notify_by_pspec (G_OBJECT (gesture), pspec: properties[PROP_BUTTON]);
462}
463
464/**
465 * gtk_gesture_single_get_current_button:
466 * @gesture: a `GtkGestureSingle`
467 *
468 * Returns the button number currently interacting
469 * with @gesture, or 0 if there is none.
470 *
471 * Returns: The current button number
472 */
473guint
474gtk_gesture_single_get_current_button (GtkGestureSingle *gesture)
475{
476 GtkGestureSinglePrivate *priv;
477
478 g_return_val_if_fail (GTK_IS_GESTURE_SINGLE (gesture), 0);
479
480 priv = gtk_gesture_single_get_instance_private (self: gesture);
481
482 return priv->current_button;
483}
484
485/**
486 * gtk_gesture_single_get_current_sequence:
487 * @gesture: a `GtkGestureSingle`
488 *
489 * Returns the event sequence currently interacting with @gesture.
490 *
491 * This is only meaningful if [method@Gtk.Gesture.is_active]
492 * returns %TRUE.
493 *
494 * Returns: (nullable): the current sequence
495 */
496GdkEventSequence *
497gtk_gesture_single_get_current_sequence (GtkGestureSingle *gesture)
498{
499 GtkGestureSinglePrivate *priv;
500
501 g_return_val_if_fail (GTK_IS_GESTURE_SINGLE (gesture), NULL);
502
503 priv = gtk_gesture_single_get_instance_private (self: gesture);
504
505 return priv->current_sequence;
506}
507

source code of gtk/gtk/gtkgesturesingle.c