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 * GtkGestureSwipe:
23 *
24 * `GtkGestureSwipe` is a `GtkGesture` for swipe gestures.
25 *
26 * After a press/move/.../move/release sequence happens, the
27 * [signal@Gtk.GestureSwipe::swipe] signal will be emitted,
28 * providing the velocity and directionality of the sequence
29 * at the time it was lifted.
30 *
31 * If the velocity is desired in intermediate points,
32 * [method@Gtk.GestureSwipe.get_velocity] can be called in a
33 * [signal@Gtk.Gesture::update] handler.
34 *
35 * All velocities are reported in pixels/sec units.
36 */
37
38#include "config.h"
39#include "gtkgestureswipe.h"
40#include "gtkgestureswipeprivate.h"
41#include "gtkgestureprivate.h"
42#include "gtkmarshalers.h"
43#include "gtkintl.h"
44#include "gtkmarshalers.h"
45
46#define CAPTURE_THRESHOLD_MS 150
47
48typedef struct _GtkGestureSwipePrivate GtkGestureSwipePrivate;
49typedef struct _EventData EventData;
50
51struct _EventData
52{
53 guint32 evtime;
54 int x;
55 int y;
56};
57
58struct _GtkGestureSwipePrivate
59{
60 GArray *events;
61};
62
63enum {
64 SWIPE,
65 N_SIGNALS
66};
67
68static guint signals[N_SIGNALS] = { 0 };
69
70G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureSwipe, gtk_gesture_swipe, GTK_TYPE_GESTURE_SINGLE)
71
72static void
73gtk_gesture_swipe_finalize (GObject *object)
74{
75 GtkGestureSwipePrivate *priv;
76
77 priv = gtk_gesture_swipe_get_instance_private (GTK_GESTURE_SWIPE (object));
78 g_array_free (array: priv->events, TRUE);
79
80 G_OBJECT_CLASS (gtk_gesture_swipe_parent_class)->finalize (object);
81}
82
83static gboolean
84gtk_gesture_swipe_filter_event (GtkEventController *controller,
85 GdkEvent *event)
86{
87 /* Let touchpad swipe and hold events go through, only if they match n-points */
88 if (gdk_event_get_event_type (event) == GDK_TOUCHPAD_SWIPE ||
89 gdk_event_get_event_type (event) == GDK_TOUCHPAD_HOLD)
90 {
91 guint n_points;
92 guint n_fingers;
93
94 g_object_get (G_OBJECT (controller), first_property_name: "n-points", &n_points, NULL);
95
96 n_fingers = gdk_touchpad_event_get_n_fingers (event);
97
98 if (n_fingers == n_points)
99 return FALSE;
100 else
101 return TRUE;
102 }
103
104 return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_swipe_parent_class)->filter_event (controller, event);
105}
106
107static void
108_gtk_gesture_swipe_clear_backlog (GtkGestureSwipe *gesture,
109 guint32 evtime)
110{
111 GtkGestureSwipePrivate *priv;
112 int i, length = 0;
113
114 priv = gtk_gesture_swipe_get_instance_private (self: gesture);
115
116 for (i = 0; i < (int) priv->events->len; i++)
117 {
118 EventData *data;
119
120 data = &g_array_index (priv->events, EventData, i);
121
122 if (data->evtime >= evtime - CAPTURE_THRESHOLD_MS)
123 {
124 length = i - 1;
125 break;
126 }
127 }
128
129 if (length > 0)
130 g_array_remove_range (array: priv->events, index_: 0, length);
131}
132
133static void
134gtk_gesture_swipe_append_event (GtkGestureSwipe *swipe,
135 GdkEventSequence *sequence)
136{
137 GtkGestureSwipePrivate *priv;
138 EventData new;
139 double x, y;
140
141 priv = gtk_gesture_swipe_get_instance_private (self: swipe);
142 _gtk_gesture_get_last_update_time (GTK_GESTURE (swipe), sequence, evtime: &new.evtime);
143 gtk_gesture_get_point (GTK_GESTURE (swipe), sequence, x: &x, y: &y);
144
145 new.x = x;
146 new.y = y;
147
148 _gtk_gesture_swipe_clear_backlog (gesture: swipe, evtime: new.evtime);
149 g_array_append_val (priv->events, new);
150}
151
152static void
153gtk_gesture_swipe_update (GtkGesture *gesture,
154 GdkEventSequence *sequence)
155{
156 GtkGestureSwipe *swipe = GTK_GESTURE_SWIPE (gesture);
157
158 gtk_gesture_swipe_append_event (swipe, sequence);
159}
160
161static void
162_gtk_gesture_swipe_calculate_velocity (GtkGestureSwipe *gesture,
163 double *velocity_x,
164 double *velocity_y)
165{
166 GtkGestureSwipePrivate *priv;
167 GdkEventSequence *sequence;
168 guint32 evtime, diff_time;
169 EventData *start, *end;
170 double diff_x, diff_y;
171
172 priv = gtk_gesture_swipe_get_instance_private (self: gesture);
173 *velocity_x = *velocity_y = 0;
174
175 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
176 _gtk_gesture_get_last_update_time (GTK_GESTURE (gesture), sequence, evtime: &evtime);
177 _gtk_gesture_swipe_clear_backlog (gesture, evtime);
178
179 if (priv->events->len == 0)
180 return;
181
182 start = &g_array_index (priv->events, EventData, 0);
183 end = &g_array_index (priv->events, EventData, priv->events->len - 1);
184
185 diff_time = end->evtime - start->evtime;
186 diff_x = end->x - start->x;
187 diff_y = end->y - start->y;
188
189 if (diff_time == 0)
190 return;
191
192 /* Velocity in pixels/sec */
193 *velocity_x = diff_x * 1000 / diff_time;
194 *velocity_y = diff_y * 1000 / diff_time;
195}
196
197static void
198gtk_gesture_swipe_end (GtkGesture *gesture,
199 GdkEventSequence *sequence)
200{
201 GtkGestureSwipe *swipe = GTK_GESTURE_SWIPE (gesture);
202 GtkGestureSwipePrivate *priv;
203 double velocity_x, velocity_y;
204 GdkEventSequence *seq;
205
206 seq = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
207
208 if (gtk_gesture_get_sequence_state (gesture, sequence: seq) == GTK_EVENT_SEQUENCE_DENIED)
209 return;
210
211 if (gtk_gesture_is_active (gesture))
212 return;
213
214 gtk_gesture_swipe_append_event (swipe, sequence);
215
216 priv = gtk_gesture_swipe_get_instance_private (self: swipe);
217 _gtk_gesture_swipe_calculate_velocity (gesture: swipe, velocity_x: &velocity_x, velocity_y: &velocity_y);
218 g_signal_emit (instance: gesture, signal_id: signals[SWIPE], detail: 0, velocity_x, velocity_y);
219
220 if (priv->events->len > 0)
221 g_array_remove_range (array: priv->events, index_: 0, length: priv->events->len);
222}
223
224static void
225gtk_gesture_swipe_class_init (GtkGestureSwipeClass *klass)
226{
227 GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
228 GtkEventControllerClass *event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
229 GObjectClass *object_class = G_OBJECT_CLASS (klass);
230
231 object_class->finalize = gtk_gesture_swipe_finalize;
232
233 event_controller_class->filter_event = gtk_gesture_swipe_filter_event;
234
235 gesture_class->update = gtk_gesture_swipe_update;
236 gesture_class->end = gtk_gesture_swipe_end;
237
238 /**
239 * GtkGestureSwipe::swipe:
240 * @gesture: object which received the signal
241 * @velocity_x: velocity in the X axis, in pixels/sec
242 * @velocity_y: velocity in the Y axis, in pixels/sec
243 *
244 * Emitted when the recognized gesture is finished.
245 *
246 * Velocity and direction are a product of previously recorded events.
247 */
248 signals[SWIPE] =
249 g_signal_new (I_("swipe"),
250 G_TYPE_FROM_CLASS (klass),
251 signal_flags: G_SIGNAL_RUN_LAST,
252 G_STRUCT_OFFSET (GtkGestureSwipeClass, swipe),
253 NULL, NULL,
254 c_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE,
255 G_TYPE_NONE, n_params: 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
256 g_signal_set_va_marshaller (signal_id: signals[SWIPE],
257 G_TYPE_FROM_CLASS (klass),
258 va_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLEv);
259}
260
261static void
262gtk_gesture_swipe_init (GtkGestureSwipe *gesture)
263{
264 GtkGestureSwipePrivate *priv;
265
266 priv = gtk_gesture_swipe_get_instance_private (self: gesture);
267 priv->events = g_array_new (FALSE, FALSE, element_size: sizeof (EventData));
268}
269
270/**
271 * gtk_gesture_swipe_new:
272 *
273 * Returns a newly created `GtkGesture` that recognizes swipes.
274 *
275 * Returns: a newly created `GtkGestureSwipe`
276 */
277GtkGesture *
278gtk_gesture_swipe_new (void)
279{
280 return g_object_new (GTK_TYPE_GESTURE_SWIPE,
281 NULL);
282}
283
284/**
285 * gtk_gesture_swipe_get_velocity:
286 * @gesture: a `GtkGestureSwipe`
287 * @velocity_x: (out): return value for the velocity in the X axis, in pixels/sec
288 * @velocity_y: (out): return value for the velocity in the Y axis, in pixels/sec
289 *
290 * Gets the current velocity.
291 *
292 * If the gesture is recognized, this function returns %TRUE and fills
293 * in @velocity_x and @velocity_y with the recorded velocity, as per the
294 * last events processed.
295 *
296 * Returns: whether velocity could be calculated
297 */
298gboolean
299gtk_gesture_swipe_get_velocity (GtkGestureSwipe *gesture,
300 double *velocity_x,
301 double *velocity_y)
302{
303 double vel_x, vel_y;
304
305 g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
306
307 if (!gtk_gesture_is_recognized (GTK_GESTURE (gesture)))
308 return FALSE;
309
310 _gtk_gesture_swipe_calculate_velocity (gesture, velocity_x: &vel_x, velocity_y: &vel_y);
311
312 if (velocity_x)
313 *velocity_x = vel_x;
314 if (velocity_y)
315 *velocity_y = vel_y;
316
317 return TRUE;
318}
319

source code of gtk/gtk/gtkgestureswipe.c