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-mousepad-device"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <valent.h>
10 : :
11 : : #include "valent-mousepad-device.h"
12 : : #include "valent-mousepad-keydef.h"
13 : :
14 : : #define DEFAULT_DOUBLE_CLICK_TIME (400)
15 : : #define DEFAULT_LONG_PRESS_TIME (500)
16 : :
17 : :
18 : : struct _ValentMousepadDevice
19 : : {
20 : : ValentInputAdapter parent_instance;
21 : :
22 : : ValentDevice *device;
23 : :
24 : : /* keyboard */
25 : : GArray *keyboard_keys;
26 : : KeyModifierType keyboard_modifiers;
27 : : unsigned int keyboard_flush_id;
28 : :
29 : : /* pointer */
30 : : unsigned int pointer_button;
31 : : unsigned int pointer_presses;
32 : : unsigned int pointer_releases;
33 : : unsigned int pointer_doubleclick_id;
34 : : unsigned int pointer_longpress_id;
35 : :
36 : : int double_click_time;
37 : : int long_press_time;
38 : : };
39 : :
40 [ + + + - ]: 8 : G_DEFINE_FINAL_TYPE (ValentMousepadDevice, valent_mousepad_device, VALENT_TYPE_INPUT_ADAPTER)
41 : :
42 : :
43 : : /*
44 : : * Keyboard
45 : : */
46 : : static gboolean
47 : 0 : valent_mousepad_device_keyboard_flush (gpointer data)
48 : : {
49 : 0 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (data);
50 : 0 : g_autoptr (JsonBuilder) builder = NULL;
51 [ # # ]: 0 : g_autoptr (JsonNode) packet = NULL;
52 [ # # ]: 0 : g_autoptr (GString) key = NULL;
53 : 0 : uint32_t special_key = 0;
54 : 0 : unsigned int n_handled = 0;
55 : :
56 [ # # ]: 0 : if (self->keyboard_keys->len == 0)
57 : : {
58 : 0 : self->keyboard_flush_id = 0;
59 : 0 : return G_SOURCE_REMOVE;
60 : : }
61 : :
62 [ # # ]: 0 : for (unsigned int len = self->keyboard_keys->len; n_handled < len; n_handled++)
63 : : {
64 : 0 : uint32_t *keysym = &g_array_index (self->keyboard_keys, uint32_t, n_handled);
65 : :
66 [ # # ]: 0 : if ((special_key = valent_mousepad_keysym_to_keycode (*keysym)) != 0)
67 : : {
68 : : /* If there are keys to be sent, they need to be sent first */
69 [ # # ]: 0 : if (key != NULL)
70 : 0 : special_key = 0;
71 : :
72 : : /* Otherwise, we need to send the current key and modifiers */
73 : 0 : n_handled++;
74 : 0 : break;
75 : : }
76 : : else
77 : : {
78 : 0 : gunichar wc = valent_input_keysym_to_unicode (*keysym);
79 : :
80 [ # # ]: 0 : if (wc == 0)
81 : : {
82 : 0 : g_debug ("%s(): failed to convert keysym \"%u\" to unicode",
83 : : G_STRFUNC, *keysym);
84 : 0 : continue;
85 : : }
86 : :
87 [ # # ]: 0 : if (key == NULL)
88 : 0 : key = g_string_new (NULL);
89 : :
90 : 0 : g_string_append_unichar (key, wc);
91 : : }
92 : : }
93 : 0 : g_array_remove_range (self->keyboard_keys, 0, n_handled);
94 : :
95 : : /* Build the packet */
96 : 0 : valent_packet_init (&builder, "kdeconnect.mousepad.request");
97 : :
98 [ # # ]: 0 : if (key != NULL)
99 : : {
100 : 0 : json_builder_set_member_name (builder, "key");
101 : 0 : json_builder_add_string_value (builder, key->str);
102 : : }
103 [ # # ]: 0 : else if (special_key != 0)
104 : : {
105 : 0 : json_builder_set_member_name (builder, "specialKey");
106 : 0 : json_builder_add_int_value (builder, special_key);
107 : : }
108 : :
109 : : /* Check our supported modifiers */
110 [ # # ]: 0 : if ((self->keyboard_modifiers & KEYMOD_ALT_MASK) != 0)
111 : : {
112 : 0 : json_builder_set_member_name (builder, "alt");
113 : 0 : json_builder_add_boolean_value (builder, TRUE);
114 : : }
115 : :
116 [ # # ]: 0 : if ((self->keyboard_modifiers & KEYMOD_CONTROL_MASK) != 0)
117 : : {
118 : 0 : json_builder_set_member_name (builder, "ctrl");
119 : 0 : json_builder_add_boolean_value (builder, TRUE);
120 : : }
121 : :
122 [ # # ]: 0 : if ((self->keyboard_modifiers & KEYMOD_SHIFT_MASK) != 0)
123 : : {
124 : 0 : json_builder_set_member_name (builder, "shift");
125 : 0 : json_builder_add_boolean_value (builder, TRUE);
126 : : }
127 : :
128 [ # # ]: 0 : if ((self->keyboard_modifiers & KEYMOD_SUPER_MASK) != 0)
129 : : {
130 : 0 : json_builder_set_member_name (builder, "super");
131 : 0 : json_builder_add_boolean_value (builder, TRUE);
132 : : }
133 : :
134 : : /* Request acknowledgment of the event (disabled until it can be received) */
135 : : #if 0
136 : : json_builder_set_member_name (builder, "sendAck");
137 : : json_builder_add_boolean_value (builder, TRUE);
138 : : #endif
139 : :
140 : 0 : packet = valent_packet_end (&builder);
141 : 0 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
142 : :
143 : : /* Clear the source if there's nothing left queued */
144 [ # # ]: 0 : if (self->keyboard_keys->len == 0)
145 : : {
146 : 0 : self->keyboard_flush_id = 0;
147 : 0 : return G_SOURCE_REMOVE;
148 : : }
149 : :
150 : : return G_SOURCE_CONTINUE;
151 : : }
152 : :
153 : : static inline void
154 : 4 : valent_mousepad_device_keyboard_reset (ValentMousepadDevice *self)
155 : : {
156 [ - + ]: 4 : g_assert (VALENT_IS_MOUSEPAD_DEVICE (self));
157 : :
158 : 4 : g_array_remove_range (self->keyboard_keys, 0, self->keyboard_keys->len);
159 [ - + ]: 4 : g_clear_handle_id (&self->keyboard_flush_id, g_source_remove);
160 : 4 : }
161 : :
162 : : /*
163 : : * Pointer
164 : : */
165 : : static inline gboolean
166 : 4 : valent_mousepad_device_pointer_reset (ValentMousepadDevice *self)
167 : : {
168 : 4 : self->pointer_button = 0;
169 : 4 : self->pointer_presses = 0;
170 : 4 : self->pointer_releases = 0;
171 [ - + ]: 4 : g_clear_handle_id (&self->pointer_doubleclick_id, g_source_remove);
172 [ - + ]: 4 : g_clear_handle_id (&self->pointer_longpress_id, g_source_remove);
173 : :
174 : 4 : return G_SOURCE_REMOVE;
175 : : }
176 : :
177 : : static inline gboolean
178 : 0 : valent_mousepad_device_pointer_flush (gpointer data)
179 : : {
180 : 0 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (data);
181 : 0 : g_autoptr (JsonBuilder) builder = NULL;
182 [ # # ]: 0 : g_autoptr (JsonNode) packet = NULL;
183 : :
184 [ # # ]: 0 : g_assert (VALENT_IS_MOUSEPAD_DEVICE (self));
185 : :
186 : : /* Ignore unpaired releases */
187 [ # # ]: 0 : if (self->pointer_presses < self->pointer_releases)
188 : 0 : return valent_mousepad_device_pointer_reset (self);
189 : :
190 [ # # # # ]: 0 : if (self->pointer_presses == 1 && self->pointer_releases == 1)
191 : : {
192 : 0 : valent_packet_init (&builder, "kdeconnect.mousepad.request");
193 : :
194 [ # # # # ]: 0 : switch (self->pointer_button)
195 : : {
196 : 0 : case VALENT_POINTER_PRIMARY:
197 : 0 : json_builder_set_member_name (builder, "singleclick");
198 : 0 : json_builder_add_boolean_value (builder, TRUE);
199 : 0 : break;
200 : :
201 : 0 : case VALENT_POINTER_MIDDLE:
202 : 0 : json_builder_set_member_name (builder, "middleclick");
203 : 0 : json_builder_add_boolean_value (builder, TRUE);
204 : 0 : break;
205 : :
206 : 0 : case VALENT_POINTER_SECONDARY:
207 : 0 : json_builder_set_member_name (builder, "rightclick");
208 : 0 : json_builder_add_boolean_value (builder, TRUE);
209 : 0 : break;
210 : :
211 : 0 : default:
212 : 0 : g_debug ("%s: unknown pointer button %u",
213 : : G_STRFUNC,
214 : : self->pointer_button);
215 : : }
216 : :
217 : 0 : packet = valent_packet_end (&builder);
218 : : }
219 [ # # # # ]: 0 : else if (self->pointer_button == VALENT_POINTER_PRIMARY && self->pointer_presses == 2)
220 : : {
221 : 0 : valent_packet_init (&builder, "kdeconnect.mousepad.request");
222 : 0 : json_builder_set_member_name (builder, "doubleclick");
223 : 0 : json_builder_add_boolean_value (builder, TRUE);
224 : 0 : packet = valent_packet_end (&builder);
225 : : }
226 : :
227 [ # # ]: 0 : if (packet != NULL)
228 : : {
229 : 0 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
230 : 0 : valent_mousepad_device_pointer_reset (self);
231 : : }
232 : :
233 : 0 : return G_SOURCE_REMOVE;
234 : : }
235 : :
236 : : static inline gboolean
237 : 0 : valent_mousepad_device_pointer_longpress (gpointer data)
238 : : {
239 : 0 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (data);
240 : 0 : g_autoptr (JsonBuilder) builder = NULL;
241 [ # # ]: 0 : g_autoptr (JsonNode) packet = NULL;
242 : :
243 [ # # ]: 0 : g_assert (VALENT_IS_MOUSEPAD_DEVICE (self));
244 : :
245 : 0 : valent_packet_init (&builder, "kdeconnect.mousepad.request");
246 : 0 : json_builder_set_member_name (builder, "singlehold");
247 : 0 : json_builder_add_boolean_value (builder, TRUE);
248 : 0 : packet = valent_packet_end (&builder);
249 : :
250 : 0 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
251 : :
252 [ # # ]: 0 : return valent_mousepad_device_pointer_reset (self);
253 : : }
254 : :
255 : : /*
256 : : * ValentInputAdapter
257 : : */
258 : : static void
259 : 0 : valent_mousepad_device_keyboard_keysym (ValentInputAdapter *adapter,
260 : : uint32_t keysym,
261 : : gboolean state)
262 : : {
263 : 0 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (adapter);
264 : :
265 [ # # ]: 0 : g_assert (VALENT_IS_MOUSEPAD_DEVICE (self));
266 [ # # ]: 0 : g_return_if_fail (keysym != 0);
267 : :
268 : : /* Track modifiers, but don't send anything */
269 [ # # ]: 0 : if (valent_input_keysym_to_modifier (keysym, state, &self->keyboard_modifiers))
270 : : return;
271 : :
272 : : // TODO: the KDE Connect protocol doesn't support press and release states
273 : : // for keyboard input, so only key presses are sent. A solution might
274 : : // involve matching presses and releases, or an extant convention.
275 [ # # ]: 0 : if (!state)
276 : : return;
277 : :
278 : 0 : g_array_append_val (self->keyboard_keys, keysym);
279 : :
280 : : /* If there are modifiers set, the key should be sent immediately */
281 [ # # ]: 0 : if ((self->keyboard_modifiers & KEYMOD_ANY_MASK) != 0)
282 : : {
283 : 0 : valent_mousepad_device_keyboard_flush (self);
284 : 0 : return;
285 : : }
286 : :
287 : : /* Flush in an idle callback, in case key presses can be sent as a string */
288 [ # # ]: 0 : if (self->keyboard_flush_id == 0)
289 : 0 : self->keyboard_flush_id = g_idle_add (valent_mousepad_device_keyboard_flush,
290 : : self);
291 : : }
292 : :
293 : : static void
294 : 0 : valent_mousepad_device_pointer_axis (ValentInputAdapter *adapter,
295 : : double dx,
296 : : double dy)
297 : : {
298 : 0 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (adapter);
299 : 0 : g_autoptr (JsonBuilder) builder = NULL;
300 [ # # ]: 0 : g_autoptr (JsonNode) packet = NULL;
301 : :
302 [ # # ]: 0 : g_assert (VALENT_IS_MOUSEPAD_DEVICE (self));
303 : :
304 : 0 : valent_packet_init (&builder, "kdeconnect.mousepad.request");
305 : 0 : json_builder_set_member_name (builder, "dx");
306 : 0 : json_builder_add_double_value (builder, dx);
307 : 0 : json_builder_set_member_name (builder, "dy");
308 : 0 : json_builder_add_double_value (builder, dy);
309 : 0 : json_builder_set_member_name (builder, "scroll");
310 : 0 : json_builder_add_boolean_value (builder, TRUE);
311 : 0 : packet = valent_packet_end (&builder);
312 : :
313 [ # # ]: 0 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
314 : 0 : }
315 : :
316 : : static void
317 : 0 : valent_mousepad_device_pointer_button (ValentInputAdapter *adapter,
318 : : unsigned int button,
319 : : gboolean state)
320 : : {
321 : 0 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (adapter);
322 : :
323 [ # # ]: 0 : if (self->pointer_button != button)
324 : : {
325 : 0 : self->pointer_button = button;
326 : 0 : self->pointer_presses = 0;
327 : 0 : self->pointer_releases = 0;
328 : : }
329 : :
330 [ # # ]: 0 : if (state)
331 : : {
332 : 0 : self->pointer_presses += 1;
333 : :
334 : : /* Any button press removes the double click timer; the event will either
335 : : * be accepted or rejected based on the current button state. */
336 [ # # ]: 0 : g_clear_handle_id (&self->pointer_doubleclick_id, g_source_remove);
337 : : }
338 : : else
339 : : {
340 : 0 : self->pointer_releases += 1;
341 : : }
342 : :
343 : : /* Any button event removes the long press timer; the event is accepted if the
344 : : * timeout elapses with the primary button being the only button pressed. */
345 [ # # ]: 0 : g_clear_handle_id (&self->pointer_longpress_id, g_source_remove);
346 : :
347 : : /* Handle the first press and release for the primary button, to prevent
348 : : * flushing the double click state on the first release. */
349 [ # # # # ]: 0 : if (self->pointer_button == VALENT_POINTER_PRIMARY && self->pointer_presses == 1)
350 : : {
351 : : /* Double click and long press events both start with the press event */
352 [ # # ]: 0 : if (self->pointer_releases == 0)
353 : : {
354 : : // TODO: what if double-click time < long-press time?
355 : : /* If the timeout elapses, a "singleclick" packet will be sent */
356 : 0 : self->pointer_doubleclick_id =
357 : 0 : g_timeout_add (self->double_click_time,
358 : : valent_mousepad_device_pointer_flush,
359 : : self);
360 : 0 : g_source_set_name_by_id (self->pointer_doubleclick_id,
361 : : "valent_mousepad_device_pointer_flush");
362 : :
363 : : /* If the timeout elapses, a "singlehold" packet will be sent */
364 : 0 : self->pointer_longpress_id =
365 : 0 : g_timeout_add (self->long_press_time,
366 : : valent_mousepad_device_pointer_longpress,
367 : : self);
368 : 0 : g_source_set_name_by_id (self->pointer_longpress_id,
369 : : "valent_mousepad_device_pointer_longpress");
370 : : }
371 : : }
372 : : else
373 : : {
374 : 0 : valent_mousepad_device_pointer_flush (self);
375 : : }
376 : 0 : }
377 : :
378 : : static void
379 : 0 : valent_mousepad_device_pointer_motion (ValentInputAdapter *adapter,
380 : : double dx,
381 : : double dy)
382 : : {
383 : 0 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (adapter);
384 : 0 : g_autoptr (JsonBuilder) builder = NULL;
385 [ # # ]: 0 : g_autoptr (JsonNode) packet = NULL;
386 : :
387 [ # # ]: 0 : g_assert (VALENT_IS_MOUSEPAD_DEVICE (self));
388 : :
389 : 0 : valent_packet_init (&builder, "kdeconnect.mousepad.request");
390 : 0 : json_builder_set_member_name (builder, "dx");
391 : 0 : json_builder_add_double_value (builder, dx);
392 : 0 : json_builder_set_member_name (builder, "dy");
393 : 0 : json_builder_add_double_value (builder, dy);
394 : 0 : packet = valent_packet_end (&builder);
395 : :
396 : 0 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
397 [ # # ]: 0 : valent_mousepad_device_pointer_reset (self);
398 : 0 : }
399 : :
400 : : #if 0
401 : : static void
402 : : valent_mousepad_device_pointer_release (ValentInputRemote *self)
403 : : {
404 : : g_autoptr (JsonBuilder) builder = NULL;
405 : : g_autoptr (JsonNode) packet = NULL;
406 : :
407 : : valent_packet_init (&builder, "kdeconnect.mousepad.request");
408 : : json_builder_set_member_name (builder, "singlerelease");
409 : : json_builder_add_boolean_value (builder, TRUE);
410 : : packet = valent_packet_end (&builder);
411 : :
412 : : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
413 : : }
414 : : #endif
415 : :
416 : : static void
417 : 2 : on_device_state_changed (ValentDevice *device,
418 : : GParamSpec *pspec,
419 : : ValentMousepadDevice *self)
420 : : {
421 : : #if 0
422 : : ValentDeviceState state = VALENT_DEVICE_STATE_NONE;
423 : : gboolean available;
424 : :
425 : : state = valent_device_get_state (device);
426 : : available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
427 : : (state & VALENT_DEVICE_STATE_PAIRED) != 0;
428 : : #endif
429 : 2 : }
430 : :
431 : : /*
432 : : * ValentObject
433 : : */
434 : : static void
435 : 4 : valent_mousepad_device_destroy (ValentObject *object)
436 : : {
437 : 4 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (object);
438 : :
439 : 4 : valent_mousepad_device_keyboard_reset (self);
440 : 4 : valent_mousepad_device_pointer_reset (self);
441 : :
442 : 4 : VALENT_OBJECT_CLASS (valent_mousepad_device_parent_class)->destroy (object);
443 : 4 : }
444 : :
445 : : /*
446 : : * GObject
447 : : */
448 : : static void
449 : 2 : valent_mousepad_device_constructed (GObject *object)
450 : : {
451 : 2 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (object);
452 : :
453 : 2 : G_OBJECT_CLASS (valent_mousepad_device_parent_class)->constructed (object);
454 : :
455 : 2 : self->device = valent_object_get_parent (VALENT_OBJECT (self));
456 : 2 : g_signal_connect_object (self->device,
457 : : "notify::state",
458 : : G_CALLBACK (on_device_state_changed),
459 : : self,
460 : : G_CONNECT_DEFAULT);
461 : 2 : }
462 : :
463 : : static void
464 : 2 : valent_mousepad_device_finalize (GObject *object)
465 : : {
466 : 2 : ValentMousepadDevice *self = VALENT_MOUSEPAD_DEVICE (object);
467 : :
468 [ + - ]: 2 : g_clear_pointer (&self->keyboard_keys, g_array_unref);
469 : :
470 : 2 : G_OBJECT_CLASS (valent_mousepad_device_parent_class)->finalize (object);
471 : 2 : }
472 : :
473 : : static void
474 : 1 : valent_mousepad_device_class_init (ValentMousepadDeviceClass *klass)
475 : : {
476 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
477 : 1 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
478 : 1 : ValentInputAdapterClass *input_class = VALENT_INPUT_ADAPTER_CLASS (klass);
479 : :
480 : 1 : object_class->constructed = valent_mousepad_device_constructed;
481 : 1 : object_class->finalize = valent_mousepad_device_finalize;
482 : :
483 : 1 : vobject_class->destroy = valent_mousepad_device_destroy;
484 : :
485 : 1 : input_class->keyboard_keysym = valent_mousepad_device_keyboard_keysym;
486 : 1 : input_class->pointer_axis = valent_mousepad_device_pointer_axis;
487 : 1 : input_class->pointer_button = valent_mousepad_device_pointer_button;
488 : 1 : input_class->pointer_motion = valent_mousepad_device_pointer_motion;
489 : : }
490 : :
491 : : static void
492 : 2 : valent_mousepad_device_init (ValentMousepadDevice *self)
493 : : {
494 : 2 : self->keyboard_keys = g_array_new (FALSE, FALSE, sizeof (uint32_t));
495 : 2 : self->double_click_time = DEFAULT_DOUBLE_CLICK_TIME;
496 : 2 : self->long_press_time = DEFAULT_LONG_PRESS_TIME;
497 : 2 : }
498 : :
499 : : /**
500 : : * valent_mousepad_device_new:
501 : : * @device: a `ValentDevice`
502 : : *
503 : : * Get the `ValentMousepadDevice` instance.
504 : : *
505 : : * Returns: (transfer full) (nullable): a `ValentMousepadDevice`
506 : : */
507 : : ValentMousepadDevice *
508 : 2 : valent_mousepad_device_new (ValentDevice *device)
509 : : {
510 : 4 : g_autoptr (ValentContext) context = NULL;
511 [ + - ]: 2 : g_autofree char *iri = NULL;
512 : :
513 [ - + ]: 2 : g_return_val_if_fail (VALENT_IS_DEVICE (device), NULL);
514 : :
515 : 2 : context = valent_context_new (valent_device_get_context (device),
516 : : "plugin",
517 : : "input");
518 : 2 : iri = tracker_sparql_escape_uri_printf ("urn:valent:input:%s",
519 : : valent_device_get_id (device));
520 : 2 : return g_object_new (VALENT_TYPE_MOUSEPAD_DEVICE,
521 : : "iri", iri,
522 : : "context", context,
523 : : "parent", device,
524 : : NULL);
525 : : }
526 : :
527 : : /**
528 : : * valent_media_player_update_packet:
529 : : * @player: a `ValentMousepadDevice`
530 : : * @packet: a KDE Connect packet
531 : : *
532 : : * A convenience method for updating the internal state of the player from a
533 : : * `kdeconnect.mousepad` packet.
534 : : */
535 : : void
536 : 0 : valent_mousepad_device_handle_packet (ValentMousepadDevice *player,
537 : : JsonNode *packet)
538 : : {
539 : 0 : }
|