Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-input-remote"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <adwaita.h>
9 : : #include <glib/gi18n-lib.h>
10 : : #include <gtk/gtk.h>
11 : : #include <valent.h>
12 : :
13 : : #include "valent-input-remote.h"
14 : : #include "valent-ui-utils-private.h"
15 : :
16 : : #define CAPTURE_THRESHOLD_MS 50
17 : :
18 : :
19 : : struct _ValentInputRemote
20 : : {
21 : : AdwWindow parent_instance;
22 : :
23 : : GListModel *adapters;
24 : : ValentInputAdapter *adapter;
25 : :
26 : : /* Pointer State */
27 : : unsigned int claimed : 1;
28 : : uint32_t timestamp;
29 : :
30 : : double last_x;
31 : : double last_y;
32 : : double last_v;
33 : : int scale;
34 : :
35 : : /* template */
36 : : GtkDropDown *input_adapter;
37 : : GtkWidget *editor;
38 : : GtkEventController *keyboard;
39 : : GtkWidget *touchpad;
40 : : GtkGesture *pointer_scroll;
41 : : GtkGesture *touch_single;
42 : : GtkGesture *touch_double;
43 : : GtkGesture *touch_triple;
44 : : GtkFilter *filter;
45 : : GtkFilterListModel *model;
46 : : };
47 : :
48 [ + + + - ]: 36 : G_DEFINE_FINAL_TYPE (ValentInputRemote, valent_input_remote, ADW_TYPE_WINDOW)
49 : :
50 : : typedef enum {
51 : : PROP_ADAPTERS = 1,
52 : : } ValentInputRemoteProperty;
53 : :
54 : : static GParamSpec *properties[PROP_ADAPTERS + 1] = { NULL, };
55 : :
56 : : static gboolean
57 : 1 : valent_input_remote_filter (gpointer item,
58 : : gpointer user_data)
59 : : {
60 : 1 : ValentResource *resource = VALENT_RESOURCE (item);
61 : :
62 : 1 : return VALENT_IS_DEVICE (valent_resource_get_source (resource));
63 : : }
64 : :
65 : : static inline gboolean
66 : 0 : valent_input_remote_check_adapter (ValentInputRemote *self)
67 : : {
68 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
69 : :
70 [ # # ]: 0 : if G_UNLIKELY (self->adapter == NULL)
71 : : {
72 : 0 : self->claimed = FALSE;
73 : 0 : self->timestamp = 0;
74 : 0 : self->last_x = 0.0;
75 : 0 : self->last_y = 0.0;
76 : 0 : self->last_v = 0.0;
77 : :
78 : 0 : return FALSE;
79 : : }
80 : :
81 : : return TRUE;
82 : : }
83 : :
84 : : /*
85 : : * Keyboard Input
86 : : */
87 : : static gboolean
88 : 0 : on_key_pressed (GtkEventControllerKey *controller,
89 : : unsigned int keyval,
90 : : unsigned int keycode,
91 : : GdkModifierType state,
92 : : ValentInputRemote *self)
93 : : {
94 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
95 : :
96 [ # # ]: 0 : if (valent_input_remote_check_adapter (self))
97 : 0 : valent_input_adapter_keyboard_keysym (self->adapter, keyval, TRUE);
98 : :
99 : 0 : return TRUE;
100 : : }
101 : :
102 : : static gboolean
103 : 0 : on_key_released (GtkEventControllerKey *controller,
104 : : unsigned int keyval,
105 : : unsigned int keycode,
106 : : GdkModifierType state,
107 : : ValentInputRemote *self)
108 : : {
109 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
110 : :
111 [ # # ]: 0 : if (valent_input_remote_check_adapter (self))
112 : 0 : valent_input_adapter_keyboard_keysym (self->adapter, keyval, FALSE);
113 : :
114 : 0 : return TRUE;
115 : : }
116 : :
117 : : /*
118 : : * Pointer Input
119 : : */
120 : : static inline void
121 : 0 : get_last_update_time (GtkGesture *gesture,
122 : : GdkEventSequence *sequence,
123 : : uint32_t *timestamp)
124 : : {
125 : 0 : GdkEvent *event = NULL;
126 : :
127 [ # # ]: 0 : if (sequence != NULL)
128 : 0 : event = gtk_gesture_get_last_event (gesture, sequence);
129 : :
130 [ # # ]: 0 : if (event != NULL)
131 : 0 : *timestamp = gdk_event_get_time (event);
132 : 0 : }
133 : :
134 : : static inline gboolean
135 : 0 : calculate_delta (ValentInputRemote *self,
136 : : double dx,
137 : : double dy,
138 : : uint32_t dt,
139 : : double *cx,
140 : : double *cy)
141 : : {
142 : 0 : double dr, v, m;
143 : :
144 : 0 : dr = sqrt (pow (dx, 2) + pow (dy, 2));
145 : 0 : v = dr / dt;
146 : :
147 [ # # # # ]: 0 : if (!G_APPROX_VALUE (self->last_v, 0.0, 0.01))
148 : 0 : self->last_v = (v + self->last_v) / 2;
149 : : else
150 : 0 : self->last_v = v;
151 : :
152 : : // TODO: acceleration setting
153 : 0 : m = pow (self->last_v, 1.0);
154 : 0 : m = fmin (4.0, fmax (m, 0.25));
155 : :
156 : 0 : *cx = round (dx * m);
157 : 0 : *cy = round (dy * m);
158 : :
159 : 0 : return dt >= CAPTURE_THRESHOLD_MS;
160 : : }
161 : :
162 : : static inline void
163 : 0 : valent_input_remote_pointer_reset (ValentInputRemote *self)
164 : : {
165 : 0 : self->claimed = FALSE;
166 : 0 : self->last_v = 0.0;
167 : 0 : self->last_x = 0.0;
168 : 0 : self->last_y = 0.0;
169 : 0 : self->timestamp = 0;
170 : 0 : }
171 : :
172 : : /*
173 : : * Scroll Mapping
174 : : */
175 : : static gboolean
176 : 0 : on_scroll (GtkEventControllerScroll *controller,
177 : : double dx,
178 : : double dy,
179 : : ValentInputRemote *self)
180 : : {
181 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
182 : :
183 [ # # ]: 0 : if (valent_input_remote_check_adapter (self))
184 : 0 : valent_input_adapter_pointer_axis (self->adapter, dx, dy);
185 : :
186 : 0 : return TRUE;
187 : : }
188 : :
189 : : /*
190 : : * Pointer Button Mapping
191 : : *
192 : : * This gesture maps pointer button presses and releases directly, except in the
193 : : * case of a press-move sequence of the primary button, which is used to emulate
194 : : * touchpad motion.
195 : : */
196 : : static void
197 : 0 : on_single_begin (GtkGestureDrag *gesture,
198 : : double start_x,
199 : : double start_y,
200 : : ValentInputRemote *self)
201 : : {
202 : 0 : GtkGestureSingle *single = GTK_GESTURE_SINGLE (gesture);
203 : 0 : unsigned int button = 0;
204 : :
205 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
206 : :
207 [ # # ]: 0 : if (!valent_input_remote_check_adapter (self))
208 : : return;
209 : :
210 : : /* Relative pointer motion is only emulated for the primary button, otherwise
211 : : * presses and releases are mapped directly to the adapter. */
212 : 0 : button = gtk_gesture_single_get_current_button (single);
213 : :
214 [ # # ]: 0 : if (button == GDK_BUTTON_PRIMARY)
215 : : {
216 : 0 : GdkEventSequence *sequence = NULL;
217 : 0 : uint32_t timestamp = 0;
218 : :
219 : 0 : sequence = gtk_gesture_single_get_current_sequence (single);
220 : 0 : get_last_update_time (GTK_GESTURE (gesture), sequence, ×tamp);
221 : :
222 : 0 : self->last_x = start_x;
223 : 0 : self->last_y = start_y;
224 : 0 : self->timestamp = timestamp;
225 : : }
226 : :
227 : : /* Always pass through the button press, since pointer motion is only
228 : : * emulated behaviour. */
229 : 0 : valent_input_adapter_pointer_button (self->adapter, button, TRUE);
230 : : }
231 : :
232 : : static void
233 : 0 : on_single_update (GtkGesture *gesture,
234 : : GdkEventSequence *sequence,
235 : : ValentInputRemote *self)
236 : : {
237 : 0 : unsigned int button = 0;
238 : 0 : uint32_t timestamp = 0;
239 : 0 : double x, y;
240 : 0 : double dx, dy;
241 : 0 : uint32_t dt;
242 : 0 : double cx, cy;
243 : :
244 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
245 : :
246 [ # # ]: 0 : if (!valent_input_remote_check_adapter (self))
247 : 0 : return;
248 : :
249 : 0 : button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
250 : :
251 [ # # ]: 0 : if (button != GDK_BUTTON_PRIMARY)
252 : : return;
253 : :
254 : 0 : get_last_update_time (gesture, sequence, ×tamp);
255 : 0 : gtk_gesture_get_point (gesture, sequence, &x, &y);
256 : :
257 : 0 : dt = timestamp - self->timestamp;
258 : 0 : dx = (x - self->last_x) * self->scale;
259 : 0 : dy = (y - self->last_y) * self->scale;
260 : :
261 [ # # ]: 0 : if (!calculate_delta (self, dx, dy, dt, &cx, &cy))
262 : : return;
263 : :
264 [ # # # # : 0 : if (dx >= 1.0 || dx <= -1.0 || dy >= 1.0 || dy <= -1.0)
# # # # ]
265 : : {
266 : 0 : self->claimed = TRUE;
267 : 0 : gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
268 : :
269 : 0 : self->last_x = x;
270 : 0 : self->last_y = y;
271 : 0 : self->timestamp = timestamp;
272 : :
273 : 0 : valent_input_adapter_pointer_motion (self->adapter, cx, cy);
274 : : }
275 : : }
276 : :
277 : : static void
278 : 0 : on_single_end (GtkGestureDrag *gesture,
279 : : double offset_x,
280 : : double offset_y,
281 : : ValentInputRemote *self)
282 : : {
283 : 0 : unsigned int button = 0;
284 : :
285 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
286 : :
287 [ # # ]: 0 : if (!valent_input_remote_check_adapter (self))
288 : : return;
289 : :
290 : 0 : button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
291 : 0 : valent_input_adapter_pointer_button (self->adapter, button, FALSE);
292 : 0 : valent_input_remote_pointer_reset (self);
293 : : }
294 : :
295 : : /*
296 : : * Touchpad Emulation
297 : : *
298 : : * These callbacks map gestures on the "touchpad" area to events including:
299 : : *
300 : : * - two-finger tap -> right click
301 : : * - three-finger tap -> middle click
302 : : */
303 : : static void
304 : 0 : on_double_begin (GtkGestureDrag *gesture,
305 : : double start_x,
306 : : double start_y,
307 : : ValentInputRemote *self)
308 : : {
309 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
310 : :
311 : : // TODO: In order to map two-finger presses directly to the input adapter,
312 : : // the implementation has to handle unpaired press-release sequences.
313 : : #if 0
314 : : if (!valent_input_remote_check_adapter (self))
315 : : return;
316 : :
317 : : valent_input_adapter_pointer_button (self->adapter,
318 : : GDK_BUTTON_SECONDARY,
319 : : TRUE);
320 : : #endif
321 : 0 : }
322 : :
323 : : static void
324 : 0 : on_double_end (GtkGestureDrag *gesture,
325 : : double offset_x,
326 : : double offset_y,
327 : : ValentInputRemote *self)
328 : : {
329 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
330 : :
331 [ # # ]: 0 : if (!valent_input_remote_check_adapter (self))
332 : : return;
333 : :
334 : : /* If the two-finger press wasn't claimed as a scroll event on the y-axis,
335 : : * simulate a right click by pressing and releasing the secondary button. */
336 : 0 : valent_input_adapter_pointer_button (self->adapter,
337 : : GDK_BUTTON_SECONDARY,
338 : : TRUE);
339 : 0 : valent_input_adapter_pointer_button (self->adapter,
340 : : GDK_BUTTON_SECONDARY,
341 : : FALSE);
342 : : }
343 : :
344 : : static void
345 : 0 : on_triple_begin (GtkGestureDrag *gesture,
346 : : double offset_x,
347 : : double offset_y,
348 : : ValentInputRemote *self)
349 : : {
350 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
351 : :
352 [ # # ]: 0 : if (!valent_input_remote_check_adapter (self))
353 : : return;
354 : :
355 : : /* Since there is no high-level event for three-finger drags, three-finger
356 : : * presses and releases can be mapped directly. */
357 : 0 : gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
358 : 0 : valent_input_adapter_pointer_button (self->adapter,
359 : : GDK_BUTTON_MIDDLE,
360 : : TRUE);
361 : : }
362 : :
363 : : static void
364 : 0 : on_triple_end (GtkGestureDrag *gesture,
365 : : double offset_x,
366 : : double offset_y,
367 : : ValentInputRemote *self)
368 : : {
369 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
370 : :
371 [ # # ]: 0 : if (!valent_input_remote_check_adapter (self))
372 : : return;
373 : :
374 : 0 : valent_input_adapter_pointer_button (self->adapter,
375 : : GDK_BUTTON_MIDDLE,
376 : : FALSE);
377 : : }
378 : :
379 : : static void
380 : 0 : on_selected_item (GObject *object,
381 : : GParamSpec *pspec,
382 : : ValentInputRemote *self)
383 : : {
384 : 0 : ValentInputAdapter *adapter = NULL;
385 : :
386 [ # # ]: 0 : g_assert (VALENT_IS_INPUT_REMOTE (self));
387 : :
388 : 0 : adapter = gtk_drop_down_get_selected_item (GTK_DROP_DOWN (object));
389 : :
390 [ # # ]: 0 : if (g_set_object (&self->adapter, adapter))
391 : 0 : valent_input_remote_check_adapter (self);
392 : 0 : }
393 : :
394 : : /*
395 : : * GObject
396 : : */
397 : : static void
398 : 1 : valent_input_remote_constructed (GObject *object)
399 : : {
400 : : /* ValentInputRemote *self = VALENT_INPUT_REMOTE (object); */
401 : :
402 : 1 : G_OBJECT_CLASS (valent_input_remote_parent_class)->constructed (object);
403 : 1 : }
404 : :
405 : : static void
406 : 1 : valent_input_remote_dispose (GObject *object)
407 : : {
408 : 1 : ValentInputRemote *self = VALENT_INPUT_REMOTE (object);
409 : :
410 [ - + ]: 1 : g_clear_object (&self->adapter);
411 [ + - ]: 1 : g_clear_object (&self->adapters);
412 : :
413 : 1 : gtk_widget_dispose_template (GTK_WIDGET (object), VALENT_TYPE_INPUT_REMOTE);
414 : :
415 : 1 : G_OBJECT_CLASS (valent_input_remote_parent_class)->dispose (object);
416 : 1 : }
417 : :
418 : : static void
419 : 3 : valent_input_remote_get_property (GObject *object,
420 : : guint prop_id,
421 : : GValue *value,
422 : : GParamSpec *pspec)
423 : : {
424 : 3 : ValentInputRemote *self = VALENT_INPUT_REMOTE (object);
425 : :
426 [ + - ]: 3 : switch ((ValentInputRemoteProperty)prop_id)
427 : : {
428 : 3 : case PROP_ADAPTERS:
429 : 3 : g_value_set_object (value, self->adapters);
430 : 3 : break;
431 : :
432 : 0 : default:
433 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
434 : : }
435 : 3 : }
436 : :
437 : : static void
438 : 1 : valent_input_remote_set_property (GObject *object,
439 : : guint prop_id,
440 : : const GValue *value,
441 : : GParamSpec *pspec)
442 : : {
443 : 1 : ValentInputRemote *self = VALENT_INPUT_REMOTE (object);
444 : :
445 [ + - ]: 1 : switch ((ValentInputRemoteProperty)prop_id)
446 : : {
447 : 1 : case PROP_ADAPTERS:
448 : 1 : self->adapters = g_value_dup_object (value);
449 : 1 : break;
450 : :
451 : 0 : default:
452 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
453 : : }
454 : 1 : }
455 : :
456 : : static void
457 : 1 : valent_input_remote_class_init (ValentInputRemoteClass *klass)
458 : : {
459 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
460 : 1 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
461 : :
462 : 1 : object_class->constructed = valent_input_remote_constructed;
463 : 1 : object_class->dispose = valent_input_remote_dispose;
464 : 1 : object_class->get_property = valent_input_remote_get_property;
465 : 1 : object_class->set_property = valent_input_remote_set_property;
466 : :
467 : 1 : gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-input-remote.ui");
468 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, input_adapter);
469 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, editor);
470 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, keyboard);
471 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, pointer_scroll);
472 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, touchpad);
473 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, touch_single);
474 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, touch_double);
475 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, touch_triple);
476 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, filter);
477 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentInputRemote, model);
478 : :
479 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_selected_item);
480 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_key_pressed);
481 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_key_released);
482 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_scroll);
483 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_single_begin);
484 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_single_update);
485 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_single_end);
486 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_double_begin);
487 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_double_end);
488 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_triple_begin);
489 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_triple_end);
490 : :
491 : 2 : properties [PROP_ADAPTERS] =
492 : 1 : g_param_spec_object ("adapters", NULL, NULL,
493 : : G_TYPE_LIST_MODEL,
494 : : (G_PARAM_READWRITE |
495 : : G_PARAM_CONSTRUCT_ONLY |
496 : : G_PARAM_STATIC_STRINGS));
497 : :
498 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
499 : 1 : }
500 : :
501 : : static void
502 : 1 : valent_input_remote_init (ValentInputRemote *self)
503 : : {
504 : 1 : gtk_widget_init_template (GTK_WIDGET (self));
505 : 1 : gtk_gesture_group (self->touch_single, self->touch_double);
506 : 1 : gtk_gesture_group (self->touch_single, self->touch_triple);
507 : 1 : gtk_custom_filter_set_filter_func (GTK_CUSTOM_FILTER (self->filter),
508 : : valent_input_remote_filter,
509 : : self,
510 : : NULL);
511 : 1 : self->scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
512 : 1 : }
513 : :
|