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 | |
47 | typedef struct _GtkGestureSinglePrivate GtkGestureSinglePrivate; |
48 | |
49 | struct _GtkGestureSinglePrivate |
50 | { |
51 | GdkEventSequence *current_sequence; |
52 | guint button; |
53 | guint current_button; |
54 | guint touch_only : 1; |
55 | guint exclusive : 1; |
56 | }; |
57 | |
58 | enum { |
59 | PROP_TOUCH_ONLY = 1, |
60 | PROP_EXCLUSIVE, |
61 | PROP_BUTTON, |
62 | LAST_PROP |
63 | }; |
64 | |
65 | static GParamSpec *properties[LAST_PROP] = { NULL, }; |
66 | |
67 | G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureSingle, gtk_gesture_single, GTK_TYPE_GESTURE) |
68 | |
69 | static void |
70 | gtk_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 | |
95 | static void |
96 | gtk_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 | |
120 | static void |
121 | gtk_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 | |
132 | static gboolean |
133 | gtk_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 | |
248 | static void |
249 | gtk_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 | |
304 | static void |
305 | gtk_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 | */ |
322 | gboolean |
323 | gtk_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 | */ |
345 | void |
346 | gtk_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 | */ |
373 | gboolean |
374 | gtk_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 | */ |
396 | void |
397 | gtk_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 | */ |
424 | guint |
425 | gtk_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 | */ |
447 | void |
448 | gtk_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 | */ |
473 | guint |
474 | gtk_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 | */ |
496 | GdkEventSequence * |
497 | gtk_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 | |