FreeRDP
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 
53 static int xf_input_event(xfContext* xfc, const XEvent* xevent, XIDeviceEvent* event, int evtype);
54 
55 #ifdef DEBUG_XINPUT
56 static const char* xf_input_get_class_string(int class)
57 {
58  if (class == XIKeyClass)
59  return "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";
68 
69  return "XIUnknownClass";
70 }
71 #endif
72 
73 static BOOL register_input_events(xfContext* xfc, Window window)
74 {
75 #define MAX_NR_MASKS 64
76  int ndevices = 0;
77  int nmasks = 0;
78  XIEventMask evmasks[MAX_NR_MASKS] = { 0 };
79  BYTE masks[MAX_NR_MASKS][XIMaskLen(XI_LASTEVENT)] = { 0 };
80 
81  WINPR_ASSERT(xfc);
82 
83  rdpSettings* settings = xfc->common.context.settings;
84  WINPR_ASSERT(settings);
85 
86  XIDeviceInfo* info = XIQueryDevice(xfc->display, XIAllDevices, &ndevices);
87 
88  for (int i = 0; i < MIN(ndevices, MAX_NR_MASKS); i++)
89  {
90  BOOL used = FALSE;
91  XIDeviceInfo* dev = &info[i];
92 
93  evmasks[nmasks].mask = masks[nmasks];
94  evmasks[nmasks].mask_len = sizeof(masks[0]);
95  evmasks[nmasks].deviceid = dev->deviceid;
96 
97  /* Ignore virtual core pointer */
98  if (strcmp(dev->name, "Virtual core pointer") == 0)
99  continue;
100 
101  for (int j = 0; j < dev->num_classes; j++)
102  {
103  const XIAnyClassInfo* class = dev->classes[j];
104 
105  switch (class->type)
106  {
107  case XITouchClass:
108  if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchInput))
109  {
110  const XITouchClassInfo* t = (const XITouchClassInfo*)class;
111  if (t->mode == XIDirectTouch)
112  {
113  WLog_DBG(
114  TAG,
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);
121  }
122  }
123  break;
124  case XIButtonClass:
125  {
126  const XIButtonClassInfo* t = (const XIButtonClassInfo*)class;
127  WLog_DBG(TAG, "%s button device (id: %d, mode: %d)", dev->name, dev->deviceid,
128  t->num_buttons);
129  XISetMask(masks[nmasks], XI_ButtonPress);
130  XISetMask(masks[nmasks], XI_ButtonRelease);
131  XISetMask(masks[nmasks], XI_Motion);
132  used = TRUE;
133  break;
134  }
135  case XIValuatorClass:
136  {
137  static wLog* log = NULL;
138  if (!log)
139  log = WLog_Get(TAG);
140 
141  const XIValuatorClassInfo* t = (const XIValuatorClassInfo*)class;
142  char* name = t->label ? Safe_XGetAtomName(log, xfc->display, t->label) : NULL;
143 
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);
147  free(name);
148 
149  if (t->number == 2)
150  {
151  double max_pressure = t->max;
152 
153  char devName[200] = { 0 };
154  strncpy(devName, dev->name, ARRAYSIZE(devName) - 1);
155  CharLowerBuffA(devName, ARRAYSIZE(devName));
156 
157  if (strstr(devName, "eraser") != NULL)
158  {
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");
165  }
166  else if (strstr(devName, "stylus") != NULL ||
167  strstr(devName, "pen") != NULL)
168  {
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");
173  }
174  }
175  break;
176  }
177  default:
178  break;
179  }
180  }
181  if (used)
182  nmasks++;
183  }
184 
185  XIFreeDeviceInfo(info);
186 
187  if (nmasks > 0)
188  {
189  Status xstatus = XISelectEvents(xfc->display, window, evmasks, nmasks);
190  if (xstatus != 0)
191  WLog_WARN(TAG, "XISelectEvents returned %d", xstatus);
192  }
193 
194  return TRUE;
195 }
196 
197 static BOOL register_raw_events(xfContext* xfc, Window window)
198 {
199  XIEventMask mask;
200  unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = { 0 };
201  rdpSettings* settings = NULL;
202 
203  WINPR_ASSERT(xfc);
204 
205  settings = xfc->common.context.settings;
206  WINPR_ASSERT(settings);
207 
208  if (freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove))
209  {
210  XISetMask(mask_bytes, XI_RawMotion);
211  XISetMask(mask_bytes, XI_RawButtonPress);
212  XISetMask(mask_bytes, XI_RawButtonRelease);
213 
214  mask.deviceid = XIAllMasterDevices;
215  mask.mask_len = sizeof(mask_bytes);
216  mask.mask = mask_bytes;
217 
218  XISelectEvents(xfc->display, window, &mask, 1);
219  }
220 
221  return TRUE;
222 }
223 
224 static BOOL register_device_events(xfContext* xfc, Window window)
225 {
226  XIEventMask mask;
227  unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = { 0 };
228  rdpSettings* settings = NULL;
229 
230  WINPR_ASSERT(xfc);
231 
232  settings = xfc->common.context.settings;
233  WINPR_ASSERT(settings);
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 
247 int 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 
293 static BOOL xf_input_is_duplicate(xfContext* xfc, const XGenericEventCookie* cookie)
294 {
295  const XIDeviceEvent* event = NULL;
296 
297  WINPR_ASSERT(xfc);
298  WINPR_ASSERT(cookie);
299 
300  event = cookie->data;
301  WINPR_ASSERT(event);
302 
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))
307  {
308  return TRUE;
309  }
310 
311  return FALSE;
312 }
313 
314 static void xf_input_save_last_event(xfContext* xfc, const XGenericEventCookie* cookie)
315 {
316  const XIDeviceEvent* event = NULL;
317 
318  WINPR_ASSERT(xfc);
319  WINPR_ASSERT(cookie);
320 
321  event = cookie->data;
322  WINPR_ASSERT(event);
323 
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;
329 }
330 
331 static void xf_input_detect_pan(xfContext* xfc)
332 {
333  WINPR_ASSERT(xfc);
334  rdpContext* ctx = &xfc->common.context;
335  WINPR_ASSERT(ctx);
336 
337  if (xfc->active_contacts != 2)
338  {
339  return;
340  }
341 
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);
352 
353  if (dist_y > MIN_FINGER_DIST)
354  {
355  if (xfc->px_vector > PAN_THRESHOLD)
356  {
357  {
358  PanningChangeEventArgs e;
359  EventArgsInit(&e, "xfreerdp");
360  e.dx = 5;
361  e.dy = 0;
362  PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
363  }
364  xfc->px_vector = 0;
365  xfc->py_vector = 0;
366  xfc->z_vector = 0;
367  }
368  else if (xfc->px_vector < -PAN_THRESHOLD)
369  {
370  {
371  PanningChangeEventArgs e;
372  EventArgsInit(&e, "xfreerdp");
373  e.dx = -5;
374  e.dy = 0;
375  PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
376  }
377  xfc->px_vector = 0;
378  xfc->py_vector = 0;
379  xfc->z_vector = 0;
380  }
381  }
382 
383  if (dist_x > MIN_FINGER_DIST)
384  {
385  if (xfc->py_vector > PAN_THRESHOLD)
386  {
387  {
388  PanningChangeEventArgs e;
389  EventArgsInit(&e, "xfreerdp");
390  e.dx = 0;
391  e.dy = 5;
392  PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
393  }
394  xfc->py_vector = 0;
395  xfc->px_vector = 0;
396  xfc->z_vector = 0;
397  }
398  else if (xfc->py_vector < -PAN_THRESHOLD)
399  {
400  {
401  PanningChangeEventArgs e;
402  EventArgsInit(&e, "xfreerdp");
403  e.dx = 0;
404  e.dy = -5;
405  PubSub_OnPanningChange(ctx->pubSub, xfc, &e);
406  }
407  xfc->py_vector = 0;
408  xfc->px_vector = 0;
409  xfc->z_vector = 0;
410  }
411  }
412 }
413 
414 static void xf_input_detect_pinch(xfContext* xfc)
415 {
416  ZoomingChangeEventArgs e = { 0 };
417 
418  WINPR_ASSERT(xfc);
419  rdpContext* ctx = &xfc->common.context;
420  WINPR_ASSERT(ctx);
421 
422  if (xfc->active_contacts != 2)
423  {
424  xfc->firstDist = -1.0;
425  return;
426  }
427 
428  /* first calculate the distance */
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));
431 
432  /* if this is the first 2pt touch */
433  if (xfc->firstDist <= 0)
434  {
435  xfc->firstDist = dist;
436  xfc->lastDist = xfc->firstDist;
437  xfc->z_vector = 0;
438  xfc->px_vector = 0;
439  xfc->py_vector = 0;
440  }
441  else
442  {
443  double delta = xfc->lastDist - dist;
444 
445  if (delta > 1.0)
446  delta = 1.0;
447 
448  if (delta < -1.0)
449  delta = -1.0;
450 
451  /* compare the current distance to the first one */
452  xfc->z_vector += delta;
453  xfc->lastDist = dist;
454 
455  if (xfc->z_vector > ZOOM_THRESHOLD)
456  {
457  EventArgsInit(&e, "xfreerdp");
458  e.dx = e.dy = -10;
459  PubSub_OnZoomingChange(ctx->pubSub, xfc, &e);
460  xfc->z_vector = 0;
461  xfc->px_vector = 0;
462  xfc->py_vector = 0;
463  }
464 
465  if (xfc->z_vector < -ZOOM_THRESHOLD)
466  {
467  EventArgsInit(&e, "xfreerdp");
468  e.dx = e.dy = 10;
469  PubSub_OnZoomingChange(ctx->pubSub, xfc, &e);
470  xfc->z_vector = 0;
471  xfc->px_vector = 0;
472  xfc->py_vector = 0;
473  }
474  }
475 }
476 
477 static void xf_input_touch_begin(xfContext* xfc, const XIDeviceEvent* event)
478 {
479  WINPR_UNUSED(xfc);
480  for (int i = 0; i < MAX_CONTACTS; i++)
481  {
482  if (xfc->contacts[i].id == 0)
483  {
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++;
489  break;
490  }
491  }
492 }
493 
494 static void xf_input_touch_update(xfContext* xfc, const XIDeviceEvent* event)
495 {
496  WINPR_ASSERT(xfc);
497  WINPR_ASSERT(event);
498 
499  for (int i = 0; i < MAX_CONTACTS; i++)
500  {
501  if (xfc->contacts[i].id == event->detail)
502  {
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);
510  break;
511  }
512  }
513 }
514 
515 static void xf_input_touch_end(xfContext* xfc, const XIDeviceEvent* event)
516 {
517  WINPR_UNUSED(xfc);
518  for (int i = 0; i < MAX_CONTACTS; i++)
519  {
520  if (xfc->contacts[i].id == event->detail)
521  {
522  xfc->contacts[i].id = 0;
523  xfc->contacts[i].count = 0;
524  xfc->active_contacts--;
525  break;
526  }
527  }
528 }
529 
530 static int xf_input_handle_event_local(xfContext* xfc, const XEvent* event)
531 {
532  union
533  {
534  const XGenericEventCookie* cc;
535  XGenericEventCookie* vc;
536  } cookie;
537  cookie.cc = &event->xcookie;
538  XGetEventData(xfc->display, cookie.vc);
539 
540  if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
541  {
542  switch (cookie.cc->evtype)
543  {
544  case XI_TouchBegin:
545  if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
546  xf_input_touch_begin(xfc, cookie.cc->data);
547 
548  xf_input_save_last_event(xfc, cookie.cc);
549  break;
550 
551  case XI_TouchUpdate:
552  if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
553  xf_input_touch_update(xfc, cookie.cc->data);
554 
555  xf_input_save_last_event(xfc, cookie.cc);
556  break;
557 
558  case XI_TouchEnd:
559  if (xf_input_is_duplicate(xfc, cookie.cc) == FALSE)
560  xf_input_touch_end(xfc, cookie.cc->data);
561 
562  xf_input_save_last_event(xfc, cookie.cc);
563  break;
564 
565  default:
566  xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
567  break;
568  }
569  }
570 
571  XFreeEventData(xfc->display, cookie.vc);
572  return 0;
573 }
574 
575 #ifdef WITH_DEBUG_X11
576 static char* xf_input_touch_state_string(DWORD flags)
577 {
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";
590  else
591  return "RDPINPUT_CONTACT_FLAG_UNKNOWN";
592 }
593 #endif
594 
595 static void xf_input_hide_cursor(xfContext* xfc)
596 {
597 #ifdef WITH_XCURSOR
598 
599  if (!xfc->cursorHidden)
600  {
601  XcursorImage ci = { 0 };
602  XcursorPixel xp = 0;
603  static Cursor nullcursor = None;
604  xf_lock_x11(xfc);
605  ci.version = XCURSOR_IMAGE_VERSION;
606  ci.size = sizeof(ci);
607  ci.width = ci.height = 1;
608  ci.xhot = ci.yhot = 0;
609  ci.pixels = &xp;
610  nullcursor = XcursorImageLoadCursor(xfc->display, &ci);
611 
612  if ((xfc->window) && (nullcursor != None))
613  XDefineCursor(xfc->display, xfc->window->handle, nullcursor);
614 
615  xfc->cursorHidden = TRUE;
616  xf_unlock_x11(xfc);
617  }
618 
619 #endif
620 }
621 
622 static void xf_input_show_cursor(xfContext* xfc)
623 {
624 #ifdef WITH_XCURSOR
625  xf_lock_x11(xfc);
626 
627  if (xfc->cursorHidden)
628  {
629  if (xfc->window)
630  {
631  if (!xfc->pointer)
632  XUndefineCursor(xfc->display, xfc->window->handle);
633  else
634  XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
635  }
636 
637  xfc->cursorHidden = FALSE;
638  }
639 
640  xf_unlock_x11(xfc);
641 #endif
642 }
643 
644 static int xf_input_touch_remote(xfContext* xfc, XIDeviceEvent* event, int evtype)
645 {
646  int x = 0;
647  int y = 0;
648  int touchId = 0;
649  RdpeiClientContext* rdpei = xfc->common.rdpei;
650 
651  if (!rdpei)
652  return 0;
653 
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);
659 
660  switch (evtype)
661  {
662  case XI_TouchBegin:
663  freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_DOWN, touchId, 0, x, y);
664  break;
665  case XI_TouchUpdate:
666  freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_MOTION, touchId, 0, x, y);
667  break;
668  case XI_TouchEnd:
669  freerdp_client_handle_touch(&xfc->common, FREERDP_TOUCH_UP, touchId, 0, x, y);
670  break;
671  default:
672  break;
673  }
674 
675  return 0;
676 }
677 
678 static BOOL xf_input_pen_remote(xfContext* xfc, XIDeviceEvent* event, int evtype, int deviceid)
679 {
680  int x = 0;
681  int y = 0;
682  RdpeiClientContext* rdpei = xfc->common.rdpei;
683 
684  if (!rdpei)
685  return FALSE;
686 
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);
691 
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++)
695  {
696  if (XIMaskIsSet(event->valuators.mask, i))
697  {
698  double value = *val++;
699  if (i == 2)
700  pressure = value;
701  }
702  }
703 
704  UINT32 flags = FREERDP_PEN_HAS_PRESSURE;
705  if ((evtype == XI_ButtonPress) || (evtype == XI_ButtonRelease))
706  {
707  WLog_DBG(TAG, "pen button %d", event->detail);
708  switch (event->detail)
709  {
710  case 1:
711  break;
712  case 3:
713  flags |= FREERDP_PEN_BARREL_PRESSED;
714  break;
715  default:
716  return FALSE;
717  }
718  }
719 
720  switch (evtype)
721  {
722  case XI_ButtonPress:
723  flags |= FREERDP_PEN_PRESS;
724  if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
725  return FALSE;
726  break;
727  case XI_Motion:
728  flags |= FREERDP_PEN_MOTION;
729  if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
730  return FALSE;
731  break;
732  case XI_ButtonRelease:
733  flags |= FREERDP_PEN_RELEASE;
734  if (!freerdp_client_handle_pen(&xfc->common, flags, deviceid, x, y, pressure))
735  return FALSE;
736  break;
737  default:
738  break;
739  }
740  return TRUE;
741 }
742 
743 static int xf_input_pens_unhover(xfContext* xfc)
744 {
745  WINPR_ASSERT(xfc);
746 
747  freerdp_client_pen_cancel_all(&xfc->common);
748  return 0;
749 }
750 
751 int xf_input_event(xfContext* xfc, const XEvent* xevent, XIDeviceEvent* event, int evtype)
752 {
753  const rdpSettings* settings = NULL;
754 
755  WINPR_ASSERT(xfc);
756  WINPR_ASSERT(xevent);
757  WINPR_ASSERT(event);
758 
759  settings = xfc->common.context.settings;
760  WINPR_ASSERT(settings);
761 
762  xfWindow* window = xfc->window;
763  if (window)
764  {
765  if (xf_floatbar_is_locked(window->floatbar))
766  return 0;
767  }
768 
769  xf_input_show_cursor(xfc);
770 
771  switch (evtype)
772  {
773  case XI_ButtonPress:
774  case XI_ButtonRelease:
775  xfc->xi_event = !xfc->common.mouse_grabbed ||
776  !freerdp_client_use_relative_mouse_events(&xfc->common);
777 
778  if (xfc->xi_event)
779  {
780  xf_generic_ButtonEvent(xfc, (int)event->event_x, (int)event->event_y, event->detail,
781  event->event, xfc->remote_app, evtype == XI_ButtonPress);
782  }
783  break;
784 
785  case XI_Motion:
786  xfc->xi_event = !xfc->common.mouse_grabbed ||
787  !freerdp_client_use_relative_mouse_events(&xfc->common);
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 =
798  xfc->common.mouse_grabbed && freerdp_client_use_relative_mouse_events(&xfc->common);
799 
800  if (xfc->xi_rawevent)
801  {
802  const XIRawEvent* ev = (const XIRawEvent*)event;
803  xf_generic_RawButtonEvent(xfc, ev->detail, xfc->remote_app,
804  evtype == XI_RawButtonPress);
805  }
806  break;
807  case XI_RawMotion:
808  xfc->xi_rawevent =
809  xfc->common.mouse_grabbed && freerdp_client_use_relative_mouse_events(&xfc->common);
810 
811  if (xfc->xi_rawevent)
812  {
813  const XIRawEvent* ev = (const XIRawEvent*)event;
814  double x = 0.0;
815  double y = 0.0;
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];
820 
821  xf_generic_RawMotionNotify(xfc, (int)x, (int)y, event->event, xfc->remote_app);
822  }
823  break;
824  case XI_DeviceChanged:
825  {
826  const XIDeviceChangedEvent* ev = (const XIDeviceChangedEvent*)event;
827  if (ev->reason != XIDeviceChange)
828  break;
829 
830  /*
831  * TODO:
832  * 1. Register only changed devices.
833  * 2. Both `XIDeviceChangedEvent` and `XIHierarchyEvent` have no target
834  * `Window` which is used to register xinput events. So assume
835  * `xfc->window` created by `xf_CreateDesktopWindow` is the same
836  * `Window` we registered.
837  */
838  if (xfc->window)
839  register_input_events(xfc, xfc->window->handle);
840  }
841  break;
842  case XI_HierarchyChanged:
843  if (xfc->window)
844  register_input_events(xfc, xfc->window->handle);
845  break;
846 
847  default:
848  WLog_WARN(TAG, "Unhandled event %d: Event was registered but is not handled!", evtype);
849  break;
850  }
851 
852  return 0;
853 }
854 
855 static int xf_input_handle_event_remote(xfContext* xfc, const XEvent* event)
856 {
857  union
858  {
859  const XGenericEventCookie* cc;
860  XGenericEventCookie* vc;
861  } cookie;
862  cookie.cc = &event->xcookie;
863  XGetEventData(xfc->display, cookie.vc);
864 
865  if ((cookie.cc->type == GenericEvent) && (cookie.cc->extension == xfc->XInputOpcode))
866  {
867  switch (cookie.cc->evtype)
868  {
869  case XI_TouchBegin:
870  xf_input_pens_unhover(xfc);
871  /* fallthrough */
872  WINPR_FALLTHROUGH
873  case XI_TouchUpdate:
874  case XI_TouchEnd:
875  xf_input_touch_remote(xfc, cookie.cc->data, cookie.cc->evtype);
876  break;
877  case XI_ButtonPress:
878  case XI_Motion:
879  case XI_ButtonRelease:
880  {
881  WLog_DBG(TAG, "checking for pen");
882  XIDeviceEvent* deviceEvent = (XIDeviceEvent*)cookie.cc->data;
883  int deviceid = deviceEvent->deviceid;
884 
885  if (freerdp_client_is_pen(&xfc->common, deviceid))
886  {
887  if (!xf_input_pen_remote(xfc, cookie.cc->data, cookie.cc->evtype, deviceid))
888  {
889  // XXX: don't show cursor
890  xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
891  }
892  break;
893  }
894  }
895  /* fallthrough */
896  WINPR_FALLTHROUGH
897  default:
898  xf_input_pens_unhover(xfc);
899  xf_input_event(xfc, event, cookie.cc->data, cookie.cc->evtype);
900  break;
901  }
902  }
903 
904  XFreeEventData(xfc->display, cookie.vc);
905  return 0;
906 }
907 
908 #else
909 
910 int xf_input_init(xfContext* xfc, Window window)
911 {
912  return 0;
913 }
914 
915 #endif
916 
917 int xf_input_handle_event(xfContext* xfc, const XEvent* event)
918 {
919 #ifdef WITH_XI
920  const rdpSettings* settings = NULL;
921  WINPR_ASSERT(xfc);
922 
923  settings = xfc->common.context.settings;
924  WINPR_ASSERT(settings);
925 
926  if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchInput))
927  {
928  return xf_input_handle_event_remote(xfc, event);
929  }
930  else if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
931  {
932  return xf_input_handle_event_local(xfc, event);
933  }
934  else
935  {
936  return xf_input_handle_event_local(xfc, event);
937  }
938 
939 #else
940  return 0;
941 #endif
942 }
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.