1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2017-2018, Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author(s): Carlos Garnacho <carlosg@gnome.org>
18 */
19
20/**
21 * GtkGestureStylus:
22 *
23 * `GtkGestureStylus` is a `GtkGesture` specific to stylus input.
24 *
25 * The provided signals just relay the basic information of the
26 * stylus events.
27 */
28
29#include "config.h"
30#include "gtkgesturestylus.h"
31#include "gtkgesturestylusprivate.h"
32#include "gtkprivate.h"
33#include "gtkintl.h"
34#include "gtkmarshalers.h"
35#include "gtkmain.h"
36#include "gtknative.h"
37
38G_DEFINE_TYPE (GtkGestureStylus, gtk_gesture_stylus, GTK_TYPE_GESTURE_SINGLE)
39
40enum {
41 PROXIMITY,
42 DOWN,
43 MOTION,
44 UP,
45 N_SIGNALS
46};
47
48static guint signals[N_SIGNALS] = { 0, };
49
50static gboolean
51gtk_gesture_stylus_handle_event (GtkEventController *controller,
52 GdkEvent *event,
53 double x,
54 double y)
55{
56 GdkModifierType modifiers;
57 guint n_signal;
58
59 GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_stylus_parent_class)->handle_event (controller, event, x, y);
60
61 if (!gdk_event_get_device_tool (event))
62 return FALSE;
63
64 switch ((guint) gdk_event_get_event_type (event))
65 {
66 case GDK_BUTTON_PRESS:
67 n_signal = DOWN;
68 break;
69 case GDK_BUTTON_RELEASE:
70 n_signal = UP;
71 break;
72 case GDK_MOTION_NOTIFY:
73 modifiers = gdk_event_get_modifier_state (event);
74
75 if (modifiers & GDK_BUTTON1_MASK)
76 n_signal = MOTION;
77 else
78 n_signal = PROXIMITY;
79 break;
80 default:
81 return FALSE;
82 }
83
84 g_signal_emit (instance: controller, signal_id: signals[n_signal], detail: 0, x, y);
85
86 return TRUE;
87}
88
89static void
90gtk_gesture_stylus_class_init (GtkGestureStylusClass *klass)
91{
92 GtkEventControllerClass *event_controller_class;
93
94 event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
95 event_controller_class->handle_event = gtk_gesture_stylus_handle_event;
96
97 /**
98 * GtkGestureStylus::proximity:
99 * @gesture: the `GtkGestureStylus` that emitted the signal
100 * @x: the X coordinate of the stylus event
101 * @y: the Y coordinate of the stylus event
102 *
103 * Emitted when the stylus is in proximity of the device.
104 */
105 signals[PROXIMITY] =
106 g_signal_new (I_("proximity"),
107 G_TYPE_FROM_CLASS (klass),
108 signal_flags: G_SIGNAL_RUN_LAST,
109 G_STRUCT_OFFSET (GtkGestureStylusClass, proximity),
110 NULL, NULL,
111 c_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE,
112 G_TYPE_NONE, n_params: 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
113 g_signal_set_va_marshaller (signal_id: signals[PROXIMITY],
114 G_TYPE_FROM_CLASS (klass),
115 va_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLEv);
116
117 /**
118 * GtkGestureStylus::down:
119 * @gesture: the `GtkGestureStylus` that emitted the signal
120 * @x: the X coordinate of the stylus event
121 * @y: the Y coordinate of the stylus event
122 *
123 * Emitted when the stylus touches the device.
124 */
125 signals[DOWN] =
126 g_signal_new (I_("down"),
127 G_TYPE_FROM_CLASS (klass),
128 signal_flags: G_SIGNAL_RUN_LAST,
129 G_STRUCT_OFFSET (GtkGestureStylusClass, down),
130 NULL, NULL,
131 c_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE,
132 G_TYPE_NONE, n_params: 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
133 g_signal_set_va_marshaller (signal_id: signals[DOWN],
134 G_TYPE_FROM_CLASS (klass),
135 va_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLEv);
136
137 /**
138 * GtkGestureStylus::motion:
139 * @gesture: the `GtkGestureStylus` that emitted the signal
140 * @x: the X coordinate of the stylus event
141 * @y: the Y coordinate of the stylus event
142 *
143 * Emitted when the stylus moves while touching the device.
144 */
145 signals[MOTION] =
146 g_signal_new (I_("motion"),
147 G_TYPE_FROM_CLASS (klass),
148 signal_flags: G_SIGNAL_RUN_LAST,
149 G_STRUCT_OFFSET (GtkGestureStylusClass, motion),
150 NULL, NULL,
151 c_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE,
152 G_TYPE_NONE, n_params: 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
153 g_signal_set_va_marshaller (signal_id: signals[MOTION],
154 G_TYPE_FROM_CLASS (klass),
155 va_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLEv);
156
157 /**
158 * GtkGestureStylus::up:
159 * @gesture: the `GtkGestureStylus` that emitted the signal
160 * @x: the X coordinate of the stylus event
161 * @y: the Y coordinate of the stylus event
162 *
163 * Emitted when the stylus no longer touches the device.
164 */
165 signals[UP] =
166 g_signal_new (I_("up"),
167 G_TYPE_FROM_CLASS (klass),
168 signal_flags: G_SIGNAL_RUN_LAST,
169 G_STRUCT_OFFSET (GtkGestureStylusClass, up),
170 NULL, NULL,
171 c_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE,
172 G_TYPE_NONE, n_params: 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
173 g_signal_set_va_marshaller (signal_id: signals[UP],
174 G_TYPE_FROM_CLASS (klass),
175 va_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLEv);
176}
177
178static void
179gtk_gesture_stylus_init (GtkGestureStylus *gesture)
180{
181}
182
183/**
184 * gtk_gesture_stylus_new:
185 *
186 * Creates a new `GtkGestureStylus`.
187 *
188 * Returns: a newly created stylus gesture
189 **/
190GtkGesture *
191gtk_gesture_stylus_new (void)
192{
193 return g_object_new (GTK_TYPE_GESTURE_STYLUS,
194 NULL);
195}
196
197/**
198 * gtk_gesture_stylus_get_axis:
199 * @gesture: a `GtkGestureStylus`
200 * @axis: requested device axis
201 * @value: (out): return location for the axis value
202 *
203 * Returns the current value for the requested @axis.
204 *
205 * This function must be called from the handler of one of the
206 * [signal@Gtk.GestureStylus::down], [signal@Gtk.GestureStylus::motion],
207 * [signal@Gtk.GestureStylus::up] or [signal@Gtk.GestureStylus::proximity]
208 * signals.
209 *
210 * Returns: %TRUE if there is a current value for the axis
211 **/
212gboolean
213gtk_gesture_stylus_get_axis (GtkGestureStylus *gesture,
214 GdkAxisUse axis,
215 double *value)
216{
217 GdkEvent *event;
218
219 g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE);
220 g_return_val_if_fail (axis < GDK_AXIS_LAST, FALSE);
221 g_return_val_if_fail (value != NULL, FALSE);
222
223 event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (gesture));
224 if (!event)
225 return FALSE;
226
227 return gdk_event_get_axis (event, axis_use: axis, value);
228}
229
230/**
231 * gtk_gesture_stylus_get_axes:
232 * @gesture: a `GtkGestureStylus`
233 * @axes: (array): array of requested axes, terminated with %GDK_AXIS_IGNORE
234 * @values: (out) (array): return location for the axis values
235 *
236 * Returns the current values for the requested @axes.
237 *
238 * This function must be called from the handler of one of the
239 * [signal@Gtk.GestureStylus::down], [signal@Gtk.GestureStylus::motion],
240 * [signal@Gtk.GestureStylus::up] or [signal@Gtk.GestureStylus::proximity]
241 * signals.
242 *
243 * Returns: %TRUE if there is a current value for the axes
244 */
245gboolean
246gtk_gesture_stylus_get_axes (GtkGestureStylus *gesture,
247 GdkAxisUse axes[],
248 double **values)
249{
250 GdkEvent *event;
251 GArray *array;
252 int i = 0;
253
254 g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE);
255 g_return_val_if_fail (values != NULL, FALSE);
256
257 event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (gesture));
258 if (!event)
259 return FALSE;
260
261 array = g_array_new (TRUE, FALSE, element_size: sizeof (double));
262
263 while (axes[i] != GDK_AXIS_IGNORE)
264 {
265 double value;
266
267 if (axes[i] >= GDK_AXIS_LAST)
268 {
269 g_warning ("Requesting unknown axis %d, did you "
270 "forget to add a last GDK_AXIS_IGNORE axis?",
271 axes[i]);
272 g_array_free (array, TRUE);
273 return FALSE;
274 }
275
276 gdk_event_get_axis (event, axis_use: axes[i], value: &value);
277 g_array_append_val (array, value);
278 i++;
279 }
280
281 *values = (double *) g_array_free (array, FALSE);
282 return TRUE;
283}
284
285/**
286 * gtk_gesture_stylus_get_backlog:
287 * @gesture: a `GtkGestureStylus`
288 * @backlog: (out) (array length=n_elems): coordinates and times for the backlog events
289 * @n_elems: (out): return location for the number of elements
290 *
291 * Returns the accumulated backlog of tracking information.
292 *
293 * By default, GTK will limit rate of input events. On stylus input
294 * where accuracy of strokes is paramount, this function returns the
295 * accumulated coordinate/timing state before the emission of the
296 * current [Gtk.GestureStylus::motion] signal.
297 *
298 * This function may only be called within a [signal@Gtk.GestureStylus::motion]
299 * signal handler, the state given in this signal and obtainable through
300 * [method@Gtk.GestureStylus.get_axis] express the latest (most up-to-date)
301 * state in motion history.
302 *
303 * The @backlog is provided in chronological order.
304 *
305 * Returns: %TRUE if there is a backlog to unfold in the current state.
306 */
307gboolean
308gtk_gesture_stylus_get_backlog (GtkGestureStylus *gesture,
309 GdkTimeCoord **backlog,
310 guint *n_elems)
311{
312 GdkEvent *event;
313 GArray *backlog_array;
314 GdkTimeCoord *history = NULL;
315 guint n_coords = 0, i;
316 double surf_x, surf_y;
317 GtkNative *native;
318 GtkWidget *event_widget;
319 GtkWidget *controller_widget;
320
321 g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE);
322 g_return_val_if_fail (backlog != NULL && n_elems != NULL, FALSE);
323
324 event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (gesture));
325
326 if (event && GDK_IS_EVENT_TYPE (event, GDK_MOTION_NOTIFY))
327 history = gdk_event_get_history (event, out_n_coords: &n_coords);
328
329 if (!history)
330 return FALSE;
331
332 native = gtk_widget_get_native (widget: gtk_get_event_widget (event));
333 gtk_native_get_surface_transform (self: native, x: &surf_x, y: &surf_y);
334
335 backlog_array = g_array_new (FALSE, FALSE, element_size: sizeof (GdkTimeCoord));
336 event_widget = gtk_get_event_widget (event);
337 controller_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
338 for (i = 0; i < n_coords; i++)
339 {
340 const GdkTimeCoord *time_coord = &history[i];
341 graphene_point_t p;
342
343 if (gtk_widget_compute_point (widget: event_widget, target: controller_widget,
344 point: &GRAPHENE_POINT_INIT (time_coord->axes[GDK_AXIS_X] - surf_x,
345 time_coord->axes[GDK_AXIS_Y] - surf_y),
346 out_point: &p))
347 {
348 GdkTimeCoord translated_coord = *time_coord;
349
350 translated_coord.axes[GDK_AXIS_X] = p.x;
351 translated_coord.axes[GDK_AXIS_Y] = p.y;
352
353 g_array_append_val (backlog_array, translated_coord);
354 }
355 }
356
357 *n_elems = backlog_array->len;
358 *backlog = (GdkTimeCoord *) g_array_free (array: backlog_array, FALSE);
359 g_free (mem: history);
360
361 return TRUE;
362}
363
364/**
365 * gtk_gesture_stylus_get_device_tool:
366 * @gesture: a `GtkGestureStylus`
367 *
368 * Returns the `GdkDeviceTool` currently driving input through this gesture.
369 *
370 * This function must be called from the handler of one of the
371 * [signal@Gtk.GestureStylus::down], [signal@Gtk.GestureStylus::motion],
372 * [signal@Gtk.GestureStylus::up] or [signal@Gtk.GestureStylus::proximity]
373 * signals.
374 *
375 * Returns: (nullable) (transfer none): The current stylus tool
376 */
377GdkDeviceTool *
378gtk_gesture_stylus_get_device_tool (GtkGestureStylus *gesture)
379{
380 GdkEvent *event;
381
382 g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE);
383
384 event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (gesture));
385 if (!event)
386 return NULL;
387
388 return gdk_event_get_device_tool (event);
389}
390

source code of gtk/gtk/gtkgesturestylus.c