SDL_x11events.c 81 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2025 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 <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_x11pen.h"
  27. #include "SDL_x11touch.h"
  28. #include "SDL_x11xinput2.h"
  29. #include "SDL_x11xfixes.h"
  30. #include "SDL_x11settings.h"
  31. #include "../SDL_clipboard_c.h"
  32. #include "SDL_x11xsync.h"
  33. #include "../../core/unix/SDL_poll.h"
  34. #include "../../events/SDL_events_c.h"
  35. #include "../../events/SDL_mouse_c.h"
  36. #include "../../events/SDL_touch_c.h"
  37. #include "../../core/linux/SDL_system_theme.h"
  38. #include "../SDL_sysvideo.h"
  39. #include <stdio.h>
  40. #if 0
  41. #define DEBUG_XEVENTS
  42. #endif
  43. #ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT
  44. #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
  45. #endif
  46. #ifndef _NET_WM_MOVERESIZE_SIZE_TOP
  47. #define _NET_WM_MOVERESIZE_SIZE_TOP 1
  48. #endif
  49. #ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT
  50. #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
  51. #endif
  52. #ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT
  53. #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
  54. #endif
  55. #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
  56. #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
  57. #endif
  58. #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM
  59. #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
  60. #endif
  61. #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
  62. #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
  63. #endif
  64. #ifndef _NET_WM_MOVERESIZE_SIZE_LEFT
  65. #define _NET_WM_MOVERESIZE_SIZE_LEFT 7
  66. #endif
  67. #ifndef _NET_WM_MOVERESIZE_MOVE
  68. #define _NET_WM_MOVERESIZE_MOVE 8
  69. #endif
  70. typedef struct
  71. {
  72. unsigned char *data;
  73. int format, count;
  74. Atom type;
  75. } SDL_x11Prop;
  76. /* Reads property
  77. Must call X11_XFree on results
  78. */
  79. static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop)
  80. {
  81. unsigned char *ret = NULL;
  82. Atom type;
  83. int fmt;
  84. unsigned long count;
  85. unsigned long bytes_left;
  86. int bytes_fetch = 0;
  87. do {
  88. if (ret) {
  89. X11_XFree(ret);
  90. }
  91. X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
  92. bytes_fetch += bytes_left;
  93. } while (bytes_left != 0);
  94. p->data = ret;
  95. p->format = fmt;
  96. p->count = count;
  97. p->type = type;
  98. }
  99. /* Find text-uri-list in a list of targets and return it's atom
  100. if available, else return None */
  101. static Atom X11_PickTarget(Display *disp, Atom list[], int list_count)
  102. {
  103. Atom request = None;
  104. char *name;
  105. int i;
  106. for (i = 0; i < list_count && request == None; i++) {
  107. name = X11_XGetAtomName(disp, list[i]);
  108. // Preferred MIME targets
  109. if ((SDL_strcmp("text/uri-list", name) == 0) ||
  110. (SDL_strcmp("text/plain;charset=utf-8", name) == 0) ||
  111. (SDL_strcmp("UTF8_STRING", name) == 0)) {
  112. request = list[i];
  113. }
  114. // Fallback MIME targets
  115. if ((SDL_strcmp("text/plain", name) == 0) ||
  116. (SDL_strcmp("TEXT", name) == 0)) {
  117. if (request == None) {
  118. request = list[i];
  119. }
  120. }
  121. X11_XFree(name);
  122. }
  123. return request;
  124. }
  125. /* Wrapper for X11_PickTarget for a maximum of three targets, a special
  126. case in the Xdnd protocol */
  127. static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2)
  128. {
  129. int count = 0;
  130. Atom atom[3];
  131. if (a0 != None) {
  132. atom[count++] = a0;
  133. }
  134. if (a1 != None) {
  135. atom[count++] = a1;
  136. }
  137. if (a2 != None) {
  138. atom[count++] = a2;
  139. }
  140. return X11_PickTarget(disp, atom, count);
  141. }
  142. struct KeyRepeatCheckData
  143. {
  144. XEvent *event;
  145. bool found;
  146. };
  147. static Bool X11_KeyRepeatCheckIfEvent(Display *display, XEvent *chkev,
  148. XPointer arg)
  149. {
  150. struct KeyRepeatCheckData *d = (struct KeyRepeatCheckData *)arg;
  151. if (chkev->type == KeyPress && chkev->xkey.keycode == d->event->xkey.keycode && chkev->xkey.time - d->event->xkey.time < 2) {
  152. d->found = true;
  153. }
  154. return False;
  155. }
  156. /* Check to see if this is a repeated key.
  157. (idea shamelessly lifted from GII -- thanks guys! :)
  158. */
  159. static bool X11_KeyRepeat(Display *display, XEvent *event)
  160. {
  161. XEvent dummyev;
  162. struct KeyRepeatCheckData d;
  163. d.event = event;
  164. d.found = false;
  165. if (X11_XPending(display)) {
  166. X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent, (XPointer)&d);
  167. }
  168. return d.found;
  169. }
  170. static bool X11_IsWheelEvent(Display *display, int button, int *xticks, int *yticks)
  171. {
  172. /* according to the xlib docs, no specific mouse wheel events exist.
  173. However, the defacto standard is that the vertical wheel is X buttons
  174. 4 (up) and 5 (down) and a horizontal wheel is 6 (left) and 7 (right). */
  175. // Xlib defines "Button1" through 5, so we just use literals here.
  176. switch (button) {
  177. case 4:
  178. *yticks = 1;
  179. return true;
  180. case 5:
  181. *yticks = -1;
  182. return true;
  183. case 6:
  184. *xticks = 1;
  185. return true;
  186. case 7:
  187. *xticks = -1;
  188. return true;
  189. default:
  190. break;
  191. }
  192. return false;
  193. }
  194. // An X11 event hook
  195. static SDL_X11EventHook g_X11EventHook = NULL;
  196. static void *g_X11EventHookData = NULL;
  197. void SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata)
  198. {
  199. g_X11EventHook = callback;
  200. g_X11EventHookData = userdata;
  201. }
  202. #ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
  203. static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev)
  204. {
  205. SDL_VideoData *videodata = _this->internal;
  206. // event is a union, so cookie == &event, but this is type safe.
  207. XGenericEventCookie *cookie = &xev->xcookie;
  208. if (X11_XGetEventData(videodata->display, cookie)) {
  209. if (!g_X11EventHook || g_X11EventHook(g_X11EventHookData, xev)) {
  210. X11_HandleXinput2Event(_this, cookie);
  211. }
  212. X11_XFreeEventData(videodata->display, cookie);
  213. }
  214. }
  215. #endif // SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
  216. static void X11_UpdateSystemKeyModifiers(SDL_VideoData *viddata)
  217. {
  218. Window junk_window;
  219. int x, y;
  220. X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &viddata->xkb.xkb_modifiers);
  221. }
  222. static void X11_ReconcileModifiers(SDL_VideoData *viddata)
  223. {
  224. const Uint32 xk_modifiers = viddata->xkb.xkb_modifiers;
  225. /* If a modifier was activated by a keypress, it will be tied to the
  226. * specific left/right key that initiated it. Otherwise, the ambiguous
  227. * left/right combo is used.
  228. */
  229. if (xk_modifiers & ShiftMask) {
  230. if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_SHIFT)) {
  231. viddata->xkb.sdl_modifiers |= SDL_KMOD_SHIFT;
  232. }
  233. } else {
  234. viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SHIFT;
  235. }
  236. if (xk_modifiers & ControlMask) {
  237. if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_CTRL)) {
  238. viddata->xkb.sdl_modifiers |= SDL_KMOD_CTRL;
  239. }
  240. } else {
  241. viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CTRL;
  242. }
  243. // Mod1 is used for the Alt keys
  244. if (xk_modifiers & Mod1Mask) {
  245. if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_ALT)) {
  246. viddata->xkb.sdl_modifiers |= SDL_KMOD_ALT;
  247. }
  248. } else {
  249. viddata->xkb.sdl_modifiers &= ~SDL_KMOD_ALT;
  250. }
  251. // Mod4 is used for the Super (aka GUI/Logo) keys.
  252. if (xk_modifiers & Mod4Mask) {
  253. if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_GUI)) {
  254. viddata->xkb.sdl_modifiers |= SDL_KMOD_GUI;
  255. }
  256. } else {
  257. viddata->xkb.sdl_modifiers &= ~SDL_KMOD_GUI;
  258. }
  259. // Mod3 is typically Level 5 shift.
  260. if (xk_modifiers & Mod3Mask) {
  261. viddata->xkb.sdl_modifiers |= SDL_KMOD_LEVEL5;
  262. } else {
  263. viddata->xkb.sdl_modifiers &= ~SDL_KMOD_LEVEL5;
  264. }
  265. // Mod5 is typically Level 3 shift (aka AltGr).
  266. if (xk_modifiers & Mod5Mask) {
  267. viddata->xkb.sdl_modifiers |= SDL_KMOD_MODE;
  268. } else {
  269. viddata->xkb.sdl_modifiers &= ~SDL_KMOD_MODE;
  270. }
  271. if (xk_modifiers & LockMask) {
  272. viddata->xkb.sdl_modifiers |= SDL_KMOD_CAPS;
  273. } else {
  274. viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CAPS;
  275. }
  276. if (xk_modifiers & viddata->xkb.numlock_mask) {
  277. viddata->xkb.sdl_modifiers |= SDL_KMOD_NUM;
  278. } else {
  279. viddata->xkb.sdl_modifiers &= ~SDL_KMOD_NUM;
  280. }
  281. if (xk_modifiers & viddata->xkb.scrolllock_mask) {
  282. viddata->xkb.sdl_modifiers |= SDL_KMOD_SCROLL;
  283. } else {
  284. viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SCROLL;
  285. }
  286. SDL_SetModState(viddata->xkb.sdl_modifiers);
  287. }
  288. static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool allow_reconciliation)
  289. {
  290. const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
  291. SDL_Keymod mod = SDL_KMOD_NONE;
  292. bool reconcile = false;
  293. /* SDL clients expect modifier state to be activated at the same time as the
  294. * source keypress, so we set pressed modifier state with the usual modifier
  295. * keys here, as the explicit modifier event won't arrive until after the
  296. * keypress event. If this is wrong, it will be corrected when the explicit
  297. * modifier state is checked.
  298. */
  299. switch (keycode) {
  300. case SDLK_LSHIFT:
  301. mod = SDL_KMOD_LSHIFT;
  302. break;
  303. case SDLK_RSHIFT:
  304. mod = SDL_KMOD_RSHIFT;
  305. break;
  306. case SDLK_LCTRL:
  307. mod = SDL_KMOD_LCTRL;
  308. break;
  309. case SDLK_RCTRL:
  310. mod = SDL_KMOD_RCTRL;
  311. break;
  312. case SDLK_LALT:
  313. mod = SDL_KMOD_LALT;
  314. break;
  315. case SDLK_RALT:
  316. mod = SDL_KMOD_RALT;
  317. break;
  318. case SDLK_LGUI:
  319. mod = SDL_KMOD_LGUI;
  320. break;
  321. case SDLK_RGUI:
  322. mod = SDL_KMOD_RGUI;
  323. break;
  324. case SDLK_MODE:
  325. mod = SDL_KMOD_MODE;
  326. break;
  327. case SDLK_LEVEL5_SHIFT:
  328. mod = SDL_KMOD_LEVEL5;
  329. break;
  330. case SDLK_CAPSLOCK:
  331. case SDLK_NUMLOCKCLEAR:
  332. case SDLK_SCROLLLOCK:
  333. {
  334. /* For locking modifier keys, query the lock state directly, or we may have to wait until the next
  335. * key press event to know if a lock was actually activated from the key event.
  336. */
  337. unsigned int cur_mask = viddata->xkb.xkb_modifiers;
  338. X11_UpdateSystemKeyModifiers(viddata);
  339. if (viddata->xkb.xkb_modifiers & LockMask) {
  340. cur_mask |= LockMask;
  341. } else {
  342. cur_mask &= ~LockMask;
  343. }
  344. if (viddata->xkb.xkb_modifiers & viddata->xkb.numlock_mask) {
  345. cur_mask |= viddata->xkb.numlock_mask;
  346. } else {
  347. cur_mask &= ~viddata->xkb.numlock_mask;
  348. }
  349. if (viddata->xkb.xkb_modifiers & viddata->xkb.scrolllock_mask) {
  350. cur_mask |= viddata->xkb.scrolllock_mask;
  351. } else {
  352. cur_mask &= ~viddata->xkb.scrolllock_mask;
  353. }
  354. viddata->xkb.xkb_modifiers = cur_mask;
  355. } SDL_FALLTHROUGH;
  356. default:
  357. reconcile = true;
  358. break;
  359. }
  360. if (pressed) {
  361. viddata->xkb.sdl_modifiers |= mod;
  362. } else {
  363. viddata->xkb.sdl_modifiers &= ~mod;
  364. }
  365. if (allow_reconciliation) {
  366. if (reconcile) {
  367. X11_ReconcileModifiers(viddata);
  368. } else {
  369. SDL_SetModState(viddata->xkb.sdl_modifiers);
  370. }
  371. }
  372. }
  373. void X11_ReconcileKeyboardState(SDL_VideoDevice *_this)
  374. {
  375. SDL_VideoData *videodata = _this->internal;
  376. Display *display = videodata->display;
  377. char keys[32];
  378. int keycode;
  379. const bool *keyboardState;
  380. X11_XQueryKeymap(display, keys);
  381. keyboardState = SDL_GetKeyboardState(0);
  382. for (keycode = 0; keycode < SDL_arraysize(videodata->key_layout); ++keycode) {
  383. SDL_Scancode scancode = videodata->key_layout[keycode];
  384. bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0;
  385. bool sdlKeyPressed = keyboardState[scancode];
  386. if (x11KeyPressed && !sdlKeyPressed) {
  387. // Only update modifier state for keys that are pressed in another application
  388. switch (SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false)) {
  389. case SDLK_LCTRL:
  390. case SDLK_RCTRL:
  391. case SDLK_LSHIFT:
  392. case SDLK_RSHIFT:
  393. case SDLK_LALT:
  394. case SDLK_RALT:
  395. case SDLK_LGUI:
  396. case SDLK_RGUI:
  397. case SDLK_MODE:
  398. case SDLK_LEVEL5_SHIFT:
  399. X11_HandleModifierKeys(videodata, scancode, true, false);
  400. SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true);
  401. break;
  402. default:
  403. break;
  404. }
  405. } else if (!x11KeyPressed && sdlKeyPressed) {
  406. X11_HandleModifierKeys(videodata, scancode, false, false);
  407. SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, false);
  408. }
  409. }
  410. X11_UpdateSystemKeyModifiers(videodata);
  411. X11_ReconcileModifiers(videodata);
  412. }
  413. static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data)
  414. {
  415. #ifdef DEBUG_XEVENTS
  416. SDL_Log("window 0x%lx: Dispatching FocusIn", data->xwindow);
  417. #endif
  418. SDL_SetKeyboardFocus(data->window);
  419. X11_ReconcileKeyboardState(_this);
  420. #ifdef X_HAVE_UTF8_STRING
  421. if (data->ic) {
  422. X11_XSetICFocus(data->ic);
  423. }
  424. #endif
  425. if (data->flashing_window) {
  426. X11_FlashWindow(_this, data->window, SDL_FLASH_CANCEL);
  427. }
  428. }
  429. static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data)
  430. {
  431. #ifdef DEBUG_XEVENTS
  432. SDL_Log("window 0x%lx: Dispatching FocusOut", data->xwindow);
  433. #endif
  434. /* If another window has already processed a focus in, then don't try to
  435. * remove focus here. Doing so will incorrectly remove focus from that
  436. * window, and the focus lost event for this window will have already
  437. * been dispatched anyway. */
  438. if (data->window == SDL_GetKeyboardFocus()) {
  439. SDL_SetKeyboardFocus(NULL);
  440. }
  441. #ifdef X_HAVE_UTF8_STRING
  442. if (data->ic) {
  443. X11_XUnsetICFocus(data->ic);
  444. }
  445. #endif
  446. }
  447. static void X11_DispatchMapNotify(SDL_WindowData *data)
  448. {
  449. SDL_Window *window = data->window;
  450. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
  451. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
  452. if (!(window->flags & SDL_WINDOW_HIDDEN) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  453. SDL_UpdateWindowGrab(window);
  454. }
  455. }
  456. static void X11_DispatchUnmapNotify(SDL_WindowData *data)
  457. {
  458. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
  459. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
  460. }
  461. static void DispatchWindowMove(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point)
  462. {
  463. SDL_VideoData *videodata = _this->internal;
  464. SDL_Window *window = data->window;
  465. Display *display = videodata->display;
  466. XEvent evt;
  467. // !!! FIXME: we need to regrab this if necessary when the drag is done.
  468. X11_XUngrabPointer(display, 0L);
  469. X11_XFlush(display);
  470. evt.xclient.type = ClientMessage;
  471. evt.xclient.window = data->xwindow;
  472. evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE;
  473. evt.xclient.format = 32;
  474. evt.xclient.data.l[0] = (size_t)window->x + point->x;
  475. evt.xclient.data.l[1] = (size_t)window->y + point->y;
  476. evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE;
  477. evt.xclient.data.l[3] = Button1;
  478. evt.xclient.data.l[4] = 0;
  479. X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
  480. X11_XSync(display, 0);
  481. }
  482. static void ScheduleWindowMove(SDL_VideoDevice *_this, SDL_WindowData *data, const SDL_Point *point)
  483. {
  484. data->pending_move = true;
  485. data->pending_move_point = *point;
  486. }
  487. static void InitiateWindowResize(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point, int direction)
  488. {
  489. SDL_VideoData *videodata = _this->internal;
  490. SDL_Window *window = data->window;
  491. Display *display = videodata->display;
  492. XEvent evt;
  493. if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT) {
  494. return;
  495. }
  496. // !!! FIXME: we need to regrab this if necessary when the drag is done.
  497. X11_XUngrabPointer(display, 0L);
  498. X11_XFlush(display);
  499. evt.xclient.type = ClientMessage;
  500. evt.xclient.window = data->xwindow;
  501. evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE;
  502. evt.xclient.format = 32;
  503. evt.xclient.data.l[0] = (size_t)window->x + point->x;
  504. evt.xclient.data.l[1] = (size_t)window->y + point->y;
  505. evt.xclient.data.l[2] = direction;
  506. evt.xclient.data.l[3] = Button1;
  507. evt.xclient.data.l[4] = 0;
  508. X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
  509. X11_XSync(display, 0);
  510. }
  511. bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result)
  512. {
  513. SDL_Window *window = data->window;
  514. if (!window->hit_test) return false;
  515. const SDL_Point point = { (int)x, (int)y };
  516. SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
  517. if (!force_new_result && rc == data->hit_test_result) {
  518. return true;
  519. }
  520. X11_SetHitTestCursor(rc);
  521. data->hit_test_result = rc;
  522. return true;
  523. }
  524. bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y)
  525. {
  526. SDL_Window *window = data->window;
  527. if (window->hit_test) {
  528. const SDL_Point point = { (int)x, (int)y };
  529. static const int directions[] = {
  530. _NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP,
  531. _NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT,
  532. _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, _NET_WM_MOVERESIZE_SIZE_BOTTOM,
  533. _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT
  534. };
  535. switch (data->hit_test_result) {
  536. case SDL_HITTEST_DRAGGABLE:
  537. /* Some window managers get in a bad state when a move event starts while input is transitioning
  538. to the SDL window. This can happen when clicking on a drag region of an unfocused window
  539. where the same mouse down event will trigger a drag event and a window activate. */
  540. if (data->window->flags & SDL_WINDOW_INPUT_FOCUS) {
  541. DispatchWindowMove(_this, data, &point);
  542. } else {
  543. ScheduleWindowMove(_this, data, &point);
  544. }
  545. return true;
  546. case SDL_HITTEST_RESIZE_TOPLEFT:
  547. case SDL_HITTEST_RESIZE_TOP:
  548. case SDL_HITTEST_RESIZE_TOPRIGHT:
  549. case SDL_HITTEST_RESIZE_RIGHT:
  550. case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
  551. case SDL_HITTEST_RESIZE_BOTTOM:
  552. case SDL_HITTEST_RESIZE_BOTTOMLEFT:
  553. case SDL_HITTEST_RESIZE_LEFT:
  554. InitiateWindowResize(_this, data, &point, directions[data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]);
  555. return true;
  556. default:
  557. return false;
  558. }
  559. }
  560. return false;
  561. }
  562. static void X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest)
  563. {
  564. if (latest && (latest != data->user_time)) {
  565. SDL_VideoData *videodata = data->videodata;
  566. Display *display = videodata->display;
  567. X11_XChangeProperty(display, data->xwindow, videodata->atoms._NET_WM_USER_TIME,
  568. XA_CARDINAL, 32, PropModeReplace,
  569. (const unsigned char *)&latest, 1);
  570. #ifdef DEBUG_XEVENTS
  571. SDL_Log("window 0x%lx: updating _NET_WM_USER_TIME to %lu", data->xwindow, latest);
  572. #endif
  573. data->user_time = latest;
  574. }
  575. }
  576. static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xevent)
  577. {
  578. int i;
  579. SDL_VideoData *videodata = _this->internal;
  580. Display *display = videodata->display;
  581. SDL_assert(videodata->clipboard_window != None);
  582. SDL_assert(xevent->xany.window == videodata->clipboard_window);
  583. switch (xevent->type) {
  584. // Copy the selection from our own CUTBUFFER to the requested property
  585. case SelectionRequest:
  586. {
  587. const XSelectionRequestEvent *req = &xevent->xselectionrequest;
  588. XEvent sevent;
  589. int mime_formats;
  590. unsigned char *seln_data;
  591. size_t seln_length = 0;
  592. Atom XA_TARGETS = videodata->atoms.TARGETS;
  593. SDLX11_ClipboardData *clipboard;
  594. #ifdef DEBUG_XEVENTS
  595. char *atom_name;
  596. atom_name = X11_XGetAtomName(display, req->target);
  597. SDL_Log("window CLIPBOARD: SelectionRequest (requestor = 0x%lx, target = 0x%lx, mime_type = %s)",
  598. req->requestor, req->target, atom_name);
  599. if (atom_name) {
  600. X11_XFree(atom_name);
  601. }
  602. #endif
  603. if (req->selection == XA_PRIMARY) {
  604. clipboard = &videodata->primary_selection;
  605. } else {
  606. clipboard = &videodata->clipboard;
  607. }
  608. SDL_zero(sevent);
  609. sevent.xany.type = SelectionNotify;
  610. sevent.xselection.selection = req->selection;
  611. sevent.xselection.target = None;
  612. sevent.xselection.property = None; // tell them no by default
  613. sevent.xselection.requestor = req->requestor;
  614. sevent.xselection.time = req->time;
  615. /* !!! FIXME: We were probably storing this on the root window
  616. because an SDL window might go away...? but we don't have to do
  617. this now (or ever, really). */
  618. if (req->target == XA_TARGETS) {
  619. Atom *supportedFormats;
  620. supportedFormats = SDL_malloc((clipboard->mime_count + 1) * sizeof(Atom));
  621. supportedFormats[0] = XA_TARGETS;
  622. mime_formats = 1;
  623. for (i = 0; i < clipboard->mime_count; ++i) {
  624. supportedFormats[mime_formats++] = X11_XInternAtom(display, clipboard->mime_types[i], False);
  625. }
  626. X11_XChangeProperty(display, req->requestor, req->property,
  627. XA_ATOM, 32, PropModeReplace,
  628. (unsigned char *)supportedFormats,
  629. mime_formats);
  630. sevent.xselection.property = req->property;
  631. sevent.xselection.target = XA_TARGETS;
  632. SDL_free(supportedFormats);
  633. } else {
  634. if (clipboard->callback) {
  635. for (i = 0; i < clipboard->mime_count; ++i) {
  636. const char *mime_type = clipboard->mime_types[i];
  637. if (X11_XInternAtom(display, mime_type, False) != req->target) {
  638. continue;
  639. }
  640. // FIXME: We don't support the X11 INCR protocol for large clipboards. Do we want that? - Yes, yes we do.
  641. // This is a safe cast, XChangeProperty() doesn't take a const value, but it doesn't modify the data
  642. seln_data = (unsigned char *)clipboard->callback(clipboard->userdata, mime_type, &seln_length);
  643. if (seln_data) {
  644. X11_XChangeProperty(display, req->requestor, req->property,
  645. req->target, 8, PropModeReplace,
  646. seln_data, seln_length);
  647. sevent.xselection.property = req->property;
  648. sevent.xselection.target = req->target;
  649. }
  650. break;
  651. }
  652. }
  653. }
  654. X11_XSendEvent(display, req->requestor, False, 0, &sevent);
  655. X11_XSync(display, False);
  656. } break;
  657. case SelectionNotify:
  658. {
  659. const XSelectionEvent *xsel = &xevent->xselection;
  660. #ifdef DEBUG_XEVENTS
  661. const char *propName = xsel->property ? X11_XGetAtomName(display, xsel->property) : "None";
  662. const char *targetName = xsel->target ? X11_XGetAtomName(display, xsel->target) : "None";
  663. SDL_Log("window CLIPBOARD: SelectionNotify (requestor = 0x%lx, target = %s, property = %s)",
  664. xsel->requestor, targetName, propName);
  665. #endif
  666. if (xsel->target == videodata->atoms.TARGETS && xsel->property == videodata->atoms.SDL_FORMATS) {
  667. /* the new mime formats are the SDL_FORMATS property as an array of Atoms */
  668. Atom atom = None;
  669. Atom *patom;
  670. unsigned char* data = NULL;
  671. int format_property = 0;
  672. unsigned long length = 0;
  673. unsigned long bytes_left = 0;
  674. int j;
  675. X11_XGetWindowProperty(display, GetWindow(_this), videodata->atoms.SDL_FORMATS, 0, 200,
  676. 0, XA_ATOM, &atom, &format_property, &length, &bytes_left, &data);
  677. int allocationsize = (length + 1) * sizeof(char*);
  678. for (j = 0, patom = (Atom*)data; j < length; j++, patom++) {
  679. char *atomStr = X11_XGetAtomName(display, *patom);
  680. allocationsize += SDL_strlen(atomStr) + 1;
  681. X11_XFree(atomStr);
  682. }
  683. char **new_mime_types = SDL_AllocateTemporaryMemory(allocationsize);
  684. if (new_mime_types) {
  685. char *strPtr = (char *)(new_mime_types + length + 1);
  686. for (j = 0, patom = (Atom*)data; j < length; j++, patom++) {
  687. char *atomStr = X11_XGetAtomName(display, *patom);
  688. new_mime_types[j] = strPtr;
  689. strPtr = stpcpy(strPtr, atomStr) + 1;
  690. X11_XFree(atomStr);
  691. }
  692. new_mime_types[length] = NULL;
  693. SDL_SendClipboardUpdate(false, new_mime_types, length);
  694. }
  695. if (data) {
  696. X11_XFree(data);
  697. }
  698. }
  699. videodata->selection_waiting = false;
  700. } break;
  701. case SelectionClear:
  702. {
  703. Atom XA_CLIPBOARD = videodata->atoms.CLIPBOARD;
  704. SDLX11_ClipboardData *clipboard = NULL;
  705. #ifdef DEBUG_XEVENTS
  706. SDL_Log("window CLIPBOARD: SelectionClear (requestor = 0x%lx, target = 0x%lx)",
  707. xevent->xselection.requestor, xevent->xselection.target);
  708. #endif
  709. if (xevent->xselectionclear.selection == XA_PRIMARY) {
  710. clipboard = &videodata->primary_selection;
  711. } else if (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD) {
  712. clipboard = &videodata->clipboard;
  713. }
  714. if (clipboard && clipboard->callback) {
  715. if (clipboard->sequence) {
  716. SDL_CancelClipboardData(clipboard->sequence);
  717. } else {
  718. SDL_free(clipboard->userdata);
  719. }
  720. SDL_zerop(clipboard);
  721. }
  722. } break;
  723. case PropertyNotify:
  724. {
  725. char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
  726. if (SDL_strncmp(name_of_atom, "SDL_SELECTION", sizeof("SDL_SELECTION") - 1) == 0 && xevent->xproperty.state == PropertyNewValue) {
  727. videodata->selection_incr_waiting = false;
  728. }
  729. if (name_of_atom) {
  730. X11_XFree(name_of_atom);
  731. }
  732. } break;
  733. }
  734. }
  735. static void X11_HandleSettingsEvent(SDL_VideoDevice *_this, const XEvent *xevent)
  736. {
  737. SDL_VideoData *videodata = _this->internal;
  738. SDL_assert(videodata->xsettings_window != None);
  739. SDL_assert(xevent->xany.window == videodata->xsettings_window);
  740. X11_HandleXsettings(_this, xevent);
  741. }
  742. static Bool isMapNotify(Display *display, XEvent *ev, XPointer arg)
  743. {
  744. XUnmapEvent *unmap;
  745. unmap = (XUnmapEvent *)arg;
  746. return ev->type == MapNotify &&
  747. ev->xmap.window == unmap->window &&
  748. ev->xmap.serial == unmap->serial;
  749. }
  750. static Bool isReparentNotify(Display *display, XEvent *ev, XPointer arg)
  751. {
  752. XUnmapEvent *unmap;
  753. unmap = (XUnmapEvent *)arg;
  754. return ev->type == ReparentNotify &&
  755. ev->xreparent.window == unmap->window &&
  756. ev->xreparent.serial == unmap->serial;
  757. }
  758. static bool IsHighLatin1(const char *string, int length)
  759. {
  760. while (length-- > 0) {
  761. Uint8 ch = (Uint8)*string;
  762. if (ch >= 0x80) {
  763. return true;
  764. }
  765. ++string;
  766. }
  767. return false;
  768. }
  769. static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out)
  770. {
  771. int result = X11_XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out);
  772. if (IsHighLatin1(buffer_return, result)) {
  773. char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", buffer_return, result + 1);
  774. if (utf8_text) {
  775. SDL_strlcpy(buffer_return, utf8_text, bytes_buffer);
  776. SDL_free(utf8_text);
  777. return SDL_strlen(buffer_return);
  778. } else {
  779. return 0;
  780. }
  781. }
  782. return result;
  783. }
  784. SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window)
  785. {
  786. const SDL_VideoData *videodata = _this->internal;
  787. int i;
  788. if (videodata && videodata->windowlist) {
  789. for (i = 0; i < videodata->numwindows; ++i) {
  790. if ((videodata->windowlist[i] != NULL) &&
  791. (videodata->windowlist[i]->xwindow == window)) {
  792. return videodata->windowlist[i];
  793. }
  794. }
  795. }
  796. return NULL;
  797. }
  798. Uint64 X11_GetEventTimestamp(unsigned long time)
  799. {
  800. // FIXME: Get the event time in the SDL tick time base
  801. return SDL_GetTicksNS();
  802. }
  803. void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent)
  804. {
  805. SDL_VideoData *videodata = _this->internal;
  806. Display *display = videodata->display;
  807. KeyCode keycode = xevent->xkey.keycode;
  808. KeySym keysym = NoSymbol;
  809. int text_length = 0;
  810. char text[64];
  811. Status status = 0;
  812. bool handled_by_ime = false;
  813. bool pressed = (xevent->type == KeyPress);
  814. SDL_Scancode scancode = videodata->key_layout[keycode];
  815. Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time);
  816. #ifdef DEBUG_XEVENTS
  817. SDL_Log("window 0x%lx %s (X11 keycode = 0x%X)", xevent->xany.window, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode);
  818. #endif
  819. #ifdef DEBUG_SCANCODES
  820. if (scancode == SDL_SCANCODE_UNKNOWN && keycode) {
  821. int min_keycode, max_keycode;
  822. X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
  823. keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13);
  824. 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).",
  825. keycode, keycode - min_keycode, keysym,
  826. X11_XKeysymToString(keysym));
  827. }
  828. #endif // DEBUG SCANCODES
  829. text[0] = '\0';
  830. videodata->xkb.xkb_modifiers = xevent->xkey.state;
  831. if (SDL_TextInputActive(windowdata->window)) {
  832. // filter events catches XIM events and sends them to the correct handler
  833. if (X11_XFilterEvent(xevent, None)) {
  834. #ifdef DEBUG_XEVENTS
  835. SDL_Log("Filtered event type = %d display = %p window = 0x%lx",
  836. xevent->type, xevent->xany.display, xevent->xany.window);
  837. #endif
  838. handled_by_ime = true;
  839. }
  840. if (!handled_by_ime) {
  841. #ifdef X_HAVE_UTF8_STRING
  842. if (windowdata->ic && xevent->type == KeyPress) {
  843. text_length = X11_Xutf8LookupString(windowdata->ic, &xevent->xkey, text, sizeof(text) - 1,
  844. &keysym, &status);
  845. } else {
  846. text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL);
  847. }
  848. #else
  849. text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL);
  850. #endif
  851. }
  852. }
  853. if (!handled_by_ime) {
  854. if (pressed) {
  855. X11_HandleModifierKeys(videodata, scancode, true, true);
  856. SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true);
  857. if (*text) {
  858. text[text_length] = '\0';
  859. X11_ClearComposition(windowdata);
  860. SDL_SendKeyboardText(text);
  861. }
  862. } else {
  863. if (X11_KeyRepeat(display, xevent)) {
  864. // We're about to get a repeated key down, ignore the key up
  865. return;
  866. }
  867. X11_HandleModifierKeys(videodata, scancode, false, true);
  868. SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false);
  869. }
  870. }
  871. if (pressed) {
  872. X11_UpdateUserTime(windowdata, xevent->xkey.time);
  873. }
  874. }
  875. void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time)
  876. {
  877. SDL_Window *window = windowdata->window;
  878. const SDL_VideoData *videodata = _this->internal;
  879. Display *display = videodata->display;
  880. int xticks = 0, yticks = 0;
  881. Uint64 timestamp = X11_GetEventTimestamp(time);
  882. #ifdef DEBUG_XEVENTS
  883. SDL_Log("window 0x%lx: ButtonPress (X11 button = %d)", windowdata->xwindow, button);
  884. #endif
  885. SDL_Mouse *mouse = SDL_GetMouse();
  886. if (!mouse->relative_mode && (x != mouse->x || y != mouse->y)) {
  887. X11_ProcessHitTest(_this, windowdata, x, y, false);
  888. SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y);
  889. }
  890. if (X11_IsWheelEvent(display, button, &xticks, &yticks)) {
  891. SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL);
  892. } else {
  893. bool ignore_click = false;
  894. if (button > 7) {
  895. /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ...
  896. => subtract (8-SDL_BUTTON_X1) to get value SDL expects */
  897. button -= (8 - SDL_BUTTON_X1);
  898. }
  899. if (button == Button1) {
  900. if (X11_TriggerHitTestAction(_this, windowdata, x, y)) {
  901. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
  902. return; // don't pass this event on to app.
  903. }
  904. }
  905. if (windowdata->last_focus_event_time) {
  906. const int X11_FOCUS_CLICK_TIMEOUT = 10;
  907. if (SDL_GetTicks() < (windowdata->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
  908. ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false);
  909. }
  910. windowdata->last_focus_event_time = 0;
  911. }
  912. if (!ignore_click) {
  913. SDL_SendMouseButton(timestamp, window, mouseID, button, true);
  914. }
  915. }
  916. X11_UpdateUserTime(windowdata, time);
  917. }
  918. void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time)
  919. {
  920. SDL_Window *window = windowdata->window;
  921. const SDL_VideoData *videodata = _this->internal;
  922. Display *display = videodata->display;
  923. // The X server sends a Release event for each Press for wheels. Ignore them.
  924. int xticks = 0, yticks = 0;
  925. Uint64 timestamp = X11_GetEventTimestamp(time);
  926. #ifdef DEBUG_XEVENTS
  927. SDL_Log("window 0x%lx: ButtonRelease (X11 button = %d)", windowdata->xwindow, button);
  928. #endif
  929. if (!X11_IsWheelEvent(display, button, &xticks, &yticks)) {
  930. if (button > 7) {
  931. // see explanation at case ButtonPress
  932. button -= (8 - SDL_BUTTON_X1);
  933. }
  934. SDL_SendMouseButton(timestamp, window, mouseID, button, false);
  935. }
  936. }
  937. void X11_GetBorderValues(SDL_WindowData *data)
  938. {
  939. SDL_VideoData *videodata = data->videodata;
  940. Display *display = videodata->display;
  941. Atom type;
  942. int format;
  943. unsigned long nitems, bytes_after;
  944. unsigned char *property;
  945. // Some compositors will send extents even when the border hint is turned off. Ignore them in this case.
  946. if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) {
  947. if (X11_XGetWindowProperty(display, data->xwindow, videodata->atoms._NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
  948. if (type != None && nitems == 4) {
  949. data->border_left = (int)((long *)property)[0];
  950. data->border_right = (int)((long *)property)[1];
  951. data->border_top = (int)((long *)property)[2];
  952. data->border_bottom = (int)((long *)property)[3];
  953. }
  954. X11_XFree(property);
  955. #ifdef DEBUG_XEVENTS
  956. SDL_Log("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d", data->border_left, data->border_right, data->border_top, data->border_bottom);
  957. #endif
  958. }
  959. } else {
  960. data->border_left = data->border_top = data->border_right = data->border_bottom = 0;
  961. }
  962. }
  963. static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
  964. {
  965. SDL_VideoData *videodata = _this->internal;
  966. Display *display;
  967. SDL_WindowData *data;
  968. XClientMessageEvent m;
  969. int i;
  970. SDL_assert(videodata != NULL);
  971. display = videodata->display;
  972. // filter events catches XIM events and sends them to the correct handler
  973. // Key press/release events are filtered in X11_HandleKeyEvent()
  974. if (xevent->type != KeyPress && xevent->type != KeyRelease) {
  975. if (X11_XFilterEvent(xevent, None)) {
  976. #ifdef DEBUG_XEVENTS
  977. SDL_Log("Filtered event type = %d display = %p window = 0x%lx",
  978. xevent->type, xevent->xany.display, xevent->xany.window);
  979. #endif
  980. return;
  981. }
  982. }
  983. #ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
  984. if (xevent->type == GenericEvent) {
  985. X11_HandleGenericEvent(_this, xevent);
  986. return;
  987. }
  988. #endif
  989. // Calling the event hook for generic events happens in X11_HandleGenericEvent(), where the event data is available
  990. if (g_X11EventHook) {
  991. if (!g_X11EventHook(g_X11EventHookData, xevent)) {
  992. return;
  993. }
  994. }
  995. #ifdef SDL_VIDEO_DRIVER_X11_XRANDR
  996. if (videodata->xrandr_event_base && (xevent->type == (videodata->xrandr_event_base + RRNotify))) {
  997. X11_HandleXRandREvent(_this, xevent);
  998. }
  999. #endif
  1000. #ifdef DEBUG_XEVENTS
  1001. SDL_Log("X11 event type = %d display = %p window = 0x%lx",
  1002. xevent->type, xevent->xany.display, xevent->xany.window);
  1003. #endif
  1004. #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
  1005. if (SDL_X11_HAVE_XFIXES &&
  1006. xevent->type == X11_GetXFixesSelectionNotifyEvent()) {
  1007. XFixesSelectionNotifyEvent *ev = (XFixesSelectionNotifyEvent *)xevent;
  1008. #ifdef DEBUG_XEVENTS
  1009. SDL_Log("window CLIPBOARD: XFixesSelectionNotify (selection = %s)",
  1010. X11_XGetAtomName(display, ev->selection));
  1011. #endif
  1012. if (ev->subtype == XFixesSetSelectionOwnerNotify)
  1013. {
  1014. if (ev->selection != videodata->atoms.CLIPBOARD)
  1015. return;
  1016. if (X11_XGetSelectionOwner(display, ev->selection) == videodata->clipboard_window)
  1017. return;
  1018. /* when here we're notified that the clipboard had an external change, we request the
  1019. * available mime types by asking for a conversion to the TARGETS format. We should get a
  1020. * SelectionNotify event later, and when treating these results, we will push a ClipboardUpdated
  1021. * event
  1022. */
  1023. X11_XConvertSelection(display, videodata->atoms.CLIPBOARD, videodata->atoms.TARGETS,
  1024. videodata->atoms.SDL_FORMATS, GetWindow(_this), CurrentTime);
  1025. }
  1026. return;
  1027. }
  1028. #endif // SDL_VIDEO_DRIVER_X11_XFIXES
  1029. if ((videodata->clipboard_window != None) &&
  1030. (videodata->clipboard_window == xevent->xany.window)) {
  1031. X11_HandleClipboardEvent(_this, xevent);
  1032. return;
  1033. }
  1034. if ((videodata->xsettings_window != None) &&
  1035. (videodata->xsettings_window == xevent->xany.window)) {
  1036. X11_HandleSettingsEvent(_this, xevent);
  1037. return;
  1038. }
  1039. data = X11_FindWindow(_this, xevent->xany.window);
  1040. if (!data) {
  1041. // The window for KeymapNotify, etc events is 0
  1042. if (xevent->type == KeymapNotify) {
  1043. #ifdef DEBUG_XEVENTS
  1044. SDL_Log("window 0x%lx: KeymapNotify!", xevent->xany.window);
  1045. #endif
  1046. if (SDL_GetKeyboardFocus() != NULL) {
  1047. #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
  1048. if (videodata->xkb.desc_ptr) {
  1049. XkbStateRec state;
  1050. if (X11_XkbGetState(videodata->display, XkbUseCoreKbd, &state) == Success) {
  1051. if (state.group != videodata->xkb.current_group) {
  1052. // Only rebuild the keymap if the layout has changed.
  1053. videodata->xkb.current_group = state.group;
  1054. X11_UpdateKeymap(_this, true);
  1055. }
  1056. }
  1057. }
  1058. #endif
  1059. X11_ReconcileKeyboardState(_this);
  1060. }
  1061. } else if (xevent->type == MappingNotify) {
  1062. // Has the keyboard layout changed?
  1063. const int request = xevent->xmapping.request;
  1064. #ifdef DEBUG_XEVENTS
  1065. SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window);
  1066. #endif
  1067. if ((request == MappingKeyboard) || (request == MappingModifier)) {
  1068. X11_XRefreshKeyboardMapping(&xevent->xmapping);
  1069. }
  1070. X11_UpdateKeymap(_this, true);
  1071. } else if (xevent->type == PropertyNotify && videodata && videodata->windowlist) {
  1072. char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
  1073. if (SDL_strncmp(name_of_atom, "_ICC_PROFILE", sizeof("_ICC_PROFILE") - 1) == 0) {
  1074. XWindowAttributes attrib;
  1075. int screennum;
  1076. for (i = 0; i < videodata->numwindows; ++i) {
  1077. if (videodata->windowlist[i] != NULL) {
  1078. data = videodata->windowlist[i];
  1079. X11_XGetWindowAttributes(display, data->xwindow, &attrib);
  1080. screennum = X11_XScreenNumberOfScreen(attrib.screen);
  1081. if (screennum == 0 && SDL_strcmp(name_of_atom, "_ICC_PROFILE") == 0) {
  1082. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
  1083. } else if (SDL_strncmp(name_of_atom, "_ICC_PROFILE_", sizeof("_ICC_PROFILE_") - 1) == 0 && SDL_strlen(name_of_atom) > sizeof("_ICC_PROFILE_") - 1) {
  1084. int iccscreennum = SDL_atoi(&name_of_atom[sizeof("_ICC_PROFILE_") - 1]);
  1085. if (screennum == iccscreennum) {
  1086. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
  1087. }
  1088. }
  1089. }
  1090. }
  1091. }
  1092. if (name_of_atom) {
  1093. X11_XFree(name_of_atom);
  1094. }
  1095. }
  1096. return;
  1097. }
  1098. switch (xevent->type) {
  1099. // Gaining mouse coverage?
  1100. case EnterNotify:
  1101. {
  1102. SDL_Mouse *mouse = SDL_GetMouse();
  1103. #ifdef DEBUG_XEVENTS
  1104. SDL_Log("window 0x%lx: EnterNotify! (%d,%d,%d)", xevent->xany.window,
  1105. xevent->xcrossing.x,
  1106. xevent->xcrossing.y,
  1107. xevent->xcrossing.mode);
  1108. if (xevent->xcrossing.mode == NotifyGrab) {
  1109. SDL_Log("Mode: NotifyGrab");
  1110. }
  1111. if (xevent->xcrossing.mode == NotifyUngrab) {
  1112. SDL_Log("Mode: NotifyUngrab");
  1113. }
  1114. #endif
  1115. SDL_SetMouseFocus(data->window);
  1116. mouse->last_x = xevent->xcrossing.x;
  1117. mouse->last_y = xevent->xcrossing.y;
  1118. #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
  1119. {
  1120. // Only create the barriers if we have input focus
  1121. SDL_WindowData *windowdata = data->window->internal;
  1122. if ((data->pointer_barrier_active == true) && windowdata->window->flags & SDL_WINDOW_INPUT_FOCUS) {
  1123. X11_ConfineCursorWithFlags(_this, windowdata->window, &windowdata->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
  1124. }
  1125. }
  1126. #endif
  1127. if (!mouse->relative_mode) {
  1128. SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
  1129. }
  1130. // We ungrab in LeaveNotify, so we may need to grab again here
  1131. SDL_UpdateWindowGrab(data->window);
  1132. X11_ProcessHitTest(_this, data, mouse->last_x, mouse->last_y, true);
  1133. } break;
  1134. // Losing mouse coverage?
  1135. case LeaveNotify:
  1136. {
  1137. #ifdef DEBUG_XEVENTS
  1138. SDL_Log("window 0x%lx: LeaveNotify! (%d,%d,%d)", xevent->xany.window,
  1139. xevent->xcrossing.x,
  1140. xevent->xcrossing.y,
  1141. xevent->xcrossing.mode);
  1142. if (xevent->xcrossing.mode == NotifyGrab) {
  1143. SDL_Log("Mode: NotifyGrab");
  1144. }
  1145. if (xevent->xcrossing.mode == NotifyUngrab) {
  1146. SDL_Log("Mode: NotifyUngrab");
  1147. }
  1148. #endif
  1149. if (!SDL_GetMouse()->relative_mode) {
  1150. SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
  1151. }
  1152. if (xevent->xcrossing.mode != NotifyGrab &&
  1153. xevent->xcrossing.mode != NotifyUngrab &&
  1154. xevent->xcrossing.detail != NotifyInferior) {
  1155. /* In order for interaction with the window decorations and menu to work properly
  1156. on Mutter, we need to ungrab the keyboard when the the mouse leaves. */
  1157. if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) {
  1158. X11_SetWindowKeyboardGrab(_this, data->window, false);
  1159. }
  1160. SDL_SetMouseFocus(NULL);
  1161. }
  1162. } break;
  1163. // Gaining input focus?
  1164. case FocusIn:
  1165. {
  1166. if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) {
  1167. // Someone is handling a global hotkey, ignore it
  1168. #ifdef DEBUG_XEVENTS
  1169. SDL_Log("window 0x%lx: FocusIn (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window);
  1170. #endif
  1171. break;
  1172. }
  1173. if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) {
  1174. #ifdef DEBUG_XEVENTS
  1175. SDL_Log("window 0x%lx: FocusIn (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window);
  1176. #endif
  1177. break;
  1178. }
  1179. #ifdef DEBUG_XEVENTS
  1180. SDL_Log("window 0x%lx: FocusIn!", xevent->xany.window);
  1181. #endif
  1182. if (!videodata->last_mode_change_deadline) /* no recent mode changes */ {
  1183. data->pending_focus = PENDING_FOCUS_NONE;
  1184. data->pending_focus_time = 0;
  1185. X11_DispatchFocusIn(_this, data);
  1186. } else {
  1187. data->pending_focus = PENDING_FOCUS_IN;
  1188. data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
  1189. }
  1190. data->last_focus_event_time = SDL_GetTicks();
  1191. } break;
  1192. // Losing input focus?
  1193. case FocusOut:
  1194. {
  1195. if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) {
  1196. // Someone is handling a global hotkey, ignore it
  1197. #ifdef DEBUG_XEVENTS
  1198. SDL_Log("window 0x%lx: FocusOut (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window);
  1199. #endif
  1200. break;
  1201. }
  1202. if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) {
  1203. /* We still have focus if a child gets focus. We also don't
  1204. care about the position of the pointer when the keyboard
  1205. focus changed. */
  1206. #ifdef DEBUG_XEVENTS
  1207. SDL_Log("window 0x%lx: FocusOut (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window);
  1208. #endif
  1209. break;
  1210. }
  1211. #ifdef DEBUG_XEVENTS
  1212. SDL_Log("window 0x%lx: FocusOut!", xevent->xany.window);
  1213. #endif
  1214. if (!videodata->last_mode_change_deadline) /* no recent mode changes */ {
  1215. data->pending_focus = PENDING_FOCUS_NONE;
  1216. data->pending_focus_time = 0;
  1217. X11_DispatchFocusOut(_this, data);
  1218. } else {
  1219. data->pending_focus = PENDING_FOCUS_OUT;
  1220. data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
  1221. }
  1222. #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
  1223. // Disable confinement if it is activated.
  1224. if (data->pointer_barrier_active == true) {
  1225. X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
  1226. }
  1227. #endif // SDL_VIDEO_DRIVER_X11_XFIXES
  1228. } break;
  1229. // Have we been iconified?
  1230. case UnmapNotify:
  1231. {
  1232. XEvent ev;
  1233. #ifdef DEBUG_XEVENTS
  1234. SDL_Log("window 0x%lx: UnmapNotify!", xevent->xany.window);
  1235. #endif
  1236. if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent->xunmap)) {
  1237. X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent->xunmap);
  1238. } else {
  1239. X11_DispatchUnmapNotify(data);
  1240. }
  1241. #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
  1242. // Disable confinement if the window gets hidden.
  1243. if (data->pointer_barrier_active == true) {
  1244. X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
  1245. }
  1246. #endif // SDL_VIDEO_DRIVER_X11_XFIXES
  1247. } break;
  1248. // Have we been restored?
  1249. case MapNotify:
  1250. {
  1251. #ifdef DEBUG_XEVENTS
  1252. SDL_Log("window 0x%lx: MapNotify!", xevent->xany.window);
  1253. #endif
  1254. X11_DispatchMapNotify(data);
  1255. #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
  1256. // Enable confinement if it was activated.
  1257. if (data->pointer_barrier_active == true) {
  1258. X11_ConfineCursorWithFlags(_this, data->window, &data->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
  1259. }
  1260. #endif // SDL_VIDEO_DRIVER_X11_XFIXES
  1261. } break;
  1262. // Have we been resized or moved?
  1263. case ConfigureNotify:
  1264. {
  1265. #ifdef DEBUG_XEVENTS
  1266. SDL_Log("window 0x%lx: ConfigureNotify! (position: %d,%d, size: %dx%d)", xevent->xany.window,
  1267. xevent->xconfigure.x, xevent->xconfigure.y,
  1268. xevent->xconfigure.width, xevent->xconfigure.height);
  1269. #endif
  1270. // Real configure notify events are relative to the parent, synthetic events are absolute.
  1271. if (!xevent->xconfigure.send_event)
  1272. {
  1273. unsigned int NumChildren;
  1274. Window ChildReturn, Root, Parent;
  1275. Window *Children;
  1276. // Translate these coordinates back to relative to root
  1277. X11_XQueryTree(data->videodata->display, xevent->xconfigure.window, &Root, &Parent, &Children, &NumChildren);
  1278. X11_XTranslateCoordinates(xevent->xconfigure.display,
  1279. Parent, DefaultRootWindow(xevent->xconfigure.display),
  1280. xevent->xconfigure.x, xevent->xconfigure.y,
  1281. &xevent->xconfigure.x, &xevent->xconfigure.y,
  1282. &ChildReturn);
  1283. }
  1284. if (xevent->xconfigure.x != data->last_xconfigure.x ||
  1285. xevent->xconfigure.y != data->last_xconfigure.y) {
  1286. if (!data->size_move_event_flags) {
  1287. SDL_Window *w;
  1288. int x = xevent->xconfigure.x;
  1289. int y = xevent->xconfigure.y;
  1290. data->pending_operation &= ~X11_PENDING_OP_MOVE;
  1291. SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
  1292. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
  1293. for (w = data->window->first_child; w; w = w->next_sibling) {
  1294. // Don't update hidden child popup windows, their relative position doesn't change
  1295. if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) {
  1296. X11_UpdateWindowPosition(w, true);
  1297. }
  1298. }
  1299. }
  1300. }
  1301. #ifdef SDL_VIDEO_DRIVER_X11_XSYNC
  1302. X11_HandleConfigure(data->window, &xevent->xconfigure);
  1303. #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
  1304. if (xevent->xconfigure.width != data->last_xconfigure.width ||
  1305. xevent->xconfigure.height != data->last_xconfigure.height) {
  1306. if (!data->size_move_event_flags) {
  1307. data->pending_operation &= ~X11_PENDING_OP_RESIZE;
  1308. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
  1309. xevent->xconfigure.width,
  1310. xevent->xconfigure.height);
  1311. }
  1312. }
  1313. data->last_xconfigure = xevent->xconfigure;
  1314. } break;
  1315. // Have we been requested to quit (or another client message?)
  1316. case ClientMessage:
  1317. {
  1318. static int xdnd_version = 0;
  1319. if (xevent->xclient.message_type == videodata->atoms.XdndEnter) {
  1320. bool use_list = xevent->xclient.data.l[1] & 1;
  1321. data->xdnd_source = xevent->xclient.data.l[0];
  1322. xdnd_version = (xevent->xclient.data.l[1] >> 24);
  1323. #ifdef DEBUG_XEVENTS
  1324. SDL_Log("XID of source window : 0x%lx", data->xdnd_source);
  1325. SDL_Log("Protocol version to use : %d", xdnd_version);
  1326. SDL_Log("More then 3 data types : %d", (int)use_list);
  1327. #endif
  1328. if (use_list) {
  1329. // fetch conversion targets
  1330. SDL_x11Prop p;
  1331. X11_ReadProperty(&p, display, data->xdnd_source, videodata->atoms.XdndTypeList);
  1332. // pick one
  1333. data->xdnd_req = X11_PickTarget(display, (Atom *)p.data, p.count);
  1334. X11_XFree(p.data);
  1335. } else {
  1336. // pick from list of three
  1337. data->xdnd_req = X11_PickTargetFromAtoms(display, xevent->xclient.data.l[2], xevent->xclient.data.l[3], xevent->xclient.data.l[4]);
  1338. }
  1339. } else if (xevent->xclient.message_type == videodata->atoms.XdndLeave) {
  1340. #ifdef DEBUG_XEVENTS
  1341. SDL_Log("XID of source window : 0x%lx", xevent->xclient.data.l[0]);
  1342. #endif
  1343. SDL_SendDropComplete(data->window);
  1344. } else if (xevent->xclient.message_type == videodata->atoms.XdndPosition) {
  1345. #ifdef DEBUG_XEVENTS
  1346. Atom act = videodata->atoms.XdndActionCopy;
  1347. if (xdnd_version >= 2) {
  1348. act = xevent->xclient.data.l[4];
  1349. }
  1350. SDL_Log("Action requested by user is : %s", X11_XGetAtomName(display, act));
  1351. #endif
  1352. {
  1353. // Drag and Drop position
  1354. int root_x, root_y, window_x, window_y;
  1355. Window ChildReturn;
  1356. root_x = xevent->xclient.data.l[2] >> 16;
  1357. root_y = xevent->xclient.data.l[2] & 0xffff;
  1358. // Translate from root to current window position
  1359. X11_XTranslateCoordinates(display, DefaultRootWindow(display), data->xwindow,
  1360. root_x, root_y, &window_x, &window_y, &ChildReturn);
  1361. SDL_SendDropPosition(data->window, (float)window_x, (float)window_y);
  1362. }
  1363. // reply with status
  1364. SDL_memset(&m, 0, sizeof(XClientMessageEvent));
  1365. m.type = ClientMessage;
  1366. m.display = xevent->xclient.display;
  1367. m.window = xevent->xclient.data.l[0];
  1368. m.message_type = videodata->atoms.XdndStatus;
  1369. m.format = 32;
  1370. m.data.l[0] = data->xwindow;
  1371. m.data.l[1] = (data->xdnd_req != None);
  1372. m.data.l[2] = 0; // specify an empty rectangle
  1373. m.data.l[3] = 0;
  1374. m.data.l[4] = videodata->atoms.XdndActionCopy; // we only accept copying anyway
  1375. X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
  1376. X11_XFlush(display);
  1377. } else if (xevent->xclient.message_type == videodata->atoms.XdndDrop) {
  1378. if (data->xdnd_req == None) {
  1379. // say again - not interested!
  1380. SDL_memset(&m, 0, sizeof(XClientMessageEvent));
  1381. m.type = ClientMessage;
  1382. m.display = xevent->xclient.display;
  1383. m.window = xevent->xclient.data.l[0];
  1384. m.message_type = videodata->atoms.XdndFinished;
  1385. m.format = 32;
  1386. m.data.l[0] = data->xwindow;
  1387. m.data.l[1] = 0;
  1388. m.data.l[2] = None; // fail!
  1389. X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
  1390. } else {
  1391. // convert
  1392. if (xdnd_version >= 1) {
  1393. X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, xevent->xclient.data.l[2]);
  1394. } else {
  1395. X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, CurrentTime);
  1396. }
  1397. }
  1398. } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) &&
  1399. (xevent->xclient.format == 32) &&
  1400. (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_PING)) {
  1401. Window root = DefaultRootWindow(display);
  1402. #ifdef DEBUG_XEVENTS
  1403. SDL_Log("window 0x%lx: _NET_WM_PING", xevent->xany.window);
  1404. #endif
  1405. xevent->xclient.window = root;
  1406. X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, xevent);
  1407. break;
  1408. }
  1409. else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) &&
  1410. (xevent->xclient.format == 32) &&
  1411. (xevent->xclient.data.l[0] == videodata->atoms.WM_DELETE_WINDOW)) {
  1412. #ifdef DEBUG_XEVENTS
  1413. SDL_Log("window 0x%lx: WM_DELETE_WINDOW", xevent->xany.window);
  1414. #endif
  1415. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0);
  1416. break;
  1417. } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) &&
  1418. (xevent->xclient.format == 32) &&
  1419. (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_SYNC_REQUEST)) {
  1420. #ifdef DEBUG_XEVENTS
  1421. printf("window %p: _NET_WM_SYNC_REQUEST\n", data);
  1422. #endif
  1423. #ifdef SDL_VIDEO_DRIVER_X11_XSYNC
  1424. X11_HandleSyncRequest(data->window, &xevent->xclient);
  1425. #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
  1426. break;
  1427. }
  1428. } break;
  1429. // Do we need to refresh ourselves?
  1430. case Expose:
  1431. {
  1432. #ifdef DEBUG_XEVENTS
  1433. SDL_Log("window 0x%lx: Expose (count = %d)", xevent->xany.window, xevent->xexpose.count);
  1434. #endif
  1435. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
  1436. } break;
  1437. /* Use XInput2 instead of the xevents API if possible, for:
  1438. - KeyPress
  1439. - KeyRelease
  1440. - MotionNotify
  1441. - ButtonPress
  1442. - ButtonRelease
  1443. XInput2 has more precise information, e.g., to distinguish different input devices. */
  1444. case KeyPress:
  1445. case KeyRelease:
  1446. {
  1447. if (data->xinput2_keyboard_enabled) {
  1448. // This input is being handled by XInput2
  1449. break;
  1450. }
  1451. X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent);
  1452. } break;
  1453. case MotionNotify:
  1454. {
  1455. if (data->xinput2_mouse_enabled && !data->mouse_grabbed) {
  1456. // This input is being handled by XInput2
  1457. break;
  1458. }
  1459. SDL_Mouse *mouse = SDL_GetMouse();
  1460. if (!mouse->relative_mode) {
  1461. #ifdef DEBUG_MOTION
  1462. SDL_Log("window 0x%lx: X11 motion: %d,%d", xevent->xany.window, xevent->xmotion.x, xevent->xmotion.y);
  1463. #endif
  1464. X11_ProcessHitTest(_this, data, (float)xevent->xmotion.x, (float)xevent->xmotion.y, false);
  1465. SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xmotion.x, (float)xevent->xmotion.y);
  1466. }
  1467. } break;
  1468. case ButtonPress:
  1469. {
  1470. if (data->xinput2_mouse_enabled) {
  1471. // This input is being handled by XInput2
  1472. break;
  1473. }
  1474. X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button,
  1475. xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time);
  1476. } break;
  1477. case ButtonRelease:
  1478. {
  1479. if (data->xinput2_mouse_enabled) {
  1480. // This input is being handled by XInput2
  1481. break;
  1482. }
  1483. X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, xevent->xbutton.time);
  1484. } break;
  1485. case PropertyNotify:
  1486. {
  1487. #ifdef DEBUG_XEVENTS
  1488. unsigned char *propdata;
  1489. int status, real_format;
  1490. Atom real_type;
  1491. unsigned long items_read, items_left;
  1492. char *name = X11_XGetAtomName(display, xevent->xproperty.atom);
  1493. if (name) {
  1494. SDL_Log("window 0x%lx: PropertyNotify: %s %s time=%lu", xevent->xany.window, name, (xevent->xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent->xproperty.time);
  1495. X11_XFree(name);
  1496. }
  1497. status = X11_XGetWindowProperty(display, data->xwindow, xevent->xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata);
  1498. if (status == Success && items_read > 0) {
  1499. if (real_type == XA_INTEGER) {
  1500. int *values = (int *)propdata;
  1501. SDL_Log("{");
  1502. for (i = 0; i < items_read; i++) {
  1503. SDL_Log(" %d", values[i]);
  1504. }
  1505. SDL_Log(" }");
  1506. } else if (real_type == XA_CARDINAL) {
  1507. if (real_format == 32) {
  1508. Uint32 *values = (Uint32 *)propdata;
  1509. SDL_Log("{");
  1510. for (i = 0; i < items_read; i++) {
  1511. SDL_Log(" %d", values[i]);
  1512. }
  1513. SDL_Log(" }");
  1514. } else if (real_format == 16) {
  1515. Uint16 *values = (Uint16 *)propdata;
  1516. SDL_Log("{");
  1517. for (i = 0; i < items_read; i++) {
  1518. SDL_Log(" %d", values[i]);
  1519. }
  1520. SDL_Log(" }");
  1521. } else if (real_format == 8) {
  1522. Uint8 *values = (Uint8 *)propdata;
  1523. SDL_Log("{");
  1524. for (i = 0; i < items_read; i++) {
  1525. SDL_Log(" %d", values[i]);
  1526. }
  1527. SDL_Log(" }");
  1528. }
  1529. } else if (real_type == XA_STRING ||
  1530. real_type == videodata->atoms.UTF8_STRING) {
  1531. SDL_Log("{ \"%s\" }", propdata);
  1532. } else if (real_type == XA_ATOM) {
  1533. Atom *atoms = (Atom *)propdata;
  1534. SDL_Log("{");
  1535. for (i = 0; i < items_read; i++) {
  1536. char *atomname = X11_XGetAtomName(display, atoms[i]);
  1537. if (atomname) {
  1538. SDL_Log(" %s", atomname);
  1539. X11_XFree(atomname);
  1540. }
  1541. }
  1542. SDL_Log(" }");
  1543. } else {
  1544. char *atomname = X11_XGetAtomName(display, real_type);
  1545. SDL_Log("Unknown type: 0x%lx (%s)", real_type, atomname ? atomname : "UNKNOWN");
  1546. if (atomname) {
  1547. X11_XFree(atomname);
  1548. }
  1549. }
  1550. }
  1551. if (status == Success) {
  1552. X11_XFree(propdata);
  1553. }
  1554. #endif // DEBUG_XEVENTS
  1555. /* Take advantage of this moment to make sure user_time has a
  1556. valid timestamp from the X server, so if we later try to
  1557. raise/restore this window, _NET_ACTIVE_WINDOW can have a
  1558. non-zero timestamp, even if there's never been a mouse or
  1559. key press to this window so far. Note that we don't try to
  1560. set _NET_WM_USER_TIME here, though. That's only for legit
  1561. user interaction with the window. */
  1562. if (!data->user_time) {
  1563. data->user_time = xevent->xproperty.time;
  1564. }
  1565. if (xevent->xproperty.atom == data->videodata->atoms._NET_WM_STATE) {
  1566. /* Get the new state from the window manager.
  1567. Compositing window managers can alter visibility of windows
  1568. without ever mapping / unmapping them, so we handle that here,
  1569. because they use the NETWM protocol to notify us of changes.
  1570. */
  1571. const SDL_WindowFlags flags = X11_GetNetWMState(_this, data->window, xevent->xproperty.window);
  1572. const SDL_WindowFlags changed = flags ^ data->window->flags;
  1573. if ((changed & (SDL_WINDOW_HIDDEN | SDL_WINDOW_FULLSCREEN)) != 0) {
  1574. if (flags & SDL_WINDOW_HIDDEN) {
  1575. X11_DispatchUnmapNotify(data);
  1576. } else {
  1577. X11_DispatchMapNotify(data);
  1578. }
  1579. }
  1580. if (!SDL_WINDOW_IS_POPUP(data->window)) {
  1581. if (changed & SDL_WINDOW_FULLSCREEN) {
  1582. data->pending_operation &= ~X11_PENDING_OP_FULLSCREEN;
  1583. if (flags & SDL_WINDOW_FULLSCREEN) {
  1584. if (!(flags & SDL_WINDOW_MINIMIZED)) {
  1585. const bool commit = SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0;
  1586. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
  1587. if (commit) {
  1588. /* This was initiated by the compositor, or the mode was changed between the request and the window
  1589. * becoming fullscreen. Switch to the application requested mode if necessary.
  1590. */
  1591. SDL_copyp(&data->window->current_fullscreen_mode, &data->window->requested_fullscreen_mode);
  1592. SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_UPDATE, true);
  1593. } else {
  1594. SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_ENTER, false);
  1595. }
  1596. }
  1597. } else {
  1598. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
  1599. SDL_UpdateFullscreenMode(data->window, false, false);
  1600. SDL_zero(data->requested_fullscreen_mode);
  1601. // Need to restore or update any limits changed while the window was fullscreen.
  1602. X11_SetWindowMinMax(data->window, !!(flags & SDL_WINDOW_MAXIMIZED));
  1603. // Toggle the borders if they were forced on while creating a borderless fullscreen window.
  1604. if (data->fullscreen_borders_forced_on) {
  1605. data->toggle_borders = true;
  1606. data->fullscreen_borders_forced_on = false;
  1607. }
  1608. }
  1609. if ((flags & SDL_WINDOW_FULLSCREEN) &&
  1610. (data->border_top || data->border_left || data->border_bottom || data->border_right)) {
  1611. /* If the window is entering fullscreen and the borders are
  1612. * non-zero sized, turn off size events until the borders are
  1613. * shut off to avoid bogus window sizes and positions, and
  1614. * note that the old borders were non-zero for restoration.
  1615. */
  1616. data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
  1617. data->previous_borders_nonzero = true;
  1618. } else if (!(flags & SDL_WINDOW_FULLSCREEN) &&
  1619. data->previous_borders_nonzero &&
  1620. (!data->border_top && !data->border_left && !data->border_bottom && !data->border_right)) {
  1621. /* If the window is leaving fullscreen and the current borders
  1622. * are zero sized, but weren't when entering fullscreen, turn
  1623. * off size events until the borders come back to avoid bogus
  1624. * window sizes and positions.
  1625. */
  1626. data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
  1627. data->previous_borders_nonzero = false;
  1628. } else {
  1629. data->size_move_event_flags = 0;
  1630. data->previous_borders_nonzero = false;
  1631. if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
  1632. data->toggle_borders = false;
  1633. X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS));
  1634. }
  1635. }
  1636. }
  1637. if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
  1638. data->pending_operation &= ~X11_PENDING_OP_MAXIMIZE;
  1639. if ((changed & SDL_WINDOW_MINIMIZED)) {
  1640. data->pending_operation &= ~X11_PENDING_OP_RESTORE;
  1641. // If coming out of minimized, send a restore event before sending maximized.
  1642. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
  1643. }
  1644. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
  1645. }
  1646. if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) {
  1647. data->pending_operation &= ~X11_PENDING_OP_MINIMIZE;
  1648. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
  1649. }
  1650. if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
  1651. data->pending_operation &= ~X11_PENDING_OP_RESTORE;
  1652. SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
  1653. // Apply any pending state if restored.
  1654. if (!(flags & SDL_WINDOW_FULLSCREEN)) {
  1655. if (data->pending_position) {
  1656. data->pending_position = false;
  1657. data->pending_operation |= X11_PENDING_OP_MOVE;
  1658. data->expected.x = data->window->pending.x - data->border_left;
  1659. data->expected.y = data->window->pending.y - data->border_top;
  1660. X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y);
  1661. }
  1662. if (data->pending_size) {
  1663. data->pending_size = false;
  1664. data->pending_operation |= X11_PENDING_OP_RESIZE;
  1665. data->expected.w = data->window->pending.w;
  1666. data->expected.h = data->window->pending.h;
  1667. X11_XResizeWindow(display, data->xwindow, data->window->pending.w, data->window->pending.h);
  1668. }
  1669. }
  1670. }
  1671. if ((flags & SDL_WINDOW_INPUT_FOCUS)) {
  1672. if (data->pending_move) {
  1673. DispatchWindowMove(_this, data, &data->pending_move_point);
  1674. data->pending_move = false;
  1675. }
  1676. }
  1677. }
  1678. if (changed & SDL_WINDOW_OCCLUDED) {
  1679. SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0);
  1680. }
  1681. } else if (xevent->xproperty.atom == videodata->atoms.XKLAVIER_STATE) {
  1682. /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify
  1683. events when the keyboard layout changes (for example,
  1684. changing from English to French on the menubar's keyboard
  1685. icon). Since it changes the XKLAVIER_STATE property, we
  1686. notice and reinit our keymap here. This might not be the
  1687. right approach, but it seems to work. */
  1688. X11_UpdateKeymap(_this, true);
  1689. } else if (xevent->xproperty.atom == videodata->atoms._NET_FRAME_EXTENTS) {
  1690. /* Events are disabled when leaving fullscreen until the borders appear to avoid
  1691. * incorrect size/position events.
  1692. */
  1693. if (data->size_move_event_flags) {
  1694. data->size_move_event_flags &= ~X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
  1695. X11_GetBorderValues(data);
  1696. }
  1697. if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
  1698. data->toggle_borders = false;
  1699. X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS));
  1700. }
  1701. }
  1702. } break;
  1703. case SelectionNotify:
  1704. {
  1705. Atom target = xevent->xselection.target;
  1706. #ifdef DEBUG_XEVENTS
  1707. SDL_Log("window 0x%lx: SelectionNotify (requestor = 0x%lx, target = 0x%lx)", xevent->xany.window,
  1708. xevent->xselection.requestor, xevent->xselection.target);
  1709. #endif
  1710. if (target == data->xdnd_req) {
  1711. // read data
  1712. SDL_x11Prop p;
  1713. X11_ReadProperty(&p, display, data->xwindow, videodata->atoms.PRIMARY);
  1714. if (p.format == 8) {
  1715. char *saveptr = NULL;
  1716. char *name = X11_XGetAtomName(display, target);
  1717. if (name) {
  1718. char *token = SDL_strtok_r((char *)p.data, "\r\n", &saveptr);
  1719. while (token) {
  1720. if ((SDL_strcmp("text/plain;charset=utf-8", name) == 0) ||
  1721. (SDL_strcmp("UTF8_STRING", name) == 0) ||
  1722. (SDL_strcmp("text/plain", name) == 0) ||
  1723. (SDL_strcmp("TEXT", name) == 0)) {
  1724. SDL_SendDropText(data->window, token);
  1725. } else if (SDL_strcmp("text/uri-list", name) == 0) {
  1726. if (SDL_URIToLocal(token, token) >= 0) {
  1727. SDL_SendDropFile(data->window, NULL, token);
  1728. }
  1729. }
  1730. token = SDL_strtok_r(NULL, "\r\n", &saveptr);
  1731. }
  1732. X11_XFree(name);
  1733. }
  1734. SDL_SendDropComplete(data->window);
  1735. }
  1736. X11_XFree(p.data);
  1737. // send reply
  1738. SDL_memset(&m, 0, sizeof(XClientMessageEvent));
  1739. m.type = ClientMessage;
  1740. m.display = display;
  1741. m.window = data->xdnd_source;
  1742. m.message_type = videodata->atoms.XdndFinished;
  1743. m.format = 32;
  1744. m.data.l[0] = data->xwindow;
  1745. m.data.l[1] = 1;
  1746. m.data.l[2] = videodata->atoms.XdndActionCopy;
  1747. X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent *)&m);
  1748. X11_XSync(display, False);
  1749. }
  1750. } break;
  1751. default:
  1752. {
  1753. #ifdef DEBUG_XEVENTS
  1754. SDL_Log("window 0x%lx: Unhandled event %d", xevent->xany.window, xevent->type);
  1755. #endif
  1756. } break;
  1757. }
  1758. }
  1759. static void X11_HandleFocusChanges(SDL_VideoDevice *_this)
  1760. {
  1761. SDL_VideoData *videodata = _this->internal;
  1762. int i;
  1763. if (videodata && videodata->windowlist) {
  1764. for (i = 0; i < videodata->numwindows; ++i) {
  1765. SDL_WindowData *data = videodata->windowlist[i];
  1766. if (data && data->pending_focus != PENDING_FOCUS_NONE) {
  1767. Uint64 now = SDL_GetTicks();
  1768. if (now >= data->pending_focus_time) {
  1769. if (data->pending_focus == PENDING_FOCUS_IN) {
  1770. X11_DispatchFocusIn(_this, data);
  1771. } else {
  1772. X11_DispatchFocusOut(_this, data);
  1773. }
  1774. data->pending_focus = PENDING_FOCUS_NONE;
  1775. }
  1776. }
  1777. }
  1778. }
  1779. }
  1780. static Bool isAnyEvent(Display *display, XEvent *ev, XPointer arg)
  1781. {
  1782. return True;
  1783. }
  1784. static bool X11_PollEvent(Display *display, XEvent *event)
  1785. {
  1786. if (!X11_XCheckIfEvent(display, event, isAnyEvent, NULL)) {
  1787. return false;
  1788. }
  1789. return true;
  1790. }
  1791. void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window)
  1792. {
  1793. SDL_VideoData *data = _this->internal;
  1794. Display *req_display = data->request_display;
  1795. Window xwindow = window->internal->xwindow;
  1796. XClientMessageEvent event;
  1797. SDL_memset(&event, 0, sizeof(XClientMessageEvent));
  1798. event.type = ClientMessage;
  1799. event.display = req_display;
  1800. event.send_event = True;
  1801. event.message_type = data->atoms._SDL_WAKEUP;
  1802. event.format = 8;
  1803. X11_XSendEvent(req_display, xwindow, False, NoEventMask, (XEvent *)&event);
  1804. /* XSendEvent returns a status and it could be BadValue or BadWindow. If an
  1805. error happens it is an SDL's internal error and there is nothing we can do here. */
  1806. X11_XFlush(req_display);
  1807. }
  1808. int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
  1809. {
  1810. SDL_VideoData *videodata = _this->internal;
  1811. Display *display;
  1812. XEvent xevent;
  1813. display = videodata->display;
  1814. SDL_zero(xevent);
  1815. // Flush and poll to grab any events already read and queued
  1816. X11_XFlush(display);
  1817. if (X11_PollEvent(display, &xevent)) {
  1818. // Fall through
  1819. } else if (timeoutNS == 0) {
  1820. return 0;
  1821. } else {
  1822. // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait
  1823. int err = SDL_IOReady(ConnectionNumber(display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS);
  1824. if (err > 0) {
  1825. if (!X11_PollEvent(display, &xevent)) {
  1826. /* Someone may have beat us to reading the fd. Return 1 here to
  1827. * trigger the normal spurious wakeup logic in the event core. */
  1828. return 1;
  1829. }
  1830. } else if (err == 0) {
  1831. // Timeout
  1832. return 0;
  1833. } else {
  1834. // Error returned from poll()/select()
  1835. if (errno == EINTR) {
  1836. /* If the wait was interrupted by a signal, we may have generated a
  1837. * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */
  1838. return 1;
  1839. } else {
  1840. return err;
  1841. }
  1842. }
  1843. }
  1844. X11_DispatchEvent(_this, &xevent);
  1845. #ifdef SDL_USE_LIBDBUS
  1846. SDL_DBus_PumpEvents();
  1847. #endif
  1848. return 1;
  1849. }
  1850. void X11_PumpEvents(SDL_VideoDevice *_this)
  1851. {
  1852. SDL_VideoData *data = _this->internal;
  1853. XEvent xevent;
  1854. int i;
  1855. /* Check if a display had the mode changed and is waiting for a window to asynchronously become
  1856. * fullscreen. If there is no fullscreen window past the elapsed timeout, revert the mode switch.
  1857. */
  1858. for (i = 0; i < _this->num_displays; ++i) {
  1859. if (_this->displays[i]->internal->mode_switch_deadline_ns) {
  1860. if (_this->displays[i]->fullscreen_window) {
  1861. _this->displays[i]->internal->mode_switch_deadline_ns = 0;
  1862. } else if (SDL_GetTicksNS() >= _this->displays[i]->internal->mode_switch_deadline_ns) {
  1863. SDL_LogError(SDL_LOG_CATEGORY_VIDEO,
  1864. "Time out elapsed after mode switch on display %" SDL_PRIu32 " with no window becoming fullscreen; reverting", _this->displays[i]->id);
  1865. SDL_SetDisplayModeForDisplay(_this->displays[i], NULL);
  1866. }
  1867. }
  1868. }
  1869. if (data->last_mode_change_deadline) {
  1870. if (SDL_GetTicks() >= data->last_mode_change_deadline) {
  1871. data->last_mode_change_deadline = 0; // assume we're done.
  1872. }
  1873. }
  1874. // Update activity every 30 seconds to prevent screensaver
  1875. if (_this->suspend_screensaver) {
  1876. Uint64 now = SDL_GetTicks();
  1877. if (!data->screensaver_activity || now >= (data->screensaver_activity + 30000)) {
  1878. X11_XResetScreenSaver(data->display);
  1879. #ifdef SDL_USE_LIBDBUS
  1880. SDL_DBus_ScreensaverTickle();
  1881. #endif
  1882. data->screensaver_activity = now;
  1883. }
  1884. }
  1885. SDL_zero(xevent);
  1886. // Keep processing pending events
  1887. while (X11_PollEvent(data->display, &xevent)) {
  1888. X11_DispatchEvent(_this, &xevent);
  1889. }
  1890. #ifdef SDL_USE_LIBDBUS
  1891. SDL_DBus_PumpEvents();
  1892. #endif
  1893. // FIXME: Only need to do this when there are pending focus changes
  1894. X11_HandleFocusChanges(_this);
  1895. // FIXME: Only need to do this when there are flashing windows
  1896. for (i = 0; i < data->numwindows; ++i) {
  1897. if (data->windowlist[i] != NULL &&
  1898. data->windowlist[i]->flash_cancel_time &&
  1899. SDL_GetTicks() >= data->windowlist[i]->flash_cancel_time) {
  1900. X11_FlashWindow(_this, data->windowlist[i]->window, SDL_FLASH_CANCEL);
  1901. }
  1902. }
  1903. if (data->xinput_hierarchy_changed) {
  1904. X11_Xinput2UpdateDevices(_this, false);
  1905. data->xinput_hierarchy_changed = false;
  1906. }
  1907. }
  1908. bool X11_SuspendScreenSaver(SDL_VideoDevice *_this)
  1909. {
  1910. #ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
  1911. SDL_VideoData *data = _this->internal;
  1912. int dummy;
  1913. int major_version, minor_version;
  1914. #endif // SDL_VIDEO_DRIVER_X11_XSCRNSAVER
  1915. #ifdef SDL_USE_LIBDBUS
  1916. if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) {
  1917. return true;
  1918. }
  1919. if (_this->suspend_screensaver) {
  1920. SDL_DBus_ScreensaverTickle();
  1921. }
  1922. #endif
  1923. #ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
  1924. if (SDL_X11_HAVE_XSS) {
  1925. // X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1
  1926. if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) ||
  1927. !X11_XScreenSaverQueryVersion(data->display,
  1928. &major_version, &minor_version) ||
  1929. major_version < 1 || (major_version == 1 && minor_version < 1)) {
  1930. return SDL_Unsupported();
  1931. }
  1932. X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
  1933. X11_XResetScreenSaver(data->display);
  1934. return true;
  1935. }
  1936. #endif
  1937. return SDL_Unsupported();
  1938. }
  1939. #endif // SDL_VIDEO_DRIVER_X11