FreeRDP
Loading...
Searching...
No Matches
xf_input.c
1
20#include <freerdp/config.h>
21
22#include <X11/Xlib.h>
23#include <X11/Xutil.h>
24
25#ifdef WITH_XCURSOR
26#include <X11/Xcursor/Xcursor.h>
27#endif
28
29#ifdef WITH_XI
30#include <X11/extensions/XInput2.h>
31#endif
32
33#include <math.h>
34#include <float.h>
35#include <limits.h>
36
37#include "xf_event.h"
38#include "xf_input.h"
39#include "xf_utils.h"
40
41#include <winpr/assert.h>
42#include <winpr/wtypes.h>
43#include <freerdp/log.h>
44#define TAG CLIENT_TAG("x11")
45
46#ifdef WITH_XI
47
48#define PAN_THRESHOLD 50
49#define ZOOM_THRESHOLD 10
50
51#define MIN_FINGER_DIST 5
52
53static int xf_input_event(xfContext* xfc, WINPR_ATTR_UNUSED const XEvent* xevent,
54 XIDeviceEvent* event, int evtype);
55
56#ifdef DEBUG_XINPUT
57static const char* xf_input_get_class_string(int class)
58{
59 if (class == XIKeyClass)
60 return "XIKeyClass";
61 else if (class == XIButtonClass)
62 return "XIButtonClass";
63 else if (class == XIValuatorClass)
64 return "XIValuatorClass";
65 else if (class == XIScrollClass)
66 return "XIScrollClass";
67 else if (class == XITouchClass)
68 return "XITouchClass";
69
70 return "XIUnknownClass";
71}
72#endif
73
74static BOOL register_input_events(xfContext* xfc, Window window)
75{
76#define MAX_NR_MASKS 64
77 int ndevices = 0;
78 int nmasks = 0;
79 XIEventMask evmasks[MAX_NR_MASKS] = WINPR_C_ARRAY_INIT;
80 BYTE masks[MAX_NR_MASKS][XIMaskLen(XI_LASTEVENT)] = WINPR_C_ARRAY_INIT;
81
82 WINPR_ASSERT(xfc);
83
84 rdpSettings* settings = xfc->common.context.settings;
85 WINPR_ASSERT(settings);
86
87 XIDeviceInfo* info = XIQueryDevice(xfc->display, XIAllDevices, &ndevices);
88
89 for (int i = 0; i < MIN(ndevices, MAX_NR_MASKS); i++)
90 {
91 BOOL used = FALSE;
92 XIDeviceInfo* dev = &info[i];
93
94 evmasks[nmasks].mask = masks[nmasks];
95 evmasks[nmasks].mask_len = sizeof(masks[0]);
96 evmasks[nmasks].deviceid = dev->deviceid;
97
98 /* Ignore virtual core pointer */
99 if (strcmp(dev->name, "Virtual core pointer") == 0)
100 continue;
101
102 for (int j = 0; j < dev->num_classes; j++)
103 {
104 const XIAnyClassInfo* c_class = dev->classes[j];
105
106 switch (c_class->type)
107 {
108 case XITouchClass:
109 if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchInput))
110 {
111 const XITouchClassInfo* t = (const XITouchClassInfo*)c_class;
112 if (t->mode == XIDirectTouch)
113 {
114 WLog_DBG(
115 TAG,
116 "%s %s touch device (id: %d, mode: %d), supporting %d touches.",
117 dev->name, (t->mode == XIDirectTouch) ? "direct" : "dependent",
118 dev->deviceid, t->mode, t->num_touches);
119 XISetMask(masks[nmasks], XI_TouchBegin);
120 XISetMask(masks[nmasks], XI_TouchUpdate);
121 XISetMask(masks[nmasks], XI_TouchEnd);
122 }
123 }
124 break;
125 case XIButtonClass:
126 {
127 const XIButtonClassInfo* t = (const XIButtonClassInfo*)c_class;
128 WLog_DBG(TAG, "%s button device (id: %d, mode: %d)", dev->name, dev->deviceid,
129 t->num_buttons);
130 XISetMask(masks[nmasks], XI_ButtonPress);
131 XISetMask(masks[nmasks], XI_ButtonRelease);
132 XISetMask(masks[nmasks], XI_Motion);
133 used = TRUE;
134 break;
135 }
136 case XIValuatorClass:
137 {
138 static wLog* log = nullptr;
139 if (!log)
140 log = WLog_Get(TAG);
141
142 const XIValuatorClassInfo* t = (const XIValuatorClassInfo*)c_class;
143 char* name =
144 t->label ? Safe_XGetAtomName(log, xfc->display, t->label) : nullptr;
145
146 WLog_Print(log, WLOG_DEBUG,
147 "%s device (id: %d) valuator %d label %s range %f - %f", dev->name,
148 dev->deviceid, t->number, name ? name : "None", t->min, t->max);
149 free(name);
150
151 if (t->number == 2)
152 {
153 double max_pressure = t->max;
154
155 char devName[200] = WINPR_C_ARRAY_INIT;
156 strncpy(devName, dev->name, ARRAYSIZE(devName) - 1);
157 CharLowerBuffA(devName, ARRAYSIZE(devName));
158
159 if (strstr(devName, "eraser") != nullptr)
160 {
161 if (freerdp_client_handle_pen(&xfc->common,
162 FREERDP_PEN_REGISTER |
163 FREERDP_PEN_IS_INVERTED |
164 FREERDP_PEN_HAS_PRESSURE,
165 dev->deviceid, max_pressure))
166 WLog_DBG(TAG, "registered eraser");
167 }
168 else if (strstr(devName, "stylus") != nullptr ||
169 strstr(devName, "pen") != nullptr)
170 {
171 if (freerdp_client_handle_pen(
172 &xfc->common, FREERDP_PEN_REGISTER | FREERDP_PEN_HAS_PRESSURE,
173 dev->deviceid, max_pressure))
174 WLog_DBG(TAG, "registered pen");
175 }
176 }
177 break;
178 }
179 default:
180 break;
181 }
182 }
183 if (used)
184 nmasks++;
185 }
186
187 XIFreeDeviceInfo(info);
188
189 if (nmasks > 0)
190 {
191 Status xstatus = XISelectEvents(xfc->display, window, evmasks, nmasks);
192 if (xstatus != 0)
193 WLog_WARN(TAG, "XISelectEvents returned %d", xstatus);
194 }
195
196 return TRUE;
197}
198
199static BOOL register_raw_events(xfContext* xfc, Window window)
200{
201 XIEventMask mask;
202 unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = WINPR_C_ARRAY_INIT;
203 rdpSettings* settings = nullptr;
204
205 WINPR_ASSERT(xfc);
206
207 settings = xfc->common.context.settings;
208 WINPR_ASSERT(settings);
209
210 if (freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove))
211 {
212 XISetMask(mask_bytes, XI_RawMotion);
213 XISetMask(mask_bytes, XI_RawButtonPress);
214 XISetMask(mask_bytes, XI_RawButtonRelease);
215
216 mask.deviceid = XIAllMasterDevices;
217 mask.mask_len = sizeof(mask_bytes);
218 mask.mask = mask_bytes;
219
220 XISelectEvents(xfc->display, window, &mask, 1);
221 }
222
223 return TRUE;
224}
225
226static BOOL register_device_events(xfContext* xfc, Window window)
227{
228 XIEventMask mask = WINPR_C_ARRAY_INIT;
229 unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = WINPR_C_ARRAY_INIT;
230
231 WINPR_ASSERT(xfc);
232
233 XISetMask(mask_bytes, XI_DeviceChanged);
234 XISetMask(mask_bytes, XI_HierarchyChanged);
235
236 mask.deviceid = XIAllDevices;
237 mask.mask_len = sizeof(mask_bytes);
238 mask.mask = mask_bytes;
239
240 XISelectEvents(xfc->display, window, &mask, 1);
241
242 return TRUE;
243}
244
245int xf_input_init(xfContext* xfc, Window window)
246{
247 int major = XI_2_Major;
248 int minor = XI_2_Minor;
249 int opcode = 0;
250 int event = 0;
251 int error = 0;
252
253 WINPR_ASSERT(xfc);
254
255 xfc->firstDist = -1.0;
256 xfc->z_vector = 0;
257 xfc->px_vector = 0;
258 xfc->py_vector = 0;
259 xfc->active_contacts = 0;
260
261 if (!XQueryExtension(xfc->display, "XInputExtension", &opcode, &event, &error))
262 {
263 WLog_WARN(TAG, "XInput extension not available.");
264 return -1;
265 }
266
267 xfc->XInputOpcode = opcode;
268 XIQueryVersion(xfc->display, &major, &minor);
269
270 if ((major < XI_2_Major) || ((major == XI_2_Major) && (minor < 2)))
271 {
272 WLog_WARN(TAG, "Server does not support XI 2.2");
273 return -1;
274 }
275 else
276 {
277 int scr = DefaultScreen(xfc->display);
278 Window root = RootWindow(xfc->display, scr);
279
280 if (!register_raw_events(xfc, root))
281 return -1;
282 if (!register_input_events(xfc, window))
283 return -1;
284 if (!register_device_events(xfc, window))
285 return -1;
286 }
287
288 return 0;
289}
290
291static BOOL xf_input_is_duplicate(xfContext* xfc, const XGenericEventCookie* cookie)
292{
293 const XIDeviceEvent* event = nullptr;
294
295 WINPR_ASSERT(xfc);
296 WINPR_ASSERT(cookie);
297
298 event = cookie->data;
299 WINPR_ASSERT(event);
300
301 return ((xfc->lastEvent.time == event->time) && (xfc->lastEvType == cookie->evtype) &&
302 (xfc->lastEvent.detail == event->detail) &&
303 (fabs(xfc->lastEvent.event_x - event->event_x) < DBL_EPSILON) &&
304 (fabs(xfc->lastEvent.event_y - event->event_y) < DBL_EPSILON));
305}
306
307static void xf_input_save_last_event(xfContext* xfc, const XGenericEventCookie* cookie)
308{
309 const XIDeviceEvent* event = nullptr;
310
311 WINPR_ASSERT(xfc);
312 WINPR_ASSERT(cookie);
313
314 event = cookie->data;
315 WINPR_ASSERT(event);
316
317 xfc->lastEvType = cookie->evtype;
318 xfc->lastEvent.time = event->time;
319 xfc->lastEvent.detail = event->detail;
320 xfc->lastEvent.event_x = event->event_x;
321 xfc->lastEvent.event_y = event->event_y;
322}
323
324WINPR_ATTR_NODISCARD
325static BOOL xf_input_detect_pan(xfContext* xfc)
326{
327 WINPR_ASSERT(xfc);
328 rdpContext* ctx = &xfc->common.context;
329 WINPR_ASSERT(ctx);
330
331 if (xfc->active_contacts != 2)
332 {
333 return TRUE;
334 }
335
336 const double dx[] = { xfc->contacts[0].pos_x - xfc->contacts[0].last_x,
337 xfc->contacts[1].pos_x - xfc->contacts[1].last_x };
338 const double dy[] = { xfc->contacts[0].pos_y - xfc->contacts[0].last_y,
339 xfc->contacts[1].pos_y - xfc->contacts[1].last_y };
340 const double px = fabs(dx[0]) < fabs(dx[1]) ? dx[0] : dx[1];
341 const double py = fabs(dy[0]) < fabs(dy[1]) ? dy[0] : dy[1];
342 xfc->px_vector += px;
343 xfc->py_vector += py;
344 const double dist_x = fabs(xfc->contacts[0].pos_x - xfc->contacts[1].pos_x);
345 const double dist_y = fabs(xfc->contacts[0].pos_y - xfc->contacts[1].pos_y);
346
347 if (dist_y > MIN_FINGER_DIST)
348 {
349 if (xfc->px_vector > PAN_THRESHOLD)
350 {
351 {
352 PanningChangeEventArgs e;
353 EventArgsInit(&e, "xfreerdp");
354 e.dx = 5;
355 e.dy = 0;
356 if (PubSub_OnPanningChange(ctx->pubSub, xfc, &e) < 0)
357 return FALSE;
358 }
359 xfc->px_vector = 0;
360 xfc->py_vector = 0;
361 xfc->z_vector = 0;
362 }
363 else if (xfc->px_vector < -PAN_THRESHOLD)
364 {
365 {
366 PanningChangeEventArgs e;
367 EventArgsInit(&e, "xfreerdp");
368 e.dx = -5;
369 e.dy = 0;
370 if (PubSub_OnPanningChange(ctx->pubSub, xfc, &e) < 0)
371 return FALSE;
372 }
373 xfc->px_vector = 0;
374 xfc->py_vector = 0;
375 xfc->z_vector = 0;
376 }
377 }
378
379 if (dist_x > MIN_FINGER_DIST)
380 {
381 if (xfc->py_vector > PAN_THRESHOLD)
382 {
383 {
384 PanningChangeEventArgs e;
385 EventArgsInit(&e, "xfreerdp");
386 e.dx = 0;
387 e.dy = 5;
388 if (PubSub_OnPanningChange(ctx->pubSub, xfc, &e) < 0)
389 return FALSE;
390 }
391 xfc->py_vector = 0;
392 xfc->px_vector = 0;
393 xfc->z_vector = 0;
394 }
395 else if (xfc->py_vector < -PAN_THRESHOLD)
396 {
397 {
398 PanningChangeEventArgs e;
399 EventArgsInit(&e, "xfreerdp");
400 e.dx = 0;
401 e.dy = -5;
402 if (PubSub_OnPanningChange(ctx->pubSub, xfc, &e) < 0)
403 return FALSE;
404 }
405 xfc->py_vector = 0;
406 xfc->px_vector = 0;
407 xfc->z_vector = 0;
408 }
409 }
410 return TRUE;
411}
412
413WINPR_ATTR_NODISCARD
414static BOOL xf_input_detect_pinch(xfContext* xfc)
415{
416 ZoomingChangeEventArgs e = WINPR_C_ARRAY_INIT;
417
418 WINPR_ASSERT(xfc);
419 rdpContext* ctx = &xfc->common.context;
420 WINPR_ASSERT(ctx);
421
422 if (xfc->active_contacts != 2)
423 {
424 xfc->firstDist = -1.0;
425 return TRUE;
426 }
427
428 /* first calculate the distance */
429 const double dist = sqrt(pow(xfc->contacts[1].pos_x - xfc->contacts[0].last_x, 2.0) +
430 pow(xfc->contacts[1].pos_y - xfc->contacts[0].last_y, 2.0));
431
432 /* if this is the first 2pt touch */
433 if (xfc->firstDist <= 0)
434 {
435 xfc->firstDist = dist;
436 xfc->lastDist = xfc->firstDist;
437 xfc->z_vector = 0;
438 xfc->px_vector = 0;
439 xfc->py_vector = 0;
440 }
441 else
442 {
443 double delta = xfc->lastDist - dist;
444
445 if (delta > 1.0)
446 delta = 1.0;
447
448 if (delta < -1.0)
449 delta = -1.0;
450
451 /* compare the current distance to the first one */
452 xfc->z_vector += delta;
453 xfc->lastDist = dist;
454
455 if (xfc->z_vector > ZOOM_THRESHOLD)
456 {
457 EventArgsInit(&e, "xfreerdp");
458 e.dx = e.dy = -10;
459 if (PubSub_OnZoomingChange(ctx->pubSub, xfc, &e) < 0)
460 return FALSE;
461 xfc->z_vector = 0;
462 xfc->px_vector = 0;
463 xfc->py_vector = 0;
464 }
465
466 if (xfc->z_vector < -ZOOM_THRESHOLD)
467 {
468 EventArgsInit(&e, "xfreerdp");
469 e.dx = e.dy = 10;
470 if (PubSub_OnZoomingChange(ctx->pubSub, xfc, &e) < 0)
471 return FALSE;
472 xfc->z_vector = 0;
473 xfc->px_vector = 0;
474 xfc->py_vector = 0;
475 }
476 }
477 return TRUE;
478}
479
480static void xf_input_touch_begin(xfContext* xfc, const XIDeviceEvent* event)
481{
482 WINPR_UNUSED(xfc);
483 for (int i = 0; i < MAX_CONTACTS; i++)
484 {
485 if (xfc->contacts[i].id == 0)
486 {
487 xfc->contacts[i].id = event->detail;
488 xfc->contacts[i].count = 1;
489 xfc->contacts[i].pos_x = event->event_x;
490 xfc->contacts[i].pos_y = event->event_y;
491 xfc->active_contacts++;
492 break;
493 }
494 }
495}
496
497WINPR_ATTR_NODISCARD
498static BOOL xf_input_touch_update(xfContext* xfc, const XIDeviceEvent* event)
499{
500 WINPR_ASSERT(xfc);
501 WINPR_ASSERT(event);
502
503 for (int i = 0; i < MAX_CONTACTS; i++)
504 {
505 if (xfc->contacts[i].id == event->detail)
506 {
507 xfc->contacts[i].count++;
508 xfc->contacts[i].last_x = xfc->contacts[i].pos_x;
509 xfc->contacts[i].last_y = xfc->contacts[i].pos_y;
510 xfc->contacts[i].pos_x = event->event_x;
511 xfc->contacts[i].pos_y = event->event_y;
512 if (!xf_input_detect_pinch(xfc))
513 return FALSE;
514 if (!xf_input_detect_pan(xfc))
515 return FALSE;
516 break;
517 }
518 }
519
520 return TRUE;
521}
522
523static void xf_input_touch_end(xfContext* xfc, const XIDeviceEvent* event)
524{
525 WINPR_UNUSED(xfc);
526 for (int i = 0; i < MAX_CONTACTS; i++)
527 {
528 if (xfc->contacts[i].id == event->detail)
529 {
530 xfc->contacts[i].id = 0;
531 xfc->contacts[i].count = 0;
532 xfc->active_contacts--;
533 break;
534 }
535 }
536}
537
538static int xf_input_handle_event_local(xfContext* xfc, const XEvent* event)
539{
540 union
541 {
542 const XGenericEventCookie* cc;
543 XGenericEventCookie* vc;
544 } cookie;
545 cookie.cc = &event->xcookie;
546 XGetEventData(xfc->display, cookie.vc);
547
548 if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
549 {
550 switch (cookie.cc->evtype)
551 {
552 case XI_TouchBegin:
553 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
554 xf_input_touch_begin(xfc, cookie.cc->data);
555
556 xf_input_save_last_event(xfc, cookie.cc);
557 break;
558
559 case XI_TouchUpdate:
560 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
561 {
562 if (!xf_input_touch_update(xfc, cookie.cc->data))
563 return -1;
564 }
565
566 xf_input_save_last_event(xfc, cookie.cc);
567 break;
568
569 case XI_TouchEnd:
570 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
571 xf_input_touch_end(xfc, cookie.cc->data);
572
573 xf_input_save_last_event(xfc, cookie.cc);
574 break;
575
576 default:
577 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
578 break;
579 }
580 }
581
582 XFreeEventData(xfc->display, cookie.vc);
583 return 0;
584}
585
586static void xf_input_hide_cursor(xfContext* xfc)
587{
588#ifdef WITH_XCURSOR
589
590 if (!xfc->cursorHidden)
591 {
592 XcursorImage ci = WINPR_C_ARRAY_INIT;
593 XcursorPixel xp = 0;
594 static Cursor nullcursor = None;
595 xf_lock_x11(xfc);
596 ci.version = XCURSOR_IMAGE_VERSION;
597 ci.size = sizeof(ci);
598 ci.width = ci.height = 1;
599 ci.xhot = ci.yhot = 0;
600 ci.pixels = &xp;
601 nullcursor = XcursorImageLoadCursor(xfc->display, &ci);
602
603 if ((xfc->window) && (nullcursor != None))
604 XDefineCursor(xfc->display, xfc->window->handle, nullcursor);
605
606 xfc->cursorHidden = TRUE;
607 xf_unlock_x11(xfc);
608 }
609
610#endif
611}
612
613static void xf_input_show_cursor(xfContext* xfc)
614{
615#ifdef WITH_XCURSOR
616 xf_lock_x11(xfc);
617
618 if (xfc->cursorHidden)
619 {
620 if (xfc->window)
621 {
622 if (!xfc->pointer)
623 XUndefineCursor(xfc->display, xfc->window->handle);
624 else
625 XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
626 }
627
628 xfc->cursorHidden = FALSE;
629 }
630
631 xf_unlock_x11(xfc);
632#endif
633}
634
635static int xf_input_touch_remote(xfContext* xfc, XIDeviceEvent* event, int evtype)
636{
637 int x = 0;
638 int y = 0;
639 int touchId = 0;
640 RdpeiClientContext* rdpei = xfc->common.rdpei;
641
642 if (!rdpei)
643 return 0;
644
645 xf_input_hide_cursor(xfc);
646 touchId = event->detail;
647 x = (int)event->event_x;
648 y = (int)event->event_y;
649 xf_event_adjust_coordinates(xfc, &x, &y);
650
651 switch (evtype)
652 {
653 case XI_TouchBegin:
654 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_DOWN, touchId, 0, x, y);
655 break;
656 case XI_TouchUpdate:
657 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_MOTION, touchId, 0, x, y);
658 break;
659 case XI_TouchEnd:
660 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_UP, touchId, 0, x, y);
661 break;
662 default:
663 break;
664 }
665
666 return 0;
667}
668
669static BOOL xf_input_pen_remote(xfContext* xfc, XIDeviceEvent* event, int evtype, int deviceid)
670{
671 int x = 0;
672 int y = 0;
673 RdpeiClientContext* rdpei = xfc->common.rdpei;
674
675 if (!rdpei)
676 return FALSE;
677
678 xf_input_hide_cursor(xfc);
679 x = (int)event->event_x;
680 y = (int)event->event_y;
681 xf_event_adjust_coordinates(xfc, &x, &y);
682
683 double pressure = 0.0;
684 double* val = event->valuators.values;
685 for (int i = 0; i < MIN(event->valuators.mask_len * 8, 3); i++)
686 {
687 if (XIMaskIsSet(event->valuators.mask, i))
688 {
689 double value = *val++;
690 if (i == 2)
691 pressure = value;
692 }
693 }
694
695 UINT32 flags = FREERDP_PEN_HAS_PRESSURE;
696 if ((evtype == XI_ButtonPress) || (evtype == XI_ButtonRelease))
697 {
698 WLog_DBG(TAG, "pen button %d", event->detail);
699 switch (event->detail)
700 {
701 case 1:
702 break;
703 case 3:
704 flags |= FREERDP_PEN_BARREL_PRESSED;
705 break;
706 default:
707 return FALSE;
708 }
709 }
710
711 switch (evtype)
712 {
713 case XI_ButtonPress:
714 flags |= FREERDP_PEN_PRESS;
715 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
716 return FALSE;
717 break;
718 case XI_Motion:
719 flags |= FREERDP_PEN_MOTION;
720 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
721 return FALSE;
722 break;
723 case XI_ButtonRelease:
724 flags |= FREERDP_PEN_RELEASE;
725 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
726 return FALSE;
727 break;
728 default:
729 break;
730 }
731 return TRUE;
732}
733
734static int xf_input_pens_unhover(xfContext* xfc)
735{
736 WINPR_ASSERT(xfc);
737
738 freerdp_client_pen_cancel_all(&xfc->common);
739 return 0;
740}
741
742bool xf_use_rel_mouse(xfContext* xfc)
743{
744 if (!freerdp_client_use_relative_mouse_events(&xfc->common))
745 return false;
746 if (!xfc->isCursorHidden)
747 return false;
748 return true;
749}
750
751int xf_input_event(xfContext* xfc, WINPR_ATTR_UNUSED const XEvent* xevent, XIDeviceEvent* event,
752 int evtype)
753{
754 WINPR_ASSERT(xfc);
755 WINPR_ASSERT(xevent);
756 WINPR_ASSERT(event);
757
758 xfWindow* window = xfc->window;
759 if (window)
760 {
761 if (xf_floatbar_is_locked(window->floatbar))
762 return 0;
763 }
764
765 xf_input_show_cursor(xfc);
766
767 switch (evtype)
768 {
769 case XI_ButtonPress:
770 case XI_ButtonRelease:
771 xfc->xi_event = !xfc->common.mouse_grabbed || !xf_use_rel_mouse(xfc);
772
773 if (xfc->xi_event)
774 {
775 if (!xfc_is_floatbar_window(xfc, event->event) || (evtype != XI_ButtonPress))
776 {
777 xf_generic_ButtonEvent(xfc, (int)event->event_x, (int)event->event_y,
778 event->detail, event->event, xfc->remote_app,
779 evtype == XI_ButtonPress);
780 }
781 }
782 break;
783
784 case XI_Motion:
785 xfc->xi_event = !xfc->common.mouse_grabbed || !xf_use_rel_mouse(xfc);
786
787 if (xfc->xi_event)
788 {
789 xf_generic_MotionNotify(xfc, (int)event->event_x, (int)event->event_y, event->event,
790 xfc->remote_app);
791 }
792 break;
793 case XI_RawButtonPress:
794 case XI_RawButtonRelease:
795 xfc->xi_rawevent = xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc);
796
797 if (xfc->xi_rawevent)
798 {
799 const XIRawEvent* ev = (const XIRawEvent*)event;
800 xf_generic_RawButtonEvent(xfc, ev->detail, xfc->remote_app,
801 evtype == XI_RawButtonPress);
802 }
803 break;
804 case XI_RawMotion:
805 xfc->xi_rawevent = xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc);
806
807 if (xfc->xi_rawevent)
808 {
809 const XIRawEvent* ev = (const XIRawEvent*)event;
810 double x = 0.0;
811 double y = 0.0;
812 if (XIMaskIsSet(ev->valuators.mask, 0))
813 x = ev->raw_values[0];
814 if (XIMaskIsSet(ev->valuators.mask, 1))
815 y = ev->raw_values[1];
816
817 xf_generic_RawMotionNotify(xfc, (int)x, (int)y, event->event, xfc->remote_app);
818 }
819 break;
820 case XI_DeviceChanged:
821 {
822 const XIDeviceChangedEvent* ev = (const XIDeviceChangedEvent*)event;
823 if (ev->reason != XIDeviceChange)
824 break;
825
826 /*
827 * TODO:
828 * 1. Register only changed devices.
829 * 2. Both `XIDeviceChangedEvent` and `XIHierarchyEvent` have no target
830 * `Window` which is used to register xinput events. So assume
831 * `xfc->window` created by `xf_CreateDesktopWindow` is the same
832 * `Window` we registered.
833 */
834 if (xfc->window)
835 register_input_events(xfc, xfc->window->handle);
836 }
837 break;
838 case XI_HierarchyChanged:
839 if (xfc->window)
840 register_input_events(xfc, xfc->window->handle);
841 break;
842
843 default:
844 WLog_WARN(TAG, "Unhandled event %d: Event was registered but is not handled!", evtype);
845 break;
846 }
847
848 return 0;
849}
850
851static int xf_input_handle_event_remote(xfContext* xfc, const XEvent* event)
852{
853 union
854 {
855 const XGenericEventCookie* cc;
856 XGenericEventCookie* vc;
857 } cookie;
858 cookie.cc = &event->xcookie;
859 XGetEventData(xfc->display, cookie.vc);
860
861 if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
862 {
863 switch (cookie.cc->evtype)
864 {
865 case XI_TouchBegin:
866 xf_input_pens_unhover(xfc);
867 /* fallthrough */
868 WINPR_FALLTHROUGH
869 case XI_TouchUpdate:
870 case XI_TouchEnd:
871 xf_input_touch_remote(xfc, cookie.cc->data, cookie.cc->evtype);
872 break;
873 case XI_ButtonPress:
874 case XI_Motion:
875 case XI_ButtonRelease:
876 {
877 WLog_DBG(TAG, "checking for pen");
878 XIDeviceEvent* deviceEvent = (XIDeviceEvent*)cookie.cc->data;
879 int deviceid = deviceEvent->deviceid;
880
881 if (freerdp_client_is_pen(&xfc->common, deviceid))
882 {
883 if (!xf_input_pen_remote(xfc, cookie.cc->data, cookie.cc->evtype, deviceid))
884 {
885 // XXX: don't show cursor
886 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
887 }
888 break;
889 }
890 }
891 /* fallthrough */
892 WINPR_FALLTHROUGH
893 default:
894 xf_input_pens_unhover(xfc);
895 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
896 break;
897 }
898 }
899
900 XFreeEventData(xfc->display, cookie.vc);
901 return 0;
902}
903
904#else
905
906int xf_input_init(xfContext* xfc, Window window)
907{
908 return 0;
909}
910
911#endif
912
913int xf_input_handle_event(xfContext* xfc, const XEvent* event)
914{
915#ifdef WITH_XI
916 const rdpSettings* settings = nullptr;
917 WINPR_ASSERT(xfc);
918
919 settings = xfc->common.context.settings;
920 WINPR_ASSERT(settings);
921
922 if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchInput))
923 {
924 return xf_input_handle_event_remote(xfc, event);
925 }
926 else if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
927 {
928 return xf_input_handle_event_local(xfc, event);
929 }
930 else
931 {
932 return xf_input_handle_event_local(xfc, event);
933 }
934
935#else
936 return 0;
937#endif
938}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.