SDL_x11xinput2.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "../../SDL_internal.h"
  19. #if SDL_VIDEO_DRIVER_X11
  20. #include "SDL_x11video.h"
  21. #include "SDL_x11xinput2.h"
  22. #include "../../events/SDL_mouse_c.h"
  23. #include "../../events/SDL_touch_c.h"
  24. #define MAX_AXIS 16
  25. #if SDL_VIDEO_DRIVER_X11_XINPUT2
  26. static int xinput2_initialized = 0;
  27. #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  28. static int xinput2_multitouch_supported = 0;
  29. #endif
  30. /* Opcode returned X11_XQueryExtension
  31. * It will be used in event processing
  32. * to know that the event came from
  33. * this extension */
  34. static int xinput2_opcode;
  35. static void parse_valuators(const double *input_values,unsigned char *mask,int mask_len,
  36. double *output_values,int output_values_len) {
  37. int i = 0,z = 0;
  38. int top = mask_len * 8;
  39. if (top > MAX_AXIS)
  40. top = MAX_AXIS;
  41. SDL_memset(output_values,0,output_values_len * sizeof(double));
  42. for (; i < top && z < output_values_len; i++) {
  43. if (XIMaskIsSet(mask, i)) {
  44. const int value = (int) *input_values;
  45. output_values[z] = value;
  46. input_values++;
  47. }
  48. z++;
  49. }
  50. }
  51. static int
  52. query_xinput2_version(Display *display, int major, int minor)
  53. {
  54. /* We don't care if this fails, so long as it sets major/minor on it's way out the door. */
  55. X11_XIQueryVersion(display, &major, &minor);
  56. return ((major * 1000) + minor);
  57. }
  58. static SDL_bool
  59. xinput2_version_atleast(const int version, const int wantmajor, const int wantminor)
  60. {
  61. return ( version >= ((wantmajor * 1000) + wantminor) );
  62. }
  63. #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  64. static void
  65. xinput2_normalize_touch_coordinates(SDL_VideoData *videodata, Window window,
  66. double in_x, double in_y, float *out_x, float *out_y)
  67. {
  68. int i;
  69. for (i = 0; i < videodata->numwindows; i++) {
  70. SDL_WindowData *d = videodata->windowlist[i];
  71. if (d->xwindow == window) {
  72. *out_x = in_x / d->window->w;
  73. *out_y = in_y / d->window->h;
  74. return;
  75. }
  76. }
  77. // couldn't find the window...
  78. *out_x = in_x;
  79. *out_y = in_y;
  80. }
  81. #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */
  82. #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
  83. void
  84. X11_InitXinput2(_THIS)
  85. {
  86. #if SDL_VIDEO_DRIVER_X11_XINPUT2
  87. SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
  88. int version = 0;
  89. XIEventMask eventmask;
  90. unsigned char mask[3] = { 0,0,0 };
  91. int event, err;
  92. /*
  93. * Initialize XInput 2
  94. * According to http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html its better
  95. * to inform Xserver what version of Xinput we support.The server will store the version we support.
  96. * "As XI2 progresses it becomes important that you use this call as the server may treat the client
  97. * differently depending on the supported version".
  98. *
  99. * FIXME:event and err are not needed but if not passed X11_XQueryExtension returns SegmentationFault
  100. */
  101. if (!SDL_X11_HAVE_XINPUT2 ||
  102. !X11_XQueryExtension(data->display, "XInputExtension", &xinput2_opcode, &event, &err)) {
  103. return; /* X server does not have XInput at all */
  104. }
  105. /* We need at least 2.2 for Multitouch, 2.0 otherwise. */
  106. version = query_xinput2_version(data->display, 2, 2);
  107. if (!xinput2_version_atleast(version, 2, 0)) {
  108. return; /* X server does not support the version we want at all. */
  109. }
  110. xinput2_initialized = 1;
  111. #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH /* Multitouch needs XInput 2.2 */
  112. xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2);
  113. #endif
  114. /* Enable Raw motion events for this display */
  115. eventmask.deviceid = XIAllMasterDevices;
  116. eventmask.mask_len = sizeof(mask);
  117. eventmask.mask = mask;
  118. XISetMask(mask, XI_RawMotion);
  119. XISetMask(mask, XI_RawButtonPress);
  120. XISetMask(mask, XI_RawButtonRelease);
  121. if (X11_XISelectEvents(data->display,DefaultRootWindow(data->display),&eventmask,1) != Success) {
  122. return;
  123. }
  124. #endif
  125. }
  126. int
  127. X11_HandleXinput2Event(SDL_VideoData *videodata,XGenericEventCookie *cookie)
  128. {
  129. #if SDL_VIDEO_DRIVER_X11_XINPUT2
  130. if(cookie->extension != xinput2_opcode) {
  131. return 0;
  132. }
  133. switch(cookie->evtype) {
  134. case XI_RawMotion: {
  135. const XIRawEvent *rawev = (const XIRawEvent*)cookie->data;
  136. SDL_Mouse *mouse = SDL_GetMouse();
  137. double relative_coords[2];
  138. static Time prev_time = 0;
  139. static double prev_rel_coords[2];
  140. videodata->global_mouse_changed = SDL_TRUE;
  141. if (!mouse->relative_mode || mouse->relative_mode_warp) {
  142. return 0;
  143. }
  144. parse_valuators(rawev->raw_values,rawev->valuators.mask,
  145. rawev->valuators.mask_len,relative_coords,2);
  146. if ((rawev->time == prev_time) && (relative_coords[0] == prev_rel_coords[0]) && (relative_coords[1] == prev_rel_coords[1])) {
  147. return 0; /* duplicate event, drop it. */
  148. }
  149. SDL_SendMouseMotion(mouse->focus,mouse->mouseID,1,(int)relative_coords[0],(int)relative_coords[1]);
  150. prev_rel_coords[0] = relative_coords[0];
  151. prev_rel_coords[1] = relative_coords[1];
  152. prev_time = rawev->time;
  153. return 1;
  154. }
  155. break;
  156. case XI_RawButtonPress:
  157. case XI_RawButtonRelease:
  158. videodata->global_mouse_changed = SDL_TRUE;
  159. break;
  160. #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  161. case XI_TouchBegin: {
  162. const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
  163. float x, y;
  164. xinput2_normalize_touch_coordinates(videodata, xev->event,
  165. xev->event_x, xev->event_y, &x, &y);
  166. SDL_SendTouch(xev->sourceid,xev->detail, SDL_TRUE, x, y, 1.0);
  167. return 1;
  168. }
  169. break;
  170. case XI_TouchEnd: {
  171. const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
  172. float x, y;
  173. xinput2_normalize_touch_coordinates(videodata, xev->event,
  174. xev->event_x, xev->event_y, &x, &y);
  175. SDL_SendTouch(xev->sourceid,xev->detail, SDL_FALSE, x, y, 1.0);
  176. return 1;
  177. }
  178. break;
  179. case XI_TouchUpdate: {
  180. const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
  181. float x, y;
  182. xinput2_normalize_touch_coordinates(videodata, xev->event,
  183. xev->event_x, xev->event_y, &x, &y);
  184. SDL_SendTouchMotion(xev->sourceid,xev->detail, x, y, 1.0);
  185. return 1;
  186. }
  187. break;
  188. #endif
  189. }
  190. #endif
  191. return 0;
  192. }
  193. void
  194. X11_InitXinput2Multitouch(_THIS)
  195. {
  196. #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  197. SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
  198. XIDeviceInfo *info;
  199. int ndevices,i,j;
  200. info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices);
  201. for (i = 0; i < ndevices; i++) {
  202. XIDeviceInfo *dev = &info[i];
  203. for (j = 0; j < dev->num_classes; j++) {
  204. SDL_TouchID touchId;
  205. XIAnyClassInfo *class = dev->classes[j];
  206. XITouchClassInfo *t = (XITouchClassInfo*)class;
  207. /* Only touch devices */
  208. if (class->type != XITouchClass)
  209. continue;
  210. touchId = t->sourceid;
  211. SDL_AddTouch(touchId, dev->name);
  212. }
  213. }
  214. X11_XIFreeDeviceInfo(info);
  215. #endif
  216. }
  217. void
  218. X11_Xinput2SelectTouch(_THIS, SDL_Window *window)
  219. {
  220. #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  221. SDL_VideoData *data = NULL;
  222. XIEventMask eventmask;
  223. unsigned char mask[3] = { 0,0,0 };
  224. SDL_WindowData *window_data = NULL;
  225. if (!X11_Xinput2IsMultitouchSupported()) {
  226. return;
  227. }
  228. data = (SDL_VideoData *) _this->driverdata;
  229. window_data = (SDL_WindowData*)window->driverdata;
  230. eventmask.deviceid = XIAllMasterDevices;
  231. eventmask.mask_len = sizeof(mask);
  232. eventmask.mask = mask;
  233. XISetMask(mask, XI_TouchBegin);
  234. XISetMask(mask, XI_TouchUpdate);
  235. XISetMask(mask, XI_TouchEnd);
  236. X11_XISelectEvents(data->display,window_data->xwindow,&eventmask,1);
  237. #endif
  238. }
  239. int
  240. X11_Xinput2IsInitialized()
  241. {
  242. #if SDL_VIDEO_DRIVER_X11_XINPUT2
  243. return xinput2_initialized;
  244. #else
  245. return 0;
  246. #endif
  247. }
  248. int
  249. X11_Xinput2IsMultitouchSupported()
  250. {
  251. #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  252. return xinput2_initialized && xinput2_multitouch_supported;
  253. #else
  254. return 0;
  255. #endif
  256. }
  257. #endif /* SDL_VIDEO_DRIVER_X11 */
  258. /* vi: set ts=4 sw=4 expandtab: */