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