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