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