SDL_x11xinput2.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2024 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. #ifdef SDL_VIDEO_DRIVER_X11
  20. #include "SDL_x11pen.h"
  21. #include "SDL_x11video.h"
  22. #include "SDL_x11xinput2.h"
  23. #include "../../events/SDL_events_c.h"
  24. #include "../../events/SDL_mouse_c.h"
  25. #include "../../events/SDL_pen_c.h"
  26. #include "../../events/SDL_touch_c.h"
  27. #define MAX_AXIS 16
  28. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
  29. static int xinput2_initialized = 0;
  30. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  31. static int xinput2_multitouch_supported = 0;
  32. #endif
  33. /* Opcode returned X11_XQueryExtension
  34. * It will be used in event processing
  35. * to know that the event came from
  36. * this extension */
  37. static int xinput2_opcode;
  38. static void parse_valuators(const double *input_values, const unsigned char *mask, int mask_len,
  39. double *output_values, int output_values_len)
  40. {
  41. int i = 0, z = 0;
  42. int top = mask_len * 8;
  43. if (top > MAX_AXIS) {
  44. top = MAX_AXIS;
  45. }
  46. SDL_memset(output_values, 0, output_values_len * sizeof(double));
  47. for (; i < top && z < output_values_len; i++) {
  48. if (XIMaskIsSet(mask, i)) {
  49. const int value = (int)*input_values;
  50. output_values[z] = value;
  51. input_values++;
  52. }
  53. z++;
  54. }
  55. }
  56. static int query_xinput2_version(Display *display, int major, int minor)
  57. {
  58. /* We don't care if this fails, so long as it sets major/minor on it's way out the door. */
  59. X11_XIQueryVersion(display, &major, &minor);
  60. return (major * 1000) + minor;
  61. }
  62. static SDL_bool xinput2_version_atleast(const int version, const int wantmajor, const int wantminor)
  63. {
  64. return version >= ((wantmajor * 1000) + wantminor);
  65. }
  66. static SDL_WindowData *xinput2_get_sdlwindowdata(SDL_VideoData *videodata, Window window)
  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. return d;
  73. }
  74. }
  75. return NULL;
  76. }
  77. static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window)
  78. {
  79. const SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, window);
  80. return windowdata ? windowdata->window : NULL;
  81. }
  82. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  83. static void xinput2_normalize_touch_coordinates(SDL_Window *window, double in_x, double in_y, float *out_x, float *out_y)
  84. {
  85. if (window) {
  86. if (window->w == 1) {
  87. *out_x = 0.5f;
  88. } else {
  89. *out_x = in_x / (window->w - 1);
  90. }
  91. if (window->h == 1) {
  92. *out_y = 0.5f;
  93. } else {
  94. *out_y = in_y / (window->h - 1);
  95. }
  96. } else {
  97. // couldn't find the window...
  98. *out_x = in_x;
  99. *out_y = in_y;
  100. }
  101. }
  102. #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */
  103. #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
  104. SDL_bool X11_InitXinput2(SDL_VideoDevice *_this)
  105. {
  106. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
  107. SDL_VideoData *data = _this->driverdata;
  108. int version = 0;
  109. XIEventMask eventmask;
  110. unsigned char mask[4] = { 0, 0, 0, 0 };
  111. int event, err;
  112. /*
  113. * Initialize XInput 2
  114. * According to http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html its better
  115. * to inform Xserver what version of Xinput we support.The server will store the version we support.
  116. * "As XI2 progresses it becomes important that you use this call as the server may treat the client
  117. * differently depending on the supported version".
  118. *
  119. * FIXME:event and err are not needed but if not passed X11_XQueryExtension returns SegmentationFault
  120. */
  121. if (!SDL_X11_HAVE_XINPUT2 ||
  122. !X11_XQueryExtension(data->display, "XInputExtension", &xinput2_opcode, &event, &err)) {
  123. return SDL_FALSE; /* X server does not have XInput at all */
  124. }
  125. /* We need at least 2.2 for Multitouch, 2.0 otherwise. */
  126. version = query_xinput2_version(data->display, 2, 2);
  127. if (!xinput2_version_atleast(version, 2, 0)) {
  128. return SDL_FALSE; /* X server does not support the version we want at all. */
  129. }
  130. xinput2_initialized = 1;
  131. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH /* Multitouch needs XInput 2.2 */
  132. xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2);
  133. #endif
  134. /* Enable raw motion events for this display */
  135. SDL_zero(eventmask);
  136. SDL_zeroa(mask);
  137. eventmask.deviceid = XIAllMasterDevices;
  138. eventmask.mask_len = sizeof(mask);
  139. eventmask.mask = mask;
  140. XISetMask(mask, XI_RawMotion);
  141. XISetMask(mask, XI_RawButtonPress);
  142. XISetMask(mask, XI_RawButtonRelease);
  143. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  144. /* Enable raw touch events if supported */
  145. if (X11_Xinput2IsMultitouchSupported()) {
  146. XISetMask(mask, XI_RawTouchBegin);
  147. XISetMask(mask, XI_RawTouchUpdate);
  148. XISetMask(mask, XI_RawTouchEnd);
  149. }
  150. #endif
  151. X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1);
  152. SDL_zero(eventmask);
  153. SDL_zeroa(mask);
  154. eventmask.deviceid = XIAllDevices;
  155. eventmask.mask_len = sizeof(mask);
  156. eventmask.mask = mask;
  157. XISetMask(mask, XI_HierarchyChanged);
  158. X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1);
  159. X11_Xinput2UpdateDevices(_this, SDL_TRUE);
  160. return SDL_TRUE;
  161. #else
  162. return SDL_FALSE;
  163. #endif
  164. }
  165. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
  166. /* xi2 device went away? take it out of the list. */
  167. static void xinput2_remove_device_info(SDL_VideoData *videodata, const int device_id)
  168. {
  169. SDL_XInput2DeviceInfo *prev = NULL;
  170. SDL_XInput2DeviceInfo *devinfo;
  171. for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) {
  172. if (devinfo->device_id == device_id) {
  173. SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL));
  174. if (!prev) {
  175. videodata->mouse_device_info = devinfo->next;
  176. } else {
  177. prev->next = devinfo->next;
  178. }
  179. SDL_free(devinfo);
  180. return;
  181. }
  182. prev = devinfo;
  183. }
  184. }
  185. static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata, const int device_id)
  186. {
  187. /* cache device info as we see new devices. */
  188. SDL_XInput2DeviceInfo *prev = NULL;
  189. SDL_XInput2DeviceInfo *devinfo;
  190. XIDeviceInfo *xidevinfo;
  191. int axis = 0;
  192. int i;
  193. for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) {
  194. if (devinfo->device_id == device_id) {
  195. SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL));
  196. if (prev) { /* move this to the front of the list, assuming we'll get more from this one. */
  197. prev->next = devinfo->next;
  198. devinfo->next = videodata->mouse_device_info;
  199. videodata->mouse_device_info = devinfo;
  200. }
  201. return devinfo;
  202. }
  203. prev = devinfo;
  204. }
  205. /* don't know about this device yet, query and cache it. */
  206. devinfo = (SDL_XInput2DeviceInfo *)SDL_calloc(1, sizeof(SDL_XInput2DeviceInfo));
  207. if (!devinfo) {
  208. return NULL;
  209. }
  210. xidevinfo = X11_XIQueryDevice(videodata->display, device_id, &i);
  211. if (!xidevinfo) {
  212. SDL_free(devinfo);
  213. return NULL;
  214. }
  215. devinfo->device_id = device_id;
  216. /* !!! FIXME: this is sort of hacky because we only care about the first two axes we see, but any given
  217. !!! FIXME: axis could be relative or absolute, and they might not even be the X and Y axes!
  218. !!! FIXME: But we go on, for now. Maybe we need a more robust mouse API in SDL3... */
  219. for (i = 0; i < xidevinfo->num_classes; i++) {
  220. const XIValuatorClassInfo *v = (const XIValuatorClassInfo *)xidevinfo->classes[i];
  221. if (v->type == XIValuatorClass) {
  222. devinfo->relative[axis] = (v->mode == XIModeRelative);
  223. devinfo->minval[axis] = v->min;
  224. devinfo->maxval[axis] = v->max;
  225. if (++axis >= 2) {
  226. break;
  227. }
  228. }
  229. }
  230. X11_XIFreeDeviceInfo(xidevinfo);
  231. devinfo->next = videodata->mouse_device_info;
  232. videodata->mouse_device_info = devinfo;
  233. return devinfo;
  234. }
  235. static void xinput2_pen_ensure_window(SDL_VideoDevice *_this, const SDL_Pen *pen, Window window)
  236. {
  237. /* When "flipping" a Wacom eraser pen, we get an XI_DeviceChanged event
  238. * with the newly-activated pen, but this event is global for the display.
  239. * We won't get a window until the pen starts triggering motion or
  240. * button events, so we instead hook the pen to its window at that point. */
  241. const SDL_WindowData *windowdata = X11_FindWindow(_this, window);
  242. if (windowdata) {
  243. SDL_SendPenWindowEvent(0, pen->header.id, windowdata->window);
  244. }
  245. }
  246. #endif
  247. void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
  248. {
  249. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
  250. SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
  251. if (cookie->extension != xinput2_opcode) {
  252. return;
  253. }
  254. switch (cookie->evtype) {
  255. case XI_HierarchyChanged:
  256. {
  257. const XIHierarchyEvent *hierev = (const XIHierarchyEvent *)cookie->data;
  258. int i;
  259. for (i = 0; i < hierev->num_info; i++) {
  260. if (hierev->info[i].flags & XISlaveRemoved) {
  261. xinput2_remove_device_info(videodata, hierev->info[i].deviceid);
  262. }
  263. }
  264. videodata->xinput_hierarchy_changed = SDL_TRUE;
  265. } break;
  266. case XI_PropertyEvent:
  267. case XI_DeviceChanged:
  268. {
  269. // FIXME: We shouldn't rescan all devices for pen changes every time a property or active slave changes
  270. X11_InitPen(_this);
  271. } break;
  272. case XI_Enter:
  273. case XI_Leave:
  274. {
  275. const XIEnterEvent *enterev = (const XIEnterEvent *)cookie->data;
  276. const SDL_WindowData *windowdata = X11_FindWindow(_this, enterev->event);
  277. const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(enterev->sourceid));
  278. SDL_Window *window = (windowdata && (cookie->evtype == XI_Enter)) ? windowdata->window : NULL;
  279. if (pen) {
  280. SDL_SendPenWindowEvent(0, pen->header.id, window);
  281. }
  282. } break;
  283. case XI_RawMotion:
  284. {
  285. const XIRawEvent *rawev = (const XIRawEvent *)cookie->data;
  286. const SDL_bool is_pen = X11_PenIDFromDeviceID(rawev->sourceid) != SDL_PEN_INVALID;
  287. SDL_Mouse *mouse = SDL_GetMouse();
  288. SDL_XInput2DeviceInfo *devinfo;
  289. double coords[2];
  290. double processed_coords[2];
  291. int i;
  292. videodata->global_mouse_changed = SDL_TRUE;
  293. if (is_pen) {
  294. break; /* Pens check for XI_Motion instead */
  295. }
  296. /* Non-pen: */
  297. if (!mouse->relative_mode || mouse->relative_mode_warp) {
  298. break;
  299. }
  300. /* Relative mouse motion is delivered to the window with keyboard focus */
  301. if (!SDL_GetKeyboardFocus()) {
  302. break;
  303. }
  304. devinfo = xinput2_get_device_info(videodata, rawev->deviceid);
  305. if (!devinfo) {
  306. break; /* oh well. */
  307. }
  308. parse_valuators(rawev->raw_values, rawev->valuators.mask,
  309. rawev->valuators.mask_len, coords, 2);
  310. for (i = 0; i < 2; i++) {
  311. if (devinfo->relative[i]) {
  312. processed_coords[i] = coords[i];
  313. } else {
  314. processed_coords[i] = devinfo->prev_coords[i] - coords[i]; /* convert absolute to relative */
  315. }
  316. }
  317. SDL_SendMouseMotion(0, mouse->focus, (SDL_MouseID)rawev->sourceid, SDL_TRUE, (float)processed_coords[0], (float)processed_coords[1]);
  318. devinfo->prev_coords[0] = coords[0];
  319. devinfo->prev_coords[1] = coords[1];
  320. } break;
  321. case XI_KeyPress:
  322. case XI_KeyRelease:
  323. {
  324. const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
  325. SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event);
  326. XEvent xevent;
  327. if (xev->deviceid != xev->sourceid) {
  328. /* Discard events from "Master" devices to avoid duplicates. */
  329. break;
  330. }
  331. if (cookie->evtype == XI_KeyPress) {
  332. xevent.type = KeyPress;
  333. } else {
  334. xevent.type = KeyRelease;
  335. }
  336. xevent.xkey.serial = xev->serial;
  337. xevent.xkey.send_event = xev->send_event;
  338. xevent.xkey.display = xev->display;
  339. xevent.xkey.window = xev->event;
  340. xevent.xkey.root = xev->root;
  341. xevent.xkey.subwindow = xev->child;
  342. xevent.xkey.time = xev->time;
  343. xevent.xkey.x = xev->event_x;
  344. xevent.xkey.y = xev->event_y;
  345. xevent.xkey.x_root = xev->root_x;
  346. xevent.xkey.y_root = xev->root_y;
  347. xevent.xkey.state = xev->mods.effective;
  348. xevent.xkey.keycode = xev->detail;
  349. xevent.xkey.same_screen = 1;
  350. X11_HandleKeyEvent(_this, windowdata, (SDL_KeyboardID)xev->sourceid, &xevent);
  351. } break;
  352. case XI_RawButtonPress:
  353. case XI_RawButtonRelease:
  354. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  355. case XI_RawTouchBegin:
  356. case XI_RawTouchUpdate:
  357. case XI_RawTouchEnd:
  358. #endif
  359. {
  360. videodata->global_mouse_changed = SDL_TRUE;
  361. } break;
  362. case XI_ButtonPress:
  363. case XI_ButtonRelease:
  364. {
  365. const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
  366. const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(xev->deviceid));
  367. const int button = xev->detail;
  368. const SDL_bool pressed = (cookie->evtype == XI_ButtonPress) ? SDL_TRUE : SDL_FALSE;
  369. if (pen) {
  370. xinput2_pen_ensure_window(_this, pen, xev->event);
  371. /* Only report button event; if there was also pen movement / pressure changes, we expect
  372. an XI_Motion event first anyway */
  373. if (button == 1) {
  374. /* button 1 is the pen tip */
  375. if (pressed && SDL_PenPerformHitTest()) {
  376. /* Check whether we should handle window resize / move events */
  377. SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event);
  378. if (windowdata && X11_TriggerHitTestAction(_this, windowdata, pen->last.x, pen->last.y)) {
  379. SDL_SendWindowEvent(windowdata->window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
  380. break; /* Don't pass on this event */
  381. }
  382. }
  383. SDL_SendPenTipEvent(0, pen->header.id,
  384. pressed ? SDL_PRESSED : SDL_RELEASED);
  385. } else {
  386. SDL_SendPenButton(0, pen->header.id,
  387. pressed ? SDL_PRESSED : SDL_RELEASED,
  388. button - 1);
  389. }
  390. } else {
  391. /* Otherwise assume a regular mouse */
  392. SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, xev->event);
  393. if (xev->deviceid != xev->sourceid) {
  394. /* Discard events from "Master" devices to avoid duplicates. */
  395. break;
  396. }
  397. if (pressed) {
  398. X11_HandleButtonPress(_this, windowdata, (SDL_MouseID)xev->sourceid, button,
  399. xev->event_x, xev->event_y, xev->time);
  400. } else {
  401. X11_HandleButtonRelease(_this, windowdata, (SDL_MouseID)xev->sourceid, button);
  402. }
  403. }
  404. } break;
  405. /* Register to receive XI_Motion (which deactivates MotionNotify), so that we can distinguish
  406. real mouse motions from synthetic ones, for multitouch and pen support. */
  407. case XI_Motion:
  408. {
  409. const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
  410. const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(xev->deviceid));
  411. #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  412. SDL_bool pointer_emulated = ((xev->flags & XIPointerEmulated) != 0);
  413. #else
  414. SDL_bool pointer_emulated = SDL_FALSE;
  415. #endif
  416. videodata->global_mouse_changed = SDL_TRUE;
  417. if (xev->deviceid != xev->sourceid) {
  418. /* Discard events from "Master" devices to avoid duplicates. */
  419. break;
  420. }
  421. if (pen) {
  422. SDL_PenStatusInfo pen_status;
  423. pen_status.x = xev->event_x;
  424. pen_status.y = xev->event_y;
  425. X11_PenAxesFromValuators(pen,
  426. xev->valuators.values, xev->valuators.mask, xev->valuators.mask_len,
  427. &pen_status.axes[0]);
  428. xinput2_pen_ensure_window(_this, pen, xev->event);
  429. SDL_SendPenMotion(0, pen->header.id,
  430. SDL_TRUE,
  431. &pen_status);
  432. break;
  433. }
  434. if (!pointer_emulated) {
  435. SDL_Mouse *mouse = SDL_GetMouse();
  436. if (!mouse->relative_mode || mouse->relative_mode_warp) {
  437. SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
  438. if (window) {
  439. X11_ProcessHitTest(_this, window->driverdata, (float)xev->event_x, (float)xev->event_y, SDL_FALSE);
  440. SDL_SendMouseMotion(0, window, (SDL_MouseID)xev->sourceid, SDL_FALSE, (float)xev->event_x, (float)xev->event_y);
  441. }
  442. }
  443. }
  444. } break;
  445. #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  446. case XI_TouchBegin:
  447. {
  448. const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
  449. float x, y;
  450. SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
  451. xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
  452. SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_TRUE, x, y, 1.0);
  453. } break;
  454. case XI_TouchEnd:
  455. {
  456. const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
  457. float x, y;
  458. SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
  459. xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
  460. SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_FALSE, x, y, 1.0);
  461. } break;
  462. case XI_TouchUpdate:
  463. {
  464. const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
  465. float x, y;
  466. SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
  467. xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
  468. SDL_SendTouchMotion(0, xev->sourceid, xev->detail, window, x, y, 1.0);
  469. } break;
  470. #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */
  471. }
  472. #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
  473. }
  474. void X11_InitXinput2Multitouch(SDL_VideoDevice *_this)
  475. {
  476. }
  477. void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window)
  478. {
  479. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  480. SDL_VideoData *data = NULL;
  481. XIEventMask eventmask;
  482. unsigned char mask[4] = { 0, 0, 0, 0 };
  483. SDL_WindowData *window_data = NULL;
  484. if (!X11_Xinput2IsMultitouchSupported()) {
  485. return;
  486. }
  487. data = _this->driverdata;
  488. window_data = window->driverdata;
  489. eventmask.deviceid = XIAllMasterDevices;
  490. eventmask.mask_len = sizeof(mask);
  491. eventmask.mask = mask;
  492. XISetMask(mask, XI_TouchBegin);
  493. XISetMask(mask, XI_TouchUpdate);
  494. XISetMask(mask, XI_TouchEnd);
  495. XISetMask(mask, XI_Motion);
  496. X11_XISelectEvents(data->display, window_data->xwindow, &eventmask, 1);
  497. #endif
  498. }
  499. int X11_Xinput2IsInitialized(void)
  500. {
  501. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
  502. return xinput2_initialized;
  503. #else
  504. return 0;
  505. #endif
  506. }
  507. SDL_bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
  508. {
  509. SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata;
  510. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
  511. const SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
  512. if (X11_Xinput2IsInitialized()) {
  513. XIEventMask eventmask;
  514. unsigned char mask[4] = { 0, 0, 0, 0 };
  515. eventmask.mask_len = sizeof(mask);
  516. eventmask.mask = mask;
  517. eventmask.deviceid = XIAllDevices;
  518. /* This is not enabled by default because these events are only delivered to the window with mouse focus, not keyboard focus */
  519. #ifdef USE_XINPUT2_KEYBOARD
  520. XISetMask(mask, XI_KeyPress);
  521. XISetMask(mask, XI_KeyRelease);
  522. windowdata->xinput2_keyboard_enabled = SDL_TRUE;
  523. #endif
  524. XISetMask(mask, XI_ButtonPress);
  525. XISetMask(mask, XI_ButtonRelease);
  526. XISetMask(mask, XI_Motion);
  527. windowdata->xinput2_mouse_enabled = SDL_TRUE;
  528. XISetMask(mask, XI_Enter);
  529. XISetMask(mask, XI_Leave);
  530. /* Hotplugging: */
  531. XISetMask(mask, XI_DeviceChanged);
  532. XISetMask(mask, XI_HierarchyChanged);
  533. XISetMask(mask, XI_PropertyEvent); /* E.g., when swapping tablet pens */
  534. if (X11_XISelectEvents(data->display, windowdata->xwindow, &eventmask, 1) != Success) {
  535. SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Could not enable XInput2 event handling\n");
  536. windowdata->xinput2_keyboard_enabled = SDL_FALSE;
  537. windowdata->xinput2_mouse_enabled = SDL_FALSE;
  538. }
  539. }
  540. #endif
  541. if (windowdata->xinput2_keyboard_enabled || windowdata->xinput2_mouse_enabled) {
  542. return SDL_TRUE;
  543. }
  544. return SDL_FALSE;
  545. }
  546. int X11_Xinput2IsMultitouchSupported(void)
  547. {
  548. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  549. return xinput2_initialized && xinput2_multitouch_supported;
  550. #else
  551. return 0;
  552. #endif
  553. }
  554. void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window)
  555. {
  556. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  557. SDL_WindowData *data = window->driverdata;
  558. Display *display = data->videodata->display;
  559. unsigned char mask[4] = { 0, 0, 0, 0 };
  560. XIGrabModifiers mods;
  561. XIEventMask eventmask;
  562. if (!X11_Xinput2IsMultitouchSupported()) {
  563. return;
  564. }
  565. mods.modifiers = XIAnyModifier;
  566. mods.status = 0;
  567. eventmask.deviceid = XIAllDevices;
  568. eventmask.mask_len = sizeof(mask);
  569. eventmask.mask = mask;
  570. XISetMask(eventmask.mask, XI_TouchBegin);
  571. XISetMask(eventmask.mask, XI_TouchUpdate);
  572. XISetMask(eventmask.mask, XI_TouchEnd);
  573. XISetMask(eventmask.mask, XI_Motion);
  574. X11_XIGrabTouchBegin(display, XIAllDevices, data->xwindow, True, &eventmask, 1, &mods);
  575. #endif
  576. }
  577. void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window)
  578. {
  579. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  580. SDL_WindowData *data = window->driverdata;
  581. Display *display = data->videodata->display;
  582. XIGrabModifiers mods;
  583. if (!X11_Xinput2IsMultitouchSupported()) {
  584. return;
  585. }
  586. mods.modifiers = XIAnyModifier;
  587. mods.status = 0;
  588. X11_XIUngrabTouchBegin(display, XIAllDevices, data->xwindow, 1, &mods);
  589. #endif
  590. }
  591. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
  592. static void AddDeviceID(Uint32 deviceID, Uint32 **list, int *count)
  593. {
  594. int new_count = (*count + 1);
  595. Uint32 *new_list = (Uint32 *)SDL_realloc(*list, new_count * sizeof(*new_list));
  596. if (!new_list) {
  597. /* Oh well, we'll drop this one */
  598. return;
  599. }
  600. new_list[new_count - 1] = deviceID;
  601. *count = new_count;
  602. *list = new_list;
  603. }
  604. static SDL_bool HasDeviceID(Uint32 deviceID, Uint32 *list, int count)
  605. {
  606. for (int i = 0; i < count; ++i) {
  607. if (deviceID == list[i]) {
  608. return SDL_TRUE;
  609. }
  610. }
  611. return SDL_FALSE;
  612. }
  613. #endif // SDL_VIDEO_DRIVER_X11_XINPUT2
  614. void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, SDL_bool initial_check)
  615. {
  616. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
  617. SDL_VideoData *data = _this->driverdata;
  618. XIDeviceInfo *info;
  619. int ndevices;
  620. int old_keyboard_count = 0;
  621. SDL_KeyboardID *old_keyboards = NULL;
  622. int new_keyboard_count = 0;
  623. SDL_KeyboardID *new_keyboards = NULL;
  624. int old_mouse_count = 0;
  625. SDL_MouseID *old_mice = NULL;
  626. int new_mouse_count = 0;
  627. SDL_MouseID *new_mice = NULL;
  628. int old_touch_count = 0;
  629. SDL_TouchID *old_touch_devices64 = NULL;
  630. Uint32 *old_touch_devices = NULL;
  631. int new_touch_count = 0;
  632. Uint32 *new_touch_devices = NULL;
  633. SDL_bool send_event = !initial_check;
  634. SDL_assert(X11_Xinput2IsInitialized());
  635. info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices);
  636. old_keyboards = SDL_GetKeyboards(&old_keyboard_count);
  637. old_mice = SDL_GetMice(&old_mouse_count);
  638. /* SDL_TouchID is 64-bit, but our helper functions take Uint32 */
  639. old_touch_devices64 = SDL_GetTouchDevices(&old_touch_count);
  640. if (old_touch_count > 0) {
  641. old_touch_devices = (Uint32 *)SDL_malloc(old_touch_count * sizeof(*old_touch_devices));
  642. if (old_touch_devices) {
  643. for (int i = 0; i < old_touch_count; ++i) {
  644. old_touch_devices[i] = (Uint32)old_touch_devices64[i];
  645. }
  646. }
  647. }
  648. SDL_free(old_touch_devices64);
  649. for (int i = 0; i < ndevices; i++) {
  650. XIDeviceInfo *dev = &info[i];
  651. switch (dev->use) {
  652. case XIMasterKeyboard:
  653. case XISlaveKeyboard:
  654. {
  655. SDL_KeyboardID keyboardID = (SDL_KeyboardID)dev->deviceid;
  656. AddDeviceID(keyboardID, &new_keyboards, &new_keyboard_count);
  657. if (!HasDeviceID(keyboardID, old_keyboards, old_keyboard_count)) {
  658. SDL_AddKeyboard(keyboardID, dev->name, send_event);
  659. }
  660. }
  661. break;
  662. case XIMasterPointer:
  663. case XISlavePointer:
  664. {
  665. SDL_MouseID mouseID = (SDL_MouseID)dev->deviceid;
  666. AddDeviceID(mouseID, &new_mice, &new_mouse_count);
  667. if (!HasDeviceID(mouseID, old_mice, old_mouse_count)) {
  668. SDL_AddMouse(mouseID, dev->name, send_event);
  669. }
  670. }
  671. break;
  672. default:
  673. break;
  674. }
  675. #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  676. for (int j = 0; j < dev->num_classes; j++) {
  677. Uint32 touchID;
  678. SDL_TouchDeviceType touchType;
  679. XIAnyClassInfo *class = dev->classes[j];
  680. XITouchClassInfo *t = (XITouchClassInfo *)class;
  681. /* Only touch devices */
  682. if (class->type != XITouchClass) {
  683. continue;
  684. }
  685. touchID = (Uint32)t->sourceid;
  686. AddDeviceID(touchID, &new_touch_devices, &new_touch_count);
  687. if (!HasDeviceID(touchID, old_touch_devices, old_touch_count)) {
  688. if (t->mode == XIDependentTouch) {
  689. touchType = SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
  690. } else { /* XIDirectTouch */
  691. touchType = SDL_TOUCH_DEVICE_DIRECT;
  692. }
  693. SDL_AddTouch(touchID, touchType, dev->name);
  694. }
  695. }
  696. #endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
  697. }
  698. for (int i = old_keyboard_count; i--;) {
  699. if (!HasDeviceID(old_keyboards[i], new_keyboards, new_keyboard_count)) {
  700. SDL_RemoveKeyboard(old_keyboards[i]);
  701. }
  702. }
  703. for (int i = old_mouse_count; i--;) {
  704. if (!HasDeviceID(old_mice[i], new_mice, new_mouse_count)) {
  705. SDL_RemoveMouse(old_mice[i]);
  706. }
  707. }
  708. for (int i = old_touch_count; i--;) {
  709. if (!HasDeviceID(old_touch_devices[i], new_touch_devices, new_touch_count)) {
  710. SDL_DelTouch(old_touch_devices[i]);
  711. }
  712. }
  713. SDL_free(old_keyboards);
  714. SDL_free(new_keyboards);
  715. SDL_free(old_mice);
  716. SDL_free(new_mice);
  717. SDL_free(old_touch_devices);
  718. SDL_free(new_touch_devices);
  719. X11_XIFreeDeviceInfo(info);
  720. #endif // SDL_VIDEO_DRIVER_X11_XINPUT2
  721. }
  722. #endif /* SDL_VIDEO_DRIVER_X11 */