20 #include <freerdp/config.h>
23 #include <X11/Xutil.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
53 static int xf_input_event(xfContext* xfc,
const XEvent* xevent, XIDeviceEvent* event,
int evtype);
56 static const char* xf_input_get_class_string(
int class)
58 if (
class == XIKeyClass)
60 else if (
class == XIButtonClass)
61 return "XIButtonClass";
62 else if (
class == XIValuatorClass)
63 return "XIValuatorClass";
64 else if (
class == XIScrollClass)
65 return "XIScrollClass";
66 else if (
class == XITouchClass)
67 return "XITouchClass";
69 return "XIUnknownClass";
73 static BOOL register_input_events(xfContext* xfc, Window window)
75 #define MAX_NR_MASKS 64
78 XIEventMask evmasks[MAX_NR_MASKS] = { 0 };
79 BYTE masks[MAX_NR_MASKS][XIMaskLen(XI_LASTEVENT)] = { 0 };
83 rdpSettings* settings = xfc->common.context.settings;
84 WINPR_ASSERT(settings);
86 XIDeviceInfo* info = XIQueryDevice(xfc->display, XIAllDevices, &ndevices);
88 for (
int i = 0; i < MIN(ndevices, MAX_NR_MASKS); i++)
91 XIDeviceInfo* dev = &info[i];
93 evmasks[nmasks].mask = masks[nmasks];
94 evmasks[nmasks].mask_len =
sizeof(masks[0]);
95 evmasks[nmasks].deviceid = dev->deviceid;
98 if (strcmp(dev->name,
"Virtual core pointer") == 0)
101 for (
int j = 0; j < dev->num_classes; j++)
103 const XIAnyClassInfo*
class = dev->classes[j];
110 const XITouchClassInfo* t = (
const XITouchClassInfo*)
class;
111 if (t->mode == XIDirectTouch)
115 "%s %s touch device (id: %d, mode: %d), supporting %d touches.",
116 dev->name, (t->mode == XIDirectTouch) ?
"direct" :
"dependent",
117 dev->deviceid, t->mode, t->num_touches);
118 XISetMask(masks[nmasks], XI_TouchBegin);
119 XISetMask(masks[nmasks], XI_TouchUpdate);
120 XISetMask(masks[nmasks], XI_TouchEnd);
126 const XIButtonClassInfo* t = (
const XIButtonClassInfo*)
class;
127 WLog_DBG(TAG,
"%s button device (id: %d, mode: %d)", dev->name, dev->deviceid,
129 XISetMask(masks[nmasks], XI_ButtonPress);
130 XISetMask(masks[nmasks], XI_ButtonRelease);
131 XISetMask(masks[nmasks], XI_Motion);
135 case XIValuatorClass:
137 static wLog* log = NULL;
141 const XIValuatorClassInfo* t = (
const XIValuatorClassInfo*)
class;
142 char* name = t->label ? Safe_XGetAtomName(log, xfc->display, t->label) : NULL;
144 WLog_Print(log, WLOG_DEBUG,
145 "%s device (id: %d) valuator %d label %s range %f - %f", dev->name,
146 dev->deviceid, t->number, name ? name :
"None", t->min, t->max);
151 double max_pressure = t->max;
153 char devName[200] = { 0 };
154 strncpy(devName, dev->name, ARRAYSIZE(devName) - 1);
155 CharLowerBuffA(devName, ARRAYSIZE(devName));
157 if (strstr(devName,
"eraser") != NULL)
159 if (freerdp_client_handle_pen(&xfc->common,
160 FREERDP_PEN_REGISTER |
161 FREERDP_PEN_IS_INVERTED |
162 FREERDP_PEN_HAS_PRESSURE,
163 dev->deviceid, max_pressure))
164 WLog_DBG(TAG,
"registered eraser");
166 else if (strstr(devName,
"stylus") != NULL ||
167 strstr(devName,
"pen") != NULL)
169 if (freerdp_client_handle_pen(
170 &xfc->common, FREERDP_PEN_REGISTER | FREERDP_PEN_HAS_PRESSURE,
171 dev->deviceid, max_pressure))
172 WLog_DBG(TAG,
"registered pen");
185 XIFreeDeviceInfo(info);
189 Status xstatus = XISelectEvents(xfc->display, window, evmasks, nmasks);
191 WLog_WARN(TAG,
"XISelectEvents returned %d", xstatus);
197 static BOOL register_raw_events(xfContext* xfc, Window window)
200 unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = { 0 };
201 rdpSettings* settings = NULL;
205 settings = xfc->common.context.settings;
206 WINPR_ASSERT(settings);
210 XISetMask(mask_bytes, XI_RawMotion);
211 XISetMask(mask_bytes, XI_RawButtonPress);
212 XISetMask(mask_bytes, XI_RawButtonRelease);
214 mask.deviceid = XIAllMasterDevices;
215 mask.mask_len =
sizeof(mask_bytes);
216 mask.mask = mask_bytes;
218 XISelectEvents(xfc->display, window, &mask, 1);
224 static BOOL register_device_events(xfContext* xfc, Window window)
227 unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = { 0 };
228 rdpSettings* settings = NULL;
232 settings = xfc->common.context.settings;
233 WINPR_ASSERT(settings);
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);
247 int 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))
293 static BOOL xf_input_is_duplicate(xfContext* xfc,
const XGenericEventCookie* cookie)
295 const XIDeviceEvent*
event = NULL;
298 WINPR_ASSERT(cookie);
300 event = cookie->data;
303 if ((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))
314 static void xf_input_save_last_event(xfContext* xfc,
const XGenericEventCookie* cookie)
316 const XIDeviceEvent*
event = NULL;
319 WINPR_ASSERT(cookie);
321 event = cookie->data;
324 xfc->lastEvType = cookie->evtype;
325 xfc->lastEvent.time =
event->time;
326 xfc->lastEvent.detail =
event->detail;
327 xfc->lastEvent.event_x =
event->event_x;
328 xfc->lastEvent.event_y =
event->event_y;
331 static void xf_input_detect_pan(xfContext* xfc)
334 rdpContext* ctx = &xfc->common.context;
337 if (xfc->active_contacts != 2)
342 const double dx[] = { xfc->contacts[0].pos_x - xfc->contacts[0].last_x,
343 xfc->contacts[1].pos_x - xfc->contacts[1].last_x };
344 const double dy[] = { xfc->contacts[0].pos_y - xfc->contacts[0].last_y,
345 xfc->contacts[1].pos_y - xfc->contacts[1].last_y };
346 const double px = fabs(dx[0]) < fabs(dx[1]) ? dx[0] : dx[1];
347 const double py = fabs(dy[0]) < fabs(dy[1]) ? dy[0] : dy[1];
348 xfc->px_vector += px;
349 xfc->py_vector += py;
350 const double dist_x = fabs(xfc->contacts[0].pos_x - xfc->contacts[1].pos_x);
351 const double dist_y = fabs(xfc->contacts[0].pos_y - xfc->contacts[1].pos_y);
353 if (dist_y > MIN_FINGER_DIST)
355 if (xfc->px_vector > PAN_THRESHOLD)
358 PanningChangeEventArgs e;
359 EventArgsInit(&e,
"xfreerdp");
362 PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
368 else if (xfc->px_vector < -PAN_THRESHOLD)
371 PanningChangeEventArgs e;
372 EventArgsInit(&e,
"xfreerdp");
375 PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
383 if (dist_x > MIN_FINGER_DIST)
385 if (xfc->py_vector > PAN_THRESHOLD)
388 PanningChangeEventArgs e;
389 EventArgsInit(&e,
"xfreerdp");
392 PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
398 else if (xfc->py_vector < -PAN_THRESHOLD)
401 PanningChangeEventArgs e;
402 EventArgsInit(&e,
"xfreerdp");
405 PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
414 static void xf_input_detect_pinch(xfContext* xfc)
416 ZoomingChangeEventArgs e = { 0 };
419 rdpContext* ctx = &xfc->common.context;
422 if (xfc->active_contacts != 2)
424 xfc->firstDist = -1.0;
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));
433 if (xfc->firstDist <= 0)
435 xfc->firstDist = dist;
436 xfc->lastDist = xfc->firstDist;
443 double delta = xfc->lastDist - dist;
452 xfc->z_vector += delta;
453 xfc->lastDist = dist;
455 if (xfc->z_vector > ZOOM_THRESHOLD)
457 EventArgsInit(&e,
"xfreerdp");
459 PubSub_OnZoomingChange(ctx->pubSub, xfc, &e);
465 if (xfc->z_vector < -ZOOM_THRESHOLD)
467 EventArgsInit(&e,
"xfreerdp");
469 PubSub_OnZoomingChange(ctx->pubSub, xfc, &e);
477 static void xf_input_touch_begin(xfContext* xfc,
const XIDeviceEvent* event)
480 for (
int i = 0; i < MAX_CONTACTS; i++)
482 if (xfc->contacts[i].id == 0)
484 xfc->contacts[i].id =
event->detail;
485 xfc->contacts[i].count = 1;
486 xfc->contacts[i].pos_x =
event->event_x;
487 xfc->contacts[i].pos_y =
event->event_y;
488 xfc->active_contacts++;
494 static void xf_input_touch_update(xfContext* xfc,
const XIDeviceEvent* event)
499 for (
int i = 0; i < MAX_CONTACTS; i++)
501 if (xfc->contacts[i].id == event->detail)
503 xfc->contacts[i].count++;
504 xfc->contacts[i].last_x = xfc->contacts[i].pos_x;
505 xfc->contacts[i].last_y = xfc->contacts[i].pos_y;
506 xfc->contacts[i].pos_x =
event->event_x;
507 xfc->contacts[i].pos_y =
event->event_y;
508 xf_input_detect_pinch(xfc);
509 xf_input_detect_pan(xfc);
515 static void xf_input_touch_end(xfContext* xfc,
const XIDeviceEvent* event)
518 for (
int i = 0; i < MAX_CONTACTS; i++)
520 if (xfc->contacts[i].id == event->detail)
522 xfc->contacts[i].id = 0;
523 xfc->contacts[i].count = 0;
524 xfc->active_contacts--;
530 static int xf_input_handle_event_local(xfContext* xfc,
const XEvent* event)
534 const XGenericEventCookie* cc;
535 XGenericEventCookie* vc;
537 cookie.cc = &
event->xcookie;
538 XGetEventData(xfc->display, cookie.vc);
540 if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
542 switch (cookie.cc->evtype)
545 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
546 xf_input_touch_begin(xfc, cookie.cc->data);
548 xf_input_save_last_event(xfc, cookie.cc);
552 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
553 xf_input_touch_update(xfc, cookie.cc->data);
555 xf_input_save_last_event(xfc, cookie.cc);
559 if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
560 xf_input_touch_end(xfc, cookie.cc->data);
562 xf_input_save_last_event(xfc, cookie.cc);
566 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
571 XFreeEventData(xfc->display, cookie.vc);
575 #ifdef WITH_DEBUG_X11
576 static char* xf_input_touch_state_string(DWORD flags)
578 if (flags & RDPINPUT_CONTACT_FLAG_DOWN)
579 return "RDPINPUT_CONTACT_FLAG_DOWN";
580 else if (flags & RDPINPUT_CONTACT_FLAG_UPDATE)
581 return "RDPINPUT_CONTACT_FLAG_UPDATE";
582 else if (flags & RDPINPUT_CONTACT_FLAG_UP)
583 return "RDPINPUT_CONTACT_FLAG_UP";
584 else if (flags & RDPINPUT_CONTACT_FLAG_INRANGE)
585 return "RDPINPUT_CONTACT_FLAG_INRANGE";
586 else if (flags & RDPINPUT_CONTACT_FLAG_INCONTACT)
587 return "RDPINPUT_CONTACT_FLAG_INCONTACT";
588 else if (flags & RDPINPUT_CONTACT_FLAG_CANCELED)
589 return "RDPINPUT_CONTACT_FLAG_CANCELED";
591 return "RDPINPUT_CONTACT_FLAG_UNKNOWN";
595 static void xf_input_hide_cursor(xfContext* xfc)
599 if (!xfc->cursorHidden)
601 XcursorImage ci = { 0 };
603 static Cursor nullcursor = None;
605 ci.version = XCURSOR_IMAGE_VERSION;
606 ci.size =
sizeof(ci);
607 ci.width = ci.height = 1;
608 ci.xhot = ci.yhot = 0;
610 nullcursor = XcursorImageLoadCursor(xfc->display, &ci);
612 if ((xfc->window) && (nullcursor != None))
613 XDefineCursor(xfc->display, xfc->window->handle, nullcursor);
615 xfc->cursorHidden = TRUE;
622 static void xf_input_show_cursor(xfContext* xfc)
627 if (xfc->cursorHidden)
632 XUndefineCursor(xfc->display, xfc->window->handle);
634 XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
637 xfc->cursorHidden = FALSE;
644 static int xf_input_touch_remote(xfContext* xfc, XIDeviceEvent* event,
int evtype)
649 RdpeiClientContext* rdpei = xfc->common.rdpei;
654 xf_input_hide_cursor(xfc);
655 touchId =
event->detail;
656 x = (int)event->event_x;
657 y = (
int)
event->event_y;
658 xf_event_adjust_coordinates(xfc, &x, &y);
663 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_DOWN, touchId, 0, x, y);
666 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_MOTION, touchId, 0, x, y);
669 freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_UP, touchId, 0, x, y);
678 static BOOL xf_input_pen_remote(xfContext* xfc, XIDeviceEvent* event,
int evtype,
int deviceid)
682 RdpeiClientContext* rdpei = xfc->common.rdpei;
687 xf_input_hide_cursor(xfc);
688 x = (int)event->event_x;
689 y = (
int)
event->event_y;
690 xf_event_adjust_coordinates(xfc, &x, &y);
692 double pressure = 0.0;
693 double* val =
event->valuators.values;
694 for (
int i = 0; i < MIN(event->valuators.mask_len * 8, 3); i++)
696 if (XIMaskIsSet(event->valuators.mask, i))
698 double value = *val++;
704 UINT32 flags = FREERDP_PEN_HAS_PRESSURE;
705 if ((evtype == XI_ButtonPress) || (evtype == XI_ButtonRelease))
707 WLog_DBG(TAG,
"pen button %d", event->detail);
708 switch (event->detail)
713 flags |= FREERDP_PEN_BARREL_PRESSED;
723 flags |= FREERDP_PEN_PRESS;
724 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
728 flags |= FREERDP_PEN_MOTION;
729 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
732 case XI_ButtonRelease:
733 flags |= FREERDP_PEN_RELEASE;
734 if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
743 static int xf_input_pens_unhover(xfContext* xfc)
747 freerdp_client_pen_cancel_all(&xfc->common);
751 int xf_input_event(xfContext* xfc,
const XEvent* xevent, XIDeviceEvent* event,
int evtype)
753 const rdpSettings* settings = NULL;
756 WINPR_ASSERT(xevent);
759 settings = xfc->common.context.settings;
760 WINPR_ASSERT(settings);
762 xfWindow* window = xfc->window;
765 if (xf_floatbar_is_locked(window->floatbar))
769 xf_input_show_cursor(xfc);
774 case XI_ButtonRelease:
775 xfc->xi_event = !xfc->common.mouse_grabbed ||
776 !freerdp_client_use_relative_mouse_events(&xfc->common);
780 xf_generic_ButtonEvent(xfc, (
int)event->event_x, (
int)event->event_y, event->detail,
781 event->event, xfc->remote_app, evtype == XI_ButtonPress);
786 xfc->xi_event = !xfc->common.mouse_grabbed ||
787 !freerdp_client_use_relative_mouse_events(&xfc->common);
791 xf_generic_MotionNotify(xfc, (
int)event->event_x, (
int)event->event_y,
792 event->detail, event->event, xfc->remote_app);
795 case XI_RawButtonPress:
796 case XI_RawButtonRelease:
798 xfc->common.mouse_grabbed && freerdp_client_use_relative_mouse_events(&xfc->common);
800 if (xfc->xi_rawevent)
802 const XIRawEvent* ev = (
const XIRawEvent*)event;
803 xf_generic_RawButtonEvent(xfc, ev->detail, xfc->remote_app,
804 evtype == XI_RawButtonPress);
809 xfc->common.mouse_grabbed && freerdp_client_use_relative_mouse_events(&xfc->common);
811 if (xfc->xi_rawevent)
813 const XIRawEvent* ev = (
const XIRawEvent*)event;
816 if (XIMaskIsSet(ev->valuators.mask, 0))
817 x = ev->raw_values[0];
818 if (XIMaskIsSet(ev->valuators.mask, 1))
819 y = ev->raw_values[1];
821 xf_generic_RawMotionNotify(xfc, (
int)x, (int)y, event->event, xfc->remote_app);
824 case XI_DeviceChanged:
826 const XIDeviceChangedEvent* ev = (
const XIDeviceChangedEvent*)event;
827 if (ev->reason != XIDeviceChange)
839 register_input_events(xfc, xfc->window->handle);
842 case XI_HierarchyChanged:
844 register_input_events(xfc, xfc->window->handle);
848 WLog_WARN(TAG,
"Unhandled event %d: Event was registered but is not handled!", evtype);
855 static int xf_input_handle_event_remote(xfContext* xfc,
const XEvent* event)
859 const XGenericEventCookie* cc;
860 XGenericEventCookie* vc;
862 cookie.cc = &
event->xcookie;
863 XGetEventData(xfc->display, cookie.vc);
865 if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
867 switch (cookie.cc->evtype)
870 xf_input_pens_unhover(xfc);
875 xf_input_touch_remote(xfc, cookie.cc->data, cookie.cc->evtype);
879 case XI_ButtonRelease:
881 WLog_DBG(TAG,
"checking for pen");
882 XIDeviceEvent* deviceEvent = (XIDeviceEvent*)cookie.cc->data;
883 int deviceid = deviceEvent->deviceid;
885 if (freerdp_client_is_pen(&xfc->common, deviceid))
887 if (!xf_input_pen_remote(xfc, cookie.cc->data, cookie.cc->evtype, deviceid))
890 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
898 xf_input_pens_unhover(xfc);
899 xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
904 XFreeEventData(xfc->display, cookie.vc);
910 int xf_input_init(xfContext* xfc, Window window)
917 int xf_input_handle_event(xfContext* xfc,
const XEvent* event)
920 const rdpSettings* settings = NULL;
923 settings = xfc->common.context.settings;
924 WINPR_ASSERT(settings);
928 return xf_input_handle_event_remote(xfc, event);
932 return xf_input_handle_event_local(xfc, event);
936 return xf_input_handle_event_local(xfc, event);
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.