20#include <freerdp/config.h>
26#include <X11/Xcursor/Xcursor.h>
30#include <X11/extensions/XInput2.h>
41#include <winpr/assert.h>
42#include <winpr/wtypes.h>
43#include <freerdp/log.h>
44#define TAG CLIENT_TAG("x11")
48#define PAN_THRESHOLD 50
49#define ZOOM_THRESHOLD 10
51#define MIN_FINGER_DIST 5
53static int xf_input_event(xfContext* xfc, WINPR_ATTR_UNUSED
const XEvent* xevent,
54 XIDeviceEvent* event,
int evtype);
57static const char* xf_input_get_class_string(
int class)
59 if (
class == 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";
70 return "XIUnknownClass";
74static BOOL register_input_events(xfContext* xfc, Window window)
76#define MAX_NR_MASKS 64
79 XIEventMask evmasks[MAX_NR_MASKS] = WINPR_C_ARRAY_INIT;
80 BYTE masks[MAX_NR_MASKS][XIMaskLen(XI_LASTEVENT)] = WINPR_C_ARRAY_INIT;
84 rdpSettings* settings = xfc->common.context.settings;
85 WINPR_ASSERT(settings);
87 XIDeviceInfo* info = XIQueryDevice(xfc->display, XIAllDevices, &ndevices);
89 for (
int i = 0; i < MIN(ndevices, MAX_NR_MASKS); i++)
92 XIDeviceInfo* dev = &info[i];
94 evmasks[nmasks].mask = masks[nmasks];
95 evmasks[nmasks].mask_len =
sizeof(masks[0]);
96 evmasks[nmasks].deviceid = dev->deviceid;
99 if (strcmp(dev->name,
"Virtual core pointer") == 0)
102 for (
int j = 0; j < dev->num_classes; j++)
104 const XIAnyClassInfo* c_class = dev->classes[j];
106 switch (c_class->type)
111 const XITouchClassInfo* t = (
const XITouchClassInfo*)c_class;
112 if (t->mode == XIDirectTouch)
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);
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,
131 XISetMask(masks[nmasks], XI_ButtonPress);
132 XISetMask(masks[nmasks], XI_ButtonRelease);
133 XISetMask(masks[nmasks], XI_Motion);
137 case XIValuatorClass:
139 static wLog* log =
nullptr;
143 const XIValuatorClassInfo* t =
144 WINPR_PACKED_ALIGN_CAST(
const XIValuatorClassInfo*, c_class);
146 t->label ? Safe_XGetAtomName(log, xfc->display, t->label) : nullptr;
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);
155 double max_pressure = t->max;
157 char devName[200] = WINPR_C_ARRAY_INIT;
158 strncpy(devName, dev->name, ARRAYSIZE(devName) - 1);
159 CharLowerBuffA(devName, ARRAYSIZE(devName));
161 if (strstr(devName,
"eraser") !=
nullptr)
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");
170 else if (strstr(devName,
"stylus") !=
nullptr ||
171 strstr(devName,
"pen") !=
nullptr)
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");
189 XIFreeDeviceInfo(info);
193 Status xstatus = XISelectEvents(xfc->display, window, evmasks, nmasks);
195 WLog_WARN(TAG,
"XISelectEvents returned %d", xstatus);
201static BOOL register_raw_events(xfContext* xfc, Window window)
204 unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = WINPR_C_ARRAY_INIT;
205 rdpSettings* settings =
nullptr;
209 settings = xfc->common.context.settings;
210 WINPR_ASSERT(settings);
214 XISetMask(mask_bytes, XI_RawMotion);
215 XISetMask(mask_bytes, XI_RawButtonPress);
216 XISetMask(mask_bytes, XI_RawButtonRelease);
218 mask.deviceid = XIAllMasterDevices;
219 mask.mask_len =
sizeof(mask_bytes);
220 mask.mask = mask_bytes;
222 XISelectEvents(xfc->display, window, &mask, 1);
228static BOOL register_device_events(xfContext* xfc, Window window)
230 XIEventMask mask = WINPR_C_ARRAY_INIT;
231 unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = WINPR_C_ARRAY_INIT;
235 XISetMask(mask_bytes, XI_DeviceChanged);
236 XISetMask(mask_bytes, XI_HierarchyChanged);
238 mask.deviceid = XIAllDevices;
239 mask.mask_len =
sizeof(mask_bytes);
240 mask.mask = mask_bytes;
242 XISelectEvents(xfc->display, window, &mask, 1);
247int xf_input_init(xfContext* xfc, Window window)
249 int major = XI_2_Major;
250 int minor = XI_2_Minor;
257 xfc->firstDist = -1.0;
261 xfc->active_contacts = 0;
263 if (!XQueryExtension(xfc->display,
"XInputExtension", &opcode, &event, &error))
265 WLog_WARN(TAG,
"XInput extension not available.");
269 xfc->XInputOpcode = opcode;
270 XIQueryVersion(xfc->display, &major, &minor);
272 if ((major < XI_2_Major) || ((major == XI_2_Major) && (minor < 2)))
274 WLog_WARN(TAG,
"Server does not support XI 2.2");
279 int scr = DefaultScreen(xfc->display);
280 Window root = RootWindow(xfc->display, scr);
282 if (!register_raw_events(xfc, root))
284 if (!register_input_events(xfc, window))
286 if (!register_device_events(xfc, window))
293static BOOL xf_input_is_duplicate(xfContext* xfc,
const XGenericEventCookie* cookie)
295 const XIDeviceEvent*
event =
nullptr;
298 WINPR_ASSERT(cookie);
300 event = cookie->data;
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));
309static void xf_input_save_last_event(xfContext* xfc,
const XGenericEventCookie* cookie)
311 const XIDeviceEvent*
event =
nullptr;
314 WINPR_ASSERT(cookie);
316 event = cookie->data;
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;
327static BOOL xf_input_detect_pan(xfContext* xfc)
330 rdpContext* ctx = &xfc->common.context;
333 if (xfc->active_contacts != 2)
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);
349 if (dist_y > MIN_FINGER_DIST)
351 if (xfc->px_vector > PAN_THRESHOLD)
354 PanningChangeEventArgs e;
355 EventArgsInit(&e,
"xfreerdp");
358 if (PubSub_OnPanningChange(ctx->pubSub, xfc, &e) < 0)
365 else if (xfc->px_vector < -PAN_THRESHOLD)
368 PanningChangeEventArgs e;
369 EventArgsInit(&e,
"xfreerdp");
372 if (PubSub_OnPanningChange(ctx->pubSub, xfc, &e) < 0)
381 if (dist_x > MIN_FINGER_DIST)
383 if (xfc->py_vector > PAN_THRESHOLD)
386 PanningChangeEventArgs e;
387 EventArgsInit(&e,
"xfreerdp");
390 if (PubSub_OnPanningChange(ctx->pubSub, xfc, &e) < 0)
397 else if (xfc->py_vector < -PAN_THRESHOLD)
400 PanningChangeEventArgs e;
401 EventArgsInit(&e,
"xfreerdp");
404 if (PubSub_OnPanningChange(ctx->pubSub, xfc, &e) < 0)
416static BOOL xf_input_detect_pinch(xfContext* xfc)
418 ZoomingChangeEventArgs e = WINPR_C_ARRAY_INIT;
421 rdpContext* ctx = &xfc->common.context;
424 if (xfc->active_contacts != 2)
426 xfc->firstDist = -1.0;
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));
435 if (xfc->firstDist <= 0)
437 xfc->firstDist = dist;
438 xfc->lastDist = xfc->firstDist;
445 double delta = xfc->lastDist - dist;
454 xfc->z_vector += delta;
455 xfc->lastDist = dist;
457 if (xfc->z_vector > ZOOM_THRESHOLD)
459 EventArgsInit(&e,
"xfreerdp");
461 if (PubSub_OnZoomingChange(ctx->pubSub, xfc, &e) < 0)
468 if (xfc->z_vector < -ZOOM_THRESHOLD)
470 EventArgsInit(&e,
"xfreerdp");
472 if (PubSub_OnZoomingChange(ctx->pubSub, xfc, &e) < 0)
482static void xf_input_touch_begin(xfContext* xfc,
const XIDeviceEvent* event)
485 for (
int i = 0; i < MAX_CONTACTS; i++)
487 if (xfc->contacts[i].id == 0)
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++;
500static BOOL xf_input_touch_update(xfContext* xfc,
const XIDeviceEvent* event)
505 for (
int i = 0; i < MAX_CONTACTS; i++)
507 if (xfc->contacts[i].id == event->detail)
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))
516 if (!xf_input_detect_pan(xfc))
525static void xf_input_touch_end(xfContext* xfc,
const XIDeviceEvent* event)
528 for (
int i = 0; i < MAX_CONTACTS; i++)
530 if (xfc->contacts[i].id == event->detail)
532 xfc->contacts[i].id = 0;
533 xfc->contacts[i].count = 0;
534 xfc->active_contacts--;
540static int xf_input_handle_event_local(xfContext* xfc,
const XEvent* event)
544 const XGenericEventCookie* cc;
545 XGenericEventCookie* vc;
547 cookie.cc = &
event->xcookie;
548 XGetEventData(xfc->display, cookie.vc);
550 if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
552 switch (cookie.cc->evtype)
555 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
556 xf_input_touch_begin(xfc, cookie.cc->data);
558 xf_input_save_last_event(xfc, cookie.cc);
562 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
564 if (!xf_input_touch_update(xfc, cookie.cc->data))
568 xf_input_save_last_event(xfc, cookie.cc);
572 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
573 xf_input_touch_end(xfc, cookie.cc->data);
575 xf_input_save_last_event(xfc, cookie.cc);
579 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
584 XFreeEventData(xfc->display, cookie.vc);
588static void xf_input_hide_cursor(xfContext* xfc)
592 if (!xfc->cursorHidden)
594 XcursorImage ci = WINPR_C_ARRAY_INIT;
596 static Cursor nullcursor = None;
598 ci.version = XCURSOR_IMAGE_VERSION;
599 ci.size =
sizeof(ci);
600 ci.width = ci.height = 1;
601 ci.xhot = ci.yhot = 0;
603 nullcursor = XcursorImageLoadCursor(xfc->display, &ci);
605 if ((xfc->window) && (nullcursor != None))
606 XDefineCursor(xfc->display, xfc->window->handle, nullcursor);
608 xfc->cursorHidden = TRUE;
615static void xf_input_show_cursor(xfContext* xfc)
620 if (xfc->cursorHidden)
625 XUndefineCursor(xfc->display, xfc->window->handle);
627 XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
630 xfc->cursorHidden = FALSE;
637static int xf_input_touch_remote(xfContext* xfc, XIDeviceEvent* event,
int evtype)
642 RdpeiClientContext* rdpei = xfc->common.rdpei;
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);
656 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_DOWN, touchId, 0, x, y);
659 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_MOTION, touchId, 0, x, y);
662 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_UP, touchId, 0, x, y);
671static BOOL xf_input_pen_remote(xfContext* xfc, XIDeviceEvent* event,
int evtype,
int deviceid)
675 RdpeiClientContext* rdpei = xfc->common.rdpei;
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);
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++)
689 if (XIMaskIsSet(event->valuators.mask, i))
691 double value = *val++;
697 UINT32 flags = FREERDP_PEN_HAS_PRESSURE;
698 if ((evtype == XI_ButtonPress) || (evtype == XI_ButtonRelease))
700 WLog_DBG(TAG,
"pen button %d", event->detail);
701 switch (event->detail)
706 flags |= FREERDP_PEN_BARREL_PRESSED;
716 flags |= FREERDP_PEN_PRESS;
717 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
721 flags |= FREERDP_PEN_MOTION;
722 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
725 case XI_ButtonRelease:
726 flags |= FREERDP_PEN_RELEASE;
727 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
736static int xf_input_pens_unhover(xfContext* xfc)
740 freerdp_client_pen_cancel_all(&xfc->common);
744int xf_input_event(xfContext* xfc, WINPR_ATTR_UNUSED
const XEvent* xevent, XIDeviceEvent* event,
748 WINPR_ASSERT(xevent);
751 xfWindow* window = xfc->window;
754 if (xf_floatbar_is_locked(window->floatbar))
758 xf_input_show_cursor(xfc);
763 case XI_ButtonRelease:
764 xfc->xi_event = !xfc->common.mouse_grabbed || !xf_use_rel_mouse(xfc);
768 if (!xfc_is_floatbar_window(xfc, event->event) || (evtype != XI_ButtonPress))
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);
778 xfc->xi_event = !xfc->common.mouse_grabbed || !xf_use_rel_mouse(xfc);
782 xf_generic_MotionNotify(xfc, (
int)event->event_x, (
int)event->event_y, event->event,
786 case XI_RawButtonPress:
787 case XI_RawButtonRelease:
788 xfc->xi_rawevent = xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc);
790 if (xfc->xi_rawevent)
792 const XIRawEvent* ev = (
const XIRawEvent*)event;
793 xf_generic_RawButtonEvent(xfc, ev->detail, xfc->remote_app,
794 evtype == XI_RawButtonPress);
798 xfc->xi_rawevent = xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc);
800 if (xfc->xi_rawevent)
802 const XIRawEvent* ev = (
const XIRawEvent*)event;
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];
810 xf_generic_RawMotionNotify(xfc, (
int)x, (
int)y, event->event, xfc->remote_app);
813 case XI_DeviceChanged:
815 const XIDeviceChangedEvent* ev = (
const XIDeviceChangedEvent*)event;
816 if (ev->reason != XIDeviceChange)
828 register_input_events(xfc, xfc->window->handle);
831 case XI_HierarchyChanged:
833 register_input_events(xfc, xfc->window->handle);
837 WLog_WARN(TAG,
"Unhandled event %d: Event was registered but is not handled!", evtype);
844static int xf_input_handle_event_remote(xfContext* xfc,
const XEvent* event)
848 const XGenericEventCookie* cc;
849 XGenericEventCookie* vc;
851 cookie.cc = &
event->xcookie;
852 XGetEventData(xfc->display, cookie.vc);
854 if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
856 switch (cookie.cc->evtype)
859 xf_input_pens_unhover(xfc);
864 xf_input_touch_remote(xfc, cookie.cc->data, cookie.cc->evtype);
868 case XI_ButtonRelease:
870 WLog_DBG(TAG,
"checking for pen");
871 XIDeviceEvent* deviceEvent = (XIDeviceEvent*)cookie.cc->data;
872 int deviceid = deviceEvent->deviceid;
874 if (freerdp_client_is_pen(&xfc->common, deviceid))
876 if (!xf_input_pen_remote(xfc, cookie.cc->data, cookie.cc->evtype, deviceid))
879 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
887 xf_input_pens_unhover(xfc);
888 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
893 XFreeEventData(xfc->display, cookie.vc);
899int xf_input_init(xfContext* xfc, Window window)
906int xf_input_handle_event(xfContext* xfc,
const XEvent* event)
909 const rdpSettings* settings =
nullptr;
912 settings = xfc->common.context.settings;
913 WINPR_ASSERT(settings);
917 return xf_input_handle_event_remote(xfc, event);
921 return xf_input_handle_event_local(xfc, event);
925 return xf_input_handle_event_local(xfc, event);
933bool xf_use_rel_mouse(xfContext* xfc)
935 if (!freerdp_client_use_relative_mouse_events(&xfc->common))
937 if (!xfc->isCursorHidden)
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.