SDL_x11events.c 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2023 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 <sys/types.h>
  21. #include <sys/time.h>
  22. #include <signal.h>
  23. #include <unistd.h>
  24. #include <limits.h> /* For INT_MAX */
  25. #include "SDL_x11video.h"
  26. #include "SDL_x11touch.h"
  27. #include "SDL_x11xinput2.h"
  28. #include "SDL_x11xfixes.h"
  29. #include "../../core/unix/SDL_poll.h"
  30. #include "../../events/SDL_events_c.h"
  31. #include "../../events/SDL_mouse_c.h"
  32. #include "../../events/SDL_touch_c.h"
  33. #include "../../core/linux/SDL_system_theme.h"
  34. #include <SDL3/SDL_syswm.h>
  35. #include <stdio.h>
  36. /*#define DEBUG_XEVENTS*/
  37. #ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT
  38. #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
  39. #endif
  40. #ifndef _NET_WM_MOVERESIZE_SIZE_TOP
  41. #define _NET_WM_MOVERESIZE_SIZE_TOP 1
  42. #endif
  43. #ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT
  44. #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
  45. #endif
  46. #ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT
  47. #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
  48. #endif
  49. #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
  50. #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
  51. #endif
  52. #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM
  53. #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
  54. #endif
  55. #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
  56. #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
  57. #endif
  58. #ifndef _NET_WM_MOVERESIZE_SIZE_LEFT
  59. #define _NET_WM_MOVERESIZE_SIZE_LEFT 7
  60. #endif
  61. #ifndef _NET_WM_MOVERESIZE_MOVE
  62. #define _NET_WM_MOVERESIZE_MOVE 8
  63. #endif
  64. typedef struct
  65. {
  66. unsigned char *data;
  67. int format, count;
  68. Atom type;
  69. } SDL_x11Prop;
  70. /* Reads property
  71. Must call X11_XFree on results
  72. */
  73. static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop)
  74. {
  75. unsigned char *ret = NULL;
  76. Atom type;
  77. int fmt;
  78. unsigned long count;
  79. unsigned long bytes_left;
  80. int bytes_fetch = 0;
  81. do {
  82. if (ret != NULL) {
  83. X11_XFree(ret);
  84. }
  85. X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
  86. bytes_fetch += bytes_left;
  87. } while (bytes_left != 0);
  88. p->data = ret;
  89. p->format = fmt;
  90. p->count = count;
  91. p->type = type;
  92. }
  93. /* Find text-uri-list in a list of targets and return it's atom
  94. if available, else return None */
  95. static Atom X11_PickTarget(Display *disp, Atom list[], int list_count)
  96. {
  97. Atom request = None;
  98. char *name;
  99. int i;
  100. for (i = 0; i < list_count && request == None; i++) {
  101. name = X11_XGetAtomName(disp, list[i]);
  102. if ((SDL_strcmp("text/uri-list", name) == 0) || (SDL_strcmp("text/plain", name) == 0)) {
  103. request = list[i];
  104. }
  105. X11_XFree(name);
  106. }
  107. return request;
  108. }
  109. /* Wrapper for X11_PickTarget for a maximum of three targets, a special
  110. case in the Xdnd protocol */
  111. static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2)
  112. {
  113. int count = 0;
  114. Atom atom[3];
  115. if (a0 != None) {
  116. atom[count++] = a0;
  117. }
  118. if (a1 != None) {
  119. atom[count++] = a1;
  120. }
  121. if (a2 != None) {
  122. atom[count++] = a2;
  123. }
  124. return X11_PickTarget(disp, atom, count);
  125. }
  126. struct KeyRepeatCheckData
  127. {
  128. XEvent *event;
  129. SDL_bool found;
  130. };
  131. static Bool X11_KeyRepeatCheckIfEvent(Display *display, XEvent *chkev,
  132. XPointer arg)
  133. {
  134. struct KeyRepeatCheckData *d = (struct KeyRepeatCheckData *)arg;
  135. if (chkev->type == KeyPress && chkev->xkey.keycode == d->event->xkey.keycode && chkev->xkey.time - d->event->xkey.time < 2) {
  136. d->found = SDL_TRUE;
  137. }
  138. return False;
  139. }
  140. /* Check to see if this is a repeated key.
  141. (idea shamelessly lifted from GII -- thanks guys! :)
  142. */
  143. static SDL_bool X11_KeyRepeat(Display *display, XEvent *event)
  144. {
  145. XEvent dummyev;
  146. struct KeyRepeatCheckData d;
  147. d.event = event;
  148. d.found = SDL_FALSE;
  149. if (X11_XPending(display)) {
  150. X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent, (XPointer)&d);
  151. }
  152. return d.found;
  153. }
  154. static SDL_bool X11_IsWheelEvent(Display *display, XEvent *event, int *xticks, int *yticks)
  155. {
  156. /* according to the xlib docs, no specific mouse wheel events exist.
  157. However, the defacto standard is that the vertical wheel is X buttons
  158. 4 (up) and 5 (down) and a horizontal wheel is 6 (left) and 7 (right). */
  159. /* Xlib defines "Button1" through 5, so we just use literals here. */
  160. switch (event->xbutton.button) {
  161. case 4:
  162. *yticks = 1;
  163. return SDL_TRUE;
  164. case 5:
  165. *yticks = -1;
  166. return SDL_TRUE;
  167. case 6:
  168. *xticks = 1;
  169. return SDL_TRUE;
  170. case 7:
  171. *xticks = -1;
  172. return SDL_TRUE;
  173. default:
  174. break;
  175. }
  176. return SDL_FALSE;
  177. }
  178. /* Decodes URI escape sequences in string buf of len bytes
  179. (excluding the terminating NULL byte) in-place. Since
  180. URI-encoded characters take three times the space of
  181. normal characters, this should not be an issue.
  182. Returns the number of decoded bytes that wound up in
  183. the buffer, excluding the terminating NULL byte.
  184. The buffer is guaranteed to be NULL-terminated but
  185. may contain embedded NULL bytes.
  186. On error, -1 is returned.
  187. */
  188. static int X11_URIDecode(char *buf, int len)
  189. {
  190. int ri, wi, di;
  191. char decode = '\0';
  192. if (buf == NULL || len < 0) {
  193. errno = EINVAL;
  194. return -1;
  195. }
  196. if (len == 0) {
  197. len = SDL_strlen(buf);
  198. }
  199. for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) {
  200. if (di == 0) {
  201. /* start decoding */
  202. if (buf[ri] == '%') {
  203. decode = '\0';
  204. di += 1;
  205. continue;
  206. }
  207. /* normal write */
  208. buf[wi] = buf[ri];
  209. wi += 1;
  210. continue;
  211. } else if (di == 1 || di == 2) {
  212. char off = '\0';
  213. char isa = buf[ri] >= 'a' && buf[ri] <= 'f';
  214. char isA = buf[ri] >= 'A' && buf[ri] <= 'F';
  215. char isn = buf[ri] >= '0' && buf[ri] <= '9';
  216. if (!(isa || isA || isn)) {
  217. /* not a hexadecimal */
  218. int sri;
  219. for (sri = ri - di; sri <= ri; sri += 1) {
  220. buf[wi] = buf[sri];
  221. wi += 1;
  222. }
  223. di = 0;
  224. continue;
  225. }
  226. /* itsy bitsy magicsy */
  227. if (isn) {
  228. off = 0 - '0';
  229. } else if (isa) {
  230. off = 10 - 'a';
  231. } else if (isA) {
  232. off = 10 - 'A';
  233. }
  234. decode |= (buf[ri] + off) << (2 - di) * 4;
  235. if (di == 2) {
  236. buf[wi] = decode;
  237. wi += 1;
  238. di = 0;
  239. } else {
  240. di += 1;
  241. }
  242. continue;
  243. }
  244. }
  245. buf[wi] = '\0';
  246. return wi;
  247. }
  248. /* Convert URI to local filename
  249. return filename if possible, else NULL
  250. */
  251. static char *X11_URIToLocal(char *uri)
  252. {
  253. char *file = NULL;
  254. SDL_bool local;
  255. if (SDL_memcmp(uri, "file:/", 6) == 0) {
  256. uri += 6; /* local file? */
  257. } else if (SDL_strstr(uri, ":/") != NULL) {
  258. return file; /* wrong scheme */
  259. }
  260. local = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/');
  261. /* got a hostname? */
  262. if (!local && uri[0] == '/' && uri[2] != '/') {
  263. char *hostname_end = SDL_strchr(uri + 1, '/');
  264. if (hostname_end != NULL) {
  265. char hostname[257];
  266. if (gethostname(hostname, 255) == 0) {
  267. hostname[256] = '\0';
  268. if (SDL_memcmp(uri + 1, hostname, hostname_end - (uri + 1)) == 0) {
  269. uri = hostname_end + 1;
  270. local = SDL_TRUE;
  271. }
  272. }
  273. }
  274. }
  275. if (local) {
  276. file = uri;
  277. /* Convert URI escape sequences to real characters */
  278. X11_URIDecode(file, 0);
  279. if (uri[1] == '/') {
  280. file++;
  281. } else {
  282. file--;
  283. }
  284. }
  285. return file;
  286. }
  287. #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
  288. static void X11_HandleGenericEvent(SDL_VideoData *videodata, XEvent *xev)
  289. {
  290. /* event is a union, so cookie == &event, but this is type safe. */
  291. XGenericEventCookie *cookie = &xev->xcookie;
  292. if (X11_XGetEventData(videodata->display, cookie)) {
  293. X11_HandleXinput2Event(videodata, cookie);
  294. /* Send a SDL_EVENT_SYSWM if the application wants them.
  295. * Since event data is only available until XFreeEventData is called,
  296. * the *only* way for an application to access it is to register an event filter/watcher
  297. * and do all the processing on the SDL_EVENT_SYSWM inside the callback. */
  298. if (SDL_EventEnabled(SDL_EVENT_SYSWM)) {
  299. SDL_SysWMmsg wmmsg;
  300. wmmsg.version = SDL_SYSWM_CURRENT_VERSION;
  301. wmmsg.subsystem = SDL_SYSWM_X11;
  302. wmmsg.msg.x11.event = *xev;
  303. SDL_SendSysWMEvent(&wmmsg);
  304. }
  305. X11_XFreeEventData(videodata->display, cookie);
  306. }
  307. }
  308. #endif /* SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS */
  309. static unsigned X11_GetNumLockModifierMask(_THIS)
  310. {
  311. SDL_VideoData *viddata = _this->driverdata;
  312. Display *display = viddata->display;
  313. unsigned num_mask = 0;
  314. int i, j;
  315. XModifierKeymap *xmods;
  316. unsigned n;
  317. xmods = X11_XGetModifierMapping(display);
  318. n = xmods->max_keypermod;
  319. for (i = 3; i < 8; i++) {
  320. for (j = 0; j < n; j++) {
  321. KeyCode kc = xmods->modifiermap[i * n + j];
  322. if (viddata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) {
  323. num_mask = 1 << i;
  324. break;
  325. }
  326. }
  327. }
  328. X11_XFreeModifiermap(xmods);
  329. return num_mask;
  330. }
  331. static unsigned X11_GetScrollLockModifierMask(_THIS)
  332. {
  333. SDL_VideoData *viddata = _this->driverdata;
  334. Display *display = viddata->display;
  335. unsigned num_mask = 0;
  336. int i, j;
  337. XModifierKeymap *xmods;
  338. unsigned n;
  339. xmods = X11_XGetModifierMapping(display);
  340. n = xmods->max_keypermod;
  341. for (i = 3; i < 8; i++) {
  342. for (j = 0; j < n; j++) {
  343. KeyCode kc = xmods->modifiermap[i * n + j];
  344. if (viddata->key_layout[kc] == SDL_SCANCODE_SCROLLLOCK) {
  345. num_mask = 1 << i;
  346. break;
  347. }
  348. }
  349. }
  350. X11_XFreeModifiermap(xmods);
  351. return num_mask;
  352. }
  353. void X11_ReconcileKeyboardState(_THIS)
  354. {
  355. SDL_VideoData *viddata = _this->driverdata;
  356. Display *display = viddata->display;
  357. char keys[32];
  358. int keycode;
  359. Window junk_window;
  360. int x, y;
  361. unsigned int mask;
  362. const Uint8 *keyboardState;
  363. X11_XQueryKeymap(display, keys);
  364. /* Sync up the keyboard modifier state */
  365. if (X11_XQueryPointer(display, DefaultRootWindow(display), &junk_window, &junk_window, &x, &y, &x, &y, &mask)) {
  366. SDL_ToggleModState(SDL_KMOD_CAPS, (mask & LockMask) ? SDL_TRUE : SDL_FALSE);
  367. SDL_ToggleModState(SDL_KMOD_NUM, (mask & X11_GetNumLockModifierMask(_this)) ? SDL_TRUE : SDL_FALSE);
  368. SDL_ToggleModState(SDL_KMOD_SCROLL, (mask & X11_GetScrollLockModifierMask(_this)) ? SDL_TRUE : SDL_FALSE);
  369. }
  370. keyboardState = SDL_GetKeyboardState(0);
  371. for (keycode = 0; keycode < SDL_arraysize(viddata->key_layout); ++keycode) {
  372. SDL_Scancode scancode = viddata->key_layout[keycode];
  373. SDL_bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0;
  374. SDL_bool sdlKeyPressed = keyboardState[scancode] == SDL_PRESSED;
  375. if (x11KeyPressed && !sdlKeyPressed) {
  376. /* Only update modifier state for keys that are pressed in another application */
  377. switch (SDL_GetKeyFromScancode(scancode)) {
  378. case SDLK_LCTRL:
  379. case SDLK_RCTRL:
  380. case SDLK_LSHIFT:
  381. case SDLK_RSHIFT:
  382. case SDLK_LALT:
  383. case SDLK_RALT:
  384. case SDLK_LGUI:
  385. case SDLK_RGUI:
  386. case SDLK_MODE:
  387. SDL_SendKeyboardKey(0, SDL_PRESSED, scancode);
  388. break;
  389. default:
  390. break;
  391. }
  392. } else if (!x11KeyPressed && sdlKeyPressed) {
  393. SDL_SendKeyboardKey(0, SDL_RELEASED, scancode);
  394. }
  395. }
  396. }
  397. static void X11_DispatchFocusIn(_THIS, SDL_WindowData *data)
  398. {
  399. #ifdef DEBUG_XEVENTS
  400. printf("window %p: Dispatching FocusIn\n", data);
  401. #endif
  402. SDL_SetKeyboardFocus(data->window);
  403. X11_ReconcileKeyboardState(_this);
  404. #ifdef X_HAVE_UTF8_STRING
  405. if (data->ic) {
  406. X11_XSetICFocus(data->ic);
  407. }
  408. #endif
  409. #ifdef SDL_USE_IME
  410. SDL_IME_SetFocus(SDL_TRUE);
  411. #endif
  412. if (data->flashing_window) {
  413. X11_FlashWindow(_this, data->window, SDL_FLASH_CANCEL);
  414. }
  415. }
  416. static void X11_DispatchFocusOut(_THIS, SDL_WindowData *data)
  417. {
  418. #ifdef DEBUG_XEVENTS
  419. printf("window %p: Dispatching FocusOut\n", data);
  420. #endif
  421. /* If another window has already processed a focus in, then don't try to
  422. * remove focus here. Doing so will incorrectly remove focus from that
  423. * window, and the focus lost event for this window will have already
  424. * been dispatched anyway. */
  425. if (data->window == SDL_GetKeyboardFocus()) {
  426. SDL_SetKeyboardFocus(NULL);
  427. }
  428. #ifdef X_HAVE_UTF8_STRING
  429. if (data->ic) {
  430. X11_XUnsetICFocus(data->ic);
  431. }
  432. #endif
  433. #ifdef SDL_USE_IME
  434. SDL_IME_SetFocus(SDL_FALSE);
  435. #endif
  436. }
  437. static void X11_DispatchMapNotify(SDL_WindowData *data)
  438. {
  439. SDL_Window *window = data->window;
  440. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
  441. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
  442. if (!(window->flags & SDL_WINDOW_HIDDEN) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  443. SDL_UpdateWindowGrab(window);
  444. }
  445. }
  446. static void X11_DispatchUnmapNotify(SDL_WindowData *data)
  447. {
  448. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
  449. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
  450. }
  451. static void InitiateWindowMove(_THIS, const SDL_WindowData *data, const SDL_Point *point)
  452. {
  453. SDL_VideoData *viddata = _this->driverdata;
  454. SDL_Window *window = data->window;
  455. Display *display = viddata->display;
  456. XEvent evt;
  457. /* !!! FIXME: we need to regrab this if necessary when the drag is done. */
  458. X11_XUngrabPointer(display, 0L);
  459. X11_XFlush(display);
  460. evt.xclient.type = ClientMessage;
  461. evt.xclient.window = data->xwindow;
  462. evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True);
  463. evt.xclient.format = 32;
  464. evt.xclient.data.l[0] = (size_t)window->x + point->x;
  465. evt.xclient.data.l[1] = (size_t)window->y + point->y;
  466. evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE;
  467. evt.xclient.data.l[3] = Button1;
  468. evt.xclient.data.l[4] = 0;
  469. X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
  470. X11_XSync(display, 0);
  471. }
  472. static void InitiateWindowResize(_THIS, const SDL_WindowData *data, const SDL_Point *point, int direction)
  473. {
  474. SDL_VideoData *viddata = _this->driverdata;
  475. SDL_Window *window = data->window;
  476. Display *display = viddata->display;
  477. XEvent evt;
  478. if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT) {
  479. return;
  480. }
  481. /* !!! FIXME: we need to regrab this if necessary when the drag is done. */
  482. X11_XUngrabPointer(display, 0L);
  483. X11_XFlush(display);
  484. evt.xclient.type = ClientMessage;
  485. evt.xclient.window = data->xwindow;
  486. evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True);
  487. evt.xclient.format = 32;
  488. evt.xclient.data.l[0] = (size_t)window->x + point->x;
  489. evt.xclient.data.l[1] = (size_t)window->y + point->y;
  490. evt.xclient.data.l[2] = direction;
  491. evt.xclient.data.l[3] = Button1;
  492. evt.xclient.data.l[4] = 0;
  493. X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
  494. X11_XSync(display, 0);
  495. }
  496. static SDL_bool ProcessHitTest(_THIS, const SDL_WindowData *data, const XEvent *xev)
  497. {
  498. SDL_Window *window = data->window;
  499. if (window->hit_test) {
  500. const SDL_Point point = { xev->xbutton.x, xev->xbutton.y };
  501. const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
  502. static const int directions[] = {
  503. _NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP,
  504. _NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT,
  505. _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, _NET_WM_MOVERESIZE_SIZE_BOTTOM,
  506. _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT
  507. };
  508. switch (rc) {
  509. case SDL_HITTEST_DRAGGABLE:
  510. InitiateWindowMove(_this, data, &point);
  511. return SDL_TRUE;
  512. case SDL_HITTEST_RESIZE_TOPLEFT:
  513. case SDL_HITTEST_RESIZE_TOP:
  514. case SDL_HITTEST_RESIZE_TOPRIGHT:
  515. case SDL_HITTEST_RESIZE_RIGHT:
  516. case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
  517. case SDL_HITTEST_RESIZE_BOTTOM:
  518. case SDL_HITTEST_RESIZE_BOTTOMLEFT:
  519. case SDL_HITTEST_RESIZE_LEFT:
  520. InitiateWindowResize(_this, data, &point, directions[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
  521. return SDL_TRUE;
  522. default:
  523. return SDL_FALSE;
  524. }
  525. }
  526. return SDL_FALSE;
  527. }
  528. static void X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest)
  529. {
  530. if (latest && (latest != data->user_time)) {
  531. SDL_VideoData *videodata = data->videodata;
  532. Display *display = videodata->display;
  533. X11_XChangeProperty(display, data->xwindow, videodata->_NET_WM_USER_TIME,
  534. XA_CARDINAL, 32, PropModeReplace,
  535. (const unsigned char *)&latest, 1);
  536. #ifdef DEBUG_XEVENTS
  537. printf("window %p: updating _NET_WM_USER_TIME to %lu\n", data, latest);
  538. #endif
  539. data->user_time = latest;
  540. }
  541. }
  542. static void X11_HandleClipboardEvent(_THIS, const XEvent *xevent)
  543. {
  544. int i;
  545. SDL_VideoData *videodata = _this->driverdata;
  546. Display *display = videodata->display;
  547. SDL_assert(videodata->clipboard_window != None);
  548. SDL_assert(xevent->xany.window == videodata->clipboard_window);
  549. switch (xevent->type) {
  550. /* Copy the selection from our own CUTBUFFER to the requested property */
  551. case SelectionRequest:
  552. {
  553. const XSelectionRequestEvent *req = &xevent->xselectionrequest;
  554. XEvent sevent;
  555. int seln_format, mime_formats;
  556. unsigned long nbytes;
  557. unsigned long overflow;
  558. unsigned char *seln_data;
  559. Atom supportedFormats[SDL_X11_CLIPBOARD_MIME_TYPE_MAX + 1];
  560. Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
  561. #ifdef DEBUG_XEVENTS
  562. printf("window CLIPBOARD: SelectionRequest (requestor = %ld, target = %ld)\n",
  563. req->requestor, req->target);
  564. #endif
  565. SDL_zero(sevent);
  566. sevent.xany.type = SelectionNotify;
  567. sevent.xselection.selection = req->selection;
  568. sevent.xselection.target = None;
  569. sevent.xselection.property = None; /* tell them no by default */
  570. sevent.xselection.requestor = req->requestor;
  571. sevent.xselection.time = req->time;
  572. /* !!! FIXME: We were probably storing this on the root window
  573. because an SDL window might go away...? but we don't have to do
  574. this now (or ever, really). */
  575. if (req->target == XA_TARGETS) {
  576. supportedFormats[0] = XA_TARGETS;
  577. mime_formats = 1;
  578. for (i = 0; i < SDL_X11_CLIPBOARD_MIME_TYPE_MAX; ++i) {
  579. supportedFormats[mime_formats++] = X11_GetSDLCutBufferClipboardExternalFormat(display, i);
  580. }
  581. X11_XChangeProperty(display, req->requestor, req->property,
  582. XA_ATOM, 32, PropModeReplace,
  583. (unsigned char *)supportedFormats,
  584. mime_formats);
  585. sevent.xselection.property = req->property;
  586. sevent.xselection.target = XA_TARGETS;
  587. } else {
  588. for (i = 0; i < SDL_X11_CLIPBOARD_MIME_TYPE_MAX; ++i) {
  589. if (X11_GetSDLCutBufferClipboardExternalFormat(display, i) != req->target) {
  590. continue;
  591. }
  592. if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
  593. X11_GetSDLCutBufferClipboardType(display, i, req->selection), 0, INT_MAX / 4, False, X11_GetSDLCutBufferClipboardInternalFormat(display, i),
  594. &sevent.xselection.target, &seln_format, &nbytes,
  595. &overflow, &seln_data) == Success) {
  596. if (seln_format != None) {
  597. X11_XChangeProperty(display, req->requestor, req->property,
  598. sevent.xselection.target, seln_format, PropModeReplace,
  599. seln_data, nbytes);
  600. sevent.xselection.property = req->property;
  601. X11_XFree(seln_data);
  602. break;
  603. } else {
  604. X11_XFree(seln_data);
  605. }
  606. }
  607. }
  608. }
  609. X11_XSendEvent(display, req->requestor, False, 0, &sevent);
  610. X11_XSync(display, False);
  611. } break;
  612. case SelectionNotify:
  613. {
  614. #ifdef DEBUG_XEVENTS
  615. printf("window CLIPBOARD: SelectionNotify (requestor = %ld, target = %ld)\n",
  616. xevent->xselection.requestor, xevent->xselection.target);
  617. #endif
  618. videodata->selection_waiting = SDL_FALSE;
  619. } break;
  620. case SelectionClear:
  621. {
  622. /* !!! FIXME: cache atoms */
  623. Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
  624. #ifdef DEBUG_XEVENTS
  625. printf("window CLIPBOARD: SelectionClear (requestor = %ld, target = %ld)\n",
  626. xevent->xselection.requestor, xevent->xselection.target);
  627. #endif
  628. if (xevent->xselectionclear.selection == XA_PRIMARY ||
  629. (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD)) {
  630. SDL_SendClipboardUpdate();
  631. }
  632. } break;
  633. }
  634. }
  635. static Bool isMapNotify(Display *display, XEvent *ev, XPointer arg)
  636. {
  637. XUnmapEvent *unmap;
  638. unmap = (XUnmapEvent *)arg;
  639. return ev->type == MapNotify &&
  640. ev->xmap.window == unmap->window &&
  641. ev->xmap.serial == unmap->serial;
  642. }
  643. static Bool isReparentNotify(Display *display, XEvent *ev, XPointer arg)
  644. {
  645. XUnmapEvent *unmap;
  646. unmap = (XUnmapEvent *)arg;
  647. return ev->type == ReparentNotify &&
  648. ev->xreparent.window == unmap->window &&
  649. ev->xreparent.serial == unmap->serial;
  650. }
  651. static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out)
  652. {
  653. int result = X11_XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out);
  654. if (result > 0) {
  655. char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", buffer_return, result);
  656. if (utf8_text) {
  657. SDL_strlcpy(buffer_return, utf8_text, bytes_buffer);
  658. SDL_free(utf8_text);
  659. return SDL_strlen(buffer_return);
  660. } else {
  661. return 0;
  662. }
  663. }
  664. return result;
  665. }
  666. static void X11_DispatchEvent(_THIS, XEvent *xevent)
  667. {
  668. SDL_VideoData *videodata = _this->driverdata;
  669. Display *display;
  670. SDL_WindowData *data;
  671. int orig_event_type;
  672. KeyCode orig_keycode;
  673. XClientMessageEvent m;
  674. int i;
  675. SDL_assert(videodata != NULL);
  676. display = videodata->display;
  677. /* Save the original keycode for dead keys, which are filtered out by
  678. the XFilterEvent() call below.
  679. */
  680. orig_event_type = xevent->type;
  681. if (orig_event_type == KeyPress || orig_event_type == KeyRelease) {
  682. orig_keycode = xevent->xkey.keycode;
  683. } else {
  684. orig_keycode = 0;
  685. }
  686. /* filter events catchs XIM events and sends them to the correct handler */
  687. if (X11_XFilterEvent(xevent, None) == True) {
  688. #if 0
  689. printf("Filtered event type = %d display = %d window = %d\n",
  690. xevent->type, xevent->xany.display, xevent->xany.window);
  691. #endif
  692. /* Make sure dead key press/release events are sent */
  693. /* But only if we're using one of the DBus IMEs, otherwise
  694. some XIM IMEs will generate duplicate events */
  695. if (orig_keycode) {
  696. #if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX)
  697. SDL_Scancode scancode = videodata->key_layout[orig_keycode];
  698. videodata->filter_code = orig_keycode;
  699. videodata->filter_time = xevent->xkey.time;
  700. if (orig_event_type == KeyPress) {
  701. SDL_SendKeyboardKey(0, SDL_PRESSED, scancode);
  702. } else {
  703. SDL_SendKeyboardKey(0, SDL_RELEASED, scancode);
  704. }
  705. #endif
  706. }
  707. return;
  708. }
  709. #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
  710. if (xevent->type == GenericEvent) {
  711. X11_HandleGenericEvent(videodata, xevent);
  712. return;
  713. }
  714. #endif
  715. #if SDL_VIDEO_DRIVER_X11_XRANDR
  716. if (videodata->xrandr_event_base && (xevent->type == (videodata->xrandr_event_base + RRNotify))) {
  717. X11_HandleXRandREvent(_this, xevent);
  718. }
  719. #endif
  720. /* Send a SDL_EVENT_SYSWM if the application wants them */
  721. if (SDL_EventEnabled(SDL_EVENT_SYSWM)) {
  722. SDL_SysWMmsg wmmsg;
  723. wmmsg.version = SDL_SYSWM_CURRENT_VERSION;
  724. wmmsg.subsystem = SDL_SYSWM_X11;
  725. wmmsg.msg.x11.event = *xevent;
  726. SDL_SendSysWMEvent(&wmmsg);
  727. }
  728. #if 0
  729. printf("type = %d display = %d window = %d\n",
  730. xevent->type, xevent->xany.display, xevent->xany.window);
  731. #endif
  732. if ((videodata->clipboard_window != None) &&
  733. (videodata->clipboard_window == xevent->xany.window)) {
  734. X11_HandleClipboardEvent(_this, xevent);
  735. return;
  736. }
  737. data = NULL;
  738. if (videodata && videodata->windowlist) {
  739. for (i = 0; i < videodata->numwindows; ++i) {
  740. if ((videodata->windowlist[i] != NULL) &&
  741. (videodata->windowlist[i]->xwindow == xevent->xany.window)) {
  742. data = videodata->windowlist[i];
  743. break;
  744. }
  745. }
  746. }
  747. if (data == NULL) {
  748. /* The window for KeymapNotify, etc events is 0 */
  749. if (xevent->type == KeymapNotify) {
  750. #ifdef DEBUG_XEVENTS
  751. printf("window %p: KeymapNotify!\n", data);
  752. #endif
  753. if (SDL_GetKeyboardFocus() != NULL) {
  754. X11_ReconcileKeyboardState(_this);
  755. }
  756. } else if (xevent->type == MappingNotify) {
  757. /* Has the keyboard layout changed? */
  758. const int request = xevent->xmapping.request;
  759. #ifdef DEBUG_XEVENTS
  760. printf("window %p: MappingNotify!\n", data);
  761. #endif
  762. if ((request == MappingKeyboard) || (request == MappingModifier)) {
  763. X11_XRefreshKeyboardMapping(&xevent->xmapping);
  764. }
  765. X11_UpdateKeymap(_this, SDL_TRUE);
  766. } else if (xevent->type == PropertyNotify && videodata && videodata->windowlist) {
  767. char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
  768. if (SDL_strncmp(name_of_atom, "_ICC_PROFILE", sizeof("_ICC_PROFILE") - 1) == 0) {
  769. XWindowAttributes attrib;
  770. int screennum;
  771. for (i = 0; i < videodata->numwindows; ++i) {
  772. if (videodata->windowlist[i] != NULL) {
  773. data = videodata->windowlist[i];
  774. X11_XGetWindowAttributes(display, data->xwindow, &attrib);
  775. screennum = X11_XScreenNumberOfScreen(attrib.screen);
  776. if (screennum == 0 && SDL_strcmp(name_of_atom, "_ICC_PROFILE") == 0) {
  777. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
  778. } else if (SDL_strncmp(name_of_atom, "_ICC_PROFILE_", sizeof("_ICC_PROFILE_") - 1) == 0 && SDL_strlen(name_of_atom) > sizeof("_ICC_PROFILE_") - 1) {
  779. int iccscreennum = SDL_atoi(&name_of_atom[sizeof("_ICC_PROFILE_") - 1]);
  780. if (screennum == iccscreennum) {
  781. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
  782. }
  783. }
  784. }
  785. }
  786. }
  787. if (name_of_atom) {
  788. X11_XFree(name_of_atom);
  789. }
  790. }
  791. return;
  792. }
  793. switch (xevent->type) {
  794. /* Gaining mouse coverage? */
  795. case EnterNotify:
  796. {
  797. SDL_Mouse *mouse = SDL_GetMouse();
  798. #ifdef DEBUG_XEVENTS
  799. printf("window %p: EnterNotify! (%d,%d,%d)\n", data,
  800. xevent->xcrossing.x,
  801. xevent->xcrossing.y,
  802. xevent->xcrossing.mode);
  803. if (xevent->xcrossing.mode == NotifyGrab) {
  804. printf("Mode: NotifyGrab\n");
  805. }
  806. if (xevent->xcrossing.mode == NotifyUngrab) {
  807. printf("Mode: NotifyUngrab\n");
  808. }
  809. #endif
  810. SDL_SetMouseFocus(data->window);
  811. mouse->last_x = xevent->xcrossing.x;
  812. mouse->last_y = xevent->xcrossing.y;
  813. #if SDL_VIDEO_DRIVER_X11_XFIXES
  814. {
  815. /* Only create the barriers if we have input focus */
  816. SDL_WindowData *windowdata = data->window->driverdata;
  817. if ((data->pointer_barrier_active == SDL_TRUE) && windowdata->window->flags & SDL_WINDOW_INPUT_FOCUS) {
  818. X11_ConfineCursorWithFlags(_this, windowdata->window, &windowdata->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
  819. }
  820. }
  821. #endif
  822. if (!mouse->relative_mode) {
  823. SDL_SendMouseMotion(0, data->window, 0, 0, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
  824. }
  825. /* We ungrab in LeaveNotify, so we may need to grab again here */
  826. SDL_UpdateWindowGrab(data->window);
  827. } break;
  828. /* Losing mouse coverage? */
  829. case LeaveNotify:
  830. {
  831. #ifdef DEBUG_XEVENTS
  832. printf("window %p: LeaveNotify! (%d,%d,%d)\n", data,
  833. xevent->xcrossing.x,
  834. xevent->xcrossing.y,
  835. xevent->xcrossing.mode);
  836. if (xevent->xcrossing.mode == NotifyGrab) {
  837. printf("Mode: NotifyGrab\n");
  838. }
  839. if (xevent->xcrossing.mode == NotifyUngrab) {
  840. printf("Mode: NotifyUngrab\n");
  841. }
  842. #endif
  843. if (!SDL_GetMouse()->relative_mode) {
  844. SDL_SendMouseMotion(0, data->window, 0, 0, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
  845. }
  846. if (xevent->xcrossing.mode != NotifyGrab &&
  847. xevent->xcrossing.mode != NotifyUngrab &&
  848. xevent->xcrossing.detail != NotifyInferior) {
  849. /* In order for interaction with the window decorations and menu to work properly
  850. on Mutter, we need to ungrab the keyboard when the the mouse leaves. */
  851. if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) {
  852. X11_SetWindowKeyboardGrab(_this, data->window, SDL_FALSE);
  853. }
  854. SDL_SetMouseFocus(NULL);
  855. }
  856. } break;
  857. /* Gaining input focus? */
  858. case FocusIn:
  859. {
  860. if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) {
  861. /* Someone is handling a global hotkey, ignore it */
  862. #ifdef DEBUG_XEVENTS
  863. printf("window %p: FocusIn (NotifyGrab/NotifyUngrab, ignoring)\n", data);
  864. #endif
  865. break;
  866. }
  867. if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) {
  868. #ifdef DEBUG_XEVENTS
  869. printf("window %p: FocusIn (NotifyInferior/NotifyPointer, ignoring)\n", data);
  870. #endif
  871. break;
  872. }
  873. #ifdef DEBUG_XEVENTS
  874. printf("window %p: FocusIn!\n", data);
  875. #endif
  876. if (!videodata->last_mode_change_deadline) /* no recent mode changes */ {
  877. data->pending_focus = PENDING_FOCUS_NONE;
  878. data->pending_focus_time = 0;
  879. X11_DispatchFocusIn(_this, data);
  880. } else {
  881. data->pending_focus = PENDING_FOCUS_IN;
  882. data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
  883. }
  884. data->last_focus_event_time = SDL_GetTicks();
  885. } break;
  886. /* Losing input focus? */
  887. case FocusOut:
  888. {
  889. if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) {
  890. /* Someone is handling a global hotkey, ignore it */
  891. #ifdef DEBUG_XEVENTS
  892. printf("window %p: FocusOut (NotifyGrab/NotifyUngrab, ignoring)\n", data);
  893. #endif
  894. break;
  895. }
  896. if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) {
  897. /* We still have focus if a child gets focus. We also don't
  898. care about the position of the pointer when the keyboard
  899. focus changed. */
  900. #ifdef DEBUG_XEVENTS
  901. printf("window %p: FocusOut (NotifyInferior/NotifyPointer, ignoring)\n", data);
  902. #endif
  903. break;
  904. }
  905. #ifdef DEBUG_XEVENTS
  906. printf("window %p: FocusOut!\n", data);
  907. #endif
  908. if (!videodata->last_mode_change_deadline) /* no recent mode changes */ {
  909. data->pending_focus = PENDING_FOCUS_NONE;
  910. data->pending_focus_time = 0;
  911. X11_DispatchFocusOut(_this, data);
  912. } else {
  913. data->pending_focus = PENDING_FOCUS_OUT;
  914. data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
  915. }
  916. #if SDL_VIDEO_DRIVER_X11_XFIXES
  917. /* Disable confinement if it is activated. */
  918. if (data->pointer_barrier_active == SDL_TRUE) {
  919. X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
  920. }
  921. #endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
  922. } break;
  923. /* Key press/release? */
  924. case KeyPress:
  925. case KeyRelease:
  926. {
  927. KeyCode keycode = xevent->xkey.keycode;
  928. KeySym keysym = NoSymbol;
  929. char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
  930. Status status = 0;
  931. SDL_bool handled_by_ime = SDL_FALSE;
  932. #ifdef DEBUG_XEVENTS
  933. printf("window %p: %s (X11 keycode = 0x%X)\n", data, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode);
  934. #endif
  935. #ifdef DEBUG_SCANCODES
  936. if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
  937. int min_keycode, max_keycode;
  938. X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
  939. keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13);
  940. SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n",
  941. keycode, keycode - min_keycode, keysym,
  942. X11_XKeysymToString(keysym));
  943. }
  944. #endif /* DEBUG SCANCODES */
  945. SDL_zeroa(text);
  946. #ifdef X_HAVE_UTF8_STRING
  947. if (data->ic && xevent->type == KeyPress) {
  948. X11_Xutf8LookupString(data->ic, &xevent->xkey, text, sizeof(text),
  949. &keysym, &status);
  950. } else {
  951. XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text), &keysym, NULL);
  952. }
  953. #else
  954. XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text), &keysym, NULL);
  955. #endif
  956. #ifdef SDL_USE_IME
  957. if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
  958. handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode, (xevent->type == KeyPress ? SDL_PRESSED : SDL_RELEASED));
  959. }
  960. #endif
  961. if (!handled_by_ime) {
  962. if (xevent->type == KeyPress) {
  963. /* Don't send the key if it looks like a duplicate of a filtered key sent by an IME */
  964. if (xevent->xkey.keycode != videodata->filter_code || xevent->xkey.time != videodata->filter_time) {
  965. SDL_SendKeyboardKey(0, SDL_PRESSED, videodata->key_layout[keycode]);
  966. }
  967. if (*text) {
  968. SDL_SendKeyboardText(text);
  969. }
  970. } else {
  971. if (X11_KeyRepeat(display, xevent)) {
  972. /* We're about to get a repeated key down, ignore the key up */
  973. break;
  974. }
  975. SDL_SendKeyboardKey(0, SDL_RELEASED, videodata->key_layout[keycode]);
  976. }
  977. }
  978. if (xevent->type == KeyPress) {
  979. X11_UpdateUserTime(data, xevent->xkey.time);
  980. }
  981. } break;
  982. /* Have we been iconified? */
  983. case UnmapNotify:
  984. {
  985. XEvent ev;
  986. #ifdef DEBUG_XEVENTS
  987. printf("window %p: UnmapNotify!\n", data);
  988. #endif
  989. if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent->xunmap)) {
  990. X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent->xunmap);
  991. } else {
  992. X11_DispatchUnmapNotify(data);
  993. }
  994. #if SDL_VIDEO_DRIVER_X11_XFIXES
  995. /* Disable confinement if the window gets hidden. */
  996. if (data->pointer_barrier_active == SDL_TRUE) {
  997. X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
  998. }
  999. #endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
  1000. } break;
  1001. /* Have we been restored? */
  1002. case MapNotify:
  1003. {
  1004. #ifdef DEBUG_XEVENTS
  1005. printf("window %p: MapNotify!\n", data);
  1006. #endif
  1007. X11_DispatchMapNotify(data);
  1008. #if SDL_VIDEO_DRIVER_X11_XFIXES
  1009. /* Enable confinement if it was activated. */
  1010. if (data->pointer_barrier_active == SDL_TRUE) {
  1011. X11_ConfineCursorWithFlags(_this, data->window, &data->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
  1012. }
  1013. #endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
  1014. } break;
  1015. /* Have we been resized or moved? */
  1016. case ConfigureNotify:
  1017. {
  1018. #ifdef DEBUG_XEVENTS
  1019. printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data,
  1020. xevent->xconfigure.x, xevent->xconfigure.y,
  1021. xevent->xconfigure.width, xevent->xconfigure.height);
  1022. #endif
  1023. /* Real configure notify events are relative to the parent, synthetic events are absolute. */
  1024. if (!xevent->xconfigure.send_event) {
  1025. unsigned int NumChildren;
  1026. Window ChildReturn, Root, Parent;
  1027. Window *Children;
  1028. /* Translate these coodinates back to relative to root */
  1029. X11_XQueryTree(data->videodata->display, xevent->xconfigure.window, &Root, &Parent, &Children, &NumChildren);
  1030. X11_XTranslateCoordinates(xevent->xconfigure.display,
  1031. Parent, DefaultRootWindow(xevent->xconfigure.display),
  1032. xevent->xconfigure.x, xevent->xconfigure.y,
  1033. &xevent->xconfigure.x, &xevent->xconfigure.y,
  1034. &ChildReturn);
  1035. }
  1036. if (xevent->xconfigure.x != data->last_xconfigure.x ||
  1037. xevent->xconfigure.y != data->last_xconfigure.y) {
  1038. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED,
  1039. xevent->xconfigure.x, xevent->xconfigure.y);
  1040. #ifdef SDL_USE_IME
  1041. if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
  1042. /* Update IME candidate list position */
  1043. SDL_IME_UpdateTextRect(NULL);
  1044. }
  1045. #endif
  1046. }
  1047. if (xevent->xconfigure.width != data->last_xconfigure.width ||
  1048. xevent->xconfigure.height != data->last_xconfigure.height) {
  1049. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
  1050. xevent->xconfigure.width,
  1051. xevent->xconfigure.height);
  1052. }
  1053. data->last_xconfigure = xevent->xconfigure;
  1054. } break;
  1055. /* Have we been requested to quit (or another client message?) */
  1056. case ClientMessage:
  1057. {
  1058. static int xdnd_version = 0;
  1059. if (xevent->xclient.message_type == videodata->XdndEnter) {
  1060. SDL_bool use_list = xevent->xclient.data.l[1] & 1;
  1061. data->xdnd_source = xevent->xclient.data.l[0];
  1062. xdnd_version = (xevent->xclient.data.l[1] >> 24);
  1063. #ifdef DEBUG_XEVENTS
  1064. printf("XID of source window : %ld\n", data->xdnd_source);
  1065. printf("Protocol version to use : %d\n", xdnd_version);
  1066. printf("More then 3 data types : %d\n", (int)use_list);
  1067. #endif
  1068. if (use_list) {
  1069. /* fetch conversion targets */
  1070. SDL_x11Prop p;
  1071. X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList);
  1072. /* pick one */
  1073. data->xdnd_req = X11_PickTarget(display, (Atom *)p.data, p.count);
  1074. X11_XFree(p.data);
  1075. } else {
  1076. /* pick from list of three */
  1077. data->xdnd_req = X11_PickTargetFromAtoms(display, xevent->xclient.data.l[2], xevent->xclient.data.l[3], xevent->xclient.data.l[4]);
  1078. }
  1079. } else if (xevent->xclient.message_type == videodata->XdndPosition) {
  1080. #ifdef DEBUG_XEVENTS
  1081. Atom act = videodata->XdndActionCopy;
  1082. if (xdnd_version >= 2) {
  1083. act = xevent->xclient.data.l[4];
  1084. }
  1085. printf("Action requested by user is : %s\n", X11_XGetAtomName(display, act));
  1086. #endif
  1087. {
  1088. /* Drag and Drop position */
  1089. int root_x, root_y, window_x, window_y;
  1090. Window ChildReturn;
  1091. root_x = xevent->xclient.data.l[2] >> 16;
  1092. root_y = xevent->xclient.data.l[2] & 0xffff;
  1093. /* Translate from root to current window position */
  1094. X11_XTranslateCoordinates(display, DefaultRootWindow(display), data->xwindow,
  1095. root_x, root_y, &window_x, &window_y, &ChildReturn);
  1096. SDL_SendDropPosition(data->window, NULL, (float)window_x, (float)window_y); /* FIXME, can we get the filename ? */
  1097. }
  1098. /* reply with status */
  1099. SDL_memset(&m, 0, sizeof(XClientMessageEvent));
  1100. m.type = ClientMessage;
  1101. m.display = xevent->xclient.display;
  1102. m.window = xevent->xclient.data.l[0];
  1103. m.message_type = videodata->XdndStatus;
  1104. m.format = 32;
  1105. m.data.l[0] = data->xwindow;
  1106. m.data.l[1] = (data->xdnd_req != None);
  1107. m.data.l[2] = 0; /* specify an empty rectangle */
  1108. m.data.l[3] = 0;
  1109. m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */
  1110. X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
  1111. X11_XFlush(display);
  1112. } else if (xevent->xclient.message_type == videodata->XdndDrop) {
  1113. if (data->xdnd_req == None) {
  1114. /* say again - not interested! */
  1115. SDL_memset(&m, 0, sizeof(XClientMessageEvent));
  1116. m.type = ClientMessage;
  1117. m.display = xevent->xclient.display;
  1118. m.window = xevent->xclient.data.l[0];
  1119. m.message_type = videodata->XdndFinished;
  1120. m.format = 32;
  1121. m.data.l[0] = data->xwindow;
  1122. m.data.l[1] = 0;
  1123. m.data.l[2] = None; /* fail! */
  1124. X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
  1125. } else {
  1126. /* convert */
  1127. if (xdnd_version >= 1) {
  1128. X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent->xclient.data.l[2]);
  1129. } else {
  1130. X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
  1131. }
  1132. }
  1133. } else if ((xevent->xclient.message_type == videodata->WM_PROTOCOLS) &&
  1134. (xevent->xclient.format == 32) &&
  1135. (xevent->xclient.data.l[0] == videodata->_NET_WM_PING)) {
  1136. Window root = DefaultRootWindow(display);
  1137. #ifdef DEBUG_XEVENTS
  1138. printf("window %p: _NET_WM_PING\n", data);
  1139. #endif
  1140. xevent->xclient.window = root;
  1141. X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, xevent);
  1142. break;
  1143. }
  1144. else if ((xevent->xclient.message_type == videodata->WM_PROTOCOLS) &&
  1145. (xevent->xclient.format == 32) &&
  1146. (xevent->xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) {
  1147. #ifdef DEBUG_XEVENTS
  1148. printf("window %p: WM_DELETE_WINDOW\n", data);
  1149. #endif
  1150. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0);
  1151. break;
  1152. } else if ((xevent->xclient.message_type == videodata->WM_PROTOCOLS) &&
  1153. (xevent->xclient.format == 32) &&
  1154. (xevent->xclient.data.l[0] == videodata->WM_TAKE_FOCUS)) {
  1155. #ifdef DEBUG_XEVENTS
  1156. printf("window %p: WM_TAKE_FOCUS\n", data);
  1157. #endif
  1158. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_TAKE_FOCUS, 0, 0);
  1159. break;
  1160. }
  1161. } break;
  1162. /* Do we need to refresh ourselves? */
  1163. case Expose:
  1164. {
  1165. #ifdef DEBUG_XEVENTS
  1166. printf("window %p: Expose (count = %d)\n", data, xevent->xexpose.count);
  1167. #endif
  1168. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
  1169. } break;
  1170. case MotionNotify:
  1171. {
  1172. SDL_Mouse *mouse = SDL_GetMouse();
  1173. if (!mouse->relative_mode || mouse->relative_mode_warp) {
  1174. #ifdef DEBUG_MOTION
  1175. printf("window %p: X11 motion: %d,%d\n", data, xevent->xmotion.x, xevent->xmotion.y);
  1176. #endif
  1177. SDL_SendMouseMotion(0, data->window, 0, 0, (float)xevent->xmotion.x, (float)xevent->xmotion.y);
  1178. }
  1179. } break;
  1180. case ButtonPress:
  1181. {
  1182. int xticks = 0, yticks = 0;
  1183. #ifdef DEBUG_XEVENTS
  1184. printf("window %p: ButtonPress (X11 button = %d)\n", data, xevent->xbutton.button);
  1185. #endif
  1186. if (X11_IsWheelEvent(display, xevent, &xticks, &yticks)) {
  1187. SDL_SendMouseWheel(0, data->window, 0, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL);
  1188. } else {
  1189. SDL_bool ignore_click = SDL_FALSE;
  1190. int button = xevent->xbutton.button;
  1191. if (button == Button1) {
  1192. if (ProcessHitTest(_this, data, xevent)) {
  1193. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
  1194. break; /* don't pass this event on to app. */
  1195. }
  1196. } else if (button > 7) {
  1197. /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ...
  1198. => subtract (8-SDL_BUTTON_X1) to get value SDL expects */
  1199. button -= (8 - SDL_BUTTON_X1);
  1200. }
  1201. if (data->last_focus_event_time) {
  1202. const int X11_FOCUS_CLICK_TIMEOUT = 10;
  1203. if (SDL_GetTicks() < (data->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
  1204. ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE);
  1205. }
  1206. data->last_focus_event_time = 0;
  1207. }
  1208. if (!ignore_click) {
  1209. SDL_SendMouseButton(0, data->window, 0, SDL_PRESSED, button);
  1210. }
  1211. }
  1212. X11_UpdateUserTime(data, xevent->xbutton.time);
  1213. } break;
  1214. case ButtonRelease:
  1215. {
  1216. int button = xevent->xbutton.button;
  1217. /* The X server sends a Release event for each Press for wheels. Ignore them. */
  1218. int xticks = 0, yticks = 0;
  1219. #ifdef DEBUG_XEVENTS
  1220. printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent->xbutton.button);
  1221. #endif
  1222. if (!X11_IsWheelEvent(display, xevent, &xticks, &yticks)) {
  1223. if (button > 7) {
  1224. /* see explanation at case ButtonPress */
  1225. button -= (8 - SDL_BUTTON_X1);
  1226. }
  1227. SDL_SendMouseButton(0, data->window, 0, SDL_RELEASED, button);
  1228. }
  1229. } break;
  1230. case PropertyNotify:
  1231. {
  1232. #ifdef DEBUG_XEVENTS
  1233. unsigned char *propdata;
  1234. int status, real_format;
  1235. Atom real_type;
  1236. unsigned long items_read, items_left;
  1237. char *name = X11_XGetAtomName(display, xevent->xproperty.atom);
  1238. if (name) {
  1239. printf("window %p: PropertyNotify: %s %s time=%lu\n", data, name, (xevent->xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent->xproperty.time);
  1240. X11_XFree(name);
  1241. }
  1242. status = X11_XGetWindowProperty(display, data->xwindow, xevent->xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata);
  1243. if (status == Success && items_read > 0) {
  1244. if (real_type == XA_INTEGER) {
  1245. int *values = (int *)propdata;
  1246. printf("{");
  1247. for (i = 0; i < items_read; i++) {
  1248. printf(" %d", values[i]);
  1249. }
  1250. printf(" }\n");
  1251. } else if (real_type == XA_CARDINAL) {
  1252. if (real_format == 32) {
  1253. Uint32 *values = (Uint32 *)propdata;
  1254. printf("{");
  1255. for (i = 0; i < items_read; i++) {
  1256. printf(" %d", values[i]);
  1257. }
  1258. printf(" }\n");
  1259. } else if (real_format == 16) {
  1260. Uint16 *values = (Uint16 *)propdata;
  1261. printf("{");
  1262. for (i = 0; i < items_read; i++) {
  1263. printf(" %d", values[i]);
  1264. }
  1265. printf(" }\n");
  1266. } else if (real_format == 8) {
  1267. Uint8 *values = (Uint8 *)propdata;
  1268. printf("{");
  1269. for (i = 0; i < items_read; i++) {
  1270. printf(" %d", values[i]);
  1271. }
  1272. printf(" }\n");
  1273. }
  1274. } else if (real_type == XA_STRING ||
  1275. real_type == videodata->UTF8_STRING) {
  1276. printf("{ \"%s\" }\n", propdata);
  1277. } else if (real_type == XA_ATOM) {
  1278. Atom *atoms = (Atom *)propdata;
  1279. printf("{");
  1280. for (i = 0; i < items_read; i++) {
  1281. char *atomname = X11_XGetAtomName(display, atoms[i]);
  1282. if (atomname) {
  1283. printf(" %s", atomname);
  1284. X11_XFree(atomname);
  1285. }
  1286. }
  1287. printf(" }\n");
  1288. } else {
  1289. char *atomname = X11_XGetAtomName(display, real_type);
  1290. printf("Unknown type: %ld (%s)\n", real_type, atomname ? atomname : "UNKNOWN");
  1291. if (atomname) {
  1292. X11_XFree(atomname);
  1293. }
  1294. }
  1295. }
  1296. if (status == Success) {
  1297. X11_XFree(propdata);
  1298. }
  1299. #endif /* DEBUG_XEVENTS */
  1300. /* Take advantage of this moment to make sure user_time has a
  1301. valid timestamp from the X server, so if we later try to
  1302. raise/restore this window, _NET_ACTIVE_WINDOW can have a
  1303. non-zero timestamp, even if there's never been a mouse or
  1304. key press to this window so far. Note that we don't try to
  1305. set _NET_WM_USER_TIME here, though. That's only for legit
  1306. user interaction with the window. */
  1307. if (!data->user_time) {
  1308. data->user_time = xevent->xproperty.time;
  1309. }
  1310. if (xevent->xproperty.atom == data->videodata->_NET_WM_STATE) {
  1311. /* Get the new state from the window manager.
  1312. Compositing window managers can alter visibility of windows
  1313. without ever mapping / unmapping them, so we handle that here,
  1314. because they use the NETWM protocol to notify us of changes.
  1315. */
  1316. const Uint32 flags = X11_GetNetWMState(_this, data->window, xevent->xproperty.window);
  1317. const Uint32 changed = flags ^ data->window->flags;
  1318. if ((changed & (SDL_WINDOW_HIDDEN | SDL_WINDOW_FULLSCREEN)) != 0) {
  1319. if (flags & SDL_WINDOW_HIDDEN) {
  1320. X11_DispatchUnmapNotify(data);
  1321. } else {
  1322. X11_DispatchMapNotify(data);
  1323. }
  1324. }
  1325. if (changed & SDL_WINDOW_MAXIMIZED) {
  1326. if (flags & SDL_WINDOW_MAXIMIZED) {
  1327. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
  1328. } else {
  1329. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
  1330. }
  1331. }
  1332. } else if (xevent->xproperty.atom == videodata->XKLAVIER_STATE) {
  1333. /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify
  1334. events when the keyboard layout changes (for example,
  1335. changing from English to French on the menubar's keyboard
  1336. icon). Since it changes the XKLAVIER_STATE property, we
  1337. notice and reinit our keymap here. This might not be the
  1338. right approach, but it seems to work. */
  1339. X11_UpdateKeymap(_this, SDL_TRUE);
  1340. } else if (xevent->xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
  1341. Atom type;
  1342. int format;
  1343. unsigned long nitems, bytes_after;
  1344. unsigned char *property;
  1345. if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
  1346. if (type != None && nitems == 4) {
  1347. data->border_left = (int)((long *)property)[0];
  1348. data->border_right = (int)((long *)property)[1];
  1349. data->border_top = (int)((long *)property)[2];
  1350. data->border_bottom = (int)((long *)property)[3];
  1351. }
  1352. X11_XFree(property);
  1353. #ifdef DEBUG_XEVENTS
  1354. printf("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d\n", data->border_left, data->border_right, data->border_top, data->border_bottom);
  1355. #endif
  1356. }
  1357. }
  1358. } break;
  1359. case SelectionNotify:
  1360. {
  1361. Atom target = xevent->xselection.target;
  1362. #ifdef DEBUG_XEVENTS
  1363. printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
  1364. xevent->xselection.requestor, xevent->xselection.target);
  1365. #endif
  1366. if (target == data->xdnd_req) {
  1367. /* read data */
  1368. SDL_x11Prop p;
  1369. X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY);
  1370. if (p.format == 8) {
  1371. char *saveptr = NULL;
  1372. char *name = X11_XGetAtomName(display, target);
  1373. if (name) {
  1374. char *token = SDL_strtokr((char *)p.data, "\r\n", &saveptr);
  1375. while (token != NULL) {
  1376. if (SDL_strcmp("text/plain", name) == 0) {
  1377. SDL_SendDropText(data->window, token);
  1378. } else if (SDL_strcmp("text/uri-list", name) == 0) {
  1379. char *fn = X11_URIToLocal(token);
  1380. if (fn) {
  1381. SDL_SendDropFile(data->window, fn);
  1382. }
  1383. }
  1384. token = SDL_strtokr(NULL, "\r\n", &saveptr);
  1385. }
  1386. X11_XFree(name);
  1387. }
  1388. SDL_SendDropComplete(data->window);
  1389. }
  1390. X11_XFree(p.data);
  1391. /* send reply */
  1392. SDL_memset(&m, 0, sizeof(XClientMessageEvent));
  1393. m.type = ClientMessage;
  1394. m.display = display;
  1395. m.window = data->xdnd_source;
  1396. m.message_type = videodata->XdndFinished;
  1397. m.format = 32;
  1398. m.data.l[0] = data->xwindow;
  1399. m.data.l[1] = 1;
  1400. m.data.l[2] = videodata->XdndActionCopy;
  1401. X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent *)&m);
  1402. X11_XSync(display, False);
  1403. }
  1404. } break;
  1405. default:
  1406. {
  1407. #ifdef DEBUG_XEVENTS
  1408. printf("window %p: Unhandled event %d\n", data, xevent->type);
  1409. #endif
  1410. } break;
  1411. }
  1412. }
  1413. static void X11_HandleFocusChanges(_THIS)
  1414. {
  1415. SDL_VideoData *videodata = _this->driverdata;
  1416. int i;
  1417. if (videodata && videodata->windowlist) {
  1418. for (i = 0; i < videodata->numwindows; ++i) {
  1419. SDL_WindowData *data = videodata->windowlist[i];
  1420. if (data && data->pending_focus != PENDING_FOCUS_NONE) {
  1421. Uint64 now = SDL_GetTicks();
  1422. if (now >= data->pending_focus_time) {
  1423. if (data->pending_focus == PENDING_FOCUS_IN) {
  1424. X11_DispatchFocusIn(_this, data);
  1425. } else {
  1426. X11_DispatchFocusOut(_this, data);
  1427. }
  1428. data->pending_focus = PENDING_FOCUS_NONE;
  1429. }
  1430. }
  1431. }
  1432. }
  1433. }
  1434. static Bool isAnyEvent(Display *display, XEvent *ev, XPointer arg)
  1435. {
  1436. return True;
  1437. }
  1438. static SDL_bool X11_PollEvent(Display *display, XEvent *event)
  1439. {
  1440. if (!X11_XCheckIfEvent(display, event, isAnyEvent, NULL)) {
  1441. return SDL_FALSE;
  1442. }
  1443. return SDL_TRUE;
  1444. }
  1445. void X11_SendWakeupEvent(_THIS, SDL_Window *window)
  1446. {
  1447. SDL_VideoData *data = _this->driverdata;
  1448. Display *req_display = data->request_display;
  1449. Window xwindow = window->driverdata->xwindow;
  1450. XClientMessageEvent event;
  1451. SDL_memset(&event, 0, sizeof(XClientMessageEvent));
  1452. event.type = ClientMessage;
  1453. event.display = req_display;
  1454. event.send_event = True;
  1455. event.message_type = data->_SDL_WAKEUP;
  1456. event.format = 8;
  1457. X11_XSendEvent(req_display, xwindow, False, NoEventMask, (XEvent *)&event);
  1458. /* XSendEvent returns a status and it could be BadValue or BadWindow. If an
  1459. error happens it is an SDL's internal error and there is nothing we can do here. */
  1460. X11_XFlush(req_display);
  1461. }
  1462. int X11_WaitEventTimeout(_THIS, Sint64 timeoutNS)
  1463. {
  1464. SDL_VideoData *videodata = _this->driverdata;
  1465. Display *display;
  1466. XEvent xevent;
  1467. display = videodata->display;
  1468. SDL_zero(xevent);
  1469. /* Flush and poll to grab any events already read and queued */
  1470. X11_XFlush(display);
  1471. if (X11_PollEvent(display, &xevent)) {
  1472. /* Fall through */
  1473. } else if (timeoutNS == 0) {
  1474. return 0;
  1475. } else {
  1476. /* Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait */
  1477. int err = SDL_IOReady(ConnectionNumber(display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS);
  1478. if (err > 0) {
  1479. if (!X11_PollEvent(display, &xevent)) {
  1480. /* Someone may have beat us to reading the fd. Return 1 here to
  1481. * trigger the normal spurious wakeup logic in the event core. */
  1482. return 1;
  1483. }
  1484. } else if (err == 0) {
  1485. /* Timeout */
  1486. return 0;
  1487. } else {
  1488. /* Error returned from poll()/select() */
  1489. if (errno == EINTR) {
  1490. /* If the wait was interrupted by a signal, we may have generated a
  1491. * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */
  1492. return 1;
  1493. } else {
  1494. return err;
  1495. }
  1496. }
  1497. }
  1498. X11_DispatchEvent(_this, &xevent);
  1499. #ifdef SDL_USE_IME
  1500. if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
  1501. SDL_IME_PumpEvents();
  1502. }
  1503. #endif
  1504. #ifdef SDL_USE_LIBDBUS
  1505. SDL_SystemTheme_PumpEvents();
  1506. #endif
  1507. return 1;
  1508. }
  1509. void X11_PumpEvents(_THIS)
  1510. {
  1511. SDL_VideoData *data = _this->driverdata;
  1512. XEvent xevent;
  1513. int i;
  1514. if (data->last_mode_change_deadline) {
  1515. if (SDL_GetTicks() >= data->last_mode_change_deadline) {
  1516. data->last_mode_change_deadline = 0; /* assume we're done. */
  1517. }
  1518. }
  1519. /* Update activity every 30 seconds to prevent screensaver */
  1520. if (_this->suspend_screensaver) {
  1521. Uint64 now = SDL_GetTicks();
  1522. if (!data->screensaver_activity || now >= (data->screensaver_activity + 30000)) {
  1523. X11_XResetScreenSaver(data->display);
  1524. #if SDL_USE_LIBDBUS
  1525. SDL_DBus_ScreensaverTickle();
  1526. #endif
  1527. data->screensaver_activity = now;
  1528. }
  1529. }
  1530. SDL_zero(xevent);
  1531. /* Keep processing pending events */
  1532. while (X11_PollEvent(data->display, &xevent)) {
  1533. X11_DispatchEvent(_this, &xevent);
  1534. }
  1535. #ifdef SDL_USE_IME
  1536. if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
  1537. SDL_IME_PumpEvents();
  1538. }
  1539. #endif
  1540. #ifdef SDL_USE_LIBDBUS
  1541. SDL_SystemTheme_PumpEvents();
  1542. #endif
  1543. /* FIXME: Only need to do this when there are pending focus changes */
  1544. X11_HandleFocusChanges(_this);
  1545. /* FIXME: Only need to do this when there are flashing windows */
  1546. for (i = 0; i < data->numwindows; ++i) {
  1547. if (data->windowlist[i] != NULL &&
  1548. data->windowlist[i]->flash_cancel_time &&
  1549. SDL_GetTicks() >= data->windowlist[i]->flash_cancel_time) {
  1550. X11_FlashWindow(_this, data->windowlist[i]->window, SDL_FLASH_CANCEL);
  1551. }
  1552. }
  1553. }
  1554. int X11_SuspendScreenSaver(_THIS)
  1555. {
  1556. #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
  1557. SDL_VideoData *data = _this->driverdata;
  1558. int dummy;
  1559. int major_version, minor_version;
  1560. #endif /* SDL_VIDEO_DRIVER_X11_XSCRNSAVER */
  1561. #if SDL_USE_LIBDBUS
  1562. if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) {
  1563. return 0;
  1564. }
  1565. if (_this->suspend_screensaver) {
  1566. SDL_DBus_ScreensaverTickle();
  1567. }
  1568. #endif
  1569. #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
  1570. if (SDL_X11_HAVE_XSS) {
  1571. /* X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 */
  1572. if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) ||
  1573. !X11_XScreenSaverQueryVersion(data->display,
  1574. &major_version, &minor_version) ||
  1575. major_version < 1 || (major_version == 1 && minor_version < 1)) {
  1576. return SDL_Unsupported();
  1577. }
  1578. X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
  1579. X11_XResetScreenSaver(data->display);
  1580. return 0;
  1581. }
  1582. #endif
  1583. return SDL_Unsupported();
  1584. }
  1585. #endif /* SDL_VIDEO_DRIVER_X11 */