SDL_x11window.c 87 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #ifdef SDL_VIDEO_DRIVER_X11
  20. #include "../SDL_sysvideo.h"
  21. #include "../SDL_pixels_c.h"
  22. #include "../../events/SDL_keyboard_c.h"
  23. #include "../../events/SDL_mouse_c.h"
  24. #include "../../events/SDL_events_c.h"
  25. #include "../../core/unix/SDL_appid.h"
  26. #include "SDL_x11video.h"
  27. #include "SDL_x11mouse.h"
  28. #include "SDL_x11xinput2.h"
  29. #include "SDL_x11xfixes.h"
  30. #ifdef SDL_VIDEO_OPENGL_EGL
  31. #include "SDL_x11opengles.h"
  32. #endif
  33. #include "SDL_x11xsync.h"
  34. #define _NET_WM_STATE_REMOVE 0l
  35. #define _NET_WM_STATE_ADD 1l
  36. #define CHECK_WINDOW_DATA(window) \
  37. if (!window) { \
  38. return SDL_SetError("Invalid window"); \
  39. } \
  40. if (!window->internal) { \
  41. return SDL_SetError("Invalid window driver data"); \
  42. }
  43. #define CHECK_DISPLAY_DATA(display) \
  44. if (!_display) { \
  45. return SDL_SetError("Invalid display"); \
  46. } \
  47. if (!_display->internal) { \
  48. return SDL_SetError("Invalid display driver data"); \
  49. }
  50. static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win) // NOLINT(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef
  51. {
  52. return ev->type == MapNotify && ev->xmap.window == *((Window *)win);
  53. }
  54. static Bool isUnmapNotify(Display *dpy, XEvent *ev, XPointer win) // NOLINT(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef
  55. {
  56. return ev->type == UnmapNotify && ev->xunmap.window == *((Window *)win);
  57. }
  58. /*
  59. static Bool isConfigureNotify(Display *dpy, XEvent *ev, XPointer win)
  60. {
  61. return ev->type == ConfigureNotify && ev->xconfigure.window == *((Window *)win);
  62. }
  63. static Bool X11_XIfEventTimeout(Display *display, XEvent *event_return, Bool (*predicate)(), XPointer arg, int timeoutMS)
  64. {
  65. Uint64 start = SDL_GetTicks();
  66. while (!X11_XCheckIfEvent(display, event_return, predicate, arg)) {
  67. if (SDL_GetTicks() >= (start + timeoutMS)) {
  68. return False;
  69. }
  70. }
  71. return True;
  72. }
  73. */
  74. static bool X11_CheckCurrentDesktop(const char *name)
  75. {
  76. SDL_Environment *env = SDL_GetEnvironment();
  77. const char *desktopVar = SDL_GetEnvironmentVariable(env, "DESKTOP_SESSION");
  78. if (desktopVar && SDL_strcasecmp(desktopVar, name) == 0) {
  79. return true;
  80. }
  81. desktopVar = SDL_GetEnvironmentVariable(env, "XDG_CURRENT_DESKTOP");
  82. if (desktopVar && SDL_strcasestr(desktopVar, name)) {
  83. return true;
  84. }
  85. return false;
  86. }
  87. static bool X11_IsWindowMapped(SDL_VideoDevice *_this, SDL_Window *window)
  88. {
  89. SDL_WindowData *data = window->internal;
  90. SDL_VideoData *videodata = _this->internal;
  91. XWindowAttributes attr;
  92. X11_XGetWindowAttributes(videodata->display, data->xwindow, &attr);
  93. if (attr.map_state != IsUnmapped) {
  94. return true;
  95. } else {
  96. return false;
  97. }
  98. }
  99. static bool X11_IsDisplayOk(Display *display)
  100. {
  101. if (display->flags & XlibDisplayIOError) {
  102. return false;
  103. }
  104. return true;
  105. }
  106. #if 0
  107. static bool X11_IsActionAllowed(SDL_Window *window, Atom action)
  108. {
  109. SDL_WindowData *data = window->internal;
  110. Atom _NET_WM_ALLOWED_ACTIONS = data->videodata->_NET_WM_ALLOWED_ACTIONS;
  111. Atom type;
  112. Display *display = data->videodata->display;
  113. int form;
  114. unsigned long remain;
  115. unsigned long len, i;
  116. Atom *list;
  117. bool ret = false;
  118. if (X11_XGetWindowProperty(display, data->xwindow, _NET_WM_ALLOWED_ACTIONS, 0, 1024, False, XA_ATOM, &type, &form, &len, &remain, (unsigned char **)&list) == Success) {
  119. for (i=0; i<len; ++i) {
  120. if (list[i] == action) {
  121. ret = true;
  122. break;
  123. }
  124. }
  125. X11_XFree(list);
  126. }
  127. return ret;
  128. }
  129. #endif // 0
  130. static void X11_FlushPendingEvents(SDL_VideoDevice *_this, SDL_Window *window)
  131. {
  132. // Serialize and restore the pending flags, as they may be overwritten while flushing.
  133. const bool last_position_pending = window->last_position_pending;
  134. const bool last_size_pending = window->last_size_pending;
  135. X11_SyncWindow(_this, window);
  136. window->last_position_pending = last_position_pending;
  137. window->last_size_pending = last_size_pending;
  138. }
  139. void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags)
  140. {
  141. SDL_VideoData *videodata = _this->internal;
  142. Display *display = videodata->display;
  143. // !!! FIXME: just dereference videodata below instead of copying to locals.
  144. Atom _NET_WM_STATE = videodata->atoms._NET_WM_STATE;
  145. // Atom _NET_WM_STATE_HIDDEN = videodata->atoms._NET_WM_STATE_HIDDEN;
  146. Atom _NET_WM_STATE_FOCUSED = videodata->atoms._NET_WM_STATE_FOCUSED;
  147. Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT;
  148. Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ;
  149. Atom _NET_WM_STATE_FULLSCREEN = videodata->atoms._NET_WM_STATE_FULLSCREEN;
  150. Atom _NET_WM_STATE_ABOVE = videodata->atoms._NET_WM_STATE_ABOVE;
  151. Atom _NET_WM_STATE_SKIP_TASKBAR = videodata->atoms._NET_WM_STATE_SKIP_TASKBAR;
  152. Atom _NET_WM_STATE_SKIP_PAGER = videodata->atoms._NET_WM_STATE_SKIP_PAGER;
  153. Atom _NET_WM_STATE_MODAL = videodata->atoms._NET_WM_STATE_MODAL;
  154. Atom atoms[16];
  155. int count = 0;
  156. /* The window manager sets this property, we shouldn't set it.
  157. If we did, this would indicate to the window manager that we don't
  158. actually want to be mapped during X11_XMapRaised(), which would be bad.
  159. *
  160. if ((flags & SDL_WINDOW_HIDDEN) != 0) {
  161. atoms[count++] = _NET_WM_STATE_HIDDEN;
  162. }
  163. */
  164. if (flags & SDL_WINDOW_ALWAYS_ON_TOP) {
  165. atoms[count++] = _NET_WM_STATE_ABOVE;
  166. }
  167. if (flags & SDL_WINDOW_UTILITY) {
  168. atoms[count++] = _NET_WM_STATE_SKIP_TASKBAR;
  169. atoms[count++] = _NET_WM_STATE_SKIP_PAGER;
  170. }
  171. if (flags & SDL_WINDOW_INPUT_FOCUS) {
  172. atoms[count++] = _NET_WM_STATE_FOCUSED;
  173. }
  174. if (flags & SDL_WINDOW_MAXIMIZED) {
  175. atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT;
  176. atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ;
  177. }
  178. if (flags & SDL_WINDOW_FULLSCREEN) {
  179. atoms[count++] = _NET_WM_STATE_FULLSCREEN;
  180. }
  181. if (flags & SDL_WINDOW_MODAL) {
  182. atoms[count++] = _NET_WM_STATE_MODAL;
  183. }
  184. SDL_assert(count <= SDL_arraysize(atoms));
  185. if (count > 0) {
  186. X11_XChangeProperty(display, xwindow, _NET_WM_STATE, XA_ATOM, 32,
  187. PropModeReplace, (unsigned char *)atoms, count);
  188. } else {
  189. X11_XDeleteProperty(display, xwindow, _NET_WM_STATE);
  190. }
  191. }
  192. static void X11_ConstrainPopup(SDL_Window *window, bool output_to_pending)
  193. {
  194. // Clamp popup windows to the output borders
  195. if (SDL_WINDOW_IS_POPUP(window)) {
  196. SDL_Window *w;
  197. SDL_DisplayID displayID;
  198. SDL_Rect rect;
  199. int abs_x = window->last_position_pending ? window->pending.x : window->floating.x;
  200. int abs_y = window->last_position_pending ? window->pending.y : window->floating.y;
  201. int offset_x = 0, offset_y = 0;
  202. if (window->constrain_popup) {
  203. // Calculate the total offset from the parents
  204. for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
  205. offset_x += w->x;
  206. offset_y += w->y;
  207. }
  208. offset_x += w->x;
  209. offset_y += w->y;
  210. abs_x += offset_x;
  211. abs_y += offset_y;
  212. displayID = SDL_GetDisplayForWindow(w);
  213. SDL_GetDisplayBounds(displayID, &rect);
  214. if (abs_x + window->w > rect.x + rect.w) {
  215. abs_x -= (abs_x + window->w) - (rect.x + rect.w);
  216. }
  217. if (abs_y + window->h > rect.y + rect.h) {
  218. abs_y -= (abs_y + window->h) - (rect.y + rect.h);
  219. }
  220. abs_x = SDL_max(abs_x, rect.x);
  221. abs_y = SDL_max(abs_y, rect.y);
  222. }
  223. if (output_to_pending) {
  224. window->pending.x = abs_x - offset_x;
  225. window->pending.y = abs_y - offset_y;
  226. } else {
  227. window->floating.x = window->windowed.x = abs_x - offset_x;
  228. window->floating.y = window->windowed.y = abs_y - offset_y;
  229. }
  230. }
  231. }
  232. static void X11_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
  233. {
  234. SDL_Window *toplevel = window;
  235. // Find the toplevel parent
  236. while (SDL_WINDOW_IS_POPUP(toplevel)) {
  237. toplevel = toplevel->parent;
  238. }
  239. toplevel->keyboard_focus = window;
  240. if (set_active_focus && !window->is_hiding && !window->is_destroying) {
  241. SDL_SetKeyboardFocus(window);
  242. }
  243. }
  244. SDL_WindowFlags X11_GetNetWMState(SDL_VideoDevice *_this, SDL_Window *window, Window xwindow)
  245. {
  246. SDL_VideoData *videodata = _this->internal;
  247. Display *display = videodata->display;
  248. Atom _NET_WM_STATE = videodata->atoms._NET_WM_STATE;
  249. Atom _NET_WM_STATE_HIDDEN = videodata->atoms._NET_WM_STATE_HIDDEN;
  250. Atom _NET_WM_STATE_FOCUSED = videodata->atoms._NET_WM_STATE_FOCUSED;
  251. Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT;
  252. Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ;
  253. Atom _NET_WM_STATE_FULLSCREEN = videodata->atoms._NET_WM_STATE_FULLSCREEN;
  254. Atom actualType;
  255. int actualFormat;
  256. unsigned long i, numItems, bytesAfter;
  257. unsigned char *propertyValue = NULL;
  258. long maxLength = 1024;
  259. SDL_WindowFlags flags = 0;
  260. if (X11_XGetWindowProperty(display, xwindow, _NET_WM_STATE,
  261. 0l, maxLength, False, XA_ATOM, &actualType,
  262. &actualFormat, &numItems, &bytesAfter,
  263. &propertyValue) == Success) {
  264. Atom *atoms = (Atom *)propertyValue;
  265. int maximized = 0;
  266. int fullscreen = 0;
  267. for (i = 0; i < numItems; ++i) {
  268. if (atoms[i] == _NET_WM_STATE_HIDDEN) {
  269. flags |= SDL_WINDOW_MINIMIZED | SDL_WINDOW_OCCLUDED;
  270. } else if (atoms[i] == _NET_WM_STATE_FOCUSED) {
  271. flags |= SDL_WINDOW_INPUT_FOCUS;
  272. } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) {
  273. maximized |= 1;
  274. } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
  275. maximized |= 2;
  276. } else if (atoms[i] == _NET_WM_STATE_FULLSCREEN) {
  277. fullscreen = 1;
  278. }
  279. }
  280. if (fullscreen == 1) {
  281. if (window->flags & SDL_WINDOW_FULLSCREEN) {
  282. // Pick whatever state the window expects
  283. flags |= (window->flags & SDL_WINDOW_FULLSCREEN);
  284. } else {
  285. // Assume we're fullscreen desktop
  286. flags |= SDL_WINDOW_FULLSCREEN;
  287. }
  288. }
  289. if (maximized == 3) {
  290. /* Fullscreen windows are maximized on some window managers,
  291. and this is functional behavior - if maximized is removed,
  292. the windows remain floating centered and not covering the
  293. rest of the desktop. So we just won't change the maximize
  294. state for fullscreen windows here, otherwise SDL would think
  295. we're always maximized when fullscreen and not restore the
  296. correct state when leaving fullscreen.
  297. */
  298. if (fullscreen) {
  299. flags |= (window->flags & SDL_WINDOW_MAXIMIZED);
  300. } else {
  301. flags |= SDL_WINDOW_MAXIMIZED;
  302. }
  303. }
  304. /* If the window is unmapped, numItems will be zero and _NET_WM_STATE_HIDDEN
  305. * will not be set. Do an additional check to see if the window is unmapped
  306. * and mark it as SDL_WINDOW_HIDDEN if it is.
  307. */
  308. {
  309. XWindowAttributes attr;
  310. SDL_zero(attr);
  311. X11_XGetWindowAttributes(videodata->display, xwindow, &attr);
  312. if (attr.map_state == IsUnmapped) {
  313. flags |= SDL_WINDOW_HIDDEN;
  314. }
  315. }
  316. X11_XFree(propertyValue);
  317. }
  318. // FIXME, check the size hints for resizable
  319. // flags |= SDL_WINDOW_RESIZABLE;
  320. return flags;
  321. }
  322. static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, Window w)
  323. {
  324. SDL_VideoData *videodata = _this->internal;
  325. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  326. SDL_WindowData *data;
  327. int numwindows = videodata->numwindows;
  328. int windowlistlength = videodata->windowlistlength;
  329. SDL_WindowData **windowlist = videodata->windowlist;
  330. // Allocate the window data
  331. data = (SDL_WindowData *)SDL_calloc(1, sizeof(*data));
  332. if (!data) {
  333. return false;
  334. }
  335. data->videodata = videodata;
  336. data->window = window;
  337. data->xwindow = w;
  338. data->hit_test_result = SDL_HITTEST_NORMAL;
  339. X11_CreateInputContext(data);
  340. // Associate the data with the window
  341. if (numwindows < windowlistlength) {
  342. windowlist[numwindows] = data;
  343. videodata->numwindows++;
  344. } else {
  345. SDL_WindowData ** new_windowlist = (SDL_WindowData **)SDL_realloc(windowlist, (numwindows + 1) * sizeof(*windowlist));
  346. if (!new_windowlist) {
  347. goto error_cleanup;
  348. }
  349. windowlist = new_windowlist;
  350. windowlist[numwindows] = data;
  351. videodata->numwindows++;
  352. videodata->windowlistlength++;
  353. videodata->windowlist = windowlist;
  354. }
  355. // Fill in the SDL window with the window data
  356. {
  357. XWindowAttributes attrib;
  358. X11_XGetWindowAttributes(data->videodata->display, w, &attrib);
  359. if (!SDL_WINDOW_IS_POPUP(window)) {
  360. window->x = window->windowed.x = window->floating.x = attrib.x;
  361. window->y = window->windowed.y = window->floating.y = attrib.y - data->border_top;
  362. }
  363. window->w = window->windowed.w = window->floating.w = attrib.width;
  364. window->h = window->windowed.h = window->floating.h = attrib.height;
  365. if (attrib.map_state != IsUnmapped) {
  366. window->flags &= ~SDL_WINDOW_HIDDEN;
  367. } else {
  368. window->flags |= SDL_WINDOW_HIDDEN;
  369. }
  370. data->visual = attrib.visual;
  371. data->colormap = attrib.colormap;
  372. }
  373. window->flags |= X11_GetNetWMState(_this, window, w);
  374. {
  375. Window FocalWindow;
  376. int RevertTo = 0;
  377. X11_XGetInputFocus(data->videodata->display, &FocalWindow, &RevertTo);
  378. if (FocalWindow == w) {
  379. window->flags |= SDL_WINDOW_INPUT_FOCUS;
  380. }
  381. if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
  382. SDL_SetKeyboardFocus(data->window);
  383. }
  384. if (window->flags & SDL_WINDOW_MOUSE_GRABBED) {
  385. // Tell x11 to clip mouse
  386. }
  387. }
  388. if (window->flags & SDL_WINDOW_EXTERNAL) {
  389. // Query the title from the existing window
  390. window->title = X11_GetWindowTitle(_this, w);
  391. }
  392. SDL_PropertiesID props = SDL_GetWindowProperties(window);
  393. int screen = (displaydata ? displaydata->screen : 0);
  394. SDL_SetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, data->videodata->display);
  395. SDL_SetNumberProperty(props, SDL_PROP_WINDOW_X11_SCREEN_NUMBER, screen);
  396. SDL_SetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, data->xwindow);
  397. #if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) || defined(SDL_VIDEO_OPENGL_EGL)
  398. if ((window->flags & SDL_WINDOW_OPENGL) &&
  399. ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) ||
  400. SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false))
  401. #ifdef SDL_VIDEO_OPENGL_GLX
  402. && (!_this->gl_data || X11_GL_UseEGL(_this))
  403. #endif
  404. ) {
  405. #ifdef SDL_VIDEO_OPENGL_EGL
  406. if (!_this->egl_data) {
  407. goto error_cleanup;
  408. }
  409. // Create the GLES window surface
  410. data->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)w);
  411. if (data->egl_surface == EGL_NO_SURFACE) {
  412. SDL_SetError("Could not create GLES window surface");
  413. goto error_cleanup;
  414. }
  415. #else
  416. SDL_SetError("Could not create GLES window surface (EGL support not configured)");
  417. goto error_cleanup;
  418. #endif // SDL_VIDEO_OPENGL_EGL
  419. }
  420. #endif
  421. // All done!
  422. window->internal = data;
  423. return true;
  424. error_cleanup:
  425. X11_DestroyInputContext(data);
  426. SDL_free(data);
  427. return false;
  428. }
  429. static void SetupWindowInput(SDL_VideoDevice *_this, SDL_Window *window)
  430. {
  431. long fevent = 0;
  432. SDL_WindowData *data = window->internal;
  433. Window xwindow = data->xwindow;
  434. #ifdef X_HAVE_UTF8_STRING
  435. if (SDL_X11_HAVE_UTF8 && data->ic) {
  436. X11_XGetICValues(data->ic, XNFilterEvents, &fevent, NULL);
  437. }
  438. #endif
  439. X11_Xinput2Select(_this, window);
  440. {
  441. unsigned int x11_keyboard_events = KeyPressMask | KeyReleaseMask;
  442. unsigned int x11_pointer_events = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
  443. X11_Xinput2SelectMouseAndKeyboard(_this, window);
  444. // If XInput2 can handle pointer and keyboard events, we don't track them here
  445. if (data->xinput2_keyboard_enabled) {
  446. x11_keyboard_events = 0;
  447. }
  448. if (data->xinput2_mouse_enabled) {
  449. x11_pointer_events = 0;
  450. }
  451. X11_XSelectInput(data->videodata->display, xwindow,
  452. (FocusChangeMask | EnterWindowMask | LeaveWindowMask | ExposureMask |
  453. x11_keyboard_events | x11_pointer_events |
  454. PropertyChangeMask | StructureNotifyMask |
  455. KeymapStateMask | fevent));
  456. }
  457. }
  458. static void SetWindowBordered(Display *display, int screen, Window window, bool border)
  459. {
  460. /*
  461. * this code used to check for KWM_WIN_DECORATION, but KDE hasn't
  462. * supported it for years and years. It now respects _MOTIF_WM_HINTS.
  463. * Gnome is similar: just use the Motif atom.
  464. */
  465. Atom WM_HINTS = X11_XInternAtom(display, "_MOTIF_WM_HINTS", True);
  466. if (WM_HINTS != None) {
  467. // Hints used by Motif compliant window managers
  468. struct
  469. {
  470. unsigned long flags;
  471. unsigned long functions;
  472. unsigned long decorations;
  473. long input_mode;
  474. unsigned long status;
  475. } MWMHints = {
  476. (1L << 1), 0, border ? 1 : 0, 0, 0
  477. };
  478. X11_XChangeProperty(display, window, WM_HINTS, WM_HINTS, 32,
  479. PropModeReplace, (unsigned char *)&MWMHints,
  480. sizeof(MWMHints) / sizeof(long));
  481. } else { // set the transient hints instead, if necessary
  482. X11_XSetTransientForHint(display, window, RootWindow(display, screen));
  483. }
  484. }
  485. bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
  486. {
  487. Window w = (Window)SDL_GetNumberProperty(create_props, SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER,
  488. (Window)SDL_GetPointerProperty(create_props, "sdl2-compat.external_window", NULL));
  489. if (w) {
  490. window->flags |= SDL_WINDOW_EXTERNAL;
  491. if (!SetupWindowData(_this, window, w)) {
  492. return false;
  493. }
  494. if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_EXTERNAL_WINDOW_INPUT, true)) {
  495. SetupWindowInput(_this, window);
  496. }
  497. return true;
  498. }
  499. SDL_VideoData *data = _this->internal;
  500. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  501. if (!displaydata) {
  502. return SDL_SetError("Could not find display info");
  503. }
  504. const bool force_override_redirect = SDL_GetHintBoolean(SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT, false);
  505. const bool use_resize_sync = SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_ENABLE_XSYNC_EXT, false) &&
  506. (window->flags & SDL_WINDOW_OPENGL) != 0; // Doesn't work well with Vulkan
  507. SDL_WindowData *windowdata;
  508. Display *display = data->display;
  509. int screen = displaydata->screen;
  510. Visual *visual;
  511. int depth;
  512. XSetWindowAttributes xattr;
  513. XSizeHints *sizehints;
  514. XWMHints *wmhints;
  515. XClassHint *classhints;
  516. Atom _NET_WM_BYPASS_COMPOSITOR;
  517. Atom _NET_WM_WINDOW_TYPE;
  518. Atom wintype;
  519. const char *wintype_name = NULL;
  520. long compositor = 1;
  521. Atom _NET_WM_PID;
  522. const char *hint = NULL;
  523. int win_x, win_y;
  524. bool undefined_position = false;
  525. #if defined(SDL_VIDEO_OPENGL_GLX) || defined(SDL_VIDEO_OPENGL_EGL)
  526. const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? true : false;
  527. const char *forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID);
  528. const char *display_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_VISUALID);
  529. if (forced_visual_id && *forced_visual_id) {
  530. XVisualInfo *vi, template;
  531. int nvis;
  532. SDL_zero(template);
  533. template.visualid = SDL_strtol(forced_visual_id, NULL, 0);
  534. vi = X11_XGetVisualInfo(display, VisualIDMask, &template, &nvis);
  535. if (vi) {
  536. visual = vi->visual;
  537. depth = vi->depth;
  538. X11_XFree(vi);
  539. } else {
  540. return false;
  541. }
  542. } else if ((window->flags & SDL_WINDOW_OPENGL) &&
  543. (!display_visual_id || !*display_visual_id)) {
  544. XVisualInfo *vinfo = NULL;
  545. #ifdef SDL_VIDEO_OPENGL_EGL
  546. if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) ||
  547. SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false))
  548. #ifdef SDL_VIDEO_OPENGL_GLX
  549. && (!_this->gl_data || X11_GL_UseEGL(_this))
  550. #endif
  551. ) {
  552. vinfo = X11_GLES_GetVisual(_this, display, screen, transparent);
  553. } else
  554. #endif
  555. {
  556. #ifdef SDL_VIDEO_OPENGL_GLX
  557. vinfo = X11_GL_GetVisual(_this, display, screen, transparent);
  558. #endif
  559. }
  560. if (!vinfo) {
  561. return false;
  562. }
  563. visual = vinfo->visual;
  564. depth = vinfo->depth;
  565. X11_XFree(vinfo);
  566. } else
  567. #endif
  568. {
  569. visual = displaydata->visual;
  570. depth = displaydata->depth;
  571. }
  572. xattr.override_redirect = ((window->flags & SDL_WINDOW_TOOLTIP) || (window->flags & SDL_WINDOW_POPUP_MENU) || force_override_redirect) ? True : False;
  573. xattr.backing_store = NotUseful;
  574. xattr.background_pixmap = None;
  575. xattr.border_pixel = 0;
  576. if (visual->class == DirectColor) {
  577. XColor *colorcells;
  578. int i;
  579. int ncolors;
  580. int rmax, gmax, bmax;
  581. int rmask, gmask, bmask;
  582. int rshift, gshift, bshift;
  583. xattr.colormap =
  584. X11_XCreateColormap(display, RootWindow(display, screen),
  585. visual, AllocAll);
  586. // If we can't create a colormap, then we must die
  587. if (!xattr.colormap) {
  588. return SDL_SetError("Could not create writable colormap");
  589. }
  590. // OK, we got a colormap, now fill it in as best as we can
  591. colorcells = SDL_malloc(visual->map_entries * sizeof(XColor));
  592. if (!colorcells) {
  593. return false;
  594. }
  595. ncolors = visual->map_entries;
  596. rmax = 0xffff;
  597. gmax = 0xffff;
  598. bmax = 0xffff;
  599. rshift = 0;
  600. rmask = visual->red_mask;
  601. while (0 == (rmask & 1)) {
  602. rshift++;
  603. rmask >>= 1;
  604. }
  605. gshift = 0;
  606. gmask = visual->green_mask;
  607. while (0 == (gmask & 1)) {
  608. gshift++;
  609. gmask >>= 1;
  610. }
  611. bshift = 0;
  612. bmask = visual->blue_mask;
  613. while (0 == (bmask & 1)) {
  614. bshift++;
  615. bmask >>= 1;
  616. }
  617. // build the color table pixel values
  618. for (i = 0; i < ncolors; i++) {
  619. Uint32 red = (rmax * i) / (ncolors - 1);
  620. Uint32 green = (gmax * i) / (ncolors - 1);
  621. Uint32 blue = (bmax * i) / (ncolors - 1);
  622. Uint32 rbits = (rmask * i) / (ncolors - 1);
  623. Uint32 gbits = (gmask * i) / (ncolors - 1);
  624. Uint32 bbits = (bmask * i) / (ncolors - 1);
  625. Uint32 pix =
  626. (rbits << rshift) | (gbits << gshift) | (bbits << bshift);
  627. colorcells[i].pixel = pix;
  628. colorcells[i].red = red;
  629. colorcells[i].green = green;
  630. colorcells[i].blue = blue;
  631. colorcells[i].flags = DoRed | DoGreen | DoBlue;
  632. }
  633. X11_XStoreColors(display, xattr.colormap, colorcells, ncolors);
  634. SDL_free(colorcells);
  635. } else {
  636. xattr.colormap =
  637. X11_XCreateColormap(display, RootWindow(display, screen),
  638. visual, AllocNone);
  639. }
  640. if (window->undefined_x && window->undefined_y &&
  641. window->displayID == SDL_GetPrimaryDisplay()) {
  642. undefined_position = true;
  643. }
  644. if (SDL_WINDOW_IS_POPUP(window)) {
  645. X11_ConstrainPopup(window, false);
  646. }
  647. SDL_RelativeToGlobalForWindow(window,
  648. window->floating.x, window->floating.y,
  649. &win_x, &win_y);
  650. /* Always create this with the window->floating.* fields; if we're creating a windowed mode window,
  651. * that's fine. If we're creating a maximized or fullscreen window, the window manager will want to
  652. * know these values so it can use them if we go _back_ to the base floating windowed mode. SDL manages
  653. * migration to fullscreen after CreateSDLWindow returns, which will put all the SDL_Window fields and
  654. * system state as expected.
  655. */
  656. w = X11_XCreateWindow(display, RootWindow(display, screen),
  657. win_x, win_y, window->floating.w, window->floating.h,
  658. 0, depth, InputOutput, visual,
  659. (CWOverrideRedirect | CWBackPixmap | CWBorderPixel |
  660. CWBackingStore | CWColormap),
  661. &xattr);
  662. if (!w) {
  663. return SDL_SetError("Couldn't create window");
  664. }
  665. /* Don't set the borderless flag if we're about to go fullscreen.
  666. * This prevents the window manager from moving a full-screen borderless
  667. * window to a different display before we actually go fullscreen.
  668. */
  669. if (!(window->pending_flags & SDL_WINDOW_FULLSCREEN)) {
  670. SetWindowBordered(display, screen, w, !(window->flags & SDL_WINDOW_BORDERLESS));
  671. }
  672. sizehints = X11_XAllocSizeHints();
  673. // Setup the normal size hints
  674. sizehints->flags = 0;
  675. if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
  676. sizehints->min_width = sizehints->max_width = window->floating.w;
  677. sizehints->min_height = sizehints->max_height = window->floating.h;
  678. sizehints->flags |= (PMaxSize | PMinSize);
  679. }
  680. if (!undefined_position) {
  681. sizehints->x = win_x;
  682. sizehints->y = win_y;
  683. sizehints->flags |= USPosition;
  684. }
  685. // Setup the input hints so we get keyboard input
  686. wmhints = X11_XAllocWMHints();
  687. wmhints->input = !(window->flags & SDL_WINDOW_NOT_FOCUSABLE) ? True : False;
  688. wmhints->window_group = data->window_group;
  689. wmhints->flags = InputHint | WindowGroupHint;
  690. // Setup the class hints so we can get an icon (AfterStep)
  691. classhints = X11_XAllocClassHint();
  692. classhints->res_name = (char *)SDL_GetExeName();
  693. classhints->res_class = (char *)SDL_GetAppID();
  694. // Set the size, input and class hints, and define WM_CLIENT_MACHINE and WM_LOCALE_NAME
  695. X11_XSetWMProperties(display, w, NULL, NULL, NULL, 0, sizehints, wmhints, classhints);
  696. X11_XFree(sizehints);
  697. X11_XFree(wmhints);
  698. X11_XFree(classhints);
  699. // Set the PID related to the window for the given hostname, if possible
  700. if (data->pid > 0) {
  701. long pid = (long)data->pid;
  702. _NET_WM_PID = X11_XInternAtom(display, "_NET_WM_PID", False);
  703. X11_XChangeProperty(display, w, _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace,
  704. (unsigned char *)&pid, 1);
  705. }
  706. // Set the window manager state
  707. X11_SetNetWMState(_this, w, window->flags);
  708. compositor = 2; // don't disable compositing except for "normal" windows
  709. hint = SDL_GetHint(SDL_HINT_X11_WINDOW_TYPE);
  710. if (window->flags & SDL_WINDOW_UTILITY) {
  711. wintype_name = "_NET_WM_WINDOW_TYPE_UTILITY";
  712. } else if (window->flags & SDL_WINDOW_TOOLTIP) {
  713. wintype_name = "_NET_WM_WINDOW_TYPE_TOOLTIP";
  714. } else if (window->flags & SDL_WINDOW_POPUP_MENU) {
  715. wintype_name = "_NET_WM_WINDOW_TYPE_POPUP_MENU";
  716. } else if (hint && *hint) {
  717. wintype_name = hint;
  718. } else {
  719. wintype_name = "_NET_WM_WINDOW_TYPE_NORMAL";
  720. compositor = 1; // disable compositing for "normal" windows
  721. }
  722. // Let the window manager know what type of window we are.
  723. _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
  724. wintype = X11_XInternAtom(display, wintype_name, False);
  725. X11_XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
  726. PropModeReplace, (unsigned char *)&wintype, 1);
  727. if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, true)) {
  728. _NET_WM_BYPASS_COMPOSITOR = X11_XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False);
  729. X11_XChangeProperty(display, w, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
  730. PropModeReplace,
  731. (unsigned char *)&compositor, 1);
  732. }
  733. {
  734. Atom protocols[4];
  735. int proto_count = 0;
  736. protocols[proto_count++] = data->atoms.WM_DELETE_WINDOW; // Allow window to be deleted by the WM
  737. protocols[proto_count++] = data->atoms.WM_TAKE_FOCUS; // Since we will want to set input focus explicitly
  738. // Default to using ping if there is no hint
  739. if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_PING, true)) {
  740. protocols[proto_count++] = data->atoms._NET_WM_PING; // Respond so WM knows we're alive
  741. }
  742. #ifdef SDL_VIDEO_DRIVER_X11_XSYNC
  743. if (use_resize_sync) {
  744. protocols[proto_count++] = data->atoms._NET_WM_SYNC_REQUEST; /* Respond after completing resize */
  745. }
  746. #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
  747. SDL_assert(proto_count <= SDL_arraysize(protocols));
  748. X11_XSetWMProtocols(display, w, protocols, proto_count);
  749. }
  750. if (!SetupWindowData(_this, window, w)) {
  751. X11_XDestroyWindow(display, w);
  752. return false;
  753. }
  754. windowdata = window->internal;
  755. // Set the parent if this is a non-popup window.
  756. if (!SDL_WINDOW_IS_POPUP(window) && window->parent) {
  757. X11_XSetTransientForHint(display, w, window->parent->internal->xwindow);
  758. }
  759. // Set the flag if the borders were forced on when creating a fullscreen window for later removal.
  760. windowdata->fullscreen_borders_forced_on = !!(window->pending_flags & SDL_WINDOW_FULLSCREEN) &&
  761. !!(window->flags & SDL_WINDOW_BORDERLESS);
  762. #ifdef SDL_VIDEO_DRIVER_X11_XSYNC
  763. if (use_resize_sync) {
  764. X11_InitResizeSync(window);
  765. }
  766. #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
  767. #ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
  768. // Tooltips do not receive input
  769. if (window->flags & SDL_WINDOW_TOOLTIP) {
  770. Region region = X11_XCreateRegion();
  771. X11_XShapeCombineRegion(display, w, ShapeInput, 0, 0, region, ShapeSet);
  772. X11_XDestroyRegion(region);
  773. }
  774. #endif
  775. SetupWindowInput(_this, window);
  776. // For _ICC_PROFILE.
  777. X11_XSelectInput(display, RootWindow(display, screen), PropertyChangeMask);
  778. X11_XFlush(display);
  779. return true;
  780. }
  781. char *X11_GetWindowTitle(SDL_VideoDevice *_this, Window xwindow)
  782. {
  783. SDL_VideoData *data = _this->internal;
  784. Display *display = data->display;
  785. int status, real_format;
  786. Atom real_type;
  787. unsigned long items_read, items_left;
  788. unsigned char *propdata;
  789. char *title = NULL;
  790. status = X11_XGetWindowProperty(display, xwindow, data->atoms._NET_WM_NAME,
  791. 0L, 8192L, False, data->atoms.UTF8_STRING, &real_type, &real_format,
  792. &items_read, &items_left, &propdata);
  793. if (status == Success && propdata) {
  794. title = SDL_strdup(SDL_static_cast(char *, propdata));
  795. X11_XFree(propdata);
  796. } else {
  797. status = X11_XGetWindowProperty(display, xwindow, XA_WM_NAME,
  798. 0L, 8192L, False, XA_STRING, &real_type, &real_format,
  799. &items_read, &items_left, &propdata);
  800. if (status == Success && propdata) {
  801. title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char *, propdata), items_read + 1);
  802. SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Failed to convert WM_NAME title expecting UTF8! Title: %s", title);
  803. X11_XFree(propdata);
  804. } else {
  805. SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Could not get any window title response from Xorg, returning empty string!");
  806. title = SDL_strdup("");
  807. }
  808. }
  809. return title;
  810. }
  811. void X11_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
  812. {
  813. SDL_WindowData *data = window->internal;
  814. Window xwindow = data->xwindow;
  815. Display *display = data->videodata->display;
  816. char *title = window->title ? window->title : "";
  817. SDL_X11_SetWindowTitle(display, xwindow, title);
  818. }
  819. static bool caught_x11_error = false;
  820. static int X11_CatchAnyError(Display *d, XErrorEvent *e)
  821. {
  822. /* this may happen during tumultuous times when we are polling anyhow,
  823. so just note we had an error and return control. */
  824. caught_x11_error = true;
  825. return 0;
  826. }
  827. static void X11_ExternalResizeMoveSync(SDL_Window *window)
  828. {
  829. SDL_WindowData *data = window->internal;
  830. Display *display = data->videodata->display;
  831. int (*prev_handler)(Display *, XErrorEvent *);
  832. unsigned int childCount;
  833. Window childReturn, root, parent;
  834. Window *children;
  835. XWindowAttributes attrs;
  836. Uint64 timeout = 0;
  837. int x, y;
  838. const bool send_move = !!(data->pending_operation & X11_PENDING_OP_MOVE);
  839. const bool send_resize = !!(data->pending_operation & X11_PENDING_OP_RESIZE);
  840. X11_XSync(display, False);
  841. X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount);
  842. prev_handler = X11_XSetErrorHandler(X11_CatchAnyError);
  843. /* Wait a brief time to see if the window manager decided to let the move or resize happen.
  844. * If the window changes at all, even to an unexpected value, we break out.
  845. */
  846. timeout = SDL_GetTicksNS() + SDL_MS_TO_NS(100);
  847. while (true) {
  848. caught_x11_error = false;
  849. X11_XSync(display, False);
  850. X11_XGetWindowAttributes(display, data->xwindow, &attrs);
  851. X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
  852. attrs.x, attrs.y, &x, &y, &childReturn);
  853. SDL_GlobalToRelativeForWindow(window, x, y, &x, &y);
  854. if (!caught_x11_error) {
  855. if ((data->pending_operation & X11_PENDING_OP_MOVE) && (x == data->expected.x + data->border_left && y == data->expected.y + data->border_top)) {
  856. data->pending_operation &= ~X11_PENDING_OP_MOVE;
  857. }
  858. if ((data->pending_operation & X11_PENDING_OP_RESIZE) && (attrs.width == data->expected.w && attrs.height == data->expected.h)) {
  859. data->pending_operation &= ~X11_PENDING_OP_RESIZE;
  860. }
  861. if (data->pending_operation == X11_PENDING_OP_NONE) {
  862. break;
  863. }
  864. }
  865. if (SDL_GetTicksNS() >= timeout) {
  866. // Timed out without the expected values. Update the requested data so future sync calls won't block.
  867. data->pending_operation &= ~(X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE);
  868. data->expected.x = x;
  869. data->expected.y = y;
  870. data->expected.w = attrs.width;
  871. data->expected.h = attrs.height;
  872. break;
  873. }
  874. SDL_Delay(10);
  875. }
  876. if (!caught_x11_error) {
  877. if (send_move) {
  878. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
  879. }
  880. if (send_resize) {
  881. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height);
  882. }
  883. }
  884. X11_XSetErrorHandler(prev_handler);
  885. caught_x11_error = false;
  886. }
  887. /* Wait a brief time, or not, to see if the window manager decided to move/resize the window.
  888. * Send MOVED and RESIZED window events */
  889. static bool X11_SyncWindowTimeout(SDL_VideoDevice *_this, SDL_Window *window, Uint64 param_timeout)
  890. {
  891. SDL_WindowData *data = window->internal;
  892. Display *display = data->videodata->display;
  893. int (*prev_handler)(Display *, XErrorEvent *);
  894. Uint64 timeout = 0;
  895. bool force_exit = false;
  896. bool result = true;
  897. X11_XSync(display, False);
  898. prev_handler = X11_XSetErrorHandler(X11_CatchAnyError);
  899. if (param_timeout) {
  900. timeout = SDL_GetTicksNS() + param_timeout;
  901. }
  902. while (true) {
  903. X11_XSync(display, False);
  904. X11_PumpEvents(_this);
  905. if ((data->pending_operation & X11_PENDING_OP_MOVE) && (window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top)) {
  906. data->pending_operation &= ~X11_PENDING_OP_MOVE;
  907. }
  908. if ((data->pending_operation & X11_PENDING_OP_RESIZE) && (window->w == data->expected.w && window->h == data->expected.h)) {
  909. data->pending_operation &= ~X11_PENDING_OP_RESIZE;
  910. }
  911. if (data->pending_operation == X11_PENDING_OP_NONE) {
  912. if (force_exit ||
  913. (window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top &&
  914. window->w == data->expected.w && window->h == data->expected.h)) {
  915. // The window is in the expected state and nothing is pending. Done.
  916. break;
  917. }
  918. /* No operations are pending, but the window still isn't in the expected state.
  919. * Try one more time before exiting.
  920. */
  921. force_exit = true;
  922. }
  923. if (SDL_GetTicksNS() >= timeout) {
  924. // Timed out without the expected values. Update the requested data so future sync calls won't block.
  925. data->expected.x = window->x;
  926. data->expected.y = window->y;
  927. data->expected.w = window->w;
  928. data->expected.h = window->h;
  929. result = false;
  930. break;
  931. }
  932. SDL_Delay(10);
  933. }
  934. data->pending_operation = X11_PENDING_OP_NONE;
  935. if (!caught_x11_error) {
  936. X11_PumpEvents(_this);
  937. } else {
  938. result = false;
  939. }
  940. X11_XSetErrorHandler(prev_handler);
  941. caught_x11_error = false;
  942. return result;
  943. }
  944. bool X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon)
  945. {
  946. SDL_WindowData *data = window->internal;
  947. Display *display = data->videodata->display;
  948. Atom _NET_WM_ICON = data->videodata->atoms._NET_WM_ICON;
  949. int (*prevHandler)(Display *, XErrorEvent *) = NULL;
  950. bool result = true;
  951. if (icon) {
  952. int x, y;
  953. int propsize;
  954. long *propdata;
  955. Uint32 *src;
  956. long *dst;
  957. // Set the _NET_WM_ICON property
  958. SDL_assert(icon->format == SDL_PIXELFORMAT_ARGB8888);
  959. propsize = 2 + (icon->w * icon->h);
  960. propdata = SDL_malloc(propsize * sizeof(long));
  961. if (!propdata) {
  962. return false;
  963. }
  964. X11_XSync(display, False);
  965. prevHandler = X11_XSetErrorHandler(X11_CatchAnyError);
  966. propdata[0] = icon->w;
  967. propdata[1] = icon->h;
  968. dst = &propdata[2];
  969. for (y = 0; y < icon->h; ++y) {
  970. src = (Uint32 *)((Uint8 *)icon->pixels + y * icon->pitch);
  971. for (x = 0; x < icon->w; ++x) {
  972. *dst++ = *src++;
  973. }
  974. }
  975. X11_XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL,
  976. 32, PropModeReplace, (unsigned char *)propdata,
  977. propsize);
  978. SDL_free(propdata);
  979. if (caught_x11_error) {
  980. result = SDL_SetError("An error occurred while trying to set the window's icon");
  981. }
  982. }
  983. X11_XFlush(display);
  984. if (prevHandler) {
  985. X11_XSetErrorHandler(prevHandler);
  986. caught_x11_error = false;
  987. }
  988. return result;
  989. }
  990. void X11_UpdateWindowPosition(SDL_Window *window, bool use_current_position)
  991. {
  992. SDL_WindowData *data = window->internal;
  993. Display *display = data->videodata->display;
  994. const int rel_x = use_current_position ? window->x : window->pending.x;
  995. const int rel_y = use_current_position ? window->y : window->pending.y;
  996. SDL_RelativeToGlobalForWindow(window,
  997. rel_x - data->border_left, rel_y - data->border_top,
  998. &data->expected.x, &data->expected.y);
  999. // Attempt to move the window
  1000. if (window->flags & SDL_WINDOW_HIDDEN) {
  1001. window->internal->pending_position = true;
  1002. } else {
  1003. data->pending_operation |= X11_PENDING_OP_MOVE;
  1004. X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y);
  1005. }
  1006. }
  1007. bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
  1008. {
  1009. // Sync any pending fullscreen or maximize events.
  1010. if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) {
  1011. X11_FlushPendingEvents(_this, window);
  1012. }
  1013. // Set the position as pending if the window is maximized with a restore pending.
  1014. if (window->flags & SDL_WINDOW_MAXIMIZED) {
  1015. if (window->internal->pending_operation & X11_PENDING_OP_RESTORE) {
  1016. window->internal->pending_position = true;
  1017. }
  1018. return true;
  1019. }
  1020. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1021. if (SDL_WINDOW_IS_POPUP(window)) {
  1022. X11_ConstrainPopup(window, true);
  1023. }
  1024. X11_UpdateWindowPosition(window, false);
  1025. } else {
  1026. SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_UPDATE, true);
  1027. }
  1028. return true;
  1029. }
  1030. void X11_SetWindowMinMax(SDL_Window *window, bool use_current)
  1031. {
  1032. SDL_WindowData *data = window->internal;
  1033. Display *display = data->videodata->display;
  1034. XSizeHints *sizehints = X11_XAllocSizeHints();
  1035. long hint_flags = 0;
  1036. X11_XGetWMNormalHints(display, data->xwindow, sizehints, &hint_flags);
  1037. sizehints->flags &= ~(PMinSize | PMaxSize | PAspect);
  1038. if (data->window->flags & SDL_WINDOW_RESIZABLE) {
  1039. if (data->window->min_w || data->window->min_h) {
  1040. sizehints->flags |= PMinSize;
  1041. sizehints->min_width = data->window->min_w;
  1042. sizehints->min_height = data->window->min_h;
  1043. }
  1044. if (data->window->max_w || data->window->max_h) {
  1045. sizehints->flags |= PMaxSize;
  1046. sizehints->max_width = data->window->max_w;
  1047. sizehints->max_height = data->window->max_h;
  1048. }
  1049. if (data->window->min_aspect > 0.0f || data->window->max_aspect > 0.0f) {
  1050. sizehints->flags |= PAspect;
  1051. SDL_CalculateFraction(data->window->min_aspect, &sizehints->min_aspect.x, &sizehints->min_aspect.y);
  1052. SDL_CalculateFraction(data->window->max_aspect, &sizehints->max_aspect.x, &sizehints->max_aspect.y);
  1053. }
  1054. } else {
  1055. // Set the min/max to the same values to make the window non-resizable
  1056. sizehints->flags |= PMinSize | PMaxSize;
  1057. if (use_current) {
  1058. sizehints->min_width = sizehints->max_width = window->last_size_pending ? window->pending.w : data->window->floating.w;
  1059. sizehints->min_height = sizehints->max_height = window->last_size_pending ? window->pending.h : data->window->floating.h;
  1060. } else {
  1061. sizehints->min_width = sizehints->max_width = window->last_size_pending ? window->pending.w : data->window->windowed.w;
  1062. sizehints->min_height = sizehints->max_height = window->last_size_pending ? window->pending.h : data->window->windowed.h;
  1063. }
  1064. }
  1065. X11_XSetWMNormalHints(display, data->xwindow, sizehints);
  1066. X11_XFree(sizehints);
  1067. }
  1068. void X11_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window)
  1069. {
  1070. if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) {
  1071. X11_SyncWindow(_this, window);
  1072. }
  1073. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1074. X11_SetWindowMinMax(window, true);
  1075. }
  1076. }
  1077. void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window)
  1078. {
  1079. if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) {
  1080. X11_SyncWindow(_this, window);
  1081. }
  1082. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1083. X11_SetWindowMinMax(window, true);
  1084. }
  1085. }
  1086. void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window)
  1087. {
  1088. if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) {
  1089. X11_SyncWindow(_this, window);
  1090. }
  1091. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1092. X11_SetWindowMinMax(window, true);
  1093. }
  1094. }
  1095. void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
  1096. {
  1097. SDL_WindowData *data = window->internal;
  1098. Display *display = data->videodata->display;
  1099. /* Wait for pending maximize and fullscreen operations to complete, as these windows
  1100. * don't get size changes.
  1101. */
  1102. if (data->pending_operation & (X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_FULLSCREEN)) {
  1103. X11_FlushPendingEvents(_this, window);
  1104. }
  1105. // Set the size as pending if the window is being restored.
  1106. if (window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN)) {
  1107. // New size will be set when the window is restored.
  1108. if (data->pending_operation & X11_PENDING_OP_RESTORE) {
  1109. data->pending_size = true;
  1110. } else {
  1111. // Can't resize the window.
  1112. window->last_size_pending = false;
  1113. }
  1114. return;
  1115. }
  1116. if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
  1117. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1118. /* Apparently, if the X11 Window is set to a 'non-resizable' window, you cannot resize it using the X11_XResizeWindow, thus
  1119. * we must set the size hints to adjust the window size.
  1120. */
  1121. XSizeHints *sizehints = X11_XAllocSizeHints();
  1122. long userhints;
  1123. int dest_x, dest_y;
  1124. X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
  1125. data->expected.w = sizehints->min_width = sizehints->max_width = window->pending.w;
  1126. data->expected.h = sizehints->min_height = sizehints->max_height = window->pending.h;
  1127. sizehints->flags |= PMinSize | PMaxSize;
  1128. data->pending_operation |= X11_PENDING_OP_RESIZE;
  1129. X11_XSetWMNormalHints(display, data->xwindow, sizehints);
  1130. /* From Pierre-Loup:
  1131. WMs each have their little quirks with that. When you change the
  1132. size hints, they get a ConfigureNotify event with the
  1133. WM_NORMAL_SIZE_HINTS Atom. They all save the hints then, but they
  1134. don't all resize the window right away to enforce the new hints.
  1135. Some of them resize only after:
  1136. - A user-initiated move or resize
  1137. - A code-initiated move or resize
  1138. - Hiding & showing window (Unmap & map)
  1139. The following move & resize seems to help a lot of WMs that didn't
  1140. properly update after the hints were changed. We don't do a
  1141. hide/show, because there are supposedly subtle problems with doing so
  1142. and transitioning from windowed to fullscreen in Unity.
  1143. */
  1144. X11_XResizeWindow(display, data->xwindow, window->pending.w, window->pending.h);
  1145. const int x = window->last_position_pending ? window->pending.x : window->x;
  1146. const int y = window->last_position_pending ? window->pending.y : window->y;
  1147. SDL_RelativeToGlobalForWindow(window,
  1148. x - data->border_left,
  1149. y - data->border_top,
  1150. &dest_x, &dest_y);
  1151. X11_XMoveWindow(display, data->xwindow, dest_x, dest_y);
  1152. X11_XRaiseWindow(display, data->xwindow);
  1153. X11_XFree(sizehints);
  1154. }
  1155. } else {
  1156. data->expected.w = window->pending.w;
  1157. data->expected.h = window->pending.h;
  1158. data->pending_operation |= X11_PENDING_OP_RESIZE;
  1159. X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h);
  1160. }
  1161. /* External windows may call this to update the renderer size, but not pump SDL events,
  1162. * so the size event needs to be synthesized for external windows. If it is wrong, the
  1163. * true size will be sent if/when events are processed.
  1164. */
  1165. if (window->flags & SDL_WINDOW_EXTERNAL) {
  1166. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h);
  1167. }
  1168. }
  1169. bool X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right)
  1170. {
  1171. SDL_WindowData *data = window->internal;
  1172. *left = data->border_left;
  1173. *right = data->border_right;
  1174. *top = data->border_top;
  1175. *bottom = data->border_bottom;
  1176. return true;
  1177. }
  1178. bool X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity)
  1179. {
  1180. SDL_WindowData *data = window->internal;
  1181. Display *display = data->videodata->display;
  1182. Atom _NET_WM_WINDOW_OPACITY = data->videodata->atoms._NET_WM_WINDOW_OPACITY;
  1183. if (opacity == 1.0f) {
  1184. X11_XDeleteProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY);
  1185. } else {
  1186. const Uint32 FullyOpaque = 0xFFFFFFFF;
  1187. const long alpha = (long)((double)opacity * (double)FullyOpaque);
  1188. X11_XChangeProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
  1189. PropModeReplace, (unsigned char *)&alpha, 1);
  1190. }
  1191. return true;
  1192. }
  1193. bool X11_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent)
  1194. {
  1195. SDL_WindowData *data = window->internal;
  1196. SDL_WindowData *parent_data = parent ? parent->internal : NULL;
  1197. SDL_VideoData *video_data = _this->internal;
  1198. Display *display = video_data->display;
  1199. if (parent_data) {
  1200. X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow);
  1201. } else {
  1202. X11_XDeleteProperty(display, data->xwindow, video_data->atoms.WM_TRANSIENT_FOR);
  1203. }
  1204. return true;
  1205. }
  1206. bool X11_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal)
  1207. {
  1208. SDL_WindowData *data = window->internal;
  1209. SDL_VideoData *video_data = _this->internal;
  1210. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  1211. Display *display = video_data->display;
  1212. Uint32 flags = window->flags;
  1213. Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
  1214. Atom _NET_WM_STATE_MODAL = data->videodata->atoms._NET_WM_STATE_MODAL;
  1215. if (modal) {
  1216. flags |= SDL_WINDOW_MODAL;
  1217. } else {
  1218. flags &= ~SDL_WINDOW_MODAL;
  1219. X11_XDeleteProperty(display, data->xwindow, video_data->atoms.WM_TRANSIENT_FOR);
  1220. }
  1221. if (X11_IsWindowMapped(_this, window)) {
  1222. XEvent e;
  1223. SDL_zero(e);
  1224. e.xany.type = ClientMessage;
  1225. e.xclient.message_type = _NET_WM_STATE;
  1226. e.xclient.format = 32;
  1227. e.xclient.window = data->xwindow;
  1228. e.xclient.data.l[0] = modal ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  1229. e.xclient.data.l[1] = _NET_WM_STATE_MODAL;
  1230. e.xclient.data.l[3] = 0l;
  1231. X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1232. SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1233. } else {
  1234. X11_SetNetWMState(_this, data->xwindow, flags);
  1235. }
  1236. X11_XFlush(display);
  1237. return true;
  1238. }
  1239. void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered)
  1240. {
  1241. const bool focused = (window->flags & SDL_WINDOW_INPUT_FOCUS) != 0;
  1242. const bool visible = !(window->flags & SDL_WINDOW_HIDDEN) && !window->is_hiding;
  1243. SDL_WindowData *data = window->internal;
  1244. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  1245. Display *display = data->videodata->display;
  1246. XEvent event;
  1247. if (data->pending_operation & X11_PENDING_OP_FULLSCREEN) {
  1248. X11_SyncWindow(_this, window);
  1249. }
  1250. // If the window is fullscreen, the resize capability will be set/cleared when it is returned to windowed mode.
  1251. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1252. SetWindowBordered(display, displaydata->screen, data->xwindow, bordered);
  1253. X11_XFlush(display);
  1254. if (visible) {
  1255. XWindowAttributes attr;
  1256. do {
  1257. X11_XSync(display, False);
  1258. X11_XGetWindowAttributes(display, data->xwindow, &attr);
  1259. } while (attr.map_state != IsViewable);
  1260. if (focused) {
  1261. X11_XSetInputFocus(display, data->xwindow, RevertToParent, CurrentTime);
  1262. }
  1263. }
  1264. // make sure these don't make it to the real event queue if they fired here.
  1265. X11_XSync(display, False);
  1266. X11_XCheckIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
  1267. X11_XCheckIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
  1268. // Turning the borders off doesn't send an extent event, so they must be cleared here.
  1269. X11_GetBorderValues(data);
  1270. // Make sure the window manager didn't resize our window for the difference.
  1271. X11_XResizeWindow(display, data->xwindow, window->floating.w, window->floating.h);
  1272. X11_XSync(display, False);
  1273. } else {
  1274. // If fullscreen, set a flag to toggle the borders when returning to windowed mode.
  1275. data->toggle_borders = true;
  1276. data->fullscreen_borders_forced_on = false;
  1277. }
  1278. }
  1279. void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable)
  1280. {
  1281. SDL_WindowData *data = window->internal;
  1282. if (data->pending_operation & X11_PENDING_OP_FULLSCREEN) {
  1283. X11_SyncWindow(_this, window);
  1284. }
  1285. // If the window is fullscreen, the resize capability will be set/cleared when it is returned to windowed mode.
  1286. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1287. X11_SetWindowMinMax(window, true);
  1288. }
  1289. }
  1290. void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top)
  1291. {
  1292. SDL_WindowData *data = window->internal;
  1293. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  1294. Display *display = data->videodata->display;
  1295. Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
  1296. Atom _NET_WM_STATE_ABOVE = data->videodata->atoms._NET_WM_STATE_ABOVE;
  1297. if (X11_IsWindowMapped(_this, window)) {
  1298. XEvent e;
  1299. SDL_zero(e);
  1300. e.xany.type = ClientMessage;
  1301. e.xclient.message_type = _NET_WM_STATE;
  1302. e.xclient.format = 32;
  1303. e.xclient.window = data->xwindow;
  1304. e.xclient.data.l[0] =
  1305. on_top ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  1306. e.xclient.data.l[1] = _NET_WM_STATE_ABOVE;
  1307. e.xclient.data.l[3] = 0l;
  1308. X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1309. SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1310. } else {
  1311. X11_SetNetWMState(_this, data->xwindow, window->flags);
  1312. }
  1313. X11_XFlush(display);
  1314. }
  1315. void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
  1316. {
  1317. SDL_WindowData *data = window->internal;
  1318. Display *display = data->videodata->display;
  1319. bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, true);
  1320. bool set_position = false;
  1321. XEvent event;
  1322. // If the window was previously shown, pump events to avoid possible positioning issues.
  1323. if (data->was_shown) {
  1324. X11_PumpEvents(_this);
  1325. }
  1326. if (SDL_WINDOW_IS_POPUP(window)) {
  1327. // Update the position in case the parent moved while we were hidden
  1328. X11_ConstrainPopup(window, true);
  1329. data->pending_position = true;
  1330. set_position = true;
  1331. }
  1332. /* Whether XMapRaised focuses the window is based on the window type and it is
  1333. * wm specific. There isn't much we can do here */
  1334. (void)bActivate;
  1335. if (!X11_IsWindowMapped(_this, window)) {
  1336. X11_XMapRaised(display, data->xwindow);
  1337. /* Blocking wait for "MapNotify" event.
  1338. * We use X11_XIfEvent because pXWindowEvent takes a mask rather than a type,
  1339. * and XCheckTypedWindowEvent doesn't block */
  1340. if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) {
  1341. X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
  1342. }
  1343. X11_XFlush(display);
  1344. set_position = data->pending_position ||
  1345. (!(window->flags & SDL_WINDOW_BORDERLESS) && !window->undefined_x && !window->undefined_y);
  1346. }
  1347. if (!data->videodata->net_wm) {
  1348. // no WM means no FocusIn event, which confuses us. Force it.
  1349. X11_XSync(display, False);
  1350. X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime);
  1351. X11_XFlush(display);
  1352. }
  1353. // Grabbing popup menus get keyboard focus.
  1354. if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
  1355. X11_SetKeyboardFocus(window, true);
  1356. }
  1357. // Get some valid border values, if we haven't received them yet
  1358. if (data->border_left == 0 && data->border_right == 0 && data->border_top == 0 && data->border_bottom == 0) {
  1359. X11_GetBorderValues(data);
  1360. }
  1361. if (set_position) {
  1362. // Apply the window position, accounting for offsets due to the borders appearing, but only when initially mapping.
  1363. const int tx = (data->pending_position ? window->pending.x : window->x) - (data->was_shown ? 0 : data->border_left);
  1364. const int ty = (data->pending_position ? window->pending.y : window->y) - (data->was_shown ? 0 : data->border_top);
  1365. int x, y;
  1366. SDL_RelativeToGlobalForWindow(window, tx, ty, &x, &y);
  1367. data->pending_position = false;
  1368. X11_XMoveWindow(display, data->xwindow, x, y);
  1369. }
  1370. /* XMonad ignores size hints and shrinks the client area to overlay borders on fixed-size windows,
  1371. * even if no borders were requested, resulting in the window client area being smaller than
  1372. * requested. Calling XResizeWindow after mapping seems to fix it, even though resizing fixed-size
  1373. * windows in this manner doesn't work on any other window manager.
  1374. */
  1375. if (!(window->flags & SDL_WINDOW_RESIZABLE) && X11_CheckCurrentDesktop("xmonad")) {
  1376. X11_XResizeWindow(display, data->xwindow, window->w, window->h);
  1377. }
  1378. /* Some window managers can send garbage coordinates while mapping the window, so don't emit size and position
  1379. * events during the initial configure events.
  1380. */
  1381. data->size_move_event_flags = X11_SIZE_MOVE_EVENTS_DISABLE;
  1382. X11_XSync(display, False);
  1383. X11_PumpEvents(_this);
  1384. data->size_move_event_flags = 0;
  1385. /* A MapNotify or PropertyNotify may not have arrived, so ensure that the shown event is dispatched
  1386. * to apply pending state before clearing the flag.
  1387. */
  1388. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
  1389. data->was_shown = true;
  1390. // If a configure event was received (type is non-zero), send the final window size and coordinates.
  1391. if (data->last_xconfigure.type) {
  1392. int x, y;
  1393. SDL_GlobalToRelativeForWindow(data->window, data->last_xconfigure.x, data->last_xconfigure.y, &x, &y);
  1394. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, data->last_xconfigure.width, data->last_xconfigure.height);
  1395. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
  1396. }
  1397. }
  1398. void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
  1399. {
  1400. SDL_WindowData *data = window->internal;
  1401. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  1402. int screen = (displaydata ? displaydata->screen : 0);
  1403. Display *display = data->videodata->display;
  1404. XEvent event;
  1405. if (X11_IsWindowMapped(_this, window)) {
  1406. X11_XWithdrawWindow(display, data->xwindow, screen);
  1407. // Blocking wait for "UnmapNotify" event
  1408. if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) {
  1409. X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
  1410. }
  1411. X11_XFlush(display);
  1412. }
  1413. // Transfer keyboard focus back to the parent
  1414. if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
  1415. SDL_Window *new_focus;
  1416. const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
  1417. X11_SetKeyboardFocus(new_focus, set_focus);
  1418. }
  1419. X11_XSync(display, False);
  1420. X11_PumpEvents(_this);
  1421. }
  1422. static bool X11_SetWindowActive(SDL_VideoDevice *_this, SDL_Window *window)
  1423. {
  1424. CHECK_WINDOW_DATA(window);
  1425. SDL_WindowData *data = window->internal;
  1426. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  1427. Display *display = data->videodata->display;
  1428. Atom _NET_ACTIVE_WINDOW = data->videodata->atoms._NET_ACTIVE_WINDOW;
  1429. if (X11_IsWindowMapped(_this, window)) {
  1430. XEvent e;
  1431. // printf("SDL Window %p: sending _NET_ACTIVE_WINDOW with timestamp %lu\n", window, data->user_time);
  1432. SDL_zero(e);
  1433. e.xany.type = ClientMessage;
  1434. e.xclient.message_type = _NET_ACTIVE_WINDOW;
  1435. e.xclient.format = 32;
  1436. e.xclient.window = data->xwindow;
  1437. e.xclient.data.l[0] = 1; // source indication. 1 = application
  1438. e.xclient.data.l[1] = data->user_time;
  1439. e.xclient.data.l[2] = 0;
  1440. X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1441. SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1442. X11_XFlush(display);
  1443. }
  1444. return true;
  1445. }
  1446. void X11_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
  1447. {
  1448. SDL_WindowData *data = window->internal;
  1449. Display *display = data->videodata->display;
  1450. bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, true);
  1451. X11_XRaiseWindow(display, data->xwindow);
  1452. if (bActivate) {
  1453. X11_SetWindowActive(_this, window);
  1454. }
  1455. X11_XFlush(display);
  1456. }
  1457. static bool X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, bool maximized)
  1458. {
  1459. CHECK_WINDOW_DATA(window);
  1460. SDL_WindowData *data = window->internal;
  1461. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  1462. Display *display = data->videodata->display;
  1463. Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
  1464. Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT;
  1465. Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ;
  1466. if (window->flags & SDL_WINDOW_FULLSCREEN) {
  1467. /* Fullscreen windows are maximized on some window managers,
  1468. and this is functional behavior, so don't remove that state
  1469. now, we'll take care of it when we leave fullscreen mode.
  1470. */
  1471. return true;
  1472. }
  1473. if (X11_IsWindowMapped(_this, window)) {
  1474. XEvent e;
  1475. SDL_zero(e);
  1476. e.xany.type = ClientMessage;
  1477. e.xclient.message_type = _NET_WM_STATE;
  1478. e.xclient.format = 32;
  1479. e.xclient.window = data->xwindow;
  1480. e.xclient.data.l[0] =
  1481. maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  1482. e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT;
  1483. e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ;
  1484. e.xclient.data.l[3] = 0l;
  1485. if (maximized) {
  1486. SDL_DisplayID displayID = SDL_GetDisplayForWindow(window);
  1487. SDL_Rect bounds;
  1488. SDL_zero(bounds);
  1489. SDL_GetDisplayUsableBounds(displayID, &bounds);
  1490. data->expected.x = bounds.x + data->border_left;
  1491. data->expected.y = bounds.y + data->border_top;
  1492. data->expected.w = bounds.w - (data->border_left + data->border_right);
  1493. data->expected.h = bounds.h - (data->border_top + data->border_bottom);
  1494. } else {
  1495. data->expected.x = window->floating.x;
  1496. data->expected.y = window->floating.y;
  1497. data->expected.w = window->floating.w;
  1498. data->expected.h = window->floating.h;
  1499. }
  1500. X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1501. SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1502. } else {
  1503. X11_SetNetWMState(_this, data->xwindow, window->flags);
  1504. }
  1505. X11_XFlush(display);
  1506. return true;
  1507. }
  1508. void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
  1509. {
  1510. if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MINIMIZE)) {
  1511. SDL_SyncWindow(window);
  1512. }
  1513. if (window->flags & SDL_WINDOW_FULLSCREEN) {
  1514. // If fullscreen, just toggle the restored state.
  1515. window->internal->window_was_maximized = true;
  1516. return;
  1517. }
  1518. if (!(window->flags & SDL_WINDOW_MINIMIZED)) {
  1519. window->internal->pending_operation |= X11_PENDING_OP_MAXIMIZE;
  1520. X11_SetWindowMaximized(_this, window, true);
  1521. }
  1522. }
  1523. void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
  1524. {
  1525. SDL_WindowData *data = window->internal;
  1526. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  1527. Display *display = data->videodata->display;
  1528. if (data->pending_operation & SDL_WINDOW_FULLSCREEN) {
  1529. SDL_SyncWindow(window);
  1530. }
  1531. data->pending_operation |= X11_PENDING_OP_MINIMIZE;
  1532. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1533. data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED);
  1534. }
  1535. X11_XIconifyWindow(display, data->xwindow, displaydata->screen);
  1536. X11_XFlush(display);
  1537. }
  1538. void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
  1539. {
  1540. // Don't restore the window the first time it is being shown.
  1541. if (!window->internal->was_shown) {
  1542. return;
  1543. }
  1544. if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MINIMIZE)) {
  1545. SDL_SyncWindow(window);
  1546. }
  1547. if ((window->flags & SDL_WINDOW_FULLSCREEN) && !(window->flags & SDL_WINDOW_MINIMIZED)) {
  1548. // If fullscreen and not minimized, just toggle the restored state.
  1549. window->internal->window_was_maximized = false;
  1550. return;
  1551. }
  1552. if (window->flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED) ||
  1553. (window->internal->pending_operation & X11_PENDING_OP_MINIMIZE)) {
  1554. window->internal->pending_operation |= X11_PENDING_OP_RESTORE;
  1555. }
  1556. // If the window was minimized while maximized, restore as maximized.
  1557. const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized;
  1558. X11_SetWindowMaximized(_this, window, maximize);
  1559. X11_ShowWindow(_this, window);
  1560. X11_SetWindowActive(_this, window);
  1561. }
  1562. // This asks the Window Manager to handle fullscreen for us. This is the modern way.
  1563. static SDL_FullscreenResult X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen)
  1564. {
  1565. CHECK_WINDOW_DATA(window);
  1566. CHECK_DISPLAY_DATA(_display);
  1567. SDL_WindowData *data = window->internal;
  1568. SDL_DisplayData *displaydata = _display->internal;
  1569. Display *display = data->videodata->display;
  1570. Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
  1571. Atom _NET_WM_STATE_FULLSCREEN = data->videodata->atoms._NET_WM_STATE_FULLSCREEN;
  1572. if (!data->was_shown && fullscreen == SDL_FULLSCREEN_OP_LEAVE) {
  1573. return SDL_FULLSCREEN_SUCCEEDED;
  1574. }
  1575. if (X11_IsWindowMapped(_this, window)) {
  1576. XEvent e;
  1577. // Flush any pending fullscreen events.
  1578. if (data->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MOVE)) {
  1579. X11_SyncWindow(_this, window);
  1580. }
  1581. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1582. if (fullscreen == SDL_FULLSCREEN_OP_UPDATE) {
  1583. // Request was out of date; set -1 to signal the video core to undo a mode switch.
  1584. return SDL_FULLSCREEN_FAILED;
  1585. } else if (fullscreen == SDL_FULLSCREEN_OP_LEAVE) {
  1586. // Nothing to do.
  1587. return SDL_FULLSCREEN_SUCCEEDED;
  1588. }
  1589. }
  1590. if (fullscreen && !(window->flags & SDL_WINDOW_RESIZABLE)) {
  1591. /* Compiz refuses fullscreen toggle if we're not resizable, so update the hints so we
  1592. can be resized to the fullscreen resolution (or reset so we're not resizable again) */
  1593. XSizeHints *sizehints = X11_XAllocSizeHints();
  1594. long flags = 0;
  1595. X11_XGetWMNormalHints(display, data->xwindow, sizehints, &flags);
  1596. // we are going fullscreen so turn the flags off
  1597. sizehints->flags &= ~(PMinSize | PMaxSize | PAspect);
  1598. X11_XSetWMNormalHints(display, data->xwindow, sizehints);
  1599. X11_XFree(sizehints);
  1600. }
  1601. SDL_zero(e);
  1602. e.xany.type = ClientMessage;
  1603. e.xclient.message_type = _NET_WM_STATE;
  1604. e.xclient.format = 32;
  1605. e.xclient.window = data->xwindow;
  1606. e.xclient.data.l[0] =
  1607. fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  1608. e.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN;
  1609. e.xclient.data.l[3] = 0l;
  1610. X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1611. SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1612. if (!!(window->flags & SDL_WINDOW_FULLSCREEN) != fullscreen) {
  1613. data->pending_operation |= X11_PENDING_OP_FULLSCREEN;
  1614. }
  1615. // Set the position so the window will be on the target display
  1616. if (fullscreen) {
  1617. SDL_DisplayID current = SDL_GetDisplayForWindowPosition(window);
  1618. SDL_copyp(&data->requested_fullscreen_mode, &window->current_fullscreen_mode);
  1619. if (fullscreen != !!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  1620. data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED);
  1621. }
  1622. data->expected.x = displaydata->x;
  1623. data->expected.y = displaydata->y;
  1624. data->expected.w = _display->current_mode->w;
  1625. data->expected.h = _display->current_mode->h;
  1626. // Only move the window if it isn't fullscreen or already on the target display.
  1627. if (!(window->flags & SDL_WINDOW_FULLSCREEN) || (!current || current != _display->id)) {
  1628. X11_XMoveWindow(display, data->xwindow, displaydata->x, displaydata->y);
  1629. data->pending_operation |= X11_PENDING_OP_MOVE;
  1630. }
  1631. } else {
  1632. SDL_zero(data->requested_fullscreen_mode);
  1633. /* Fullscreen windows sometimes end up being marked maximized by
  1634. * window managers. Force it back to how we expect it to be.
  1635. */
  1636. SDL_zero(e);
  1637. e.xany.type = ClientMessage;
  1638. e.xclient.message_type = _NET_WM_STATE;
  1639. e.xclient.format = 32;
  1640. e.xclient.window = data->xwindow;
  1641. if (data->window_was_maximized) {
  1642. e.xclient.data.l[0] = _NET_WM_STATE_ADD;
  1643. } else {
  1644. e.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
  1645. }
  1646. e.xclient.data.l[1] = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT;
  1647. e.xclient.data.l[2] = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ;
  1648. e.xclient.data.l[3] = 0l;
  1649. X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1650. SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1651. }
  1652. } else {
  1653. SDL_WindowFlags flags;
  1654. flags = window->flags;
  1655. if (fullscreen) {
  1656. flags |= SDL_WINDOW_FULLSCREEN;
  1657. } else {
  1658. flags &= ~SDL_WINDOW_FULLSCREEN;
  1659. }
  1660. X11_SetNetWMState(_this, data->xwindow, flags);
  1661. }
  1662. if (data->visual->class == DirectColor) {
  1663. if (fullscreen) {
  1664. X11_XInstallColormap(display, data->colormap);
  1665. } else {
  1666. X11_XUninstallColormap(display, data->colormap);
  1667. }
  1668. }
  1669. return SDL_FULLSCREEN_PENDING;
  1670. }
  1671. SDL_FullscreenResult X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen)
  1672. {
  1673. return X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen);
  1674. }
  1675. typedef struct
  1676. {
  1677. unsigned char *data;
  1678. int format, count;
  1679. Atom type;
  1680. } SDL_x11Prop;
  1681. /* Reads property
  1682. Must call X11_XFree on results
  1683. */
  1684. static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop)
  1685. {
  1686. unsigned char *ret = NULL;
  1687. Atom type;
  1688. int fmt;
  1689. unsigned long count;
  1690. unsigned long bytes_left;
  1691. int bytes_fetch = 0;
  1692. do {
  1693. if (ret) {
  1694. X11_XFree(ret);
  1695. }
  1696. X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
  1697. bytes_fetch += bytes_left;
  1698. } while (bytes_left != 0);
  1699. p->data = ret;
  1700. p->format = fmt;
  1701. p->count = count;
  1702. p->type = type;
  1703. }
  1704. void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size)
  1705. {
  1706. SDL_WindowData *data = window->internal;
  1707. Display *display = data->videodata->display;
  1708. XWindowAttributes attributes;
  1709. Atom icc_profile_atom;
  1710. char icc_atom_string[sizeof("_ICC_PROFILE_") + 12];
  1711. void *ret_icc_profile_data = NULL;
  1712. CARD8 *icc_profile_data;
  1713. int real_format;
  1714. unsigned long real_nitems;
  1715. SDL_x11Prop atomProp;
  1716. X11_XGetWindowAttributes(display, data->xwindow, &attributes);
  1717. if (X11_XScreenNumberOfScreen(attributes.screen) > 0) {
  1718. (void)SDL_snprintf(icc_atom_string, sizeof("_ICC_PROFILE_") + 12, "%s%d", "_ICC_PROFILE_", X11_XScreenNumberOfScreen(attributes.screen));
  1719. } else {
  1720. SDL_strlcpy(icc_atom_string, "_ICC_PROFILE", sizeof("_ICC_PROFILE"));
  1721. }
  1722. X11_XGetWindowAttributes(display, RootWindowOfScreen(attributes.screen), &attributes);
  1723. icc_profile_atom = X11_XInternAtom(display, icc_atom_string, True);
  1724. if (icc_profile_atom == None) {
  1725. SDL_SetError("Screen is not calibrated.");
  1726. return NULL;
  1727. }
  1728. X11_ReadProperty(&atomProp, display, RootWindowOfScreen(attributes.screen), icc_profile_atom);
  1729. real_format = atomProp.format;
  1730. real_nitems = atomProp.count;
  1731. icc_profile_data = atomProp.data;
  1732. if (real_format == None) {
  1733. SDL_SetError("Screen is not calibrated.");
  1734. return NULL;
  1735. }
  1736. ret_icc_profile_data = SDL_malloc(real_nitems);
  1737. if (!ret_icc_profile_data) {
  1738. return NULL;
  1739. }
  1740. SDL_memcpy(ret_icc_profile_data, icc_profile_data, real_nitems);
  1741. *size = real_nitems;
  1742. X11_XFree(icc_profile_data);
  1743. return ret_icc_profile_data;
  1744. }
  1745. bool X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed)
  1746. {
  1747. SDL_WindowData *data = window->internal;
  1748. Display *display;
  1749. if (!data) {
  1750. return SDL_SetError("Invalid window data");
  1751. }
  1752. data->mouse_grabbed = false;
  1753. data->pending_grab = false;
  1754. display = data->videodata->display;
  1755. if (grabbed) {
  1756. /* If the window is unmapped, XGrab calls return GrabNotViewable,
  1757. so when we get a MapNotify later, we'll try to update the grab as
  1758. appropriate. */
  1759. if (window->flags & SDL_WINDOW_HIDDEN) {
  1760. return true;
  1761. }
  1762. /* If XInput2 is enabled, it will grab the pointer on button presses,
  1763. * which results in XGrabPointer returning AlreadyGrabbed. If buttons
  1764. * are currently pressed, clear any existing grabs before attempting
  1765. * the confinement grab.
  1766. */
  1767. if (data->xinput2_mouse_enabled && SDL_GetMouseState(NULL, NULL)) {
  1768. data->pending_grab = true;
  1769. return true;
  1770. }
  1771. // Try to grab the mouse
  1772. if (!data->videodata->broken_pointer_grab) {
  1773. const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
  1774. int attempts;
  1775. int result = 0;
  1776. // Try for up to 5000ms (5s) to grab. If it still fails, stop trying.
  1777. for (attempts = 0; attempts < 100; attempts++) {
  1778. result = X11_XGrabPointer(display, data->xwindow, False, mask, GrabModeAsync,
  1779. GrabModeAsync, data->xwindow, None, CurrentTime);
  1780. if (result == GrabSuccess) {
  1781. data->mouse_grabbed = true;
  1782. break;
  1783. }
  1784. SDL_Delay(50);
  1785. }
  1786. if (result != GrabSuccess) {
  1787. data->videodata->broken_pointer_grab = true; // don't try again.
  1788. }
  1789. }
  1790. X11_Xinput2GrabTouch(_this, window);
  1791. // Raise the window if we grab the mouse
  1792. X11_XRaiseWindow(display, data->xwindow);
  1793. } else {
  1794. X11_XUngrabPointer(display, CurrentTime);
  1795. X11_Xinput2UngrabTouch(_this, window);
  1796. }
  1797. X11_XSync(display, False);
  1798. if (!data->videodata->broken_pointer_grab) {
  1799. return true;
  1800. } else {
  1801. return SDL_SetError("The X server refused to let us grab the mouse. You might experience input bugs.");
  1802. }
  1803. }
  1804. bool X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed)
  1805. {
  1806. SDL_WindowData *data = window->internal;
  1807. Display *display;
  1808. if (!data) {
  1809. return SDL_SetError("Invalid window data");
  1810. }
  1811. display = data->videodata->display;
  1812. if (grabbed) {
  1813. /* If the window is unmapped, XGrab calls return GrabNotViewable,
  1814. so when we get a MapNotify later, we'll try to update the grab as
  1815. appropriate. */
  1816. if (window->flags & SDL_WINDOW_HIDDEN) {
  1817. return true;
  1818. }
  1819. /* GNOME needs the _XWAYLAND_MAY_GRAB_KEYBOARD message on XWayland:
  1820. *
  1821. * - message_type set to "_XWAYLAND_MAY_GRAB_KEYBOARD"
  1822. * - window set to the xid of the window on which the grab is to be issued
  1823. * - data.l[0] to a non-zero value
  1824. *
  1825. * The dconf setting `org/gnome/mutter/wayland/xwayland-allow-grabs` must be enabled as well.
  1826. *
  1827. * https://gitlab.gnome.org/GNOME/mutter/-/commit/5f132f39750f684c3732b4346dec810cd218d609
  1828. */
  1829. if (_this->internal->is_xwayland) {
  1830. Atom _XWAYLAND_MAY_GRAB_ATOM = X11_XInternAtom(display, "_XWAYLAND_MAY_GRAB_KEYBOARD", False);
  1831. if (_XWAYLAND_MAY_GRAB_ATOM != None) {
  1832. XClientMessageEvent client_message;
  1833. client_message.type = ClientMessage;
  1834. client_message.window = data->xwindow;
  1835. client_message.format = 32;
  1836. client_message.message_type = _XWAYLAND_MAY_GRAB_ATOM;
  1837. client_message.data.l[0] = 1;
  1838. client_message.data.l[1] = CurrentTime;
  1839. X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&client_message);
  1840. X11_XFlush(display);
  1841. }
  1842. }
  1843. X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync,
  1844. GrabModeAsync, CurrentTime);
  1845. } else {
  1846. X11_XUngrabKeyboard(display, CurrentTime);
  1847. }
  1848. X11_XSync(display, False);
  1849. return true;
  1850. }
  1851. void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
  1852. {
  1853. SDL_WindowData *data = window->internal;
  1854. if (data) {
  1855. SDL_VideoData *videodata = data->videodata;
  1856. Display *display = videodata->display;
  1857. int numwindows = videodata->numwindows;
  1858. SDL_WindowData **windowlist = videodata->windowlist;
  1859. int i;
  1860. if (windowlist) {
  1861. for (i = 0; i < numwindows; ++i) {
  1862. if (windowlist[i] && (windowlist[i]->window == window)) {
  1863. windowlist[i] = windowlist[numwindows - 1];
  1864. windowlist[numwindows - 1] = NULL;
  1865. videodata->numwindows--;
  1866. break;
  1867. }
  1868. }
  1869. }
  1870. X11_DestroyInputContext(data);
  1871. #ifdef SDL_VIDEO_DRIVER_X11_XSYNC
  1872. X11_TermResizeSync(window);
  1873. #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
  1874. if (!(window->flags & SDL_WINDOW_EXTERNAL)) {
  1875. X11_XDestroyWindow(display, data->xwindow);
  1876. X11_XFlush(display);
  1877. }
  1878. SDL_free(data);
  1879. #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
  1880. // If the pointer barriers are active for this, deactivate it.
  1881. if (videodata->active_cursor_confined_window == window) {
  1882. X11_DestroyPointerBarrier(_this, window);
  1883. }
  1884. #endif // SDL_VIDEO_DRIVER_X11_XFIXES
  1885. }
  1886. window->internal = NULL;
  1887. }
  1888. bool X11_SetWindowHitTest(SDL_Window *window, bool enabled)
  1889. {
  1890. return true; // just succeed, the real work is done elsewhere.
  1891. }
  1892. void X11_AcceptDragAndDrop(SDL_Window *window, bool accept)
  1893. {
  1894. SDL_WindowData *data = window->internal;
  1895. Display *display = data->videodata->display;
  1896. Atom XdndAware = data->videodata->atoms.XdndAware;
  1897. if (accept) {
  1898. Atom xdnd_version = 5;
  1899. X11_XChangeProperty(display, data->xwindow, XdndAware, XA_ATOM, 32,
  1900. PropModeReplace, (unsigned char *)&xdnd_version, 1);
  1901. } else {
  1902. X11_XDeleteProperty(display, data->xwindow, XdndAware);
  1903. }
  1904. }
  1905. bool X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation)
  1906. {
  1907. SDL_WindowData *data = window->internal;
  1908. Display *display = data->videodata->display;
  1909. XWMHints *wmhints;
  1910. wmhints = X11_XGetWMHints(display, data->xwindow);
  1911. if (!wmhints) {
  1912. return SDL_SetError("Couldn't get WM hints");
  1913. }
  1914. wmhints->flags &= ~XUrgencyHint;
  1915. data->flashing_window = false;
  1916. data->flash_cancel_time = 0;
  1917. switch (operation) {
  1918. case SDL_FLASH_CANCEL:
  1919. // Taken care of above
  1920. break;
  1921. case SDL_FLASH_BRIEFLY:
  1922. if (!(window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1923. wmhints->flags |= XUrgencyHint;
  1924. data->flashing_window = true;
  1925. // On Ubuntu 21.04 this causes a dialog to pop up, so leave it up for a full second so users can see it
  1926. data->flash_cancel_time = SDL_GetTicks() + 1000;
  1927. }
  1928. break;
  1929. case SDL_FLASH_UNTIL_FOCUSED:
  1930. if (!(window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1931. wmhints->flags |= XUrgencyHint;
  1932. data->flashing_window = true;
  1933. }
  1934. break;
  1935. default:
  1936. break;
  1937. }
  1938. X11_XSetWMHints(display, data->xwindow, wmhints);
  1939. X11_XFree(wmhints);
  1940. return true;
  1941. }
  1942. bool SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title)
  1943. {
  1944. Atom _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
  1945. XTextProperty titleprop;
  1946. int conv = X11_XmbTextListToTextProperty(display, &title, 1, XTextStyle, &titleprop);
  1947. Status status;
  1948. if (X11_XSupportsLocale() != True) {
  1949. return SDL_SetError("Current locale not supported by X server, cannot continue.");
  1950. }
  1951. if (conv == 0) {
  1952. X11_XSetTextProperty(display, xwindow, &titleprop, XA_WM_NAME);
  1953. X11_XFree(titleprop.value);
  1954. // we know this can't be a locale error as we checked X locale validity
  1955. } else if (conv < 0) {
  1956. return SDL_OutOfMemory();
  1957. } else { // conv > 0
  1958. SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%d characters were not convertible to the current locale!", conv);
  1959. return true;
  1960. }
  1961. #ifdef X_HAVE_UTF8_STRING
  1962. status = X11_Xutf8TextListToTextProperty(display, &title, 1, XUTF8StringStyle, &titleprop);
  1963. if (status == Success) {
  1964. X11_XSetTextProperty(display, xwindow, &titleprop, _NET_WM_NAME);
  1965. X11_XFree(titleprop.value);
  1966. } else {
  1967. return SDL_SetError("Failed to convert title to UTF8! Bad encoding, or bad Xorg encoding? Window title: «%s»", title);
  1968. }
  1969. #endif
  1970. X11_XFlush(display);
  1971. return true;
  1972. }
  1973. void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
  1974. {
  1975. SDL_WindowData *data = window->internal;
  1976. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  1977. Display *display = data->videodata->display;
  1978. Window root = RootWindow(display, displaydata->screen);
  1979. XClientMessageEvent e;
  1980. Window childReturn;
  1981. int wx, wy;
  1982. SDL_zero(e);
  1983. X11_XTranslateCoordinates(display, data->xwindow, root, x, y, &wx, &wy, &childReturn);
  1984. e.type = ClientMessage;
  1985. e.window = data->xwindow;
  1986. e.message_type = X11_XInternAtom(display, "_GTK_SHOW_WINDOW_MENU", 0);
  1987. e.data.l[0] = 0; // GTK device ID (unused)
  1988. e.data.l[1] = wx; // X coordinate relative to root
  1989. e.data.l[2] = wy; // Y coordinate relative to root
  1990. e.format = 32;
  1991. X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&e);
  1992. X11_XFlush(display);
  1993. }
  1994. bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
  1995. {
  1996. SDL_WindowData *data = window->internal;
  1997. // If the window is external and has only a pending resize or move event, use the special external sync path to avoid processing events.
  1998. if ((window->flags & SDL_WINDOW_EXTERNAL) &&
  1999. (data->pending_operation & ~(X11_PENDING_OP_RESIZE | X11_PENDING_OP_MOVE)) == X11_PENDING_OP_NONE) {
  2000. X11_ExternalResizeMoveSync(window);
  2001. return true;
  2002. }
  2003. const Uint64 current_time = SDL_GetTicksNS();
  2004. Uint64 timeout = 0;
  2005. // Allow time for any pending mode switches to complete.
  2006. for (int i = 0; i < _this->num_displays; ++i) {
  2007. if (_this->displays[i]->internal->mode_switch_deadline_ns &&
  2008. current_time < _this->displays[i]->internal->mode_switch_deadline_ns) {
  2009. timeout = SDL_max(_this->displays[i]->internal->mode_switch_deadline_ns - current_time, timeout);
  2010. }
  2011. }
  2012. /* 100ms is fine for most cases, but, for some reason, maximizing
  2013. * a window can take a very long time.
  2014. */
  2015. timeout += window->internal->pending_operation & X11_PENDING_OP_MAXIMIZE ? SDL_MS_TO_NS(1000) : SDL_MS_TO_NS(100);
  2016. return X11_SyncWindowTimeout(_this, window, timeout);
  2017. }
  2018. bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
  2019. {
  2020. if (!SDL_WINDOW_IS_POPUP(window)) {
  2021. SDL_WindowData *data = window->internal;
  2022. Display *display = data->videodata->display;
  2023. XWMHints *wmhints;
  2024. wmhints = X11_XGetWMHints(display, data->xwindow);
  2025. if (!wmhints) {
  2026. return SDL_SetError("Couldn't get WM hints");
  2027. }
  2028. wmhints->input = focusable ? True : False;
  2029. wmhints->flags |= InputHint;
  2030. X11_XSetWMHints(display, data->xwindow, wmhints);
  2031. X11_XFree(wmhints);
  2032. } else if (window->flags & SDL_WINDOW_POPUP_MENU) {
  2033. if (!(window->flags & SDL_WINDOW_HIDDEN)) {
  2034. if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  2035. SDL_Window *new_focus;
  2036. const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
  2037. X11_SetKeyboardFocus(new_focus, set_focus);
  2038. } else if (focusable) {
  2039. if (SDL_ShouldFocusPopup(window)) {
  2040. X11_SetKeyboardFocus(window, true);
  2041. }
  2042. }
  2043. }
  2044. return true;
  2045. }
  2046. return true;
  2047. }
  2048. #endif // SDL_VIDEO_DRIVER_X11