x11_window.c 107 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286
  1. //========================================================================
  2. // GLFW 3.4 X11 - www.glfw.org
  3. //------------------------------------------------------------------------
  4. // Copyright (c) 2002-2006 Marcus Geelnard
  5. // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
  6. //
  7. // This software is provided 'as-is', without any express or implied
  8. // warranty. In no event will the authors be held liable for any damages
  9. // arising from the use of this software.
  10. //
  11. // Permission is granted to anyone to use this software for any purpose,
  12. // including commercial applications, and to alter it and redistribute it
  13. // freely, subject to the following restrictions:
  14. //
  15. // 1. The origin of this software must not be misrepresented; you must not
  16. // claim that you wrote the original software. If you use this software
  17. // in a product, an acknowledgment in the product documentation would
  18. // be appreciated but is not required.
  19. //
  20. // 2. Altered source versions must be plainly marked as such, and must not
  21. // be misrepresented as being the original software.
  22. //
  23. // 3. This notice may not be removed or altered from any source
  24. // distribution.
  25. //
  26. //========================================================================
  27. // It is fine to use C99 in this file because it will not be built with VS
  28. //========================================================================
  29. #define _GNU_SOURCE
  30. #include "internal.h"
  31. #include "backend_utils.h"
  32. #include "linux_notify.h"
  33. #include "../kitty/monotonic.h"
  34. #include <X11/cursorfont.h>
  35. #include <X11/Xmd.h>
  36. #include <string.h>
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <limits.h>
  40. #include <errno.h>
  41. // Action for EWMH client messages
  42. #define _NET_WM_STATE_REMOVE 0
  43. #define _NET_WM_STATE_ADD 1
  44. #define _NET_WM_STATE_TOGGLE 2
  45. // Additional mouse button names for XButtonEvent
  46. #define Button6 6
  47. #define Button7 7
  48. // Motif WM hints flags
  49. #define MWM_HINTS_DECORATIONS 2
  50. #define MWM_DECOR_ALL 1
  51. #define _GLFW_XDND_VERSION 5
  52. // Wait for data to arrive using poll
  53. // This avoids blocking other threads via the per-display Xlib lock that also
  54. // covers GLX functions
  55. //
  56. static unsigned _glfwDispatchX11Events(void);
  57. static void
  58. handleEvents(monotonic_t timeout) {
  59. EVDBG("starting handleEvents(%.2f)", monotonic_t_to_s_double(timeout));
  60. int display_read_ok = pollForEvents(&_glfw.x11.eventLoopData, timeout, NULL);
  61. EVDBG("display_read_ok: %d", display_read_ok);
  62. if (display_read_ok) {
  63. unsigned dispatched = _glfwDispatchX11Events();
  64. (void)dispatched;
  65. EVDBG("dispatched %u X11 events", dispatched);
  66. }
  67. glfw_ibus_dispatch(&_glfw.x11.xkb.ibus);
  68. glfw_dbus_session_bus_dispatch();
  69. EVDBG("other dispatch done");
  70. if (_glfw.x11.eventLoopData.wakeup_fd_ready) check_for_wakeup_events(&_glfw.x11.eventLoopData);
  71. }
  72. static bool
  73. waitForX11Event(monotonic_t timeout) {
  74. // returns true if there is X11 data waiting to be read, does not run watches and timers
  75. monotonic_t end_time = glfwGetTime() + timeout;
  76. while(true) {
  77. if (timeout >= 0) {
  78. const int result = pollWithTimeout(_glfw.x11.eventLoopData.fds, 1, timeout);
  79. if (result > 0) return true;
  80. timeout = end_time - glfwGetTime();
  81. if (timeout <= 0) return false;
  82. if (result < 0 && (errno == EINTR || errno == EAGAIN)) continue;
  83. return false;
  84. } else {
  85. const int result = poll(_glfw.x11.eventLoopData.fds, 1, -1);
  86. if (result > 0) return true;
  87. if (result < 0 && (errno == EINTR || errno == EAGAIN)) continue;
  88. return false;
  89. }
  90. }
  91. }
  92. // Waits until a VisibilityNotify event arrives for the specified window or the
  93. // timeout period elapses (ICCCM section 4.2.2)
  94. //
  95. static bool waitForVisibilityNotify(_GLFWwindow* window)
  96. {
  97. XEvent dummy;
  98. while (!XCheckTypedWindowEvent(_glfw.x11.display,
  99. window->x11.handle,
  100. VisibilityNotify,
  101. &dummy))
  102. {
  103. if (!waitForX11Event(ms_to_monotonic_t(100ll)))
  104. return false;
  105. }
  106. return true;
  107. }
  108. // Returns whether the window is iconified
  109. //
  110. static int getWindowState(_GLFWwindow* window)
  111. {
  112. int result = WithdrawnState;
  113. struct {
  114. CARD32 state;
  115. Window icon;
  116. } *state = NULL;
  117. if (_glfwGetWindowPropertyX11(window->x11.handle,
  118. _glfw.x11.WM_STATE,
  119. _glfw.x11.WM_STATE,
  120. (unsigned char**) &state) >= 2)
  121. {
  122. result = state->state;
  123. }
  124. if (state)
  125. XFree(state);
  126. return result;
  127. }
  128. // Returns whether the event is a selection event
  129. //
  130. static Bool isSelectionEvent(Display* display UNUSED, XEvent* event, XPointer pointer UNUSED)
  131. {
  132. if (event->xany.window != _glfw.x11.helperWindowHandle)
  133. return False;
  134. return event->type == SelectionRequest ||
  135. event->type == SelectionNotify ||
  136. event->type == SelectionClear;
  137. }
  138. // Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
  139. //
  140. static Bool isFrameExtentsEvent(Display* display UNUSED, XEvent* event, XPointer pointer)
  141. {
  142. _GLFWwindow* window = (_GLFWwindow*) pointer;
  143. return event->type == PropertyNotify &&
  144. event->xproperty.state == PropertyNewValue &&
  145. event->xproperty.window == window->x11.handle &&
  146. event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
  147. }
  148. // Returns whether it is a property event for the specified selection transfer
  149. //
  150. static Bool isSelPropNewValueNotify(Display* display UNUSED, XEvent* event, XPointer pointer)
  151. {
  152. XEvent* notification = (XEvent*) pointer;
  153. return event->type == PropertyNotify &&
  154. event->xproperty.state == PropertyNewValue &&
  155. event->xproperty.window == notification->xselection.requestor &&
  156. event->xproperty.atom == notification->xselection.property;
  157. }
  158. // Translates an X event modifier state mask
  159. //
  160. static int translateState(int state)
  161. {
  162. int mods = 0;
  163. /* Need some way to expose hyper and meta without xkbcommon-x11 */
  164. if (state & ShiftMask)
  165. mods |= GLFW_MOD_SHIFT;
  166. if (state & ControlMask)
  167. mods |= GLFW_MOD_CONTROL;
  168. if (state & Mod1Mask)
  169. mods |= GLFW_MOD_ALT;
  170. if (state & Mod4Mask)
  171. mods |= GLFW_MOD_SUPER;
  172. if (state & LockMask)
  173. mods |= GLFW_MOD_CAPS_LOCK;
  174. if (state & Mod2Mask)
  175. mods |= GLFW_MOD_NUM_LOCK;
  176. return mods;
  177. }
  178. // Sends an EWMH or ICCCM event to the window manager
  179. //
  180. static void sendEventToWM(_GLFWwindow* window, Atom type,
  181. long a, long b, long c, long d, long e)
  182. {
  183. XEvent event = { ClientMessage };
  184. event.xclient.window = window->x11.handle;
  185. event.xclient.format = 32; // Data is 32-bit longs
  186. event.xclient.message_type = type;
  187. event.xclient.data.l[0] = a;
  188. event.xclient.data.l[1] = b;
  189. event.xclient.data.l[2] = c;
  190. event.xclient.data.l[3] = d;
  191. event.xclient.data.l[4] = e;
  192. XSendEvent(_glfw.x11.display, _glfw.x11.root,
  193. False,
  194. SubstructureNotifyMask | SubstructureRedirectMask,
  195. &event);
  196. }
  197. // Updates the normal hints according to the window settings
  198. //
  199. static void
  200. updateNormalHints(_GLFWwindow* window, int width, int height)
  201. {
  202. XSizeHints* hints = XAllocSizeHints();
  203. if (!window->monitor)
  204. {
  205. if (window->resizable)
  206. {
  207. if (window->minwidth != GLFW_DONT_CARE &&
  208. window->minheight != GLFW_DONT_CARE)
  209. {
  210. hints->flags |= PMinSize;
  211. hints->min_width = window->minwidth;
  212. hints->min_height = window->minheight;
  213. }
  214. if (window->maxwidth != GLFW_DONT_CARE &&
  215. window->maxheight != GLFW_DONT_CARE)
  216. {
  217. hints->flags |= PMaxSize;
  218. hints->max_width = window->maxwidth;
  219. hints->max_height = window->maxheight;
  220. }
  221. if (window->numer != GLFW_DONT_CARE &&
  222. window->denom != GLFW_DONT_CARE)
  223. {
  224. hints->flags |= PAspect;
  225. hints->min_aspect.x = hints->max_aspect.x = window->numer;
  226. hints->min_aspect.y = hints->max_aspect.y = window->denom;
  227. }
  228. if (window->widthincr != GLFW_DONT_CARE &&
  229. window->heightincr != GLFW_DONT_CARE && !window->x11.maximized)
  230. {
  231. hints->flags |= PResizeInc;
  232. hints->width_inc = window->widthincr;
  233. hints->height_inc = window->heightincr;
  234. }
  235. }
  236. else
  237. {
  238. hints->flags |= (PMinSize | PMaxSize);
  239. hints->min_width = hints->max_width = width;
  240. hints->min_height = hints->max_height = height;
  241. }
  242. }
  243. hints->flags |= PWinGravity;
  244. hints->win_gravity = StaticGravity;
  245. XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
  246. XFree(hints);
  247. }
  248. static bool
  249. is_window_fullscreen(_GLFWwindow* window)
  250. {
  251. Atom* states;
  252. unsigned long i;
  253. bool ans = false;
  254. if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_FULLSCREEN)
  255. return ans;
  256. const unsigned long count =
  257. _glfwGetWindowPropertyX11(window->x11.handle,
  258. _glfw.x11.NET_WM_STATE,
  259. XA_ATOM,
  260. (unsigned char**) &states);
  261. for (i = 0; i < count; i++)
  262. {
  263. if (states[i] == _glfw.x11.NET_WM_STATE_FULLSCREEN)
  264. {
  265. ans = true;
  266. break;
  267. }
  268. }
  269. if (states)
  270. XFree(states);
  271. return ans;
  272. }
  273. static void
  274. set_fullscreen(_GLFWwindow *window, bool on) {
  275. if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) {
  276. sendEventToWM(window,
  277. _glfw.x11.NET_WM_STATE,
  278. on ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
  279. _glfw.x11.NET_WM_STATE_FULLSCREEN,
  280. 0, 1, 0);
  281. // Enable compositor bypass
  282. if (!window->x11.transparent)
  283. {
  284. if (on) {
  285. const unsigned long value = 1;
  286. XChangeProperty(_glfw.x11.display, window->x11.handle,
  287. _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
  288. PropModeReplace, (unsigned char*) &value, 1);
  289. } else {
  290. XDeleteProperty(_glfw.x11.display, window->x11.handle,
  291. _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
  292. }
  293. }
  294. } else {
  295. static bool warned = false;
  296. if (!warned) {
  297. warned = true;
  298. _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
  299. "X11: Failed to toggle fullscreen, the window manager does not support it");
  300. }
  301. }
  302. }
  303. bool
  304. _glfwPlatformIsFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {
  305. return is_window_fullscreen(window);
  306. }
  307. bool
  308. _glfwPlatformToggleFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {
  309. bool already_fullscreen = is_window_fullscreen(window);
  310. set_fullscreen(window, !already_fullscreen);
  311. return !already_fullscreen;
  312. }
  313. // Updates the full screen status of the window
  314. //
  315. static void updateWindowMode(_GLFWwindow* window)
  316. {
  317. if (window->monitor)
  318. {
  319. if (_glfw.x11.xinerama.available &&
  320. _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
  321. {
  322. sendEventToWM(window,
  323. _glfw.x11.NET_WM_FULLSCREEN_MONITORS,
  324. window->monitor->x11.index,
  325. window->monitor->x11.index,
  326. window->monitor->x11.index,
  327. window->monitor->x11.index,
  328. 0);
  329. }
  330. set_fullscreen(window, true);
  331. }
  332. else
  333. {
  334. if (_glfw.x11.xinerama.available &&
  335. _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
  336. {
  337. XDeleteProperty(_glfw.x11.display, window->x11.handle,
  338. _glfw.x11.NET_WM_FULLSCREEN_MONITORS);
  339. }
  340. set_fullscreen(window, false);
  341. }
  342. }
  343. // Encode a Unicode code point to a UTF-8 stream
  344. // Based on cutef8 by Jeff Bezanson (Public Domain)
  345. //
  346. static size_t encodeUTF8(char* s, unsigned int ch)
  347. {
  348. size_t count = 0;
  349. if (ch < 0x80)
  350. s[count++] = (char) ch;
  351. else if (ch < 0x800)
  352. {
  353. s[count++] = (ch >> 6) | 0xc0;
  354. s[count++] = (ch & 0x3f) | 0x80;
  355. }
  356. else if (ch < 0x10000)
  357. {
  358. s[count++] = (ch >> 12) | 0xe0;
  359. s[count++] = ((ch >> 6) & 0x3f) | 0x80;
  360. s[count++] = (ch & 0x3f) | 0x80;
  361. }
  362. else if (ch < 0x110000)
  363. {
  364. s[count++] = (ch >> 18) | 0xf0;
  365. s[count++] = ((ch >> 12) & 0x3f) | 0x80;
  366. s[count++] = ((ch >> 6) & 0x3f) | 0x80;
  367. s[count++] = (ch & 0x3f) | 0x80;
  368. }
  369. return count;
  370. }
  371. // Convert the specified Latin-1 string to UTF-8
  372. //
  373. static char* convertLatin1toUTF8(const char* source)
  374. {
  375. size_t size = 1;
  376. const char* sp;
  377. if (source) {
  378. for (sp = source; *sp; sp++)
  379. size += (*sp & 0x80) ? 2 : 1;
  380. }
  381. char* target = calloc(size, 1);
  382. char* tp = target;
  383. if (source) {
  384. for (sp = source; *sp; sp++)
  385. tp += encodeUTF8(tp, *sp);
  386. }
  387. return target;
  388. }
  389. // Updates the cursor image according to its cursor mode
  390. //
  391. static void updateCursorImage(_GLFWwindow* window)
  392. {
  393. if (window->cursorMode == GLFW_CURSOR_NORMAL)
  394. {
  395. if (window->cursor)
  396. {
  397. XDefineCursor(_glfw.x11.display, window->x11.handle,
  398. window->cursor->x11.handle);
  399. }
  400. else
  401. XUndefineCursor(_glfw.x11.display, window->x11.handle);
  402. }
  403. else
  404. {
  405. XDefineCursor(_glfw.x11.display, window->x11.handle,
  406. _glfw.x11.hiddenCursorHandle);
  407. }
  408. }
  409. // Enable XI2 raw mouse motion events
  410. //
  411. static void enableRawMouseMotion(_GLFWwindow* window UNUSED)
  412. {
  413. XIEventMask em;
  414. unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
  415. em.deviceid = XIAllMasterDevices;
  416. em.mask_len = sizeof(mask);
  417. em.mask = mask;
  418. XISetMask(mask, XI_RawMotion);
  419. XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
  420. }
  421. // Disable XI2 raw mouse motion events
  422. //
  423. static void disableRawMouseMotion(_GLFWwindow* window UNUSED)
  424. {
  425. XIEventMask em;
  426. unsigned char mask[] = { 0 };
  427. em.deviceid = XIAllMasterDevices;
  428. em.mask_len = sizeof(mask);
  429. em.mask = mask;
  430. XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
  431. }
  432. // Apply disabled cursor mode to a focused window
  433. //
  434. static void disableCursor(_GLFWwindow* window)
  435. {
  436. if (window->rawMouseMotion)
  437. enableRawMouseMotion(window);
  438. _glfw.x11.disabledCursorWindow = window;
  439. _glfwPlatformGetCursorPos(window,
  440. &_glfw.x11.restoreCursorPosX,
  441. &_glfw.x11.restoreCursorPosY);
  442. updateCursorImage(window);
  443. _glfwCenterCursorInContentArea(window);
  444. XGrabPointer(_glfw.x11.display, window->x11.handle, True,
  445. ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
  446. GrabModeAsync, GrabModeAsync,
  447. window->x11.handle,
  448. _glfw.x11.hiddenCursorHandle,
  449. CurrentTime);
  450. }
  451. // Exit disabled cursor mode for the specified window
  452. //
  453. static void enableCursor(_GLFWwindow* window)
  454. {
  455. if (window->rawMouseMotion)
  456. disableRawMouseMotion(window);
  457. _glfw.x11.disabledCursorWindow = NULL;
  458. XUngrabPointer(_glfw.x11.display, CurrentTime);
  459. _glfwPlatformSetCursorPos(window,
  460. _glfw.x11.restoreCursorPosX,
  461. _glfw.x11.restoreCursorPosY);
  462. updateCursorImage(window);
  463. }
  464. // Create the X11 window (and its colormap)
  465. //
  466. static bool createNativeWindow(_GLFWwindow* window,
  467. const _GLFWwndconfig* wndconfig,
  468. Visual* visual, int depth)
  469. {
  470. int width = wndconfig->width;
  471. int height = wndconfig->height;
  472. if (wndconfig->scaleToMonitor)
  473. {
  474. width *= (int)_glfw.x11.contentScaleX;
  475. height *= (int)_glfw.x11.contentScaleY;
  476. }
  477. // Create a colormap based on the visual used by the current context
  478. window->x11.colormap = XCreateColormap(_glfw.x11.display,
  479. _glfw.x11.root,
  480. visual,
  481. AllocNone);
  482. window->x11.transparent = _glfwIsVisualTransparentX11(visual);
  483. XSetWindowAttributes wa = { 0 };
  484. wa.colormap = window->x11.colormap;
  485. wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
  486. PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
  487. ExposureMask | FocusChangeMask | VisibilityChangeMask |
  488. EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
  489. _glfwGrabErrorHandlerX11();
  490. window->x11.parent = _glfw.x11.root;
  491. window->x11.handle = XCreateWindow(_glfw.x11.display,
  492. _glfw.x11.root,
  493. 0, 0, // Position
  494. width, height,
  495. 0, // Border width
  496. depth, // Color depth
  497. InputOutput,
  498. visual,
  499. CWBorderPixel | CWColormap | CWEventMask,
  500. &wa);
  501. _glfwReleaseErrorHandlerX11();
  502. if (!window->x11.handle)
  503. {
  504. _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
  505. "X11: Failed to create window");
  506. return false;
  507. }
  508. XSaveContext(_glfw.x11.display,
  509. window->x11.handle,
  510. _glfw.x11.context,
  511. (XPointer) window);
  512. if (!wndconfig->decorated)
  513. _glfwPlatformSetWindowDecorated(window, false);
  514. if (_glfw.x11.NET_WM_STATE && !window->monitor)
  515. {
  516. Atom states[3];
  517. int count = 0;
  518. if (wndconfig->floating)
  519. {
  520. if (_glfw.x11.NET_WM_STATE_ABOVE)
  521. states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
  522. }
  523. if (wndconfig->maximized)
  524. {
  525. if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
  526. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  527. {
  528. states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
  529. states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
  530. window->x11.maximized = true;
  531. }
  532. }
  533. if (count)
  534. {
  535. XChangeProperty(_glfw.x11.display, window->x11.handle,
  536. _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  537. PropModeReplace, (unsigned char*) states, count);
  538. }
  539. }
  540. // Declare the WM protocols supported by GLFW
  541. {
  542. Atom protocols[] =
  543. {
  544. _glfw.x11.WM_DELETE_WINDOW,
  545. _glfw.x11.NET_WM_PING
  546. };
  547. XSetWMProtocols(_glfw.x11.display, window->x11.handle,
  548. protocols, sizeof(protocols) / sizeof(Atom));
  549. }
  550. // Declare our PID
  551. {
  552. const long pid = getpid();
  553. XChangeProperty(_glfw.x11.display, window->x11.handle,
  554. _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
  555. PropModeReplace,
  556. (unsigned char*) &pid, 1);
  557. }
  558. if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
  559. {
  560. Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
  561. XChangeProperty(_glfw.x11.display, window->x11.handle,
  562. _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
  563. PropModeReplace, (unsigned char*) &type, 1);
  564. }
  565. // Set ICCCM WM_HINTS property
  566. {
  567. XWMHints* hints = XAllocWMHints();
  568. if (!hints)
  569. {
  570. _glfwInputError(GLFW_OUT_OF_MEMORY,
  571. "X11: Failed to allocate WM hints");
  572. return false;
  573. }
  574. hints->flags = StateHint;
  575. hints->initial_state = NormalState;
  576. XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
  577. XFree(hints);
  578. }
  579. updateNormalHints(window, width, height);
  580. // Set ICCCM WM_CLASS property
  581. {
  582. XClassHint* hint = XAllocClassHint();
  583. if (strlen(wndconfig->x11.instanceName) &&
  584. strlen(wndconfig->x11.className))
  585. {
  586. hint->res_name = (char*) wndconfig->x11.instanceName;
  587. hint->res_class = (char*) wndconfig->x11.className;
  588. }
  589. else
  590. {
  591. const char* resourceName = getenv("RESOURCE_NAME");
  592. if (resourceName && strlen(resourceName))
  593. hint->res_name = (char*) resourceName;
  594. else if (strlen(wndconfig->title))
  595. hint->res_name = (char*) wndconfig->title;
  596. else
  597. hint->res_name = (char*) "glfw-application";
  598. if (strlen(wndconfig->title))
  599. hint->res_class = (char*) wndconfig->title;
  600. else
  601. hint->res_class = (char*) "GLFW-Application";
  602. }
  603. XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
  604. XFree(hint);
  605. }
  606. // Announce support for Xdnd (drag and drop)
  607. {
  608. const Atom version = _GLFW_XDND_VERSION;
  609. XChangeProperty(_glfw.x11.display, window->x11.handle,
  610. _glfw.x11.XdndAware, XA_ATOM, 32,
  611. PropModeReplace, (unsigned char*) &version, 1);
  612. }
  613. _glfwPlatformSetWindowTitle(window, wndconfig->title);
  614. _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
  615. _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
  616. if (_glfw.hints.window.blur_radius > 0) _glfwPlatformSetWindowBlur(window, _glfw.hints.window.blur_radius);
  617. return true;
  618. }
  619. static size_t
  620. get_clipboard_data(const _GLFWClipboardData *cd, const char *mime, char **data) {
  621. *data = NULL;
  622. if (cd->get_data == NULL) { return 0; }
  623. GLFWDataChunk chunk = cd->get_data(mime, NULL, cd->ctype);
  624. char *buf = NULL;
  625. size_t sz = 0, cap = 0;
  626. void *iter = chunk.iter;
  627. if (!iter) return 0;
  628. while (true) {
  629. chunk = cd->get_data(mime, iter, cd->ctype);
  630. if (!chunk.sz) break;
  631. if (cap < sz + chunk.sz) {
  632. cap = MAX(cap * 2, sz + 4 * chunk.sz);
  633. buf = realloc(buf, cap * sizeof(buf[0]));
  634. }
  635. memcpy(buf + sz, chunk.data, chunk.sz);
  636. sz += chunk.sz;
  637. if (chunk.free) chunk.free((void*)chunk.free_data);
  638. }
  639. *data = buf;
  640. cd->get_data(NULL, iter, cd->ctype);
  641. return sz;
  642. }
  643. static void
  644. get_atom_names(const Atom *atoms, int count, char **atom_names) {
  645. _glfwGrabErrorHandlerX11();
  646. XGetAtomNames(_glfw.x11.display, (Atom*)atoms, count, atom_names);
  647. _glfwReleaseErrorHandlerX11();
  648. if (_glfw.x11.errorCode != Success) {
  649. for (int i = 0; i < count; i++) {
  650. _glfwGrabErrorHandlerX11();
  651. atom_names[i] = XGetAtomName(_glfw.x11.display, atoms[i]);
  652. _glfwReleaseErrorHandlerX11();
  653. if (_glfw.x11.errorCode != Success) atom_names[i] = NULL;
  654. }
  655. }
  656. }
  657. // Set the specified property to the selection converted to the requested target
  658. //
  659. static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
  660. {
  661. const AtomArray *aa;
  662. const _GLFWClipboardData *cd;
  663. if (request->selection == _glfw.x11.PRIMARY) {
  664. aa = &_glfw.x11.primary_atoms;
  665. cd = &_glfw.primary;
  666. } else {
  667. aa = &_glfw.x11.clipboard_atoms;
  668. cd = &_glfw.clipboard;
  669. }
  670. if (request->property == None)
  671. {
  672. // The requester is a legacy client (ICCCM section 2.2)
  673. // We don't support legacy clients, so fail here
  674. return None;
  675. }
  676. if (request->target == _glfw.x11.TARGETS)
  677. {
  678. // The list of supported targets was requested
  679. Atom *targets = calloc(aa->sz + 2, sizeof(Atom));
  680. targets[0] = _glfw.x11.TARGETS;
  681. targets[1] = _glfw.x11.MULTIPLE;
  682. for (size_t i = 0; i < aa->sz; i++) targets[i+2] = aa->array[i].atom;
  683. XChangeProperty(_glfw.x11.display,
  684. request->requestor,
  685. request->property,
  686. XA_ATOM,
  687. 32,
  688. PropModeReplace,
  689. (unsigned char*) targets,
  690. aa->sz + 2);
  691. free(targets);
  692. return request->property;
  693. }
  694. if (request->target == _glfw.x11.MULTIPLE)
  695. {
  696. // Multiple conversions were requested
  697. Atom* targets;
  698. size_t i, j, count;
  699. count = _glfwGetWindowPropertyX11(request->requestor,
  700. request->property,
  701. _glfw.x11.ATOM_PAIR,
  702. (unsigned char**) &targets);
  703. for (i = 0; i < count; i += 2)
  704. {
  705. for (j = 0; j < aa->sz; j++)
  706. {
  707. if (targets[i] == aa->array[j].atom)
  708. break;
  709. }
  710. if (j < aa->sz)
  711. {
  712. char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[j].mime, &data);
  713. if (data) XChangeProperty(_glfw.x11.display,
  714. request->requestor,
  715. targets[i + 1],
  716. targets[i],
  717. 8,
  718. PropModeReplace,
  719. (unsigned char *) data,
  720. sz);
  721. free(data);
  722. }
  723. else
  724. targets[i + 1] = None;
  725. }
  726. XChangeProperty(_glfw.x11.display,
  727. request->requestor,
  728. request->property,
  729. _glfw.x11.ATOM_PAIR,
  730. 32,
  731. PropModeReplace,
  732. (unsigned char*) targets,
  733. count);
  734. XFree(targets);
  735. return request->property;
  736. }
  737. if (request->target == _glfw.x11.SAVE_TARGETS)
  738. {
  739. // The request is a check whether we support SAVE_TARGETS
  740. // It should be handled as a no-op side effect target
  741. XChangeProperty(_glfw.x11.display,
  742. request->requestor,
  743. request->property,
  744. _glfw.x11.NULL_,
  745. 32,
  746. PropModeReplace,
  747. NULL,
  748. 0);
  749. return request->property;
  750. }
  751. // Conversion to a data target was requested
  752. for (size_t i = 0; i < aa->sz; i++)
  753. {
  754. if (request->target == aa->array[i].atom)
  755. {
  756. // The requested target is one we support
  757. char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[i].mime, &data);
  758. if (data) XChangeProperty(_glfw.x11.display,
  759. request->requestor,
  760. request->property,
  761. request->target,
  762. 8,
  763. PropModeReplace,
  764. (unsigned char *) data,
  765. sz);
  766. free(data);
  767. return request->property;
  768. }
  769. }
  770. // The requested target is not supported
  771. return None;
  772. }
  773. static void handleSelectionClear(XEvent* event)
  774. {
  775. if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
  776. {
  777. _glfw_free_clipboard_data(&_glfw.primary);
  778. }
  779. else
  780. {
  781. _glfw_free_clipboard_data(&_glfw.clipboard);
  782. }
  783. }
  784. static void handleSelectionRequest(XEvent* event)
  785. {
  786. const XSelectionRequestEvent* request = &event->xselectionrequest;
  787. XEvent reply = { SelectionNotify };
  788. reply.xselection.property = writeTargetToProperty(request);
  789. reply.xselection.display = request->display;
  790. reply.xselection.requestor = request->requestor;
  791. reply.xselection.selection = request->selection;
  792. reply.xselection.target = request->target;
  793. reply.xselection.time = request->time;
  794. XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
  795. }
  796. static void
  797. getSelectionString(Atom selection, Atom *targets, size_t num_targets, GLFWclipboardwritedatafun write_data, void *object, bool report_not_found)
  798. {
  799. #define XFREE(x) { if (x) XFree(x); x = NULL; }
  800. if (XGetSelectionOwner(_glfw.x11.display, selection) == _glfw.x11.helperWindowHandle) {
  801. write_data(object, NULL, 1);
  802. return;
  803. }
  804. bool found = false;
  805. for (size_t i = 0; !found && i < num_targets; i++)
  806. {
  807. char* data = NULL;
  808. Atom actualType = None;
  809. int actualFormat = 0;
  810. unsigned long itemCount = 0, bytesAfter = 0;
  811. monotonic_t start = glfwGetTime();
  812. XEvent notification, dummy;
  813. XConvertSelection(_glfw.x11.display,
  814. selection,
  815. targets[i],
  816. _glfw.x11.GLFW_SELECTION,
  817. _glfw.x11.helperWindowHandle,
  818. CurrentTime);
  819. while (!XCheckTypedWindowEvent(_glfw.x11.display,
  820. _glfw.x11.helperWindowHandle,
  821. SelectionNotify,
  822. &notification))
  823. {
  824. monotonic_t time = glfwGetTime();
  825. if (time - start > s_to_monotonic_t(2ll)) return;
  826. waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
  827. }
  828. if (notification.xselection.property == None)
  829. continue;
  830. XCheckIfEvent(_glfw.x11.display,
  831. &dummy,
  832. isSelPropNewValueNotify,
  833. (XPointer) &notification);
  834. XGetWindowProperty(_glfw.x11.display,
  835. notification.xselection.requestor,
  836. notification.xselection.property,
  837. 0,
  838. LONG_MAX,
  839. True,
  840. AnyPropertyType,
  841. &actualType,
  842. &actualFormat,
  843. &itemCount,
  844. &bytesAfter,
  845. (unsigned char**) &data);
  846. if (actualType == _glfw.x11.INCR)
  847. {
  848. for (;;)
  849. {
  850. start = glfwGetTime();
  851. while (!XCheckIfEvent(_glfw.x11.display,
  852. &dummy,
  853. isSelPropNewValueNotify,
  854. (XPointer) &notification))
  855. {
  856. monotonic_t time = glfwGetTime();
  857. if (time - start > s_to_monotonic_t(2ll)) {
  858. return;
  859. }
  860. waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
  861. }
  862. XFREE(data);
  863. XGetWindowProperty(_glfw.x11.display,
  864. notification.xselection.requestor,
  865. notification.xselection.property,
  866. 0,
  867. LONG_MAX,
  868. True,
  869. AnyPropertyType,
  870. &actualType,
  871. &actualFormat,
  872. &itemCount,
  873. &bytesAfter,
  874. (unsigned char**) &data);
  875. if (itemCount)
  876. {
  877. const char *string = data;
  878. if (targets[i] == XA_STRING) {
  879. string = convertLatin1toUTF8(data);
  880. itemCount = strlen(string);
  881. }
  882. bool ok = write_data(object, string, itemCount);
  883. if (string != data) free((void*)string);
  884. if (!ok) { XFREE(data); break; }
  885. } else { found = true; break; }
  886. }
  887. }
  888. else if (actualType == targets[i])
  889. {
  890. if (targets[i] == XA_STRING) {
  891. const char *string = convertLatin1toUTF8(data);
  892. write_data(object, string, strlen(string)); free((void*)string);
  893. } else write_data(object, data, itemCount);
  894. found = true;
  895. }
  896. else if (actualType == XA_ATOM && targets[i] == _glfw.x11.TARGETS) {
  897. found = true;
  898. write_data(object, data, sizeof(Atom) * itemCount);
  899. }
  900. XFREE(data);
  901. }
  902. if (!found && report_not_found)
  903. {
  904. _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
  905. "X11: Failed to convert selection to data from clipboard");
  906. }
  907. #undef XFREE
  908. }
  909. // Make the specified window and its video mode active on its monitor
  910. //
  911. static void acquireMonitor(_GLFWwindow* window)
  912. {
  913. if (_glfw.x11.saver.count == 0)
  914. {
  915. // Remember old screen saver settings
  916. XGetScreenSaver(_glfw.x11.display,
  917. &_glfw.x11.saver.timeout,
  918. &_glfw.x11.saver.interval,
  919. &_glfw.x11.saver.blanking,
  920. &_glfw.x11.saver.exposure);
  921. // Disable screen saver
  922. XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
  923. DefaultExposures);
  924. }
  925. if (!window->monitor->window)
  926. _glfw.x11.saver.count++;
  927. _glfwSetVideoModeX11(window->monitor, &window->videoMode);
  928. _glfwInputMonitorWindow(window->monitor, window);
  929. }
  930. // Remove the window and restore the original video mode
  931. //
  932. static void releaseMonitor(_GLFWwindow* window)
  933. {
  934. if (window->monitor->window != window)
  935. return;
  936. _glfwInputMonitorWindow(window->monitor, NULL);
  937. _glfwRestoreVideoModeX11(window->monitor);
  938. _glfw.x11.saver.count--;
  939. if (_glfw.x11.saver.count == 0)
  940. {
  941. // Restore old screen saver settings
  942. XSetScreenSaver(_glfw.x11.display,
  943. _glfw.x11.saver.timeout,
  944. _glfw.x11.saver.interval,
  945. _glfw.x11.saver.blanking,
  946. _glfw.x11.saver.exposure);
  947. }
  948. }
  949. static void onConfigChange(void)
  950. {
  951. float xscale, yscale;
  952. _glfwGetSystemContentScaleX11(&xscale, &yscale, true);
  953. if (xscale != _glfw.x11.contentScaleX || yscale != _glfw.x11.contentScaleY)
  954. {
  955. _GLFWwindow* window = _glfw.windowListHead;
  956. _glfw.x11.contentScaleX = xscale;
  957. _glfw.x11.contentScaleY = yscale;
  958. while (window)
  959. {
  960. _glfwInputWindowContentScale(window, xscale, yscale);
  961. window = window->next;
  962. }
  963. }
  964. }
  965. // Process the specified X event
  966. //
  967. static void processEvent(XEvent *event)
  968. {
  969. static bool keymap_dirty = false;
  970. #define UPDATE_KEYMAP_IF_NEEDED if (keymap_dirty) { keymap_dirty = false; glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL); }
  971. if (_glfw.x11.randr.available)
  972. {
  973. if (event->type == _glfw.x11.randr.eventBase + RRNotify)
  974. {
  975. XRRUpdateConfiguration(event);
  976. _glfwPollMonitorsX11();
  977. return;
  978. }
  979. }
  980. if (event->type == PropertyNotify &&
  981. event->xproperty.window == _glfw.x11.root &&
  982. event->xproperty.atom == _glfw.x11.RESOURCE_MANAGER)
  983. {
  984. onConfigChange();
  985. return;
  986. }
  987. if (event->type == GenericEvent)
  988. {
  989. if (_glfw.x11.xi.available)
  990. {
  991. _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
  992. if (window &&
  993. window->rawMouseMotion &&
  994. event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
  995. XGetEventData(_glfw.x11.display, &event->xcookie) &&
  996. event->xcookie.evtype == XI_RawMotion)
  997. {
  998. XIRawEvent* re = event->xcookie.data;
  999. if (re->valuators.mask_len)
  1000. {
  1001. const double* values = re->raw_values;
  1002. double xpos = window->virtualCursorPosX;
  1003. double ypos = window->virtualCursorPosY;
  1004. if (XIMaskIsSet(re->valuators.mask, 0))
  1005. {
  1006. xpos += *values;
  1007. values++;
  1008. }
  1009. if (XIMaskIsSet(re->valuators.mask, 1))
  1010. ypos += *values;
  1011. _glfwInputCursorPos(window, xpos, ypos);
  1012. }
  1013. }
  1014. XFreeEventData(_glfw.x11.display, &event->xcookie);
  1015. }
  1016. return;
  1017. }
  1018. if (event->type == SelectionClear)
  1019. {
  1020. handleSelectionClear(event);
  1021. return;
  1022. }
  1023. else if (event->type == SelectionRequest)
  1024. {
  1025. handleSelectionRequest(event);
  1026. return;
  1027. }
  1028. else if (event->type == _glfw.x11.xkb.eventBase)
  1029. {
  1030. XkbEvent *kb_event = (XkbEvent*)event;
  1031. if (kb_event->any.device != (unsigned int)_glfw.x11.xkb.keyboard_device_id) return;
  1032. switch(kb_event->any.xkb_type) {
  1033. case XkbNewKeyboardNotify: {
  1034. XkbNewKeyboardNotifyEvent *newkb_event = (XkbNewKeyboardNotifyEvent*)kb_event;
  1035. if (_glfw.hints.init.debugKeyboard) printf(
  1036. "Got XkbNewKeyboardNotify event with changes: key codes: %d geometry: %d device id: %d\n",
  1037. !!(newkb_event->changed & XkbNKN_KeycodesMask), !!(newkb_event->changed & XkbNKN_GeometryMask),
  1038. !!(newkb_event->changed & XkbNKN_DeviceIDMask));
  1039. if (newkb_event->changed & XkbNKN_DeviceIDMask) {
  1040. keymap_dirty = true;
  1041. if (!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return;
  1042. }
  1043. if (newkb_event->changed & XkbNKN_KeycodesMask) {
  1044. keymap_dirty = true;
  1045. }
  1046. return;
  1047. }
  1048. case XkbMapNotify:
  1049. {
  1050. if (_glfw.hints.init.debugKeyboard) printf("Got XkbMapNotify event, keymaps will be reloaded\n");
  1051. keymap_dirty = true;
  1052. return;
  1053. }
  1054. case XkbStateNotify:
  1055. {
  1056. UPDATE_KEYMAP_IF_NEEDED;
  1057. XkbStateNotifyEvent *state_event = (XkbStateNotifyEvent*)kb_event;
  1058. glfw_xkb_update_modifiers(
  1059. &_glfw.x11.xkb, state_event->base_mods, state_event->latched_mods,
  1060. state_event->locked_mods, state_event->base_group, state_event->latched_group, state_event->locked_group
  1061. );
  1062. return;
  1063. }
  1064. }
  1065. return;
  1066. }
  1067. _GLFWwindow* window = NULL;
  1068. if (XFindContext(_glfw.x11.display,
  1069. event->xany.window,
  1070. _glfw.x11.context,
  1071. (XPointer*) &window) != 0)
  1072. {
  1073. // This is an event for a window that has already been destroyed
  1074. return;
  1075. }
  1076. switch (event->type)
  1077. {
  1078. case ReparentNotify:
  1079. {
  1080. window->x11.parent = event->xreparent.parent;
  1081. return;
  1082. }
  1083. case KeyPress:
  1084. {
  1085. UPDATE_KEYMAP_IF_NEEDED;
  1086. glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_PRESS);
  1087. return;
  1088. }
  1089. case KeyRelease:
  1090. {
  1091. UPDATE_KEYMAP_IF_NEEDED;
  1092. if (!_glfw.x11.xkb.detectable)
  1093. {
  1094. // HACK: Key repeat events will arrive as KeyRelease/KeyPress
  1095. // pairs with similar or identical time stamps
  1096. // The key repeat logic in _glfwInputKey expects only key
  1097. // presses to repeat, so detect and discard release events
  1098. if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
  1099. {
  1100. XEvent next;
  1101. XPeekEvent(_glfw.x11.display, &next);
  1102. if (next.type == KeyPress &&
  1103. next.xkey.window == event->xkey.window &&
  1104. next.xkey.keycode == event->xkey.keycode)
  1105. {
  1106. // HACK: The time of repeat events sometimes doesn't
  1107. // match that of the press event, so add an
  1108. // epsilon
  1109. // Toshiyuki Takahashi can press a button
  1110. // 16 times per second so it's fairly safe to
  1111. // assume that no human is pressing the key 50
  1112. // times per second (value is ms)
  1113. if ((next.xkey.time - event->xkey.time) < 20)
  1114. {
  1115. // This is very likely a server-generated key repeat
  1116. // event, so ignore it
  1117. return;
  1118. }
  1119. }
  1120. }
  1121. }
  1122. glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_RELEASE);
  1123. return;
  1124. }
  1125. case ButtonPress:
  1126. {
  1127. const int mods = translateState(event->xbutton.state);
  1128. if (event->xbutton.button == Button1)
  1129. _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
  1130. else if (event->xbutton.button == Button2)
  1131. _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
  1132. else if (event->xbutton.button == Button3)
  1133. _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
  1134. // Modern X provides scroll events as mouse button presses
  1135. else if (event->xbutton.button == Button4)
  1136. _glfwInputScroll(window, 0.0, 1.0, 0, mods);
  1137. else if (event->xbutton.button == Button5)
  1138. _glfwInputScroll(window, 0.0, -1.0, 0, mods);
  1139. else if (event->xbutton.button == Button6)
  1140. _glfwInputScroll(window, 1.0, 0.0, 0, mods);
  1141. else if (event->xbutton.button == Button7)
  1142. _glfwInputScroll(window, -1.0, 0.0, 0, mods);
  1143. else
  1144. {
  1145. // Additional buttons after 7 are treated as regular buttons
  1146. // We subtract 4 to fill the gap left by scroll input above
  1147. _glfwInputMouseClick(window,
  1148. event->xbutton.button - Button1 - 4,
  1149. GLFW_PRESS,
  1150. mods);
  1151. }
  1152. return;
  1153. }
  1154. case ButtonRelease:
  1155. {
  1156. const int mods = translateState(event->xbutton.state);
  1157. if (event->xbutton.button == Button1)
  1158. {
  1159. _glfwInputMouseClick(window,
  1160. GLFW_MOUSE_BUTTON_LEFT,
  1161. GLFW_RELEASE,
  1162. mods);
  1163. }
  1164. else if (event->xbutton.button == Button2)
  1165. {
  1166. _glfwInputMouseClick(window,
  1167. GLFW_MOUSE_BUTTON_MIDDLE,
  1168. GLFW_RELEASE,
  1169. mods);
  1170. }
  1171. else if (event->xbutton.button == Button3)
  1172. {
  1173. _glfwInputMouseClick(window,
  1174. GLFW_MOUSE_BUTTON_RIGHT,
  1175. GLFW_RELEASE,
  1176. mods);
  1177. }
  1178. else if (event->xbutton.button > Button7)
  1179. {
  1180. // Additional buttons after 7 are treated as regular buttons
  1181. // We subtract 4 to fill the gap left by scroll input above
  1182. _glfwInputMouseClick(window,
  1183. event->xbutton.button - Button1 - 4,
  1184. GLFW_RELEASE,
  1185. mods);
  1186. }
  1187. return;
  1188. }
  1189. case EnterNotify:
  1190. {
  1191. // XEnterWindowEvent is XCrossingEvent
  1192. const int x = event->xcrossing.x;
  1193. const int y = event->xcrossing.y;
  1194. // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
  1195. // ignore the defined cursor for hidden cursor mode
  1196. if (window->cursorMode == GLFW_CURSOR_HIDDEN)
  1197. updateCursorImage(window);
  1198. _glfwInputCursorEnter(window, true);
  1199. _glfwInputCursorPos(window, x, y);
  1200. window->x11.lastCursorPosX = x;
  1201. window->x11.lastCursorPosY = y;
  1202. return;
  1203. }
  1204. case LeaveNotify:
  1205. {
  1206. _glfwInputCursorEnter(window, false);
  1207. return;
  1208. }
  1209. case MotionNotify:
  1210. {
  1211. const int x = event->xmotion.x;
  1212. const int y = event->xmotion.y;
  1213. if (x != window->x11.warpCursorPosX ||
  1214. y != window->x11.warpCursorPosY)
  1215. {
  1216. // The cursor was moved by something other than GLFW
  1217. if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1218. {
  1219. if (_glfw.x11.disabledCursorWindow != window)
  1220. return;
  1221. if (window->rawMouseMotion)
  1222. return;
  1223. const int dx = x - window->x11.lastCursorPosX;
  1224. const int dy = y - window->x11.lastCursorPosY;
  1225. _glfwInputCursorPos(window,
  1226. window->virtualCursorPosX + dx,
  1227. window->virtualCursorPosY + dy);
  1228. }
  1229. else
  1230. _glfwInputCursorPos(window, x, y);
  1231. }
  1232. window->x11.lastCursorPosX = x;
  1233. window->x11.lastCursorPosY = y;
  1234. return;
  1235. }
  1236. case ConfigureNotify:
  1237. {
  1238. if (event->xconfigure.width != window->x11.width ||
  1239. event->xconfigure.height != window->x11.height)
  1240. {
  1241. _glfwInputFramebufferSize(window,
  1242. event->xconfigure.width,
  1243. event->xconfigure.height);
  1244. _glfwInputWindowSize(window,
  1245. event->xconfigure.width,
  1246. event->xconfigure.height);
  1247. window->x11.width = event->xconfigure.width;
  1248. window->x11.height = event->xconfigure.height;
  1249. }
  1250. int xpos = event->xconfigure.x;
  1251. int ypos = event->xconfigure.y;
  1252. // NOTE: ConfigureNotify events from the server are in local
  1253. // coordinates, so if we are reparented we need to translate
  1254. // the position into root (screen) coordinates
  1255. if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
  1256. {
  1257. Window dummy;
  1258. _glfwGrabErrorHandlerX11();
  1259. XTranslateCoordinates(_glfw.x11.display,
  1260. window->x11.parent,
  1261. _glfw.x11.root,
  1262. xpos, ypos,
  1263. &xpos, &ypos,
  1264. &dummy);
  1265. _glfwReleaseErrorHandlerX11();
  1266. if (_glfw.x11.errorCode != Success) {
  1267. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to translate ConfigureNotiy co-ords for reparented window");
  1268. return;
  1269. }
  1270. }
  1271. if (xpos != window->x11.xpos || ypos != window->x11.ypos)
  1272. {
  1273. _glfwInputWindowPos(window, xpos, ypos);
  1274. window->x11.xpos = xpos;
  1275. window->x11.ypos = ypos;
  1276. }
  1277. return;
  1278. }
  1279. case ClientMessage:
  1280. {
  1281. // Custom client message, probably from the window manager
  1282. if (event->xclient.message_type == None)
  1283. return;
  1284. if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
  1285. {
  1286. const Atom protocol = event->xclient.data.l[0];
  1287. if (protocol == None)
  1288. return;
  1289. if (protocol == _glfw.x11.WM_DELETE_WINDOW)
  1290. {
  1291. // The window manager was asked to close the window, for
  1292. // example by the user pressing a 'close' window decoration
  1293. // button
  1294. _glfwInputWindowCloseRequest(window);
  1295. }
  1296. else if (protocol == _glfw.x11.NET_WM_PING)
  1297. {
  1298. // The window manager is pinging the application to ensure
  1299. // it's still responding to events
  1300. XEvent reply = *event;
  1301. reply.xclient.window = _glfw.x11.root;
  1302. XSendEvent(_glfw.x11.display, _glfw.x11.root,
  1303. False,
  1304. SubstructureNotifyMask | SubstructureRedirectMask,
  1305. &reply);
  1306. }
  1307. }
  1308. else if (event->xclient.message_type == _glfw.x11.XdndEnter)
  1309. {
  1310. // A drag operation has entered the window
  1311. unsigned long i, count;
  1312. Atom* formats = NULL;
  1313. const bool list = event->xclient.data.l[1] & 1;
  1314. _glfw.x11.xdnd.source = event->xclient.data.l[0];
  1315. _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
  1316. memset(_glfw.x11.xdnd.format, 0, sizeof(_glfw.x11.xdnd.format));
  1317. _glfw.x11.xdnd.format_priority = 0;
  1318. if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
  1319. return;
  1320. if (list)
  1321. {
  1322. count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
  1323. _glfw.x11.XdndTypeList,
  1324. XA_ATOM,
  1325. (unsigned char**) &formats);
  1326. }
  1327. else
  1328. {
  1329. count = 3;
  1330. formats = (Atom*) event->xclient.data.l + 2;
  1331. }
  1332. char **atom_names = calloc(count, sizeof(char*));
  1333. if (atom_names) {
  1334. get_atom_names(formats, count, atom_names);
  1335. for (i = 0; i < count; i++)
  1336. {
  1337. if (atom_names[i]) {
  1338. int prio = _glfwInputDrop(window, atom_names[i], NULL, 0);
  1339. if (prio > _glfw.x11.xdnd.format_priority) {
  1340. _glfw.x11.xdnd.format_priority = prio;
  1341. strncpy(_glfw.x11.xdnd.format, atom_names[i], arraysz(_glfw.x11.xdnd.format) - 1);
  1342. }
  1343. XFree(atom_names[i]);
  1344. }
  1345. }
  1346. free(atom_names);
  1347. }
  1348. if (list && formats)
  1349. XFree(formats);
  1350. }
  1351. else if (event->xclient.message_type == _glfw.x11.XdndDrop)
  1352. {
  1353. // The drag operation has finished by dropping on the window
  1354. Time time = CurrentTime;
  1355. if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
  1356. return;
  1357. if (_glfw.x11.xdnd.format_priority > 0)
  1358. {
  1359. if (_glfw.x11.xdnd.version >= 1)
  1360. time = event->xclient.data.l[2];
  1361. // Request the chosen format from the source window
  1362. XConvertSelection(_glfw.x11.display,
  1363. _glfw.x11.XdndSelection,
  1364. XInternAtom(_glfw.x11.display, _glfw.x11.xdnd.format, 0),
  1365. _glfw.x11.XdndSelection,
  1366. window->x11.handle,
  1367. time);
  1368. }
  1369. else if (_glfw.x11.xdnd.version >= 2)
  1370. {
  1371. XEvent reply = { ClientMessage };
  1372. reply.xclient.window = _glfw.x11.xdnd.source;
  1373. reply.xclient.message_type = _glfw.x11.XdndFinished;
  1374. reply.xclient.format = 32;
  1375. reply.xclient.data.l[0] = window->x11.handle;
  1376. reply.xclient.data.l[1] = 0; // The drag was rejected
  1377. reply.xclient.data.l[2] = None;
  1378. XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
  1379. False, NoEventMask, &reply);
  1380. XFlush(_glfw.x11.display);
  1381. }
  1382. }
  1383. else if (event->xclient.message_type == _glfw.x11.XdndPosition)
  1384. {
  1385. // The drag operation has moved over the window
  1386. const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
  1387. const int yabs = (event->xclient.data.l[2]) & 0xffff;
  1388. Window dummy;
  1389. int xpos = 0, ypos = 0;
  1390. if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
  1391. return;
  1392. _glfwGrabErrorHandlerX11();
  1393. XTranslateCoordinates(_glfw.x11.display,
  1394. _glfw.x11.root,
  1395. window->x11.handle,
  1396. xabs, yabs,
  1397. &xpos, &ypos,
  1398. &dummy);
  1399. _glfwReleaseErrorHandlerX11();
  1400. if (_glfw.x11.errorCode != Success)
  1401. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get DND event position");
  1402. _glfwInputCursorPos(window, xpos, ypos);
  1403. XEvent reply = { ClientMessage };
  1404. reply.xclient.window = _glfw.x11.xdnd.source;
  1405. reply.xclient.message_type = _glfw.x11.XdndStatus;
  1406. reply.xclient.format = 32;
  1407. reply.xclient.data.l[0] = window->x11.handle;
  1408. reply.xclient.data.l[2] = 0; // Specify an empty rectangle
  1409. reply.xclient.data.l[3] = 0;
  1410. if (_glfw.x11.xdnd.format_priority > 0)
  1411. {
  1412. // Reply that we are ready to copy the dragged data
  1413. reply.xclient.data.l[1] = 1; // Accept with no rectangle
  1414. if (_glfw.x11.xdnd.version >= 2)
  1415. reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
  1416. }
  1417. XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
  1418. False, NoEventMask, &reply);
  1419. XFlush(_glfw.x11.display);
  1420. }
  1421. return;
  1422. }
  1423. case SelectionNotify:
  1424. {
  1425. if (event->xselection.property == _glfw.x11.XdndSelection)
  1426. {
  1427. // The converted data from the drag operation has arrived
  1428. char* data;
  1429. const unsigned long result =
  1430. _glfwGetWindowPropertyX11(event->xselection.requestor,
  1431. event->xselection.property,
  1432. event->xselection.target,
  1433. (unsigned char**) &data);
  1434. if (result)
  1435. {
  1436. _glfwInputDrop(window, _glfw.x11.xdnd.format, data, result);
  1437. }
  1438. if (data)
  1439. XFree(data);
  1440. if (_glfw.x11.xdnd.version >= 2)
  1441. {
  1442. XEvent reply = { ClientMessage };
  1443. reply.xclient.window = _glfw.x11.xdnd.source;
  1444. reply.xclient.message_type = _glfw.x11.XdndFinished;
  1445. reply.xclient.format = 32;
  1446. reply.xclient.data.l[0] = window->x11.handle;
  1447. reply.xclient.data.l[1] = result;
  1448. reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
  1449. XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
  1450. False, NoEventMask, &reply);
  1451. XFlush(_glfw.x11.display);
  1452. }
  1453. }
  1454. return;
  1455. }
  1456. case FocusIn:
  1457. {
  1458. if (event->xfocus.mode == NotifyGrab ||
  1459. event->xfocus.mode == NotifyUngrab)
  1460. {
  1461. // Ignore focus events from popup indicator windows, window menu
  1462. // key chords and window dragging
  1463. return;
  1464. }
  1465. if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1466. disableCursor(window);
  1467. _glfwInputWindowFocus(window, true);
  1468. return;
  1469. }
  1470. case FocusOut:
  1471. {
  1472. if (event->xfocus.mode == NotifyGrab ||
  1473. event->xfocus.mode == NotifyUngrab)
  1474. {
  1475. // Ignore focus events from popup indicator windows, window menu
  1476. // key chords and window dragging
  1477. return;
  1478. }
  1479. if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1480. enableCursor(window);
  1481. if (window->monitor && window->autoIconify)
  1482. _glfwPlatformIconifyWindow(window);
  1483. _glfwInputWindowFocus(window, false);
  1484. return;
  1485. }
  1486. case Expose:
  1487. {
  1488. _glfwInputWindowDamage(window);
  1489. return;
  1490. }
  1491. case PropertyNotify:
  1492. {
  1493. if (event->xproperty.state != PropertyNewValue)
  1494. return;
  1495. if (event->xproperty.atom == _glfw.x11.WM_STATE)
  1496. {
  1497. const int state = getWindowState(window);
  1498. if (state != IconicState && state != NormalState)
  1499. return;
  1500. const bool iconified = (state == IconicState);
  1501. if (window->x11.iconified != iconified)
  1502. {
  1503. if (window->monitor)
  1504. {
  1505. if (iconified)
  1506. releaseMonitor(window);
  1507. else
  1508. acquireMonitor(window);
  1509. }
  1510. window->x11.iconified = iconified;
  1511. _glfwInputWindowIconify(window, iconified);
  1512. }
  1513. }
  1514. else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
  1515. {
  1516. const bool maximized = _glfwPlatformWindowMaximized(window);
  1517. if (window->x11.maximized != maximized)
  1518. {
  1519. window->x11.maximized = maximized;
  1520. int width, height;
  1521. _glfwPlatformGetWindowSize(window, &width, &height);
  1522. updateNormalHints(window, width, height);
  1523. _glfwInputWindowMaximize(window, maximized);
  1524. }
  1525. }
  1526. return;
  1527. }
  1528. case DestroyNotify:
  1529. return;
  1530. }
  1531. #undef UPDATE_KEYMAP_IF_NEEDED
  1532. }
  1533. //////////////////////////////////////////////////////////////////////////
  1534. ////// GLFW internal API //////
  1535. //////////////////////////////////////////////////////////////////////////
  1536. // Retrieve a single window property of the specified type
  1537. // Inspired by fghGetWindowProperty from freeglut
  1538. //
  1539. unsigned long _glfwGetWindowPropertyX11(Window window,
  1540. Atom property,
  1541. Atom type,
  1542. unsigned char** value)
  1543. {
  1544. Atom actualType;
  1545. int actualFormat;
  1546. unsigned long itemCount, bytesAfter;
  1547. XGetWindowProperty(_glfw.x11.display,
  1548. window,
  1549. property,
  1550. 0,
  1551. LONG_MAX,
  1552. False,
  1553. type,
  1554. &actualType,
  1555. &actualFormat,
  1556. &itemCount,
  1557. &bytesAfter,
  1558. value);
  1559. return itemCount;
  1560. }
  1561. bool _glfwIsVisualTransparentX11(Visual* visual)
  1562. {
  1563. if (!_glfw.x11.xrender.available)
  1564. return false;
  1565. XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
  1566. return pf && pf->direct.alphaMask;
  1567. }
  1568. // Push contents of our selection to clipboard manager
  1569. //
  1570. void _glfwPushSelectionToManagerX11(void)
  1571. {
  1572. XConvertSelection(_glfw.x11.display,
  1573. _glfw.x11.CLIPBOARD_MANAGER,
  1574. _glfw.x11.SAVE_TARGETS,
  1575. None,
  1576. _glfw.x11.helperWindowHandle,
  1577. CurrentTime);
  1578. for (;;)
  1579. {
  1580. XEvent event;
  1581. while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
  1582. {
  1583. switch (event.type)
  1584. {
  1585. case SelectionRequest:
  1586. handleSelectionRequest(&event);
  1587. break;
  1588. case SelectionClear:
  1589. handleSelectionClear(&event);
  1590. break;
  1591. case SelectionNotify:
  1592. {
  1593. if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
  1594. {
  1595. // This means one of two things; either the selection
  1596. // was not owned, which means there is no clipboard
  1597. // manager, or the transfer to the clipboard manager has
  1598. // completed
  1599. // In either case, it means we are done here
  1600. return;
  1601. }
  1602. break;
  1603. }
  1604. }
  1605. }
  1606. waitForX11Event(-1);
  1607. }
  1608. }
  1609. //////////////////////////////////////////////////////////////////////////
  1610. ////// GLFW platform API //////
  1611. //////////////////////////////////////////////////////////////////////////
  1612. int _glfwPlatformCreateWindow(_GLFWwindow* window,
  1613. const _GLFWwndconfig* wndconfig,
  1614. const _GLFWctxconfig* ctxconfig,
  1615. const _GLFWfbconfig* fbconfig)
  1616. {
  1617. Visual* visual = NULL;
  1618. int depth;
  1619. if (ctxconfig->client != GLFW_NO_API)
  1620. {
  1621. if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
  1622. {
  1623. if (!_glfwInitGLX())
  1624. return false;
  1625. if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
  1626. return false;
  1627. }
  1628. else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
  1629. {
  1630. if (!_glfwInitEGL())
  1631. return false;
  1632. if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
  1633. return false;
  1634. }
  1635. else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
  1636. {
  1637. if (!_glfwInitOSMesa())
  1638. return false;
  1639. }
  1640. }
  1641. if (!visual)
  1642. {
  1643. visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
  1644. depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
  1645. }
  1646. if (!createNativeWindow(window, wndconfig, visual, depth))
  1647. return false;
  1648. if (ctxconfig->client != GLFW_NO_API)
  1649. {
  1650. if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
  1651. {
  1652. if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
  1653. return false;
  1654. }
  1655. else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
  1656. {
  1657. if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
  1658. return false;
  1659. }
  1660. else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
  1661. {
  1662. if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
  1663. return false;
  1664. }
  1665. }
  1666. if (window->monitor)
  1667. {
  1668. _glfwPlatformShowWindow(window);
  1669. updateWindowMode(window);
  1670. acquireMonitor(window);
  1671. }
  1672. XFlush(_glfw.x11.display);
  1673. return true;
  1674. }
  1675. void _glfwPlatformDestroyWindow(_GLFWwindow* window)
  1676. {
  1677. if (_glfw.x11.disabledCursorWindow == window)
  1678. _glfw.x11.disabledCursorWindow = NULL;
  1679. if (window->monitor)
  1680. releaseMonitor(window);
  1681. if (window->context.destroy)
  1682. window->context.destroy(window);
  1683. if (window->x11.handle)
  1684. {
  1685. XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
  1686. XUnmapWindow(_glfw.x11.display, window->x11.handle);
  1687. XDestroyWindow(_glfw.x11.display, window->x11.handle);
  1688. window->x11.handle = (Window) 0;
  1689. }
  1690. if (window->x11.colormap)
  1691. {
  1692. XFreeColormap(_glfw.x11.display, window->x11.colormap);
  1693. window->x11.colormap = (Colormap) 0;
  1694. }
  1695. XFlush(_glfw.x11.display);
  1696. }
  1697. void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
  1698. {
  1699. #if defined(X_HAVE_UTF8_STRING)
  1700. Xutf8SetWMProperties(_glfw.x11.display,
  1701. window->x11.handle,
  1702. title, title,
  1703. NULL, 0,
  1704. NULL, NULL, NULL);
  1705. #else
  1706. // This may be a slightly better fallback than using XStoreName and
  1707. // XSetIconName, which always store their arguments using STRING
  1708. XmbSetWMProperties(_glfw.x11.display,
  1709. window->x11.handle,
  1710. title, title,
  1711. NULL, 0,
  1712. NULL, NULL, NULL);
  1713. #endif
  1714. XChangeProperty(_glfw.x11.display, window->x11.handle,
  1715. _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
  1716. PropModeReplace,
  1717. (unsigned char*) title, strlen(title));
  1718. XChangeProperty(_glfw.x11.display, window->x11.handle,
  1719. _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
  1720. PropModeReplace,
  1721. (unsigned char*) title, strlen(title));
  1722. XFlush(_glfw.x11.display);
  1723. }
  1724. void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
  1725. int count, const GLFWimage* images)
  1726. {
  1727. if (count)
  1728. {
  1729. int i, j, longCount = 0;
  1730. for (i = 0; i < count; i++)
  1731. longCount += 2 + images[i].width * images[i].height;
  1732. unsigned long* icon = calloc(longCount, sizeof(unsigned long));
  1733. unsigned long* target = icon;
  1734. for (i = 0; i < count; i++)
  1735. {
  1736. *target++ = images[i].width;
  1737. *target++ = images[i].height;
  1738. for (j = 0; j < images[i].width * images[i].height; j++)
  1739. {
  1740. unsigned char *p = images->pixels + j * 4;
  1741. const unsigned char r = *p++, g = *p++, b = *p++, a = *p++;
  1742. *target++ = a << 24 | (r << 16) | (g << 8) | b;
  1743. }
  1744. }
  1745. XChangeProperty(_glfw.x11.display, window->x11.handle,
  1746. _glfw.x11.NET_WM_ICON,
  1747. XA_CARDINAL, 32,
  1748. PropModeReplace,
  1749. (unsigned char*) icon,
  1750. longCount);
  1751. free(icon);
  1752. }
  1753. else
  1754. {
  1755. XDeleteProperty(_glfw.x11.display, window->x11.handle,
  1756. _glfw.x11.NET_WM_ICON);
  1757. }
  1758. XFlush(_glfw.x11.display);
  1759. }
  1760. void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
  1761. {
  1762. Window dummy;
  1763. int x = 0, y = 0;
  1764. _glfwGrabErrorHandlerX11();
  1765. XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
  1766. 0, 0, &x, &y, &dummy);
  1767. _glfwReleaseErrorHandlerX11();
  1768. if (_glfw.x11.errorCode != Success)
  1769. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get window position");
  1770. if (xpos)
  1771. *xpos = x;
  1772. if (ypos)
  1773. *ypos = y;
  1774. }
  1775. void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
  1776. {
  1777. // HACK: Explicitly setting PPosition to any value causes some WMs, notably
  1778. // Compiz and Metacity, to honor the position of unmapped windows
  1779. if (!_glfwPlatformWindowVisible(window))
  1780. {
  1781. long supplied;
  1782. XSizeHints* hints = XAllocSizeHints();
  1783. if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
  1784. {
  1785. hints->flags |= PPosition;
  1786. hints->x = hints->y = 0;
  1787. XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
  1788. }
  1789. XFree(hints);
  1790. }
  1791. XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
  1792. XFlush(_glfw.x11.display);
  1793. }
  1794. void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
  1795. {
  1796. XWindowAttributes attribs;
  1797. XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
  1798. if (width)
  1799. *width = attribs.width;
  1800. if (height)
  1801. *height = attribs.height;
  1802. }
  1803. void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
  1804. {
  1805. if (window->monitor)
  1806. {
  1807. if (window->monitor->window == window)
  1808. acquireMonitor(window);
  1809. }
  1810. else
  1811. {
  1812. if (!window->resizable)
  1813. updateNormalHints(window, width, height);
  1814. XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
  1815. }
  1816. XFlush(_glfw.x11.display);
  1817. }
  1818. void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
  1819. int minwidth UNUSED, int minheight UNUSED,
  1820. int maxwidth UNUSED, int maxheight UNUSED)
  1821. {
  1822. int width, height;
  1823. _glfwPlatformGetWindowSize(window, &width, &height);
  1824. updateNormalHints(window, width, height);
  1825. XFlush(_glfw.x11.display);
  1826. }
  1827. void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer UNUSED, int denom UNUSED)
  1828. {
  1829. int width, height;
  1830. _glfwPlatformGetWindowSize(window, &width, &height);
  1831. updateNormalHints(window, width, height);
  1832. XFlush(_glfw.x11.display);
  1833. }
  1834. void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr UNUSED, int heightincr UNUSED)
  1835. {
  1836. int width, height;
  1837. _glfwPlatformGetWindowSize(window, &width, &height);
  1838. updateNormalHints(window, width, height);
  1839. XFlush(_glfw.x11.display);
  1840. }
  1841. void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
  1842. {
  1843. _glfwPlatformGetWindowSize(window, width, height);
  1844. }
  1845. void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
  1846. int* left, int* top,
  1847. int* right, int* bottom)
  1848. {
  1849. long* extents = NULL;
  1850. if (window->monitor || !window->decorated)
  1851. return;
  1852. if (_glfw.x11.NET_FRAME_EXTENTS == None)
  1853. return;
  1854. if (!_glfwPlatformWindowVisible(window) &&
  1855. _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
  1856. {
  1857. XEvent event;
  1858. // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
  1859. // function before the window is mapped
  1860. sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
  1861. 0, 0, 0, 0, 0);
  1862. // HACK: Use a timeout because earlier versions of some window managers
  1863. // (at least Unity, Fluxbox and Xfwm) failed to send the reply
  1864. // They have been fixed but broken versions are still in the wild
  1865. // If you are affected by this and your window manager is NOT
  1866. // listed above, PLEASE report it to their and our issue trackers
  1867. while (!XCheckIfEvent(_glfw.x11.display,
  1868. &event,
  1869. isFrameExtentsEvent,
  1870. (XPointer) window))
  1871. {
  1872. if (!waitForX11Event(ms_to_monotonic_t(500ll)))
  1873. {
  1874. _glfwInputError(GLFW_PLATFORM_ERROR,
  1875. "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
  1876. return;
  1877. }
  1878. }
  1879. }
  1880. if (_glfwGetWindowPropertyX11(window->x11.handle,
  1881. _glfw.x11.NET_FRAME_EXTENTS,
  1882. XA_CARDINAL,
  1883. (unsigned char**) &extents) == 4)
  1884. {
  1885. if (left)
  1886. *left = extents[0];
  1887. if (top)
  1888. *top = extents[2];
  1889. if (right)
  1890. *right = extents[1];
  1891. if (bottom)
  1892. *bottom = extents[3];
  1893. }
  1894. if (extents)
  1895. XFree(extents);
  1896. }
  1897. void _glfwPlatformGetWindowContentScale(_GLFWwindow* window UNUSED,
  1898. float* xscale, float* yscale)
  1899. {
  1900. if (xscale)
  1901. *xscale = _glfw.x11.contentScaleX;
  1902. if (yscale)
  1903. *yscale = _glfw.x11.contentScaleY;
  1904. }
  1905. monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
  1906. {
  1907. return ms_to_monotonic_t(500ll);
  1908. }
  1909. void _glfwPlatformIconifyWindow(_GLFWwindow* window)
  1910. {
  1911. XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
  1912. XFlush(_glfw.x11.display);
  1913. }
  1914. void _glfwPlatformRestoreWindow(_GLFWwindow* window)
  1915. {
  1916. if (_glfwPlatformWindowIconified(window))
  1917. {
  1918. XMapWindow(_glfw.x11.display, window->x11.handle);
  1919. waitForVisibilityNotify(window);
  1920. }
  1921. else if (_glfwPlatformWindowVisible(window))
  1922. {
  1923. if (_glfw.x11.NET_WM_STATE &&
  1924. _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
  1925. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  1926. {
  1927. sendEventToWM(window,
  1928. _glfw.x11.NET_WM_STATE,
  1929. _NET_WM_STATE_REMOVE,
  1930. _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
  1931. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
  1932. 1, 0);
  1933. }
  1934. }
  1935. XFlush(_glfw.x11.display);
  1936. }
  1937. void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
  1938. {
  1939. if (!_glfw.x11.NET_WM_STATE ||
  1940. !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
  1941. !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  1942. {
  1943. return;
  1944. }
  1945. if (_glfwPlatformWindowVisible(window))
  1946. {
  1947. sendEventToWM(window,
  1948. _glfw.x11.NET_WM_STATE,
  1949. _NET_WM_STATE_ADD,
  1950. _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
  1951. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
  1952. 1, 0);
  1953. }
  1954. else
  1955. {
  1956. Atom* states = NULL;
  1957. unsigned long count =
  1958. _glfwGetWindowPropertyX11(window->x11.handle,
  1959. _glfw.x11.NET_WM_STATE,
  1960. XA_ATOM,
  1961. (unsigned char**) &states);
  1962. // NOTE: We don't check for failure as this property may not exist yet
  1963. // and that's fine (and we'll create it implicitly with append)
  1964. Atom missing[2] =
  1965. {
  1966. _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
  1967. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
  1968. };
  1969. unsigned long missingCount = 2;
  1970. for (unsigned long i = 0; i < count; i++)
  1971. {
  1972. for (unsigned long j = 0; j < missingCount; j++)
  1973. {
  1974. if (states[i] == missing[j])
  1975. {
  1976. missing[j] = missing[missingCount - 1];
  1977. missingCount--;
  1978. }
  1979. }
  1980. }
  1981. if (states)
  1982. XFree(states);
  1983. if (!missingCount)
  1984. return;
  1985. XChangeProperty(_glfw.x11.display, window->x11.handle,
  1986. _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  1987. PropModeAppend,
  1988. (unsigned char*) missing,
  1989. missingCount);
  1990. }
  1991. XFlush(_glfw.x11.display);
  1992. }
  1993. void _glfwPlatformShowWindow(_GLFWwindow* window)
  1994. {
  1995. if (_glfwPlatformWindowVisible(window))
  1996. return;
  1997. XMapWindow(_glfw.x11.display, window->x11.handle);
  1998. waitForVisibilityNotify(window);
  1999. }
  2000. void _glfwPlatformHideWindow(_GLFWwindow* window)
  2001. {
  2002. XUnmapWindow(_glfw.x11.display, window->x11.handle);
  2003. XFlush(_glfw.x11.display);
  2004. }
  2005. void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
  2006. {
  2007. if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
  2008. return;
  2009. sendEventToWM(window,
  2010. _glfw.x11.NET_WM_STATE,
  2011. _NET_WM_STATE_ADD,
  2012. _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
  2013. 0, 1, 0);
  2014. }
  2015. int _glfwPlatformWindowBell(_GLFWwindow* window)
  2016. {
  2017. return XkbBell(_glfw.x11.display, window->x11.handle, 100, (Atom)0) ? true : false;
  2018. }
  2019. void _glfwPlatformFocusWindow(_GLFWwindow* window)
  2020. {
  2021. if (_glfw.x11.NET_ACTIVE_WINDOW)
  2022. sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
  2023. else if (_glfwPlatformWindowVisible(window))
  2024. {
  2025. XRaiseWindow(_glfw.x11.display, window->x11.handle);
  2026. XSetInputFocus(_glfw.x11.display, window->x11.handle,
  2027. RevertToParent, CurrentTime);
  2028. }
  2029. XFlush(_glfw.x11.display);
  2030. }
  2031. void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
  2032. _GLFWmonitor* monitor,
  2033. int xpos, int ypos,
  2034. int width, int height,
  2035. int refreshRate UNUSED)
  2036. {
  2037. if (window->monitor == monitor)
  2038. {
  2039. if (monitor)
  2040. {
  2041. if (monitor->window == window)
  2042. acquireMonitor(window);
  2043. }
  2044. else
  2045. {
  2046. if (!window->resizable)
  2047. updateNormalHints(window, width, height);
  2048. XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
  2049. xpos, ypos, width, height);
  2050. }
  2051. XFlush(_glfw.x11.display);
  2052. return;
  2053. }
  2054. if (window->monitor)
  2055. releaseMonitor(window);
  2056. _glfwInputWindowMonitor(window, monitor);
  2057. updateNormalHints(window, width, height);
  2058. if (window->monitor)
  2059. {
  2060. if (!_glfwPlatformWindowVisible(window))
  2061. {
  2062. XMapRaised(_glfw.x11.display, window->x11.handle);
  2063. waitForVisibilityNotify(window);
  2064. }
  2065. updateWindowMode(window);
  2066. acquireMonitor(window);
  2067. }
  2068. else
  2069. {
  2070. updateWindowMode(window);
  2071. XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
  2072. xpos, ypos, width, height);
  2073. }
  2074. XFlush(_glfw.x11.display);
  2075. }
  2076. int _glfwPlatformWindowFocused(_GLFWwindow* window)
  2077. {
  2078. Window focused;
  2079. int state;
  2080. XGetInputFocus(_glfw.x11.display, &focused, &state);
  2081. return window->x11.handle == focused;
  2082. }
  2083. int _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)
  2084. {
  2085. return false;
  2086. }
  2087. int _glfwPlatformWindowIconified(_GLFWwindow* window)
  2088. {
  2089. return getWindowState(window) == IconicState;
  2090. }
  2091. int _glfwPlatformWindowVisible(_GLFWwindow* window)
  2092. {
  2093. XWindowAttributes wa;
  2094. XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
  2095. return wa.map_state == IsViewable;
  2096. }
  2097. int _glfwPlatformWindowMaximized(_GLFWwindow* window)
  2098. {
  2099. Atom* states;
  2100. unsigned long i;
  2101. bool maximized = false;
  2102. if (!_glfw.x11.NET_WM_STATE ||
  2103. !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
  2104. !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2105. {
  2106. return maximized;
  2107. }
  2108. const unsigned long count =
  2109. _glfwGetWindowPropertyX11(window->x11.handle,
  2110. _glfw.x11.NET_WM_STATE,
  2111. XA_ATOM,
  2112. (unsigned char**) &states);
  2113. for (i = 0; i < count; i++)
  2114. {
  2115. if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
  2116. states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2117. {
  2118. maximized = true;
  2119. break;
  2120. }
  2121. }
  2122. if (states)
  2123. XFree(states);
  2124. return maximized;
  2125. }
  2126. int _glfwPlatformWindowHovered(_GLFWwindow* window)
  2127. {
  2128. Window w = _glfw.x11.root;
  2129. while (w)
  2130. {
  2131. Window root;
  2132. int rootX, rootY, childX, childY;
  2133. unsigned int mask;
  2134. _glfwGrabErrorHandlerX11();
  2135. const Bool result = XQueryPointer(_glfw.x11.display, w,
  2136. &root, &w, &rootX, &rootY,
  2137. &childX, &childY, &mask);
  2138. _glfwReleaseErrorHandlerX11();
  2139. if (_glfw.x11.errorCode == BadWindow)
  2140. w = _glfw.x11.root;
  2141. else if (!result)
  2142. return false;
  2143. else if (w == window->x11.handle)
  2144. return true;
  2145. }
  2146. return false;
  2147. }
  2148. int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
  2149. {
  2150. if (!window->x11.transparent)
  2151. return false;
  2152. return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
  2153. }
  2154. void _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled UNUSED)
  2155. {
  2156. int width, height;
  2157. _glfwPlatformGetWindowSize(window, &width, &height);
  2158. updateNormalHints(window, width, height);
  2159. }
  2160. void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)
  2161. {
  2162. struct
  2163. {
  2164. unsigned long flags;
  2165. unsigned long functions;
  2166. unsigned long decorations;
  2167. long input_mode;
  2168. unsigned long status;
  2169. } hints = {0};
  2170. hints.flags = MWM_HINTS_DECORATIONS;
  2171. hints.decorations = enabled ? MWM_DECOR_ALL : 0;
  2172. XChangeProperty(_glfw.x11.display, window->x11.handle,
  2173. _glfw.x11.MOTIF_WM_HINTS,
  2174. _glfw.x11.MOTIF_WM_HINTS, 32,
  2175. PropModeReplace,
  2176. (unsigned char*) &hints,
  2177. sizeof(hints) / sizeof(long));
  2178. }
  2179. void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
  2180. {
  2181. if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
  2182. return;
  2183. if (_glfwPlatformWindowVisible(window))
  2184. {
  2185. const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  2186. sendEventToWM(window,
  2187. _glfw.x11.NET_WM_STATE,
  2188. action,
  2189. _glfw.x11.NET_WM_STATE_ABOVE,
  2190. 0, 1, 0);
  2191. }
  2192. else
  2193. {
  2194. Atom* states = NULL;
  2195. unsigned long i, count;
  2196. count = _glfwGetWindowPropertyX11(window->x11.handle,
  2197. _glfw.x11.NET_WM_STATE,
  2198. XA_ATOM,
  2199. (unsigned char**) &states);
  2200. // NOTE: We don't check for failure as this property may not exist yet
  2201. // and that's fine (and we'll create it implicitly with append)
  2202. if (enabled)
  2203. {
  2204. for (i = 0; i < count; i++)
  2205. {
  2206. if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
  2207. break;
  2208. }
  2209. if (i < count)
  2210. return;
  2211. XChangeProperty(_glfw.x11.display, window->x11.handle,
  2212. _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  2213. PropModeAppend,
  2214. (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
  2215. 1);
  2216. }
  2217. else if (states)
  2218. {
  2219. for (i = 0; i < count; i++)
  2220. {
  2221. if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
  2222. break;
  2223. }
  2224. if (i == count)
  2225. return;
  2226. states[i] = states[count - 1];
  2227. count--;
  2228. XChangeProperty(_glfw.x11.display, window->x11.handle,
  2229. _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  2230. PropModeReplace, (unsigned char*) states, count);
  2231. }
  2232. if (states)
  2233. XFree(states);
  2234. }
  2235. XFlush(_glfw.x11.display);
  2236. }
  2237. void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled)
  2238. {
  2239. if (!_glfw.x11.xshape.available)
  2240. return;
  2241. if (enabled)
  2242. {
  2243. Region region = XCreateRegion();
  2244. XShapeCombineRegion(_glfw.x11.display, window->x11.handle,
  2245. ShapeInput, 0, 0, region, ShapeSet);
  2246. XDestroyRegion(region);
  2247. }
  2248. else
  2249. {
  2250. XShapeCombineMask(_glfw.x11.display, window->x11.handle,
  2251. ShapeInput, 0, 0, None, ShapeSet);
  2252. }
  2253. }
  2254. float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
  2255. {
  2256. float opacity = 1.f;
  2257. if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
  2258. {
  2259. CARD32* value = NULL;
  2260. if (_glfwGetWindowPropertyX11(window->x11.handle,
  2261. _glfw.x11.NET_WM_WINDOW_OPACITY,
  2262. XA_CARDINAL,
  2263. (unsigned char**) &value))
  2264. {
  2265. opacity = (float) (*value / (double) 0xffffffffu);
  2266. }
  2267. if (value)
  2268. XFree(value);
  2269. }
  2270. return opacity;
  2271. }
  2272. void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
  2273. {
  2274. const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
  2275. XChangeProperty(_glfw.x11.display, window->x11.handle,
  2276. _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
  2277. PropModeReplace, (unsigned char*) &value, 1);
  2278. }
  2279. static unsigned
  2280. dispatch_x11_queued_events(int num_events) {
  2281. unsigned dispatched = num_events > 0 ? num_events : 0;
  2282. while (num_events-- > 0) {
  2283. XEvent event;
  2284. XNextEvent(_glfw.x11.display, &event);
  2285. processEvent(&event);
  2286. }
  2287. return dispatched;
  2288. }
  2289. static unsigned
  2290. _glfwDispatchX11Events(void) {
  2291. _GLFWwindow* window;
  2292. unsigned dispatched = 0;
  2293. #if defined(__linux__)
  2294. if (_glfw.joysticksInitialized)
  2295. _glfwDetectJoystickConnectionLinux();
  2296. #endif
  2297. dispatched += dispatch_x11_queued_events(XEventsQueued(_glfw.x11.display, QueuedAfterFlush));
  2298. window = _glfw.x11.disabledCursorWindow;
  2299. if (window)
  2300. {
  2301. int width, height;
  2302. _glfwPlatformGetWindowSize(window, &width, &height);
  2303. // NOTE: Re-center the cursor only if it has moved since the last call,
  2304. // to avoid breaking glfwWaitEvents with MotionNotify
  2305. if (window->x11.lastCursorPosX != width / 2 ||
  2306. window->x11.lastCursorPosY != height / 2)
  2307. {
  2308. _glfwPlatformSetCursorPos(window, width / 2.f, height / 2.f);
  2309. }
  2310. }
  2311. XFlush(_glfw.x11.display);
  2312. // XFlush can cause events to be queued, we don't use QueuedAfterFlush here
  2313. // as something might have inserted events into the queue, but we want to guarantee
  2314. // a flush.
  2315. dispatched += dispatch_x11_queued_events(XEventsQueued(_glfw.x11.display, QueuedAlready));
  2316. return dispatched;
  2317. }
  2318. void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled)
  2319. {
  2320. if (!_glfw.x11.xi.available)
  2321. return;
  2322. if (_glfw.x11.disabledCursorWindow != window)
  2323. return;
  2324. if (enabled)
  2325. enableRawMouseMotion(window);
  2326. else
  2327. disableRawMouseMotion(window);
  2328. }
  2329. bool _glfwPlatformRawMouseMotionSupported(void)
  2330. {
  2331. return _glfw.x11.xi.available;
  2332. }
  2333. void _glfwPlatformPollEvents(void)
  2334. {
  2335. _glfwDispatchX11Events();
  2336. handleEvents(0);
  2337. }
  2338. void _glfwPlatformWaitEvents(void)
  2339. {
  2340. monotonic_t timeout = _glfwDispatchX11Events() ? 0 : -1;
  2341. handleEvents(timeout);
  2342. }
  2343. void _glfwPlatformWaitEventsTimeout(monotonic_t timeout)
  2344. {
  2345. if (_glfwDispatchX11Events()) timeout = 0;
  2346. handleEvents(timeout);
  2347. }
  2348. void _glfwPlatformPostEmptyEvent(void)
  2349. {
  2350. wakeupEventLoop(&_glfw.x11.eventLoopData);
  2351. }
  2352. void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
  2353. {
  2354. Window root, child;
  2355. int rootX, rootY, childX, childY;
  2356. unsigned int mask;
  2357. XQueryPointer(_glfw.x11.display, window->x11.handle,
  2358. &root, &child,
  2359. &rootX, &rootY, &childX, &childY,
  2360. &mask);
  2361. if (xpos)
  2362. *xpos = childX;
  2363. if (ypos)
  2364. *ypos = childY;
  2365. }
  2366. void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
  2367. {
  2368. // Store the new position so it can be recognized later
  2369. window->x11.warpCursorPosX = (int) x;
  2370. window->x11.warpCursorPosY = (int) y;
  2371. XWarpPointer(_glfw.x11.display, None, window->x11.handle,
  2372. 0,0,0,0, (int) x, (int) y);
  2373. XFlush(_glfw.x11.display);
  2374. }
  2375. void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
  2376. {
  2377. if (mode == GLFW_CURSOR_DISABLED)
  2378. {
  2379. if (_glfwPlatformWindowFocused(window))
  2380. disableCursor(window);
  2381. }
  2382. else if (_glfw.x11.disabledCursorWindow == window)
  2383. enableCursor(window);
  2384. else
  2385. updateCursorImage(window);
  2386. XFlush(_glfw.x11.display);
  2387. }
  2388. const char* _glfwPlatformGetNativeKeyName(int native_key)
  2389. {
  2390. return glfw_xkb_keysym_name(native_key);
  2391. }
  2392. int _glfwPlatformGetNativeKeyForKey(uint32_t key)
  2393. {
  2394. return glfw_xkb_sym_for_key(key);
  2395. }
  2396. int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
  2397. const GLFWimage* image,
  2398. int xhot, int yhot, int count UNUSED)
  2399. {
  2400. cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
  2401. if (!cursor->x11.handle)
  2402. return false;
  2403. return true;
  2404. }
  2405. static int
  2406. set_cursor_from_font(_GLFWcursor* cursor, int native) {
  2407. cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
  2408. if (!cursor->x11.handle) {
  2409. _glfwInputError(GLFW_PLATFORM_ERROR,
  2410. "X11: Failed to create standard cursor");
  2411. return false;
  2412. }
  2413. return true;
  2414. }
  2415. static bool
  2416. try_cursor_names(_GLFWcursor *cursor, int arg_count, ...) {
  2417. va_list ap;
  2418. va_start(ap, arg_count);
  2419. const char *first_name = "";
  2420. for (int i = 0; i < arg_count; i++) {
  2421. const char *name = va_arg(ap, const char *);
  2422. first_name = name;
  2423. cursor->x11.handle = XcursorLibraryLoadCursor(_glfw.x11.display, name);
  2424. if (cursor->x11.handle) break;
  2425. }
  2426. va_end(ap);
  2427. if (!cursor->x11.handle) {
  2428. _glfwInputError(GLFW_PLATFORM_ERROR,
  2429. "X11: Failed to load standard cursor: %s with %d aliases via Xcursor library", first_name, arg_count);
  2430. return false;
  2431. }
  2432. return true;
  2433. }
  2434. int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape)
  2435. {
  2436. switch(shape) {
  2437. /* start glfw to xc mapping (auto generated by gen-key-constants.py do not edit) */
  2438. case GLFW_DEFAULT_CURSOR: return set_cursor_from_font(cursor, XC_left_ptr);
  2439. case GLFW_TEXT_CURSOR: return set_cursor_from_font(cursor, XC_xterm);
  2440. case GLFW_POINTER_CURSOR: return set_cursor_from_font(cursor, XC_hand2);
  2441. case GLFW_HELP_CURSOR: return set_cursor_from_font(cursor, XC_question_arrow);
  2442. case GLFW_WAIT_CURSOR: return set_cursor_from_font(cursor, XC_clock);
  2443. case GLFW_PROGRESS_CURSOR: return try_cursor_names(cursor, 3, "progress", "half-busy", "left_ptr_watch");
  2444. case GLFW_CROSSHAIR_CURSOR: return set_cursor_from_font(cursor, XC_tcross);
  2445. case GLFW_CELL_CURSOR: return set_cursor_from_font(cursor, XC_plus);
  2446. case GLFW_VERTICAL_TEXT_CURSOR: return try_cursor_names(cursor, 1, "vertical-text");
  2447. case GLFW_MOVE_CURSOR: return set_cursor_from_font(cursor, XC_fleur);
  2448. case GLFW_E_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_right_side);
  2449. case GLFW_NE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_right_corner);
  2450. case GLFW_NW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_left_corner);
  2451. case GLFW_N_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_side);
  2452. case GLFW_SE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_right_corner);
  2453. case GLFW_SW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_left_corner);
  2454. case GLFW_S_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_side);
  2455. case GLFW_W_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_left_side);
  2456. case GLFW_EW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_h_double_arrow);
  2457. case GLFW_NS_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_v_double_arrow);
  2458. case GLFW_NESW_RESIZE_CURSOR: return try_cursor_names(cursor, 3, "nesw-resize", "size_bdiag", "size-bdiag");
  2459. case GLFW_NWSE_RESIZE_CURSOR: return try_cursor_names(cursor, 3, "nwse-resize", "size_fdiag", "size-fdiag");
  2460. case GLFW_ZOOM_IN_CURSOR: return try_cursor_names(cursor, 2, "zoom-in", "zoom_in");
  2461. case GLFW_ZOOM_OUT_CURSOR: return try_cursor_names(cursor, 2, "zoom-out", "zoom_out");
  2462. case GLFW_ALIAS_CURSOR: return try_cursor_names(cursor, 1, "dnd-link");
  2463. case GLFW_COPY_CURSOR: return try_cursor_names(cursor, 1, "dnd-copy");
  2464. case GLFW_NOT_ALLOWED_CURSOR: return try_cursor_names(cursor, 3, "not-allowed", "forbidden", "crossed_circle");
  2465. case GLFW_NO_DROP_CURSOR: return try_cursor_names(cursor, 2, "no-drop", "dnd-no-drop");
  2466. case GLFW_GRAB_CURSOR: return set_cursor_from_font(cursor, XC_hand1);
  2467. case GLFW_GRABBING_CURSOR: return try_cursor_names(cursor, 3, "grabbing", "closedhand", "dnd-none");
  2468. /* end glfw to xc mapping */
  2469. case GLFW_INVALID_CURSOR: return false;
  2470. }
  2471. return false;
  2472. }
  2473. void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
  2474. {
  2475. if (cursor->x11.handle)
  2476. XFreeCursor(_glfw.x11.display, cursor->x11.handle);
  2477. }
  2478. void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor UNUSED)
  2479. {
  2480. if (window->cursorMode == GLFW_CURSOR_NORMAL)
  2481. {
  2482. updateCursorImage(window);
  2483. XFlush(_glfw.x11.display);
  2484. }
  2485. }
  2486. static MimeAtom atom_for_mime(const char *mime) {
  2487. for (size_t i = 0; i < _glfw.x11.mime_atoms.sz; i++) {
  2488. MimeAtom ma = _glfw.x11.mime_atoms.array[i];
  2489. if (strcmp(ma.mime, mime) == 0) {
  2490. return ma;
  2491. }
  2492. }
  2493. MimeAtom ma = {.mime=_glfw_strdup(mime), .atom=XInternAtom(_glfw.x11.display, mime, 0)};
  2494. if (_glfw.x11.mime_atoms.capacity < _glfw.x11.mime_atoms.sz + 1) {
  2495. _glfw.x11.mime_atoms.capacity += 32;
  2496. _glfw.x11.mime_atoms.array = realloc(_glfw.x11.mime_atoms.array, _glfw.x11.mime_atoms.capacity * sizeof(_glfw.x11.mime_atoms.array[0]));
  2497. }
  2498. _glfw.x11.mime_atoms.array[_glfw.x11.mime_atoms.sz++] = ma;
  2499. return ma;
  2500. }
  2501. void _glfwPlatformSetClipboard(GLFWClipboardType t) {
  2502. Atom which = None;
  2503. _GLFWClipboardData *cd = NULL;
  2504. AtomArray *aa = NULL;
  2505. switch (t) {
  2506. case GLFW_CLIPBOARD: which = _glfw.x11.CLIPBOARD; cd = &_glfw.clipboard; aa = &_glfw.x11.clipboard_atoms; break;
  2507. case GLFW_PRIMARY_SELECTION: which = _glfw.x11.PRIMARY; cd = &_glfw.primary; aa = &_glfw.x11.primary_atoms; break;
  2508. }
  2509. XSetSelectionOwner(_glfw.x11.display, which, _glfw.x11.helperWindowHandle, CurrentTime);
  2510. if (XGetSelectionOwner(_glfw.x11.display, which) != _glfw.x11.helperWindowHandle) {
  2511. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to become owner of clipboard selection");
  2512. }
  2513. if (aa->capacity < cd->num_mime_types + 32) {
  2514. aa->capacity = cd->num_mime_types + 32;
  2515. aa->array = reallocarray(aa->array, aa->capacity, sizeof(aa->array[0]));
  2516. }
  2517. aa->sz = 0;
  2518. for (size_t i = 0; i < cd->num_mime_types; i++) {
  2519. MimeAtom *a = aa->array + aa->sz++;
  2520. *a = atom_for_mime(cd->mime_types[i]);
  2521. if (strcmp(cd->mime_types[i], "text/plain") == 0) {
  2522. a = aa->array + aa->sz++;
  2523. a->atom = _glfw.x11.UTF8_STRING;
  2524. a->mime = "text/plain";
  2525. }
  2526. }
  2527. }
  2528. typedef struct chunked_writer {
  2529. char *buf; size_t sz, cap;
  2530. bool is_self_offer;
  2531. } chunked_writer;
  2532. static bool
  2533. write_chunk(void *object, const char *data, size_t sz) {
  2534. chunked_writer *cw = object;
  2535. if (data) {
  2536. if (cw->cap < cw->sz + sz) {
  2537. cw->cap = MAX(cw->cap * 2, cw->sz + 8*sz);
  2538. cw->buf = realloc(cw->buf, cw->cap * sizeof(cw->buf[0]));
  2539. }
  2540. memcpy(cw->buf + cw->sz, data, sz);
  2541. cw->sz += sz;
  2542. } else if (sz == 1) cw->is_self_offer = true;
  2543. return true;
  2544. }
  2545. static void
  2546. get_available_mime_types(Atom which_clipboard, GLFWclipboardwritedatafun write_data, void *object) {
  2547. chunked_writer cw = {0};
  2548. getSelectionString(which_clipboard, &_glfw.x11.TARGETS, 1, write_chunk, &cw, false);
  2549. if (cw.is_self_offer) {
  2550. write_data(object, NULL, 1);
  2551. return;
  2552. }
  2553. size_t count = 0;
  2554. bool ok = true;
  2555. if (cw.buf) {
  2556. Atom *atoms = (Atom*)cw.buf;
  2557. count = cw.sz / sizeof(Atom);
  2558. char **names = calloc(count, sizeof(char*));
  2559. get_atom_names(atoms, count, names);
  2560. for (size_t i = 0; i < count; i++) {
  2561. if (strchr(names[i], '/')) {
  2562. if (ok) ok = write_data(object, names[i], strlen(names[i]));
  2563. } else {
  2564. if (atoms[i] == _glfw.x11.UTF8_STRING || atoms[i] == XA_STRING) {
  2565. if (ok) ok = write_data(object, "text/plain", strlen("text/plain"));
  2566. }
  2567. }
  2568. XFree(names[i]);
  2569. }
  2570. free(cw.buf);
  2571. free(names);
  2572. }
  2573. }
  2574. void
  2575. _glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object) {
  2576. Atom atoms[4], which = clipboard_type == GLFW_PRIMARY_SELECTION ? _glfw.x11.PRIMARY : _glfw.x11.CLIPBOARD;
  2577. if (mime_type == NULL) {
  2578. get_available_mime_types(which, write_data, object);
  2579. return;
  2580. }
  2581. size_t count = 0;
  2582. if (strcmp(mime_type, "text/plain") == 0) {
  2583. // UTF8_STRING is what xclip uses by default, and there are people out there that expect to be able to paste from it with a single read operation. See https://github.com/kovidgoyal/kitty/issues/5842
  2584. // Also ancient versions of GNOME use DOS line endings even for text/plain;charset=utf-8. See https://github.com/kovidgoyal/kitty/issues/5528#issuecomment-1325348218
  2585. atoms[count++] = _glfw.x11.UTF8_STRING;
  2586. // we need to do this because GTK/GNOME is moronic they convert text/plain to DOS line endings, see
  2587. // https://gitlab.gnome.org/GNOME/gtk/-/issues/2307
  2588. atoms[count++] = atom_for_mime("text/plain;charset=utf-8").atom;
  2589. atoms[count++] = atom_for_mime("text/plain").atom;
  2590. atoms[count++] = XA_STRING;
  2591. } else {
  2592. atoms[count++] = atom_for_mime(mime_type).atom;
  2593. }
  2594. getSelectionString(which, atoms, count, write_data, object, true);
  2595. }
  2596. EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)
  2597. {
  2598. if (_glfw.egl.ANGLE_platform_angle)
  2599. {
  2600. int type = 0;
  2601. if (_glfw.egl.ANGLE_platform_angle_opengl)
  2602. {
  2603. if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
  2604. type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
  2605. }
  2606. if (_glfw.egl.ANGLE_platform_angle_vulkan)
  2607. {
  2608. if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
  2609. type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
  2610. }
  2611. if (type)
  2612. {
  2613. *attribs = calloc(5, sizeof(EGLint));
  2614. (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
  2615. (*attribs)[1] = type;
  2616. (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE;
  2617. (*attribs)[3] = EGL_PLATFORM_X11_EXT;
  2618. (*attribs)[4] = EGL_NONE;
  2619. return EGL_PLATFORM_ANGLE_ANGLE;
  2620. }
  2621. }
  2622. if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_x11)
  2623. return EGL_PLATFORM_X11_EXT;
  2624. return 0;
  2625. }
  2626. EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)
  2627. {
  2628. return _glfw.x11.display;
  2629. }
  2630. EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)
  2631. {
  2632. if (_glfw.egl.platform)
  2633. return &window->x11.handle;
  2634. else
  2635. return (EGLNativeWindowType) window->x11.handle;
  2636. }
  2637. void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
  2638. {
  2639. if (!_glfw.vk.KHR_surface)
  2640. return;
  2641. if (!_glfw.vk.KHR_xcb_surface)
  2642. {
  2643. if (!_glfw.vk.KHR_xlib_surface)
  2644. return;
  2645. }
  2646. extensions[0] = "VK_KHR_surface";
  2647. // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
  2648. // not correctly implementing VK_KHR_xlib_surface
  2649. if (_glfw.vk.KHR_xcb_surface)
  2650. extensions[1] = "VK_KHR_xcb_surface";
  2651. else
  2652. extensions[1] = "VK_KHR_xlib_surface";
  2653. }
  2654. int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
  2655. VkPhysicalDevice device,
  2656. uint32_t queuefamily)
  2657. {
  2658. VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
  2659. _glfw.x11.screen));
  2660. if (_glfw.vk.KHR_xcb_surface)
  2661. {
  2662. PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
  2663. vkGetPhysicalDeviceXcbPresentationSupportKHR =
  2664. (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
  2665. vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
  2666. if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
  2667. {
  2668. _glfwInputError(GLFW_API_UNAVAILABLE,
  2669. "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
  2670. return false;
  2671. }
  2672. xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
  2673. if (!connection)
  2674. {
  2675. _glfwInputError(GLFW_PLATFORM_ERROR,
  2676. "X11: Failed to retrieve XCB connection");
  2677. return false;
  2678. }
  2679. return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
  2680. queuefamily,
  2681. connection,
  2682. visualID);
  2683. }
  2684. else
  2685. {
  2686. PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
  2687. vkGetPhysicalDeviceXlibPresentationSupportKHR =
  2688. (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
  2689. vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
  2690. if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
  2691. {
  2692. _glfwInputError(GLFW_API_UNAVAILABLE,
  2693. "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
  2694. return false;
  2695. }
  2696. return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
  2697. queuefamily,
  2698. _glfw.x11.display,
  2699. visualID);
  2700. }
  2701. }
  2702. VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
  2703. _GLFWwindow* window,
  2704. const VkAllocationCallbacks* allocator,
  2705. VkSurfaceKHR* surface)
  2706. {
  2707. if (_glfw.vk.KHR_xcb_surface)
  2708. {
  2709. VkResult err;
  2710. VkXcbSurfaceCreateInfoKHR sci;
  2711. PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
  2712. xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
  2713. if (!connection)
  2714. {
  2715. _glfwInputError(GLFW_PLATFORM_ERROR,
  2716. "X11: Failed to retrieve XCB connection");
  2717. return VK_ERROR_EXTENSION_NOT_PRESENT;
  2718. }
  2719. vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
  2720. vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
  2721. if (!vkCreateXcbSurfaceKHR)
  2722. {
  2723. _glfwInputError(GLFW_API_UNAVAILABLE,
  2724. "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
  2725. return VK_ERROR_EXTENSION_NOT_PRESENT;
  2726. }
  2727. memset(&sci, 0, sizeof(sci));
  2728. sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
  2729. sci.connection = connection;
  2730. sci.window = window->x11.handle;
  2731. err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
  2732. if (err)
  2733. {
  2734. _glfwInputError(GLFW_PLATFORM_ERROR,
  2735. "X11: Failed to create Vulkan XCB surface: %s",
  2736. _glfwGetVulkanResultString(err));
  2737. }
  2738. return err;
  2739. }
  2740. else
  2741. {
  2742. VkResult err;
  2743. VkXlibSurfaceCreateInfoKHR sci;
  2744. PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
  2745. vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
  2746. vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
  2747. if (!vkCreateXlibSurfaceKHR)
  2748. {
  2749. _glfwInputError(GLFW_API_UNAVAILABLE,
  2750. "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
  2751. return VK_ERROR_EXTENSION_NOT_PRESENT;
  2752. }
  2753. memset(&sci, 0, sizeof(sci));
  2754. sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
  2755. sci.dpy = _glfw.x11.display;
  2756. sci.window = window->x11.handle;
  2757. err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
  2758. if (err)
  2759. {
  2760. _glfwInputError(GLFW_PLATFORM_ERROR,
  2761. "X11: Failed to create Vulkan X11 surface: %s",
  2762. _glfwGetVulkanResultString(err));
  2763. }
  2764. return err;
  2765. }
  2766. }
  2767. void
  2768. _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
  2769. glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, ev);
  2770. }
  2771. int
  2772. _glfwPlatformSetWindowBlur(_GLFWwindow *window, int blur_radius) {
  2773. if (_glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION == None) {
  2774. _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION = XInternAtom(_glfw.x11.display, "_KDE_NET_WM_BLUR_BEHIND_REGION", False);
  2775. }
  2776. if (_glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION != None) {
  2777. uint32_t data = 0;
  2778. if (blur_radius > 0) {
  2779. XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION,
  2780. XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &data, 1);
  2781. } else {
  2782. XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION);
  2783. }
  2784. return 1;
  2785. }
  2786. return 0;
  2787. }
  2788. //////////////////////////////////////////////////////////////////////////
  2789. ////// GLFW native API //////
  2790. //////////////////////////////////////////////////////////////////////////
  2791. GLFWAPI Display* glfwGetX11Display(void)
  2792. {
  2793. _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
  2794. return _glfw.x11.display;
  2795. }
  2796. GLFWAPI unsigned long glfwGetX11Window(GLFWwindow* handle)
  2797. {
  2798. _GLFWwindow* window = (_GLFWwindow*) handle;
  2799. _GLFW_REQUIRE_INIT_OR_RETURN(None);
  2800. return window->x11.handle;
  2801. }
  2802. GLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) {
  2803. return glfw_xkb_keysym_from_name(keyName, caseSensitive);
  2804. }
  2805. GLFWAPI unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data) {
  2806. return glfw_dbus_send_user_notification(n, callback, data);
  2807. }
  2808. GLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) {
  2809. glfw_dbus_set_user_notification_activated_handler(handler);
  2810. }
  2811. GLFWAPI int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)
  2812. {
  2813. _GLFW_REQUIRE_INIT_OR_RETURN(0);
  2814. _GLFWwindow* window = (_GLFWwindow*) handle;
  2815. return XSetCommand(_glfw.x11.display, window->x11.handle, argv, argc);
  2816. }
  2817. GLFWAPI void glfwSetX11WindowAsDock(int32_t x11_window_id) {
  2818. _GLFW_REQUIRE_INIT();
  2819. Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_DOCK;
  2820. XChangeProperty(_glfw.x11.display, x11_window_id,
  2821. _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
  2822. PropModeReplace, (unsigned char*) &type, 1);
  2823. }
  2824. GLFWAPI void glfwSetX11WindowStrut(int32_t x11_window_id, uint32_t dimensions[12]) {
  2825. _GLFW_REQUIRE_INIT();
  2826. XChangeProperty(_glfw.x11.display, x11_window_id,
  2827. _glfw.x11.NET_WM_STRUT_PARTIAL, XA_CARDINAL, 32,
  2828. PropModeReplace, (unsigned char*) dimensions, 12);
  2829. }