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