You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

touch.cxx 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /* Copyright 2019-2020 Pierre Ossman <ossman@cendio.se> for Cendio AB
  2. * Copyright 2019 Aaron Sowry for Cendio AB
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include <config.h>
  21. #endif
  22. #include <assert.h>
  23. #include <map>
  24. #if !defined(WIN32) && !defined(__APPLE__)
  25. #include <X11/extensions/XInput2.h>
  26. #include <X11/extensions/XI2.h>
  27. #endif
  28. #include <FL/Fl.H>
  29. #include <FL/x.H>
  30. #include <rfb/LogWriter.h>
  31. #include "i18n.h"
  32. #include "vncviewer.h"
  33. #if !defined(WIN32) && !defined(__APPLE__)
  34. #include "XInputTouchHandler.h"
  35. #endif
  36. #include "touch.h"
  37. static rfb::LogWriter vlog("Touch");
  38. #if !defined(WIN32) && !defined(__APPLE__)
  39. static int xi_major;
  40. typedef std::map<Window, class XInputTouchHandler*> HandlerMap;
  41. static HandlerMap handlers;
  42. #endif
  43. #if !defined(WIN32) && !defined(__APPLE__)
  44. static void x11_change_touch_ownership(bool enable)
  45. {
  46. HandlerMap::const_iterator iter;
  47. XIEventMask *curmasks;
  48. int num_masks;
  49. XIEventMask newmask;
  50. unsigned char mask[XIMaskLen(XI_LASTEVENT)] = { 0 };
  51. newmask.mask = mask;
  52. newmask.mask_len = sizeof(mask);
  53. for (iter = handlers.begin(); iter != handlers.end(); ++iter) {
  54. curmasks = XIGetSelectedEvents(fl_display, iter->first, &num_masks);
  55. if (curmasks == NULL) {
  56. if (num_masks == -1)
  57. vlog.error(_("Unable to get X Input 2 event mask for window 0x%08lu"), iter->first);
  58. continue;
  59. }
  60. // Our windows should only have a single mask, which allows us to
  61. // simplify all the code handling the masks
  62. if (num_masks > 1) {
  63. vlog.error(_("Window 0x%08lu has more than one X Input 2 event mask"), iter->first);
  64. continue;
  65. }
  66. newmask.deviceid = curmasks[0].deviceid;
  67. assert(newmask.mask_len >= curmasks[0].mask_len);
  68. memcpy(newmask.mask, curmasks[0].mask, curmasks[0].mask_len);
  69. if (enable)
  70. XISetMask(newmask.mask, XI_TouchOwnership);
  71. else
  72. XIClearMask(newmask.mask, XI_TouchOwnership);
  73. XISelectEvents(fl_display, iter->first, &newmask, 1);
  74. XFree(curmasks);
  75. }
  76. }
  77. bool x11_grab_pointer(Window window)
  78. {
  79. bool ret;
  80. if (handlers.count(window) == 0) {
  81. vlog.error(_("Invalid window 0x%08lu specified for pointer grab"), window);
  82. return BadWindow;
  83. }
  84. // We need to remove XI_TouchOwnership from our event masks while
  85. // grabbing as otherwise we will get double events (one for the grab,
  86. // and one because of XI_TouchOwnership) with no way of telling them
  87. // apart. See XInputTouchHandler constructor for why we use this
  88. // event.
  89. x11_change_touch_ownership(false);
  90. ret = handlers[window]->grabPointer();
  91. if (!ret)
  92. x11_change_touch_ownership(true);
  93. return ret;
  94. }
  95. void x11_ungrab_pointer(Window window)
  96. {
  97. if (handlers.count(window) == 0) {
  98. vlog.error(_("Invalid window 0x%08lu specified for pointer grab"), window);
  99. return;
  100. }
  101. handlers[window]->ungrabPointer();
  102. // Restore XI_TouchOwnership now that the grab is gone
  103. x11_change_touch_ownership(true);
  104. }
  105. #endif
  106. static int handleTouchEvent(void *event, void *data)
  107. {
  108. #if !defined(WIN32) && !defined(__APPLE__)
  109. XEvent *xevent = (XEvent*)event;
  110. if (xevent->type == MapNotify) {
  111. handlers[xevent->xmap.window] = new XInputTouchHandler(xevent->xmap.window);
  112. // Fall through as we don't want to interfere with whatever someone
  113. // else might want to do with this event
  114. } else if (xevent->type == UnmapNotify) {
  115. delete handlers[xevent->xunmap.window];
  116. handlers.erase(xevent->xunmap.window);
  117. } else if (xevent->type == DestroyNotify) {
  118. delete handlers[xevent->xdestroywindow.window];
  119. handlers.erase(xevent->xdestroywindow.window);
  120. } else if (xevent->type == GenericEvent) {
  121. if (xevent->xgeneric.extension == xi_major) {
  122. XIDeviceEvent *devev;
  123. if (!XGetEventData(fl_display, &xevent->xcookie)) {
  124. vlog.error(_("Failed to get event data for X Input event"));
  125. return 1;
  126. }
  127. devev = (XIDeviceEvent*)xevent->xcookie.data;
  128. if (handlers.count(devev->event) == 0) {
  129. // We get these when the mouse is grabbed implicitly, so just
  130. // ignore them
  131. // https://gitlab.freedesktop.org/xorg/xserver/-/issues/1026
  132. if ((devev->evtype == XI_Enter) || (devev->evtype == XI_Leave))
  133. ;
  134. else
  135. vlog.error(_("X Input event for unknown window"));
  136. XFreeEventData(fl_display, &xevent->xcookie);
  137. return 1;
  138. }
  139. handlers[devev->event]->processEvent(devev);
  140. XFreeEventData(fl_display, &xevent->xcookie);
  141. return 1;
  142. }
  143. }
  144. #endif
  145. return 0;
  146. }
  147. void enable_touch()
  148. {
  149. #if !defined(WIN32) && !defined(__APPLE__)
  150. int ev, err;
  151. int major_ver, minor_ver;
  152. fl_open_display();
  153. if (!XQueryExtension(fl_display, "XInputExtension", &xi_major, &ev, &err)) {
  154. exit_vncviewer(_("X Input extension not available."));
  155. return; // Not reached
  156. }
  157. major_ver = 2;
  158. minor_ver = 2;
  159. if (XIQueryVersion(fl_display, &major_ver, &minor_ver) != Success) {
  160. exit_vncviewer(_("X Input 2 (or newer) is not available."));
  161. return; // Not reached
  162. }
  163. if ((major_ver == 2) && (minor_ver < 2))
  164. vlog.error(_("X Input 2.2 (or newer) is not available. Touch gestures will not be supported."));
  165. #endif
  166. Fl::add_system_handler(handleTouchEvent, NULL);
  167. }
  168. void disable_touch()
  169. {
  170. Fl::remove_system_handler(handleTouchEvent);
  171. }