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 | |
48 | typedef struct _GtkGestureSwipePrivate GtkGestureSwipePrivate; |
49 | typedef struct _EventData EventData; |
50 | |
51 | struct _EventData |
52 | { |
53 | guint32 evtime; |
54 | int x; |
55 | int y; |
56 | }; |
57 | |
58 | struct _GtkGestureSwipePrivate |
59 | { |
60 | GArray *events; |
61 | }; |
62 | |
63 | enum { |
64 | SWIPE, |
65 | N_SIGNALS |
66 | }; |
67 | |
68 | static guint signals[N_SIGNALS] = { 0 }; |
69 | |
70 | G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureSwipe, gtk_gesture_swipe, GTK_TYPE_GESTURE_SINGLE) |
71 | |
72 | static void |
73 | gtk_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 | |
83 | static gboolean |
84 | gtk_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 | |
107 | static 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 | |
133 | static void |
134 | gtk_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 | |
152 | static void |
153 | gtk_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 | |
161 | static 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 | |
197 | static void |
198 | gtk_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 | |
224 | static void |
225 | gtk_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 | |
261 | static void |
262 | gtk_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 | */ |
277 | GtkGesture * |
278 | gtk_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 | */ |
298 | gboolean |
299 | gtk_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 | |