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
324static void xf_input_detect_pan(xfContext* xfc)
325{
326 WINPR_ASSERT(xfc);
327 rdpContext* ctx = &xfc->common.context;
328 WINPR_ASSERT(ctx);
329
330 if (xfc->active_contacts != 2)
331 {
332 return;
333 }
334
335 const double dx[] = { xfc->contacts[0].pos_x - xfc->contacts[0].last_x,
336 xfc->contacts[1].pos_x - xfc->contacts[1].last_x };
337 const double dy[] = { xfc->contacts[0].pos_y - xfc->contacts[0].last_y,
338 xfc->contacts[1].pos_y - xfc->contacts[1].last_y };
339 const double px = fabs(dx[0]) < fabs(dx[1]) ? dx[0] : dx[1];
340 const double py = fabs(dy[0]) < fabs(dy[1]) ? dy[0] : dy[1];
341 xfc->px_vector += px;
342 xfc->py_vector += py;
343 const double dist_x = fabs(xfc->contacts[0].pos_x - xfc->contacts[1].pos_x);
344 const double dist_y = fabs(xfc->contacts[0].pos_y - xfc->contacts[1].pos_y);
345
346 if (dist_y > MIN_FINGER_DIST)
347 {
348 if (xfc->px_vector > PAN_THRESHOLD)
349 {
350 {
351 PanningChangeEventArgs e;
352 EventArgsInit(&e, "xfreerdp");
353 e.dx = 5;
354 e.dy = 0;
355 PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
356 }
357 xfc->px_vector = 0;
358 xfc->py_vector = 0;
359 xfc->z_vector = 0;
360 }
361 else if (xfc->px_vector < -PAN_THRESHOLD)
362 {
363 {
364 PanningChangeEventArgs e;
365 EventArgsInit(&e, "xfreerdp");
366 e.dx = -5;
367 e.dy = 0;
368 PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
369 }
370 xfc->px_vector = 0;
371 xfc->py_vector = 0;
372 xfc->z_vector = 0;
373 }
374 }
375
376 if (dist_x > MIN_FINGER_DIST)
377 {
378 if (xfc->py_vector > PAN_THRESHOLD)
379 {
380 {
381 PanningChangeEventArgs e;
382 EventArgsInit(&e, "xfreerdp");
383 e.dx = 0;
384 e.dy = 5;
385 PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
386 }
387 xfc->py_vector = 0;
388 xfc->px_vector = 0;
389 xfc->z_vector = 0;
390 }
391 else if (xfc->py_vector < -PAN_THRESHOLD)
392 {
393 {
394 PanningChangeEventArgs e;
395 EventArgsInit(&e, "xfreerdp");
396 e.dx = 0;
397 e.dy = -5;
398 PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
399 }
400 xfc->py_vector = 0;
401 xfc->px_vector = 0;
402 xfc->z_vector = 0;
403 }
404 }
405}
406
407static void xf_input_detect_pinch(xfContext* xfc)
408{
409 ZoomingChangeEventArgs e = WINPR_C_ARRAY_INIT;
410
411 WINPR_ASSERT(xfc);
412 rdpContext* ctx = &xfc->common.context;
413 WINPR_ASSERT(ctx);
414
415 if (xfc->active_contacts != 2)
416 {
417 xfc->firstDist = -1.0;
418 return;
419 }
420
421 /* first calculate the distance */
422 const double dist = sqrt(pow(xfc->contacts[1].pos_x - xfc->contacts[0].last_x, 2.0) +
423 pow(xfc->contacts[1].pos_y - xfc->contacts[0].last_y, 2.0));
424
425 /* if this is the first 2pt touch */
426 if (xfc->firstDist <= 0)
427 {
428 xfc->firstDist = dist;
429 xfc->lastDist = xfc->firstDist;
430 xfc->z_vector = 0;
431 xfc->px_vector = 0;
432 xfc->py_vector = 0;
433 }
434 else
435 {
436 double delta = xfc->lastDist - dist;
437
438 if (delta > 1.0)
439 delta = 1.0;
440
441 if (delta < -1.0)
442 delta = -1.0;
443
444 /* compare the current distance to the first one */
445 xfc->z_vector += delta;
446 xfc->lastDist = dist;
447
448 if (xfc->z_vector > ZOOM_THRESHOLD)
449 {
450 EventArgsInit(&e, "xfreerdp");
451 e.dx = e.dy = -10;
452 PubSub_OnZoomingChange(ctx->pubSub, xfc, &e);
453 xfc->z_vector = 0;
454 xfc->px_vector = 0;
455 xfc->py_vector = 0;
456 }
457
458 if (xfc->z_vector < -ZOOM_THRESHOLD)
459 {
460 EventArgsInit(&e, "xfreerdp");
461 e.dx = e.dy = 10;
462 PubSub_OnZoomingChange(ctx->pubSub, xfc, &e);
463 xfc->z_vector = 0;
464 xfc->px_vector = 0;
465 xfc->py_vector = 0;
466 }
467 }
468}
469
470static void xf_input_touch_begin(xfContext* xfc, const XIDeviceEvent* event)
471{
472 WINPR_UNUSED(xfc);
473 for (int i = 0; i < MAX_CONTACTS; i++)
474 {
475 if (xfc->contacts[i].id == 0)
476 {
477 xfc->contacts[i].id = event->detail;
478 xfc->contacts[i].count = 1;
479 xfc->contacts[i].pos_x = event->event_x;
480 xfc->contacts[i].pos_y = event->event_y;
481 xfc->active_contacts++;
482 break;
483 }
484 }
485}
486
487static void xf_input_touch_update(xfContext* xfc, const XIDeviceEvent* event)
488{
489 WINPR_ASSERT(xfc);
490 WINPR_ASSERT(event);
491
492 for (int i = 0; i < MAX_CONTACTS; i++)
493 {
494 if (xfc->contacts[i].id == event->detail)
495 {
496 xfc->contacts[i].count++;
497 xfc->contacts[i].last_x = xfc->contacts[i].pos_x;
498 xfc->contacts[i].last_y = xfc->contacts[i].pos_y;
499 xfc->contacts[i].pos_x = event->event_x;
500 xfc->contacts[i].pos_y = event->event_y;
501 xf_input_detect_pinch(xfc);
502 xf_input_detect_pan(xfc);
503 break;
504 }
505 }
506}
507
508static void xf_input_touch_end(xfContext* xfc, const XIDeviceEvent* event)
509{
510 WINPR_UNUSED(xfc);
511 for (int i = 0; i < MAX_CONTACTS; i++)
512 {
513 if (xfc->contacts[i].id == event->detail)
514 {
515 xfc->contacts[i].id = 0;
516 xfc->contacts[i].count = 0;
517 xfc->active_contacts--;
518 break;
519 }
520 }
521}
522
523static int xf_input_handle_event_local(xfContext* xfc, const XEvent* event)
524{
525 union
526 {
527 const XGenericEventCookie* cc;
528 XGenericEventCookie* vc;
529 } cookie;
530 cookie.cc = &event->xcookie;
531 XGetEventData(xfc->display, cookie.vc);
532
533 if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
534 {
535 switch (cookie.cc->evtype)
536 {
537 case XI_TouchBegin:
538 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
539 xf_input_touch_begin(xfc, cookie.cc->data);
540
541 xf_input_save_last_event(xfc, cookie.cc);
542 break;
543
544 case XI_TouchUpdate:
545 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
546 xf_input_touch_update(xfc, cookie.cc->data);
547
548 xf_input_save_last_event(xfc, cookie.cc);
549 break;
550
551 case XI_TouchEnd:
552 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
553 xf_input_touch_end(xfc, cookie.cc->data);
554
555 xf_input_save_last_event(xfc, cookie.cc);
556 break;
557
558 default:
559 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
560 break;
561 }
562 }
563
564 XFreeEventData(xfc->display, cookie.vc);
565 return 0;
566}
567
568static void xf_input_hide_cursor(xfContext* xfc)
569{
570#ifdef WITH_XCURSOR
571
572 if (!xfc->cursorHidden)
573 {
574 XcursorImage ci = WINPR_C_ARRAY_INIT;
575 XcursorPixel xp = 0;
576 static Cursor nullcursor = None;
577 xf_lock_x11(xfc);
578 ci.version = XCURSOR_IMAGE_VERSION;
579 ci.size = sizeof(ci);
580 ci.width = ci.height = 1;
581 ci.xhot = ci.yhot = 0;
582 ci.pixels = &xp;
583 nullcursor = XcursorImageLoadCursor(xfc->display, &ci);
584
585 if ((xfc->window) && (nullcursor != None))
586 XDefineCursor(xfc->display, xfc->window->handle, nullcursor);
587
588 xfc->cursorHidden = TRUE;
589 xf_unlock_x11(xfc);
590 }
591
592#endif
593}
594
595static void xf_input_show_cursor(xfContext* xfc)
596{
597#ifdef WITH_XCURSOR
598 xf_lock_x11(xfc);
599
600 if (xfc->cursorHidden)
601 {
602 if (xfc->window)
603 {
604 if (!xfc->pointer)
605 XUndefineCursor(xfc->display, xfc->window->handle);
606 else
607 XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
608 }
609
610 xfc->cursorHidden = FALSE;
611 }
612
613 xf_unlock_x11(xfc);
614#endif
615}
616
617static int xf_input_touch_remote(xfContext* xfc, XIDeviceEvent* event, int evtype)
618{
619 int x = 0;
620 int y = 0;
621 int touchId = 0;
622 RdpeiClientContext* rdpei = xfc->common.rdpei;
623
624 if (!rdpei)
625 return 0;
626
627 xf_input_hide_cursor(xfc);
628 touchId = event->detail;
629 x = (int)event->event_x;
630 y = (int)event->event_y;
631 xf_event_adjust_coordinates(xfc, &x, &y);
632
633 switch (evtype)
634 {
635 case XI_TouchBegin:
636 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_DOWN, touchId, 0, x, y);
637 break;
638 case XI_TouchUpdate:
639 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_MOTION, touchId, 0, x, y);
640 break;
641 case XI_TouchEnd:
642 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_UP, touchId, 0, x, y);
643 break;
644 default:
645 break;
646 }
647
648 return 0;
649}
650
651static BOOL xf_input_pen_remote(xfContext* xfc, XIDeviceEvent* event, int evtype, int deviceid)
652{
653 int x = 0;
654 int y = 0;
655 RdpeiClientContext* rdpei = xfc->common.rdpei;
656
657 if (!rdpei)
658 return FALSE;
659
660 xf_input_hide_cursor(xfc);
661 x = (int)event->event_x;
662 y = (int)event->event_y;
663 xf_event_adjust_coordinates(xfc, &x, &y);
664
665 double pressure = 0.0;
666 double* val = event->valuators.values;
667 for (int i = 0; i < MIN(event->valuators.mask_len * 8, 3); i++)
668 {
669 if (XIMaskIsSet(event->valuators.mask, i))
670 {
671 double value = *val++;
672 if (i == 2)
673 pressure = value;
674 }
675 }
676
677 UINT32 flags = FREERDP_PEN_HAS_PRESSURE;
678 if ((evtype == XI_ButtonPress) || (evtype == XI_ButtonRelease))
679 {
680 WLog_DBG(TAG, "pen button %d", event->detail);
681 switch (event->detail)
682 {
683 case 1:
684 break;
685 case 3:
686 flags |= FREERDP_PEN_BARREL_PRESSED;
687 break;
688 default:
689 return FALSE;
690 }
691 }
692
693 switch (evtype)
694 {
695 case XI_ButtonPress:
696 flags |= FREERDP_PEN_PRESS;
697 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
698 return FALSE;
699 break;
700 case XI_Motion:
701 flags |= FREERDP_PEN_MOTION;
702 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
703 return FALSE;
704 break;
705 case XI_ButtonRelease:
706 flags |= FREERDP_PEN_RELEASE;
707 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
708 return FALSE;
709 break;
710 default:
711 break;
712 }
713 return TRUE;
714}
715
716static int xf_input_pens_unhover(xfContext* xfc)
717{
718 WINPR_ASSERT(xfc);
719
720 freerdp_client_pen_cancel_all(&xfc->common);
721 return 0;
722}
723
724bool xf_use_rel_mouse(xfContext* xfc)
725{
726 if (!freerdp_client_use_relative_mouse_events(&xfc->common))
727 return false;
728 if (!xfc->isCursorHidden)
729 return false;
730 return true;
731}
732
733int xf_input_event(xfContext* xfc, WINPR_ATTR_UNUSED const XEvent* xevent, XIDeviceEvent* event,
734 int evtype)
735{
736 WINPR_ASSERT(xfc);
737 WINPR_ASSERT(xevent);
738 WINPR_ASSERT(event);
739
740 xfWindow* window = xfc->window;
741 if (window)
742 {
743 if (xf_floatbar_is_locked(window->floatbar))
744 return 0;
745 }
746
747 xf_input_show_cursor(xfc);
748
749 switch (evtype)
750 {
751 case XI_ButtonPress:
752 case XI_ButtonRelease:
753 xfc->xi_event = !xfc->common.mouse_grabbed || !xf_use_rel_mouse(xfc);
754
755 if (xfc->xi_event)
756 {
757 if (!xfc_is_floatbar_window(xfc, event->event) || (evtype != XI_ButtonPress))
758 {
759 xf_generic_ButtonEvent(xfc, (int)event->event_x, (int)event->event_y,
760 event->detail, event->event, xfc->remote_app,
761 evtype == XI_ButtonPress);
762 }
763 }
764 break;
765
766 case XI_Motion:
767 xfc->xi_event = !xfc->common.mouse_grabbed || !xf_use_rel_mouse(xfc);
768
769 if (xfc->xi_event)
770 {
771 xf_generic_MotionNotify(xfc, (int)event->event_x, (int)event->event_y, event->event,
772 xfc->remote_app);
773 }
774 break;
775 case XI_RawButtonPress:
776 case XI_RawButtonRelease:
777 xfc->xi_rawevent = xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc);
778
779 if (xfc->xi_rawevent)
780 {
781 const XIRawEvent* ev = (const XIRawEvent*)event;
782 xf_generic_RawButtonEvent(xfc, ev->detail, xfc->remote_app,
783 evtype == XI_RawButtonPress);
784 }
785 break;
786 case XI_RawMotion:
787 xfc->xi_rawevent = xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc);
788
789 if (xfc->xi_rawevent)
790 {
791 const XIRawEvent* ev = (const XIRawEvent*)event;
792 double x = 0.0;
793 double y = 0.0;
794 if (XIMaskIsSet(ev->valuators.mask, 0))
795 x = ev->raw_values[0];
796 if (XIMaskIsSet(ev->valuators.mask, 1))
797 y = ev->raw_values[1];
798
799 xf_generic_RawMotionNotify(xfc, (int)x, (int)y, event->event, xfc->remote_app);
800 }
801 break;
802 case XI_DeviceChanged:
803 {
804 const XIDeviceChangedEvent* ev = (const XIDeviceChangedEvent*)event;
805 if (ev->reason != XIDeviceChange)
806 break;
807
808 /*
809 * TODO:
810 * 1. Register only changed devices.
811 * 2. Both `XIDeviceChangedEvent` and `XIHierarchyEvent` have no target
812 * `Window` which is used to register xinput events. So assume
813 * `xfc->window` created by `xf_CreateDesktopWindow` is the same
814 * `Window` we registered.
815 */
816 if (xfc->window)
817 register_input_events(xfc, xfc->window->handle);
818 }
819 break;
820 case XI_HierarchyChanged:
821 if (xfc->window)
822 register_input_events(xfc, xfc->window->handle);
823 break;
824
825 default:
826 WLog_WARN(TAG, "Unhandled event %d: Event was registered but is not handled!", evtype);
827 break;
828 }
829
830 return 0;
831}
832
833static int xf_input_handle_event_remote(xfContext* xfc, const XEvent* event)
834{
835 union
836 {
837 const XGenericEventCookie* cc;
838 XGenericEventCookie* vc;
839 } cookie;
840 cookie.cc = &event->xcookie;
841 XGetEventData(xfc->display, cookie.vc);
842
843 if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
844 {
845 switch (cookie.cc->evtype)
846 {
847 case XI_TouchBegin:
848 xf_input_pens_unhover(xfc);
849 /* fallthrough */
850 WINPR_FALLTHROUGH
851 case XI_TouchUpdate:
852 case XI_TouchEnd:
853 xf_input_touch_remote(xfc, cookie.cc->data, cookie.cc->evtype);
854 break;
855 case XI_ButtonPress:
856 case XI_Motion:
857 case XI_ButtonRelease:
858 {
859 WLog_DBG(TAG, "checking for pen");
860 XIDeviceEvent* deviceEvent = (XIDeviceEvent*)cookie.cc->data;
861 int deviceid = deviceEvent->deviceid;
862
863 if (freerdp_client_is_pen(&xfc->common, deviceid))
864 {
865 if (!xf_input_pen_remote(xfc, cookie.cc->data, cookie.cc->evtype, deviceid))
866 {
867 // XXX: don't show cursor
868 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
869 }
870 break;
871 }
872 }
873 /* fallthrough */
874 WINPR_FALLTHROUGH
875 default:
876 xf_input_pens_unhover(xfc);
877 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
878 break;
879 }
880 }
881
882 XFreeEventData(xfc->display, cookie.vc);
883 return 0;
884}
885
886#else
887
888int xf_input_init(xfContext* xfc, Window window)
889{
890 return 0;
891}
892
893#endif
894
895int xf_input_handle_event(xfContext* xfc, const XEvent* event)
896{
897#ifdef WITH_XI
898 const rdpSettings* settings = nullptr;
899 WINPR_ASSERT(xfc);
900
901 settings = xfc->common.context.settings;
902 WINPR_ASSERT(settings);
903
904 if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchInput))
905 {
906 return xf_input_handle_event_remote(xfc, event);
907 }
908 else if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
909 {
910 return xf_input_handle_event_local(xfc, event);
911 }
912 else
913 {
914 return xf_input_handle_event_local(xfc, event);
915 }
916
917#else
918 return 0;
919#endif
920}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.