x11_window.c 115 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424
  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. typedef unsigned long strut_type;
  465. typedef struct WindowGeometry {
  466. int x, y, width, height;
  467. bool needs_strut;
  468. strut_type struts[12];
  469. } WindowGeometry;
  470. #define config (window->x11.layer_shell.config)
  471. static _GLFWmonitor*
  472. find_monitor_by_name(const char* name) {
  473. if (!name || !name[0]) return (_GLFWmonitor*)glfwGetPrimaryMonitor();;
  474. for (int i = 0; i < _glfw.monitorCount; i++) {
  475. _GLFWmonitor *m = _glfw.monitors[i];
  476. if (strcmp(m->name, name) == 0) return m;
  477. }
  478. return (_GLFWmonitor*)glfwGetPrimaryMonitor();;
  479. }
  480. static WindowGeometry
  481. calculate_layer_geometry(_GLFWwindow *window) {
  482. _GLFWmonitor *monitor = find_monitor_by_name(config.output_name);
  483. MonitorGeometry mg = _glfwPlatformGetMonitorGeometry((_GLFWmonitor*)glfwGetPrimaryMonitor());
  484. WindowGeometry ans = {0};
  485. debug_rendering("Monitor: %s full: %dx%d@%dx%d workarea: %dx%d@%dx%d\n", monitor->name,
  486. mg.full.width, mg.full.height, mg.full.x, mg.full.y, mg.workarea.width, mg.workarea.height, mg.workarea.x, mg.workarea.y);
  487. ans.width = mg.full.width; ans.height = mg.full.height;
  488. ans.x = mg.full.x; ans.y = mg.full.y;
  489. ans.needs_strut = config.type == GLFW_LAYER_SHELL_PANEL;
  490. if (config.type == GLFW_LAYER_SHELL_BACKGROUND) {
  491. ans.x += config.requested_left_margin; ans.y += config.requested_top_margin;
  492. ans.width -= config.requested_left_margin + config.requested_right_margin;
  493. ans.height -= config.requested_top_margin + config.requested_bottom_margin;
  494. return ans;
  495. }
  496. float xscale = (float)config.expected.xscale, yscale = (float)config.expected.yscale;
  497. _glfwPlatformGetWindowContentScale(window, &xscale, &yscale);
  498. unsigned cell_width, cell_height; double left_edge_spacing, top_edge_spacing, right_edge_spacing, bottom_edge_spacing;
  499. config.size_callback((GLFWwindow*)window, xscale, yscale, &cell_width, &cell_height, &left_edge_spacing, &top_edge_spacing, &right_edge_spacing, &bottom_edge_spacing);
  500. double spacing_x = left_edge_spacing + right_edge_spacing;
  501. double spacing_y = top_edge_spacing + bottom_edge_spacing;
  502. double xsz = config.x_size_in_pixels ? (unsigned)(config.x_size_in_pixels * xscale) : (cell_width * config.x_size_in_cells);
  503. double ysz = config.y_size_in_pixels ? (unsigned)(config.y_size_in_pixels * yscale) : (cell_height * config.y_size_in_cells);
  504. ans.width = (int)(1. + spacing_x + xsz); ans.height = (int)(1. + spacing_y + ysz);
  505. GeometryRect m = config.type == GLFW_LAYER_SHELL_TOP || config.type == GLFW_LAYER_SHELL_OVERLAY ? mg.workarea : mg.full;
  506. static const struct {
  507. unsigned left, right, top, bottom, left_start_y, left_end_y, right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x, bottom_end_x;
  508. } s = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
  509. switch (config.edge) {
  510. case GLFW_EDGE_LEFT:
  511. ans.x = m.x + config.requested_left_margin;
  512. ans.y = m.y + config.requested_top_margin;
  513. ans.height = m.height - config.requested_bottom_margin - config.requested_top_margin;
  514. ans.struts[s.left] = ans.width; ans.struts[s.left_end_y] = ans.height;
  515. break;
  516. case GLFW_EDGE_RIGHT:
  517. ans.x = m.x + m.width - config.requested_right_margin - ans.width;
  518. ans.y = m.y + config.requested_top_margin;
  519. ans.height = m.height - config.requested_bottom_margin - config.requested_top_margin;
  520. ans.struts[s.right] = ans.width; ans.struts[s.right_end_y] = ans.height;
  521. break;
  522. case GLFW_EDGE_TOP:
  523. ans.x = m.x + config.requested_left_margin;
  524. ans.y = m.y + config.requested_top_margin;
  525. ans.width = m.width - config.requested_right_margin - config.requested_left_margin;
  526. ans.struts[s.top] = ans.height; ans.struts[s.top_end_x] = ans.width;
  527. break;
  528. case GLFW_EDGE_BOTTOM:
  529. ans.x = m.x + config.requested_left_margin;
  530. ans.y = m.height - config.requested_bottom_margin - ans.height;
  531. ans.width = m.width - config.requested_right_margin - config.requested_left_margin;
  532. ans.struts[s.bottom] = ans.height; ans.struts[s.bottom_end_x] = ans.width;
  533. break;
  534. case GLFW_EDGE_CENTER_SIZED:
  535. ans.needs_strut = false;
  536. ans.x = (m.width - ans.width) / 2;
  537. ans.y = (m.height - ans.height) / 2;
  538. break;
  539. default:
  540. ans.needs_strut = false;
  541. ans.x = m.x + config.requested_left_margin;
  542. ans.y = m.y + config.requested_top_margin;
  543. ans.height = m.height - config.requested_bottom_margin - config.requested_top_margin;
  544. ans.width = m.width - config.requested_right_margin - config.requested_left_margin;
  545. break;
  546. }
  547. debug_rendering("Calculating layer geometry at scale: %f cell size: (%u, %u) -> %dx%d@%dx%d needs_strut: %d\n",
  548. xscale, cell_width, cell_height, ans.width, ans.height, ans.x, ans.y, ans.needs_strut)
  549. return ans;
  550. }
  551. GLFWAPI bool glfwIsLayerShellSupported(void) { return _glfw.x11.NET_WM_WINDOW_TYPE != 0 && _glfw.x11.NET_WM_STATE != 0; }
  552. static bool
  553. update_wm_hints(_GLFWwindow *window, const WindowGeometry *wg, const _GLFWwndconfig *wndconfig) {
  554. XWMHints* hints = XAllocWMHints();
  555. bool is_layer_shell = window->x11.layer_shell.is_active;
  556. bool ok = false;
  557. if (hints) {
  558. ok = true;
  559. hints->flags = StateHint | InputHint;
  560. hints->initial_state = NormalState;
  561. hints->input = true;
  562. if (is_layer_shell && config.focus_policy == GLFW_FOCUS_NOT_ALLOWED) hints->input = false;
  563. XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
  564. XFree(hints);
  565. } else _glfwInputError(GLFW_OUT_OF_MEMORY, "X11: Failed to allocate WM hints");
  566. if (_glfw.x11.NET_WM_WINDOW_TYPE) {
  567. Atom type = 0;
  568. if (is_layer_shell) {
  569. const char *name = NULL;
  570. #define S(which) type = _glfw.x11.which; name = #which
  571. switch (config.type) {
  572. case GLFW_LAYER_SHELL_BACKGROUND: S(NET_WM_WINDOW_TYPE_DESKTOP); break;
  573. case GLFW_LAYER_SHELL_PANEL: S(NET_WM_WINDOW_TYPE_DOCK); break;
  574. default: S(NET_WM_WINDOW_TYPE_NORMAL); break;
  575. }
  576. #undef S
  577. if (!type) {
  578. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Window manager does not support _%s", name);
  579. ok = false;
  580. }
  581. } else if (_glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
  582. if (type) XChangeProperty(
  583. _glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char*) &type, 1);
  584. } else if (is_layer_shell) {
  585. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Window manager does not support _NET_WM_WINDOW_TYPE");
  586. ok = false;
  587. }
  588. if (is_layer_shell) {
  589. if (_glfw.x11.NET_WM_STRUT_PARTIAL) {
  590. XChangeProperty(
  591. _glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STRUT_PARTIAL, XA_CARDINAL, 32, PropModeReplace,
  592. (unsigned char*)(wg->needs_strut ? wg->struts : (strut_type[12]){0}), 12);
  593. } else if (wg->needs_strut) {
  594. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Window manager does not support _NET_WM_STRUT_PARTIAL");
  595. ok = false;
  596. }
  597. }
  598. if (ok) {
  599. updateNormalHints(window, wg->width, wg->height);
  600. Atom states[8]; unsigned count = 0;
  601. if (is_layer_shell) {
  602. _glfwPlatformSetWindowDecorated(window, false);
  603. if (_glfw.x11.NET_WM_STATE_STICKY) states[count++] = _glfw.x11.NET_WM_STATE_STICKY;
  604. if (_glfw.x11.NET_WM_STATE_SKIP_PAGER) states[count++] = _glfw.x11.NET_WM_STATE_SKIP_PAGER;
  605. if (_glfw.x11.NET_WM_STATE_SKIP_TASKBAR) states[count++] = _glfw.x11.NET_WM_STATE_SKIP_TASKBAR;
  606. #define S(x) if (_glfw.x11.x) { states[count++] = _glfw.x11.x; } else { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Window manager does not support _%s", #x); ok = false; }
  607. switch (config.type) {
  608. case GLFW_LAYER_SHELL_NONE: break;
  609. case GLFW_LAYER_SHELL_BACKGROUND: S(NET_WM_STATE_BELOW); break;
  610. case GLFW_LAYER_SHELL_PANEL:
  611. // i3 does not support NET_WM_STATE_BELOW but panels work without it
  612. if (_glfw.x11.NET_WM_STATE_BELOW) { S(NET_WM_STATE_BELOW); }
  613. break;
  614. case GLFW_LAYER_SHELL_TOP: case GLFW_LAYER_SHELL_OVERLAY: S(NET_WM_STATE_ABOVE); break;
  615. }
  616. #undef S
  617. } else if (wndconfig) {
  618. if (!wndconfig->decorated) _glfwPlatformSetWindowDecorated(window, false);
  619. if (_glfw.x11.NET_WM_STATE && !window->monitor) {
  620. if (wndconfig->floating) {
  621. if (_glfw.x11.NET_WM_STATE_ABOVE) states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
  622. }
  623. if (wndconfig->maximized) {
  624. if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) {
  625. states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
  626. states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
  627. window->x11.maximized = true;
  628. }
  629. }
  630. }
  631. }
  632. if (count && _glfw.x11.NET_WM_STATE) XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STATE,
  633. XA_ATOM, 32, PropModeReplace, (unsigned char*) states, count);
  634. }
  635. if (!wndconfig && ok) {
  636. _glfwPlatformSetWindowPos(window, wg->x, wg->y);
  637. _glfwPlatformSetWindowSize(window, wg->width, wg->height);
  638. }
  639. return ok;
  640. #undef config
  641. }
  642. // Create the X11 window (and its colormap)
  643. //
  644. static bool createNativeWindow(_GLFWwindow* window,
  645. const _GLFWwndconfig* wndconfig,
  646. Visual* visual, int depth)
  647. {
  648. WindowGeometry wg = {.width=wndconfig->width, .height=wndconfig->height};
  649. if (window->x11.layer_shell.is_active) {
  650. wg = calculate_layer_geometry(window);
  651. window->resizable = false;
  652. }
  653. // Create a colormap based on the visual used by the current context
  654. window->x11.colormap = XCreateColormap(_glfw.x11.display,
  655. _glfw.x11.root,
  656. visual,
  657. AllocNone);
  658. window->x11.transparent = _glfwIsVisualTransparentX11(visual);
  659. XSetWindowAttributes wa = { 0 };
  660. wa.colormap = window->x11.colormap;
  661. wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
  662. PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
  663. ExposureMask | FocusChangeMask | VisibilityChangeMask |
  664. EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
  665. _glfwGrabErrorHandlerX11();
  666. window->x11.parent = _glfw.x11.root;
  667. debug_rendering("Creating window with geometry: %dx%d@%dx%d\n", wg.width, wg.height, wg.x, wg.y);
  668. window->x11.handle = XCreateWindow(_glfw.x11.display,
  669. _glfw.x11.root,
  670. wg.x, wg.y, // Position
  671. wg.width, wg.height,
  672. 0, // Border width
  673. depth, // Color depth
  674. InputOutput,
  675. visual,
  676. CWBorderPixel | CWColormap | CWEventMask,
  677. &wa);
  678. _glfwReleaseErrorHandlerX11();
  679. if (!window->x11.handle)
  680. {
  681. _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
  682. "X11: Failed to create window");
  683. return false;
  684. }
  685. XSaveContext(_glfw.x11.display,
  686. window->x11.handle,
  687. _glfw.x11.context,
  688. (XPointer) window);
  689. // Declare the WM protocols supported by GLFW
  690. {
  691. Atom protocols[] =
  692. {
  693. _glfw.x11.WM_DELETE_WINDOW,
  694. _glfw.x11.NET_WM_PING
  695. };
  696. XSetWMProtocols(_glfw.x11.display, window->x11.handle,
  697. protocols, sizeof(protocols) / sizeof(Atom));
  698. }
  699. // Declare our PID
  700. {
  701. const long pid = getpid();
  702. XChangeProperty(_glfw.x11.display, window->x11.handle,
  703. _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
  704. PropModeReplace,
  705. (unsigned char*) &pid, 1);
  706. }
  707. if (!update_wm_hints(window, &wg, wndconfig)) return false;
  708. // without this floating window position is incorrect on KDE
  709. if (window->x11.layer_shell.is_active) _glfwPlatformSetWindowPos(window, wg.x, wg.y);
  710. // Set ICCCM WM_CLASS property
  711. {
  712. XClassHint* hint = XAllocClassHint();
  713. if (strlen(wndconfig->x11.instanceName) &&
  714. strlen(wndconfig->x11.className))
  715. {
  716. hint->res_name = (char*) wndconfig->x11.instanceName;
  717. hint->res_class = (char*) wndconfig->x11.className;
  718. }
  719. else
  720. {
  721. const char* resourceName = getenv("RESOURCE_NAME");
  722. if (resourceName && strlen(resourceName))
  723. hint->res_name = (char*) resourceName;
  724. else if (strlen(wndconfig->title))
  725. hint->res_name = (char*) wndconfig->title;
  726. else
  727. hint->res_name = (char*) "glfw-application";
  728. if (strlen(wndconfig->title))
  729. hint->res_class = (char*) wndconfig->title;
  730. else
  731. hint->res_class = (char*) "GLFW-Application";
  732. }
  733. XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
  734. XFree(hint);
  735. }
  736. // Announce support for Xdnd (drag and drop)
  737. {
  738. const Atom version = _GLFW_XDND_VERSION;
  739. XChangeProperty(_glfw.x11.display, window->x11.handle,
  740. _glfw.x11.XdndAware, XA_ATOM, 32,
  741. PropModeReplace, (unsigned char*) &version, 1);
  742. }
  743. _glfwPlatformSetWindowTitle(window, wndconfig->title);
  744. _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
  745. _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
  746. if (_glfw.hints.window.blur_radius > 0) _glfwPlatformSetWindowBlur(window, _glfw.hints.window.blur_radius);
  747. return true;
  748. }
  749. static size_t
  750. get_clipboard_data(const _GLFWClipboardData *cd, const char *mime, char **data) {
  751. *data = NULL;
  752. if (cd->get_data == NULL) { return 0; }
  753. GLFWDataChunk chunk = cd->get_data(mime, NULL, cd->ctype);
  754. char *buf = NULL;
  755. size_t sz = 0, cap = 0;
  756. void *iter = chunk.iter;
  757. if (!iter) return 0;
  758. while (true) {
  759. chunk = cd->get_data(mime, iter, cd->ctype);
  760. if (!chunk.sz) break;
  761. if (cap < sz + chunk.sz) {
  762. cap = MAX(cap * 2, sz + 4 * chunk.sz);
  763. buf = realloc(buf, cap * sizeof(buf[0]));
  764. }
  765. memcpy(buf + sz, chunk.data, chunk.sz);
  766. sz += chunk.sz;
  767. if (chunk.free) chunk.free((void*)chunk.free_data);
  768. }
  769. *data = buf;
  770. cd->get_data(NULL, iter, cd->ctype);
  771. return sz;
  772. }
  773. static void
  774. get_atom_names(const Atom *atoms, int count, char **atom_names) {
  775. _glfwGrabErrorHandlerX11();
  776. XGetAtomNames(_glfw.x11.display, (Atom*)atoms, count, atom_names);
  777. _glfwReleaseErrorHandlerX11();
  778. if (_glfw.x11.errorCode != Success) {
  779. for (int i = 0; i < count; i++) {
  780. _glfwGrabErrorHandlerX11();
  781. atom_names[i] = XGetAtomName(_glfw.x11.display, atoms[i]);
  782. _glfwReleaseErrorHandlerX11();
  783. if (_glfw.x11.errorCode != Success) atom_names[i] = NULL;
  784. }
  785. }
  786. }
  787. // Set the specified property to the selection converted to the requested target
  788. //
  789. static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
  790. {
  791. const AtomArray *aa;
  792. const _GLFWClipboardData *cd;
  793. if (request->selection == _glfw.x11.PRIMARY) {
  794. aa = &_glfw.x11.primary_atoms;
  795. cd = &_glfw.primary;
  796. } else {
  797. aa = &_glfw.x11.clipboard_atoms;
  798. cd = &_glfw.clipboard;
  799. }
  800. if (request->property == None)
  801. {
  802. // The requester is a legacy client (ICCCM section 2.2)
  803. // We don't support legacy clients, so fail here
  804. return None;
  805. }
  806. if (request->target == _glfw.x11.TARGETS)
  807. {
  808. // The list of supported targets was requested
  809. Atom *targets = calloc(aa->sz + 2, sizeof(Atom));
  810. targets[0] = _glfw.x11.TARGETS;
  811. targets[1] = _glfw.x11.MULTIPLE;
  812. for (size_t i = 0; i < aa->sz; i++) targets[i+2] = aa->array[i].atom;
  813. XChangeProperty(_glfw.x11.display,
  814. request->requestor,
  815. request->property,
  816. XA_ATOM,
  817. 32,
  818. PropModeReplace,
  819. (unsigned char*) targets,
  820. aa->sz + 2);
  821. free(targets);
  822. return request->property;
  823. }
  824. if (request->target == _glfw.x11.MULTIPLE)
  825. {
  826. // Multiple conversions were requested
  827. Atom* targets;
  828. size_t i, j, count;
  829. count = _glfwGetWindowPropertyX11(request->requestor,
  830. request->property,
  831. _glfw.x11.ATOM_PAIR,
  832. (unsigned char**) &targets);
  833. for (i = 0; i < count; i += 2)
  834. {
  835. for (j = 0; j < aa->sz; j++)
  836. {
  837. if (targets[i] == aa->array[j].atom)
  838. break;
  839. }
  840. if (j < aa->sz)
  841. {
  842. char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[j].mime, &data);
  843. if (data) XChangeProperty(_glfw.x11.display,
  844. request->requestor,
  845. targets[i + 1],
  846. targets[i],
  847. 8,
  848. PropModeReplace,
  849. (unsigned char *) data,
  850. sz);
  851. free(data);
  852. }
  853. else
  854. targets[i + 1] = None;
  855. }
  856. XChangeProperty(_glfw.x11.display,
  857. request->requestor,
  858. request->property,
  859. _glfw.x11.ATOM_PAIR,
  860. 32,
  861. PropModeReplace,
  862. (unsigned char*) targets,
  863. count);
  864. XFree(targets);
  865. return request->property;
  866. }
  867. if (request->target == _glfw.x11.SAVE_TARGETS)
  868. {
  869. // The request is a check whether we support SAVE_TARGETS
  870. // It should be handled as a no-op side effect target
  871. XChangeProperty(_glfw.x11.display,
  872. request->requestor,
  873. request->property,
  874. _glfw.x11.NULL_,
  875. 32,
  876. PropModeReplace,
  877. NULL,
  878. 0);
  879. return request->property;
  880. }
  881. // Conversion to a data target was requested
  882. for (size_t i = 0; i < aa->sz; i++)
  883. {
  884. if (request->target == aa->array[i].atom)
  885. {
  886. // The requested target is one we support
  887. char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[i].mime, &data);
  888. if (data) XChangeProperty(_glfw.x11.display,
  889. request->requestor,
  890. request->property,
  891. request->target,
  892. 8,
  893. PropModeReplace,
  894. (unsigned char *) data,
  895. sz);
  896. free(data);
  897. return request->property;
  898. }
  899. }
  900. // The requested target is not supported
  901. return None;
  902. }
  903. static void handleSelectionClear(XEvent* event)
  904. {
  905. if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
  906. {
  907. _glfw_free_clipboard_data(&_glfw.primary);
  908. _glfwInputClipboardLost(GLFW_PRIMARY_SELECTION);
  909. }
  910. else
  911. {
  912. _glfw_free_clipboard_data(&_glfw.clipboard);
  913. _glfwInputClipboardLost(GLFW_CLIPBOARD);
  914. }
  915. }
  916. static void handleSelectionRequest(XEvent* event)
  917. {
  918. const XSelectionRequestEvent* request = &event->xselectionrequest;
  919. XEvent reply = { SelectionNotify };
  920. reply.xselection.property = writeTargetToProperty(request);
  921. reply.xselection.display = request->display;
  922. reply.xselection.requestor = request->requestor;
  923. reply.xselection.selection = request->selection;
  924. reply.xselection.target = request->target;
  925. reply.xselection.time = request->time;
  926. XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
  927. }
  928. static void
  929. getSelectionString(Atom selection, Atom *targets, size_t num_targets, GLFWclipboardwritedatafun write_data, void *object, bool report_not_found)
  930. {
  931. #define XFREE(x) { if (x) XFree(x); x = NULL; }
  932. if (XGetSelectionOwner(_glfw.x11.display, selection) == _glfw.x11.helperWindowHandle) {
  933. write_data(object, NULL, 1);
  934. return;
  935. }
  936. bool found = false;
  937. for (size_t i = 0; !found && i < num_targets; i++)
  938. {
  939. char* data = NULL;
  940. Atom actualType = None;
  941. int actualFormat = 0;
  942. unsigned long itemCount = 0, bytesAfter = 0;
  943. monotonic_t start = glfwGetTime();
  944. XEvent notification, dummy;
  945. XConvertSelection(_glfw.x11.display,
  946. selection,
  947. targets[i],
  948. _glfw.x11.GLFW_SELECTION,
  949. _glfw.x11.helperWindowHandle,
  950. CurrentTime);
  951. while (!XCheckTypedWindowEvent(_glfw.x11.display,
  952. _glfw.x11.helperWindowHandle,
  953. SelectionNotify,
  954. &notification))
  955. {
  956. monotonic_t time = glfwGetTime();
  957. if (time - start > s_to_monotonic_t(2ll)) return;
  958. waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
  959. }
  960. if (notification.xselection.property == None)
  961. continue;
  962. XCheckIfEvent(_glfw.x11.display,
  963. &dummy,
  964. isSelPropNewValueNotify,
  965. (XPointer) &notification);
  966. XGetWindowProperty(_glfw.x11.display,
  967. notification.xselection.requestor,
  968. notification.xselection.property,
  969. 0,
  970. LONG_MAX,
  971. True,
  972. AnyPropertyType,
  973. &actualType,
  974. &actualFormat,
  975. &itemCount,
  976. &bytesAfter,
  977. (unsigned char**) &data);
  978. if (actualType == _glfw.x11.INCR)
  979. {
  980. for (;;)
  981. {
  982. start = glfwGetTime();
  983. while (!XCheckIfEvent(_glfw.x11.display,
  984. &dummy,
  985. isSelPropNewValueNotify,
  986. (XPointer) &notification))
  987. {
  988. monotonic_t time = glfwGetTime();
  989. if (time - start > s_to_monotonic_t(2ll)) {
  990. return;
  991. }
  992. waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
  993. }
  994. XFREE(data);
  995. XGetWindowProperty(_glfw.x11.display,
  996. notification.xselection.requestor,
  997. notification.xselection.property,
  998. 0,
  999. LONG_MAX,
  1000. True,
  1001. AnyPropertyType,
  1002. &actualType,
  1003. &actualFormat,
  1004. &itemCount,
  1005. &bytesAfter,
  1006. (unsigned char**) &data);
  1007. if (itemCount)
  1008. {
  1009. const char *string = data;
  1010. if (targets[i] == XA_STRING) {
  1011. string = convertLatin1toUTF8(data);
  1012. itemCount = strlen(string);
  1013. }
  1014. bool ok = write_data(object, string, itemCount);
  1015. if (string != data) free((void*)string);
  1016. if (!ok) { XFREE(data); break; }
  1017. } else { found = true; break; }
  1018. }
  1019. }
  1020. else if (actualType == targets[i])
  1021. {
  1022. if (targets[i] == XA_STRING) {
  1023. const char *string = convertLatin1toUTF8(data);
  1024. write_data(object, string, strlen(string)); free((void*)string);
  1025. } else write_data(object, data, itemCount);
  1026. found = true;
  1027. }
  1028. else if (actualType == XA_ATOM && targets[i] == _glfw.x11.TARGETS) {
  1029. found = true;
  1030. write_data(object, data, sizeof(Atom) * itemCount);
  1031. }
  1032. XFREE(data);
  1033. }
  1034. if (!found && report_not_found)
  1035. {
  1036. _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
  1037. "X11: Failed to convert selection to data from clipboard");
  1038. }
  1039. #undef XFREE
  1040. }
  1041. // Make the specified window and its video mode active on its monitor
  1042. //
  1043. static void acquireMonitor(_GLFWwindow* window)
  1044. {
  1045. if (_glfw.x11.saver.count == 0)
  1046. {
  1047. // Remember old screen saver settings
  1048. XGetScreenSaver(_glfw.x11.display,
  1049. &_glfw.x11.saver.timeout,
  1050. &_glfw.x11.saver.interval,
  1051. &_glfw.x11.saver.blanking,
  1052. &_glfw.x11.saver.exposure);
  1053. // Disable screen saver
  1054. XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
  1055. DefaultExposures);
  1056. }
  1057. if (!window->monitor->window)
  1058. _glfw.x11.saver.count++;
  1059. _glfwSetVideoModeX11(window->monitor, &window->videoMode);
  1060. _glfwInputMonitorWindow(window->monitor, window);
  1061. }
  1062. // Remove the window and restore the original video mode
  1063. //
  1064. static void releaseMonitor(_GLFWwindow* window)
  1065. {
  1066. if (window->monitor->window != window)
  1067. return;
  1068. _glfwInputMonitorWindow(window->monitor, NULL);
  1069. _glfwRestoreVideoModeX11(window->monitor);
  1070. _glfw.x11.saver.count--;
  1071. if (_glfw.x11.saver.count == 0)
  1072. {
  1073. // Restore old screen saver settings
  1074. XSetScreenSaver(_glfw.x11.display,
  1075. _glfw.x11.saver.timeout,
  1076. _glfw.x11.saver.interval,
  1077. _glfw.x11.saver.blanking,
  1078. _glfw.x11.saver.exposure);
  1079. }
  1080. }
  1081. static void onConfigChange(void)
  1082. {
  1083. float xscale, yscale;
  1084. _glfwGetSystemContentScaleX11(&xscale, &yscale, true);
  1085. if (xscale != _glfw.x11.contentScaleX || yscale != _glfw.x11.contentScaleY)
  1086. {
  1087. _GLFWwindow* window = _glfw.windowListHead;
  1088. _glfw.x11.contentScaleX = xscale;
  1089. _glfw.x11.contentScaleY = yscale;
  1090. while (window)
  1091. {
  1092. _glfwInputWindowContentScale(window, xscale, yscale);
  1093. window = window->next;
  1094. }
  1095. }
  1096. }
  1097. // Process the specified X event
  1098. //
  1099. static void processEvent(XEvent *event)
  1100. {
  1101. static bool keymap_dirty = false;
  1102. #define UPDATE_KEYMAP_IF_NEEDED if (keymap_dirty) { keymap_dirty = false; glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL); }
  1103. if (_glfw.x11.randr.available)
  1104. {
  1105. if (event->type == _glfw.x11.randr.eventBase + RRNotify)
  1106. {
  1107. XRRUpdateConfiguration(event);
  1108. _glfwPollMonitorsX11();
  1109. return;
  1110. }
  1111. }
  1112. if (event->type == PropertyNotify &&
  1113. event->xproperty.window == _glfw.x11.root &&
  1114. event->xproperty.atom == _glfw.x11.RESOURCE_MANAGER)
  1115. {
  1116. onConfigChange();
  1117. return;
  1118. }
  1119. if (event->type == GenericEvent)
  1120. {
  1121. if (_glfw.x11.xi.available)
  1122. {
  1123. _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
  1124. if (window &&
  1125. window->rawMouseMotion &&
  1126. event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
  1127. XGetEventData(_glfw.x11.display, &event->xcookie) &&
  1128. event->xcookie.evtype == XI_RawMotion)
  1129. {
  1130. XIRawEvent* re = event->xcookie.data;
  1131. if (re->valuators.mask_len)
  1132. {
  1133. const double* values = re->raw_values;
  1134. double xpos = window->virtualCursorPosX;
  1135. double ypos = window->virtualCursorPosY;
  1136. if (XIMaskIsSet(re->valuators.mask, 0))
  1137. {
  1138. xpos += *values;
  1139. values++;
  1140. }
  1141. if (XIMaskIsSet(re->valuators.mask, 1))
  1142. ypos += *values;
  1143. _glfwInputCursorPos(window, xpos, ypos);
  1144. }
  1145. }
  1146. XFreeEventData(_glfw.x11.display, &event->xcookie);
  1147. }
  1148. return;
  1149. }
  1150. if (event->type == SelectionClear)
  1151. {
  1152. handleSelectionClear(event);
  1153. return;
  1154. }
  1155. else if (event->type == SelectionRequest)
  1156. {
  1157. handleSelectionRequest(event);
  1158. return;
  1159. }
  1160. else if (event->type == _glfw.x11.xkb.eventBase)
  1161. {
  1162. XkbEvent *kb_event = (XkbEvent*)event;
  1163. if (kb_event->any.device != (unsigned int)_glfw.x11.xkb.keyboard_device_id) return;
  1164. switch(kb_event->any.xkb_type) {
  1165. case XkbNewKeyboardNotify: {
  1166. XkbNewKeyboardNotifyEvent *newkb_event = (XkbNewKeyboardNotifyEvent*)kb_event;
  1167. if (_glfw.hints.init.debugKeyboard) printf(
  1168. "Got XkbNewKeyboardNotify event with changes: key codes: %d geometry: %d device id: %d\n",
  1169. !!(newkb_event->changed & XkbNKN_KeycodesMask), !!(newkb_event->changed & XkbNKN_GeometryMask),
  1170. !!(newkb_event->changed & XkbNKN_DeviceIDMask));
  1171. if (newkb_event->changed & XkbNKN_DeviceIDMask) {
  1172. keymap_dirty = true;
  1173. if (!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return;
  1174. }
  1175. if (newkb_event->changed & XkbNKN_KeycodesMask) {
  1176. keymap_dirty = true;
  1177. }
  1178. return;
  1179. }
  1180. case XkbMapNotify:
  1181. {
  1182. if (_glfw.hints.init.debugKeyboard) printf("Got XkbMapNotify event, keymaps will be reloaded\n");
  1183. keymap_dirty = true;
  1184. return;
  1185. }
  1186. case XkbStateNotify:
  1187. {
  1188. UPDATE_KEYMAP_IF_NEEDED;
  1189. XkbStateNotifyEvent *state_event = (XkbStateNotifyEvent*)kb_event;
  1190. glfw_xkb_update_modifiers(
  1191. &_glfw.x11.xkb, state_event->base_mods, state_event->latched_mods,
  1192. state_event->locked_mods, state_event->base_group, state_event->latched_group, state_event->locked_group
  1193. );
  1194. return;
  1195. }
  1196. }
  1197. return;
  1198. }
  1199. _GLFWwindow* window = NULL;
  1200. if (XFindContext(_glfw.x11.display,
  1201. event->xany.window,
  1202. _glfw.x11.context,
  1203. (XPointer*) &window) != 0)
  1204. {
  1205. // This is an event for a window that has already been destroyed
  1206. return;
  1207. }
  1208. switch (event->type)
  1209. {
  1210. case ReparentNotify:
  1211. {
  1212. window->x11.parent = event->xreparent.parent;
  1213. return;
  1214. }
  1215. case KeyPress:
  1216. {
  1217. UPDATE_KEYMAP_IF_NEEDED;
  1218. glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_PRESS);
  1219. return;
  1220. }
  1221. case KeyRelease:
  1222. {
  1223. UPDATE_KEYMAP_IF_NEEDED;
  1224. if (!_glfw.x11.xkb.detectable)
  1225. {
  1226. // HACK: Key repeat events will arrive as KeyRelease/KeyPress
  1227. // pairs with similar or identical time stamps
  1228. // The key repeat logic in _glfwInputKey expects only key
  1229. // presses to repeat, so detect and discard release events
  1230. if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
  1231. {
  1232. XEvent next;
  1233. XPeekEvent(_glfw.x11.display, &next);
  1234. if (next.type == KeyPress &&
  1235. next.xkey.window == event->xkey.window &&
  1236. next.xkey.keycode == event->xkey.keycode)
  1237. {
  1238. // HACK: The time of repeat events sometimes doesn't
  1239. // match that of the press event, so add an
  1240. // epsilon
  1241. // Toshiyuki Takahashi can press a button
  1242. // 16 times per second so it's fairly safe to
  1243. // assume that no human is pressing the key 50
  1244. // times per second (value is ms)
  1245. if ((next.xkey.time - event->xkey.time) < 20)
  1246. {
  1247. // This is very likely a server-generated key repeat
  1248. // event, so ignore it
  1249. return;
  1250. }
  1251. }
  1252. }
  1253. }
  1254. glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_RELEASE);
  1255. return;
  1256. }
  1257. case ButtonPress:
  1258. {
  1259. const int mods = translateState(event->xbutton.state);
  1260. if (event->xbutton.button == Button1)
  1261. _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
  1262. else if (event->xbutton.button == Button2)
  1263. _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
  1264. else if (event->xbutton.button == Button3)
  1265. _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
  1266. // Modern X provides scroll events as mouse button presses
  1267. else if (event->xbutton.button == Button4)
  1268. _glfwInputScroll(window, 0.0, 1.0, 0, mods);
  1269. else if (event->xbutton.button == Button5)
  1270. _glfwInputScroll(window, 0.0, -1.0, 0, mods);
  1271. else if (event->xbutton.button == Button6)
  1272. _glfwInputScroll(window, 1.0, 0.0, 0, mods);
  1273. else if (event->xbutton.button == Button7)
  1274. _glfwInputScroll(window, -1.0, 0.0, 0, mods);
  1275. else
  1276. {
  1277. // Additional buttons after 7 are treated as regular buttons
  1278. // We subtract 4 to fill the gap left by scroll input above
  1279. _glfwInputMouseClick(window,
  1280. event->xbutton.button - Button1 - 4,
  1281. GLFW_PRESS,
  1282. mods);
  1283. }
  1284. return;
  1285. }
  1286. case ButtonRelease:
  1287. {
  1288. const int mods = translateState(event->xbutton.state);
  1289. if (event->xbutton.button == Button1)
  1290. {
  1291. _glfwInputMouseClick(window,
  1292. GLFW_MOUSE_BUTTON_LEFT,
  1293. GLFW_RELEASE,
  1294. mods);
  1295. }
  1296. else if (event->xbutton.button == Button2)
  1297. {
  1298. _glfwInputMouseClick(window,
  1299. GLFW_MOUSE_BUTTON_MIDDLE,
  1300. GLFW_RELEASE,
  1301. mods);
  1302. }
  1303. else if (event->xbutton.button == Button3)
  1304. {
  1305. _glfwInputMouseClick(window,
  1306. GLFW_MOUSE_BUTTON_RIGHT,
  1307. GLFW_RELEASE,
  1308. mods);
  1309. }
  1310. else if (event->xbutton.button > Button7)
  1311. {
  1312. // Additional buttons after 7 are treated as regular buttons
  1313. // We subtract 4 to fill the gap left by scroll input above
  1314. _glfwInputMouseClick(window,
  1315. event->xbutton.button - Button1 - 4,
  1316. GLFW_RELEASE,
  1317. mods);
  1318. }
  1319. return;
  1320. }
  1321. case EnterNotify:
  1322. {
  1323. // XEnterWindowEvent is XCrossingEvent
  1324. const int x = event->xcrossing.x;
  1325. const int y = event->xcrossing.y;
  1326. // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
  1327. // ignore the defined cursor for hidden cursor mode
  1328. if (window->cursorMode == GLFW_CURSOR_HIDDEN)
  1329. updateCursorImage(window);
  1330. _glfwInputCursorEnter(window, true);
  1331. _glfwInputCursorPos(window, x, y);
  1332. window->x11.lastCursorPosX = x;
  1333. window->x11.lastCursorPosY = y;
  1334. return;
  1335. }
  1336. case LeaveNotify:
  1337. {
  1338. _glfwInputCursorEnter(window, false);
  1339. return;
  1340. }
  1341. case MotionNotify:
  1342. {
  1343. const int x = event->xmotion.x;
  1344. const int y = event->xmotion.y;
  1345. if (x != window->x11.warpCursorPosX ||
  1346. y != window->x11.warpCursorPosY)
  1347. {
  1348. // The cursor was moved by something other than GLFW
  1349. if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1350. {
  1351. if (_glfw.x11.disabledCursorWindow != window)
  1352. return;
  1353. if (window->rawMouseMotion)
  1354. return;
  1355. const int dx = x - window->x11.lastCursorPosX;
  1356. const int dy = y - window->x11.lastCursorPosY;
  1357. _glfwInputCursorPos(window,
  1358. window->virtualCursorPosX + dx,
  1359. window->virtualCursorPosY + dy);
  1360. }
  1361. else
  1362. _glfwInputCursorPos(window, x, y);
  1363. }
  1364. window->x11.lastCursorPosX = x;
  1365. window->x11.lastCursorPosY = y;
  1366. return;
  1367. }
  1368. case ConfigureNotify:
  1369. {
  1370. if (event->xconfigure.width != window->x11.width ||
  1371. event->xconfigure.height != window->x11.height)
  1372. {
  1373. debug_rendering("Window resized to: %d %d from: %d %d\n", event->xconfigure.width, event->xconfigure.height, window->x11.width, window->x11.height);
  1374. _glfwInputFramebufferSize(window,
  1375. event->xconfigure.width,
  1376. event->xconfigure.height);
  1377. _glfwInputWindowSize(window,
  1378. event->xconfigure.width,
  1379. event->xconfigure.height);
  1380. window->x11.width = event->xconfigure.width;
  1381. window->x11.height = event->xconfigure.height;
  1382. }
  1383. int xpos = event->xconfigure.x;
  1384. int ypos = event->xconfigure.y;
  1385. // NOTE: ConfigureNotify events from the server are in local
  1386. // coordinates, so if we are reparented we need to translate
  1387. // the position into root (screen) coordinates
  1388. if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
  1389. {
  1390. Window dummy;
  1391. _glfwGrabErrorHandlerX11();
  1392. XTranslateCoordinates(_glfw.x11.display,
  1393. window->x11.parent,
  1394. _glfw.x11.root,
  1395. xpos, ypos,
  1396. &xpos, &ypos,
  1397. &dummy);
  1398. _glfwReleaseErrorHandlerX11();
  1399. if (_glfw.x11.errorCode != Success) {
  1400. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to translate ConfigureNotiy co-ords for reparented window");
  1401. return;
  1402. }
  1403. }
  1404. if (xpos != window->x11.xpos || ypos != window->x11.ypos)
  1405. {
  1406. debug_rendering("Window moved to: %d %d from: %d %d\n", xpos, ypos, window->x11.xpos, window->x11.xpos);
  1407. _glfwInputWindowPos(window, xpos, ypos);
  1408. window->x11.xpos = xpos;
  1409. window->x11.ypos = ypos;
  1410. }
  1411. return;
  1412. }
  1413. case ClientMessage:
  1414. {
  1415. // Custom client message, probably from the window manager
  1416. if (event->xclient.message_type == None)
  1417. return;
  1418. if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
  1419. {
  1420. const Atom protocol = event->xclient.data.l[0];
  1421. if (protocol == None)
  1422. return;
  1423. if (protocol == _glfw.x11.WM_DELETE_WINDOW)
  1424. {
  1425. // The window manager was asked to close the window, for
  1426. // example by the user pressing a 'close' window decoration
  1427. // button
  1428. _glfwInputWindowCloseRequest(window);
  1429. }
  1430. else if (protocol == _glfw.x11.NET_WM_PING)
  1431. {
  1432. // The window manager is pinging the application to ensure
  1433. // it's still responding to events
  1434. XEvent reply = *event;
  1435. reply.xclient.window = _glfw.x11.root;
  1436. XSendEvent(_glfw.x11.display, _glfw.x11.root,
  1437. False,
  1438. SubstructureNotifyMask | SubstructureRedirectMask,
  1439. &reply);
  1440. }
  1441. }
  1442. else if (event->xclient.message_type == _glfw.x11.XdndEnter)
  1443. {
  1444. // A drag operation has entered the window
  1445. unsigned long i, count;
  1446. Atom* formats = NULL;
  1447. const bool list = event->xclient.data.l[1] & 1;
  1448. _glfw.x11.xdnd.source = event->xclient.data.l[0];
  1449. _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
  1450. memset(_glfw.x11.xdnd.format, 0, sizeof(_glfw.x11.xdnd.format));
  1451. _glfw.x11.xdnd.format_priority = 0;
  1452. if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
  1453. return;
  1454. if (list)
  1455. {
  1456. count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
  1457. _glfw.x11.XdndTypeList,
  1458. XA_ATOM,
  1459. (unsigned char**) &formats);
  1460. }
  1461. else
  1462. {
  1463. count = 3;
  1464. formats = (Atom*) event->xclient.data.l + 2;
  1465. }
  1466. char **atom_names = calloc(count, sizeof(char*));
  1467. if (atom_names) {
  1468. get_atom_names(formats, count, atom_names);
  1469. for (i = 0; i < count; i++)
  1470. {
  1471. if (atom_names[i]) {
  1472. int prio = _glfwInputDrop(window, atom_names[i], NULL, 0);
  1473. if (prio > _glfw.x11.xdnd.format_priority) {
  1474. _glfw.x11.xdnd.format_priority = prio;
  1475. strncpy(_glfw.x11.xdnd.format, atom_names[i], arraysz(_glfw.x11.xdnd.format) - 1);
  1476. }
  1477. XFree(atom_names[i]);
  1478. }
  1479. }
  1480. free(atom_names);
  1481. }
  1482. if (list && formats)
  1483. XFree(formats);
  1484. }
  1485. else if (event->xclient.message_type == _glfw.x11.XdndDrop)
  1486. {
  1487. // The drag operation has finished by dropping on the window
  1488. Time time = CurrentTime;
  1489. if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
  1490. return;
  1491. if (_glfw.x11.xdnd.format_priority > 0)
  1492. {
  1493. if (_glfw.x11.xdnd.version >= 1)
  1494. time = event->xclient.data.l[2];
  1495. // Request the chosen format from the source window
  1496. XConvertSelection(_glfw.x11.display,
  1497. _glfw.x11.XdndSelection,
  1498. XInternAtom(_glfw.x11.display, _glfw.x11.xdnd.format, 0),
  1499. _glfw.x11.XdndSelection,
  1500. window->x11.handle,
  1501. time);
  1502. }
  1503. else if (_glfw.x11.xdnd.version >= 2)
  1504. {
  1505. XEvent reply = { ClientMessage };
  1506. reply.xclient.window = _glfw.x11.xdnd.source;
  1507. reply.xclient.message_type = _glfw.x11.XdndFinished;
  1508. reply.xclient.format = 32;
  1509. reply.xclient.data.l[0] = window->x11.handle;
  1510. reply.xclient.data.l[1] = 0; // The drag was rejected
  1511. reply.xclient.data.l[2] = None;
  1512. XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
  1513. False, NoEventMask, &reply);
  1514. XFlush(_glfw.x11.display);
  1515. }
  1516. }
  1517. else if (event->xclient.message_type == _glfw.x11.XdndPosition)
  1518. {
  1519. // The drag operation has moved over the window
  1520. const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
  1521. const int yabs = (event->xclient.data.l[2]) & 0xffff;
  1522. Window dummy;
  1523. int xpos = 0, ypos = 0;
  1524. if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
  1525. return;
  1526. _glfwGrabErrorHandlerX11();
  1527. XTranslateCoordinates(_glfw.x11.display,
  1528. _glfw.x11.root,
  1529. window->x11.handle,
  1530. xabs, yabs,
  1531. &xpos, &ypos,
  1532. &dummy);
  1533. _glfwReleaseErrorHandlerX11();
  1534. if (_glfw.x11.errorCode != Success)
  1535. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get DND event position");
  1536. _glfwInputCursorPos(window, xpos, ypos);
  1537. XEvent reply = { ClientMessage };
  1538. reply.xclient.window = _glfw.x11.xdnd.source;
  1539. reply.xclient.message_type = _glfw.x11.XdndStatus;
  1540. reply.xclient.format = 32;
  1541. reply.xclient.data.l[0] = window->x11.handle;
  1542. reply.xclient.data.l[2] = 0; // Specify an empty rectangle
  1543. reply.xclient.data.l[3] = 0;
  1544. if (_glfw.x11.xdnd.format_priority > 0)
  1545. {
  1546. // Reply that we are ready to copy the dragged data
  1547. reply.xclient.data.l[1] = 1; // Accept with no rectangle
  1548. if (_glfw.x11.xdnd.version >= 2)
  1549. reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
  1550. }
  1551. XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
  1552. False, NoEventMask, &reply);
  1553. XFlush(_glfw.x11.display);
  1554. }
  1555. return;
  1556. }
  1557. case SelectionNotify:
  1558. {
  1559. if (event->xselection.property == _glfw.x11.XdndSelection)
  1560. {
  1561. // The converted data from the drag operation has arrived
  1562. char* data;
  1563. const unsigned long result =
  1564. _glfwGetWindowPropertyX11(event->xselection.requestor,
  1565. event->xselection.property,
  1566. event->xselection.target,
  1567. (unsigned char**) &data);
  1568. if (result)
  1569. {
  1570. _glfwInputDrop(window, _glfw.x11.xdnd.format, data, result);
  1571. }
  1572. if (data)
  1573. XFree(data);
  1574. if (_glfw.x11.xdnd.version >= 2)
  1575. {
  1576. XEvent reply = { ClientMessage };
  1577. reply.xclient.window = _glfw.x11.xdnd.source;
  1578. reply.xclient.message_type = _glfw.x11.XdndFinished;
  1579. reply.xclient.format = 32;
  1580. reply.xclient.data.l[0] = window->x11.handle;
  1581. reply.xclient.data.l[1] = result;
  1582. reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
  1583. XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
  1584. False, NoEventMask, &reply);
  1585. XFlush(_glfw.x11.display);
  1586. }
  1587. }
  1588. return;
  1589. }
  1590. case FocusIn:
  1591. {
  1592. if (event->xfocus.mode == NotifyGrab ||
  1593. event->xfocus.mode == NotifyUngrab)
  1594. {
  1595. // Ignore focus events from popup indicator windows, window menu
  1596. // key chords and window dragging
  1597. return;
  1598. }
  1599. if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1600. disableCursor(window);
  1601. _glfwInputWindowFocus(window, true);
  1602. return;
  1603. }
  1604. case FocusOut:
  1605. {
  1606. if (event->xfocus.mode == NotifyGrab ||
  1607. event->xfocus.mode == NotifyUngrab)
  1608. {
  1609. // Ignore focus events from popup indicator windows, window menu
  1610. // key chords and window dragging
  1611. return;
  1612. }
  1613. if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1614. enableCursor(window);
  1615. if (window->monitor && window->autoIconify)
  1616. _glfwPlatformIconifyWindow(window);
  1617. _glfwInputWindowFocus(window, false);
  1618. return;
  1619. }
  1620. case Expose:
  1621. {
  1622. _glfwInputWindowDamage(window);
  1623. return;
  1624. }
  1625. case PropertyNotify:
  1626. {
  1627. if (event->xproperty.state != PropertyNewValue)
  1628. return;
  1629. if (event->xproperty.atom == _glfw.x11.WM_STATE)
  1630. {
  1631. const int state = getWindowState(window);
  1632. if (state != IconicState && state != NormalState)
  1633. return;
  1634. const bool iconified = (state == IconicState);
  1635. if (window->x11.iconified != iconified)
  1636. {
  1637. if (window->monitor)
  1638. {
  1639. if (iconified)
  1640. releaseMonitor(window);
  1641. else
  1642. acquireMonitor(window);
  1643. }
  1644. window->x11.iconified = iconified;
  1645. _glfwInputWindowIconify(window, iconified);
  1646. }
  1647. }
  1648. else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
  1649. {
  1650. const bool maximized = _glfwPlatformWindowMaximized(window);
  1651. if (window->x11.maximized != maximized)
  1652. {
  1653. window->x11.maximized = maximized;
  1654. int width, height;
  1655. _glfwPlatformGetWindowSize(window, &width, &height);
  1656. updateNormalHints(window, width, height);
  1657. _glfwInputWindowMaximize(window, maximized);
  1658. }
  1659. }
  1660. return;
  1661. }
  1662. case DestroyNotify:
  1663. return;
  1664. }
  1665. #undef UPDATE_KEYMAP_IF_NEEDED
  1666. }
  1667. //////////////////////////////////////////////////////////////////////////
  1668. ////// GLFW internal API //////
  1669. //////////////////////////////////////////////////////////////////////////
  1670. // Retrieve a single window property of the specified type
  1671. // Inspired by fghGetWindowProperty from freeglut
  1672. //
  1673. unsigned long _glfwGetWindowPropertyX11(Window window,
  1674. Atom property,
  1675. Atom type,
  1676. unsigned char** value)
  1677. {
  1678. Atom actualType;
  1679. int actualFormat;
  1680. unsigned long itemCount, bytesAfter;
  1681. XGetWindowProperty(_glfw.x11.display,
  1682. window,
  1683. property,
  1684. 0,
  1685. LONG_MAX,
  1686. False,
  1687. type,
  1688. &actualType,
  1689. &actualFormat,
  1690. &itemCount,
  1691. &bytesAfter,
  1692. value);
  1693. return itemCount;
  1694. }
  1695. bool _glfwIsVisualTransparentX11(Visual* visual)
  1696. {
  1697. if (!_glfw.x11.xrender.available)
  1698. return false;
  1699. XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
  1700. return pf && pf->direct.alphaMask;
  1701. }
  1702. // Push contents of our selection to clipboard manager
  1703. //
  1704. void _glfwPushSelectionToManagerX11(void)
  1705. {
  1706. XConvertSelection(_glfw.x11.display,
  1707. _glfw.x11.CLIPBOARD_MANAGER,
  1708. _glfw.x11.SAVE_TARGETS,
  1709. None,
  1710. _glfw.x11.helperWindowHandle,
  1711. CurrentTime);
  1712. for (;;)
  1713. {
  1714. XEvent event;
  1715. while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
  1716. {
  1717. switch (event.type)
  1718. {
  1719. case SelectionRequest:
  1720. handleSelectionRequest(&event);
  1721. break;
  1722. case SelectionClear:
  1723. handleSelectionClear(&event);
  1724. break;
  1725. case SelectionNotify:
  1726. {
  1727. if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
  1728. {
  1729. // This means one of two things; either the selection
  1730. // was not owned, which means there is no clipboard
  1731. // manager, or the transfer to the clipboard manager has
  1732. // completed
  1733. // In either case, it means we are done here
  1734. return;
  1735. }
  1736. break;
  1737. }
  1738. }
  1739. }
  1740. waitForX11Event(-1);
  1741. }
  1742. }
  1743. //////////////////////////////////////////////////////////////////////////
  1744. ////// GLFW platform API //////
  1745. //////////////////////////////////////////////////////////////////////////
  1746. int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, const GLFWLayerShellConfig *lsc)
  1747. {
  1748. Visual* visual = NULL;
  1749. int depth;
  1750. if (lsc) {
  1751. window->x11.layer_shell.is_active = true;
  1752. window->x11.layer_shell.config = *lsc;
  1753. } else window->x11.layer_shell.is_active = false;
  1754. if (ctxconfig->client != GLFW_NO_API)
  1755. {
  1756. if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
  1757. {
  1758. if (!_glfwInitGLX())
  1759. return false;
  1760. if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
  1761. return false;
  1762. }
  1763. else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
  1764. {
  1765. if (!_glfwInitEGL())
  1766. return false;
  1767. if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
  1768. return false;
  1769. }
  1770. else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
  1771. {
  1772. if (!_glfwInitOSMesa())
  1773. return false;
  1774. }
  1775. }
  1776. if (!visual)
  1777. {
  1778. visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
  1779. depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
  1780. }
  1781. if (!createNativeWindow(window, wndconfig, visual, depth))
  1782. return false;
  1783. if (ctxconfig->client != GLFW_NO_API)
  1784. {
  1785. if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
  1786. {
  1787. if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
  1788. return false;
  1789. }
  1790. else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
  1791. {
  1792. if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
  1793. return false;
  1794. }
  1795. else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
  1796. {
  1797. if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
  1798. return false;
  1799. }
  1800. }
  1801. if (window->monitor)
  1802. {
  1803. _glfwPlatformShowWindow(window);
  1804. updateWindowMode(window);
  1805. acquireMonitor(window);
  1806. }
  1807. XFlush(_glfw.x11.display);
  1808. return true;
  1809. }
  1810. void _glfwPlatformDestroyWindow(_GLFWwindow* window)
  1811. {
  1812. if (_glfw.x11.disabledCursorWindow == window)
  1813. _glfw.x11.disabledCursorWindow = NULL;
  1814. if (window->monitor)
  1815. releaseMonitor(window);
  1816. if (window->context.destroy)
  1817. window->context.destroy(window);
  1818. if (window->x11.handle)
  1819. {
  1820. XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
  1821. XUnmapWindow(_glfw.x11.display, window->x11.handle);
  1822. XDestroyWindow(_glfw.x11.display, window->x11.handle);
  1823. window->x11.handle = (Window) 0;
  1824. }
  1825. if (window->x11.colormap)
  1826. {
  1827. XFreeColormap(_glfw.x11.display, window->x11.colormap);
  1828. window->x11.colormap = (Colormap) 0;
  1829. }
  1830. XFlush(_glfw.x11.display);
  1831. }
  1832. const GLFWLayerShellConfig*
  1833. _glfwPlatformGetLayerShellConfig(_GLFWwindow *window) {
  1834. return &window->x11.layer_shell.config;
  1835. }
  1836. bool
  1837. _glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig *value) {
  1838. if (value) window->x11.layer_shell.config = *value;
  1839. WindowGeometry wg = calculate_layer_geometry(window);
  1840. update_wm_hints(window, &wg, NULL);
  1841. return false;
  1842. }
  1843. void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
  1844. {
  1845. #if defined(X_HAVE_UTF8_STRING)
  1846. Xutf8SetWMProperties(_glfw.x11.display,
  1847. window->x11.handle,
  1848. title, title,
  1849. NULL, 0,
  1850. NULL, NULL, NULL);
  1851. #else
  1852. // This may be a slightly better fallback than using XStoreName and
  1853. // XSetIconName, which always store their arguments using STRING
  1854. XmbSetWMProperties(_glfw.x11.display,
  1855. window->x11.handle,
  1856. title, title,
  1857. NULL, 0,
  1858. NULL, NULL, NULL);
  1859. #endif
  1860. XChangeProperty(_glfw.x11.display, window->x11.handle,
  1861. _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
  1862. PropModeReplace,
  1863. (unsigned char*) title, strlen(title));
  1864. XChangeProperty(_glfw.x11.display, window->x11.handle,
  1865. _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
  1866. PropModeReplace,
  1867. (unsigned char*) title, strlen(title));
  1868. XFlush(_glfw.x11.display);
  1869. }
  1870. void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
  1871. int count, const GLFWimage* images)
  1872. {
  1873. if (count)
  1874. {
  1875. int i, j, longCount = 0;
  1876. for (i = 0; i < count; i++)
  1877. longCount += 2 + images[i].width * images[i].height;
  1878. unsigned long* icon = calloc(longCount, sizeof(unsigned long));
  1879. unsigned long* target = icon;
  1880. for (i = 0; i < count; i++)
  1881. {
  1882. *target++ = images[i].width;
  1883. *target++ = images[i].height;
  1884. for (j = 0; j < images[i].width * images[i].height; j++)
  1885. {
  1886. unsigned char *p = images->pixels + j * 4;
  1887. const unsigned char r = *p++, g = *p++, b = *p++, a = *p++;
  1888. *target++ = a << 24 | (r << 16) | (g << 8) | b;
  1889. }
  1890. }
  1891. XChangeProperty(_glfw.x11.display, window->x11.handle,
  1892. _glfw.x11.NET_WM_ICON,
  1893. XA_CARDINAL, 32,
  1894. PropModeReplace,
  1895. (unsigned char*) icon,
  1896. longCount);
  1897. free(icon);
  1898. }
  1899. else
  1900. {
  1901. XDeleteProperty(_glfw.x11.display, window->x11.handle,
  1902. _glfw.x11.NET_WM_ICON);
  1903. }
  1904. XFlush(_glfw.x11.display);
  1905. }
  1906. void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
  1907. {
  1908. Window dummy;
  1909. int x = 0, y = 0;
  1910. _glfwGrabErrorHandlerX11();
  1911. XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
  1912. 0, 0, &x, &y, &dummy);
  1913. _glfwReleaseErrorHandlerX11();
  1914. if (_glfw.x11.errorCode != Success)
  1915. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get window position");
  1916. if (xpos)
  1917. *xpos = x;
  1918. if (ypos)
  1919. *ypos = y;
  1920. }
  1921. void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
  1922. {
  1923. // HACK: Explicitly setting PPosition to any value causes some WMs, notably
  1924. // Compiz and Metacity, to honor the position of unmapped windows
  1925. if (!_glfwPlatformWindowVisible(window))
  1926. {
  1927. long supplied;
  1928. XSizeHints* hints = XAllocSizeHints();
  1929. if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
  1930. {
  1931. hints->flags |= PPosition;
  1932. hints->x = hints->y = 0;
  1933. XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
  1934. }
  1935. XFree(hints);
  1936. }
  1937. XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
  1938. XFlush(_glfw.x11.display);
  1939. }
  1940. void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
  1941. {
  1942. XWindowAttributes attribs;
  1943. XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
  1944. if (width)
  1945. *width = attribs.width;
  1946. if (height)
  1947. *height = attribs.height;
  1948. }
  1949. void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
  1950. {
  1951. if (window->monitor)
  1952. {
  1953. if (window->monitor->window == window)
  1954. acquireMonitor(window);
  1955. }
  1956. else
  1957. {
  1958. if (!window->resizable)
  1959. updateNormalHints(window, width, height);
  1960. XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
  1961. }
  1962. XFlush(_glfw.x11.display);
  1963. }
  1964. void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
  1965. int minwidth UNUSED, int minheight UNUSED,
  1966. int maxwidth UNUSED, int maxheight UNUSED)
  1967. {
  1968. int width, height;
  1969. _glfwPlatformGetWindowSize(window, &width, &height);
  1970. updateNormalHints(window, width, height);
  1971. XFlush(_glfw.x11.display);
  1972. }
  1973. void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer UNUSED, int denom UNUSED)
  1974. {
  1975. int width, height;
  1976. _glfwPlatformGetWindowSize(window, &width, &height);
  1977. updateNormalHints(window, width, height);
  1978. XFlush(_glfw.x11.display);
  1979. }
  1980. void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr UNUSED, int heightincr UNUSED)
  1981. {
  1982. int width, height;
  1983. _glfwPlatformGetWindowSize(window, &width, &height);
  1984. updateNormalHints(window, width, height);
  1985. XFlush(_glfw.x11.display);
  1986. }
  1987. void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
  1988. {
  1989. _glfwPlatformGetWindowSize(window, width, height);
  1990. }
  1991. void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
  1992. int* left, int* top,
  1993. int* right, int* bottom)
  1994. {
  1995. long* extents = NULL;
  1996. if (window->monitor || !window->decorated)
  1997. return;
  1998. if (_glfw.x11.NET_FRAME_EXTENTS == None)
  1999. return;
  2000. if (!_glfwPlatformWindowVisible(window) &&
  2001. _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
  2002. {
  2003. XEvent event;
  2004. // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
  2005. // function before the window is mapped
  2006. sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
  2007. 0, 0, 0, 0, 0);
  2008. // HACK: Use a timeout because earlier versions of some window managers
  2009. // (at least Unity, Fluxbox and Xfwm) failed to send the reply
  2010. // They have been fixed but broken versions are still in the wild
  2011. // If you are affected by this and your window manager is NOT
  2012. // listed above, PLEASE report it to their and our issue trackers
  2013. while (!XCheckIfEvent(_glfw.x11.display,
  2014. &event,
  2015. isFrameExtentsEvent,
  2016. (XPointer) window))
  2017. {
  2018. if (!waitForX11Event(ms_to_monotonic_t(500ll)))
  2019. {
  2020. _glfwInputError(GLFW_PLATFORM_ERROR,
  2021. "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
  2022. return;
  2023. }
  2024. }
  2025. }
  2026. if (_glfwGetWindowPropertyX11(window->x11.handle,
  2027. _glfw.x11.NET_FRAME_EXTENTS,
  2028. XA_CARDINAL,
  2029. (unsigned char**) &extents) == 4)
  2030. {
  2031. if (left)
  2032. *left = extents[0];
  2033. if (top)
  2034. *top = extents[2];
  2035. if (right)
  2036. *right = extents[1];
  2037. if (bottom)
  2038. *bottom = extents[3];
  2039. }
  2040. if (extents)
  2041. XFree(extents);
  2042. }
  2043. void _glfwPlatformGetWindowContentScale(_GLFWwindow* window UNUSED,
  2044. float* xscale, float* yscale)
  2045. {
  2046. if (xscale)
  2047. *xscale = _glfw.x11.contentScaleX;
  2048. if (yscale)
  2049. *yscale = _glfw.x11.contentScaleY;
  2050. }
  2051. monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
  2052. {
  2053. return ms_to_monotonic_t(500ll);
  2054. }
  2055. void _glfwPlatformIconifyWindow(_GLFWwindow* window)
  2056. {
  2057. XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
  2058. XFlush(_glfw.x11.display);
  2059. }
  2060. void _glfwPlatformRestoreWindow(_GLFWwindow* window)
  2061. {
  2062. if (_glfwPlatformWindowIconified(window))
  2063. {
  2064. XMapWindow(_glfw.x11.display, window->x11.handle);
  2065. waitForVisibilityNotify(window);
  2066. }
  2067. else if (_glfwPlatformWindowVisible(window))
  2068. {
  2069. if (_glfw.x11.NET_WM_STATE &&
  2070. _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
  2071. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2072. {
  2073. sendEventToWM(window,
  2074. _glfw.x11.NET_WM_STATE,
  2075. _NET_WM_STATE_REMOVE,
  2076. _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
  2077. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
  2078. 1, 0);
  2079. }
  2080. }
  2081. XFlush(_glfw.x11.display);
  2082. }
  2083. void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
  2084. {
  2085. if (!_glfw.x11.NET_WM_STATE ||
  2086. !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
  2087. !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2088. {
  2089. return;
  2090. }
  2091. if (_glfwPlatformWindowVisible(window))
  2092. {
  2093. sendEventToWM(window,
  2094. _glfw.x11.NET_WM_STATE,
  2095. _NET_WM_STATE_ADD,
  2096. _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
  2097. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
  2098. 1, 0);
  2099. }
  2100. else
  2101. {
  2102. Atom* states = NULL;
  2103. unsigned long count =
  2104. _glfwGetWindowPropertyX11(window->x11.handle,
  2105. _glfw.x11.NET_WM_STATE,
  2106. XA_ATOM,
  2107. (unsigned char**) &states);
  2108. // NOTE: We don't check for failure as this property may not exist yet
  2109. // and that's fine (and we'll create it implicitly with append)
  2110. Atom missing[2] =
  2111. {
  2112. _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
  2113. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
  2114. };
  2115. unsigned long missingCount = 2;
  2116. for (unsigned long i = 0; i < count; i++)
  2117. {
  2118. for (unsigned long j = 0; j < missingCount; j++)
  2119. {
  2120. if (states[i] == missing[j])
  2121. {
  2122. missing[j] = missing[missingCount - 1];
  2123. missingCount--;
  2124. }
  2125. }
  2126. }
  2127. if (states)
  2128. XFree(states);
  2129. if (!missingCount)
  2130. return;
  2131. XChangeProperty(_glfw.x11.display, window->x11.handle,
  2132. _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  2133. PropModeAppend,
  2134. (unsigned char*) missing,
  2135. missingCount);
  2136. }
  2137. XFlush(_glfw.x11.display);
  2138. }
  2139. void _glfwPlatformShowWindow(_GLFWwindow* window)
  2140. {
  2141. if (_glfwPlatformWindowVisible(window))
  2142. return;
  2143. XMapWindow(_glfw.x11.display, window->x11.handle);
  2144. // without this floating window position is incorrect on KDE
  2145. if (window->x11.layer_shell.is_active) {
  2146. WindowGeometry wg = calculate_layer_geometry(window);
  2147. _glfwPlatformSetWindowPos(window, wg.x, wg.y);
  2148. }
  2149. waitForVisibilityNotify(window);
  2150. }
  2151. void _glfwPlatformHideWindow(_GLFWwindow* window)
  2152. {
  2153. XUnmapWindow(_glfw.x11.display, window->x11.handle);
  2154. XFlush(_glfw.x11.display);
  2155. }
  2156. void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
  2157. {
  2158. if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
  2159. return;
  2160. sendEventToWM(window,
  2161. _glfw.x11.NET_WM_STATE,
  2162. _NET_WM_STATE_ADD,
  2163. _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
  2164. 0, 1, 0);
  2165. }
  2166. int _glfwPlatformWindowBell(_GLFWwindow* window)
  2167. {
  2168. return XkbBell(_glfw.x11.display, window->x11.handle, 100, (Atom)0) ? true : false;
  2169. }
  2170. void _glfwPlatformFocusWindow(_GLFWwindow* window)
  2171. {
  2172. if (_glfw.x11.NET_ACTIVE_WINDOW)
  2173. sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
  2174. else if (_glfwPlatformWindowVisible(window))
  2175. {
  2176. XRaiseWindow(_glfw.x11.display, window->x11.handle);
  2177. XSetInputFocus(_glfw.x11.display, window->x11.handle,
  2178. RevertToParent, CurrentTime);
  2179. }
  2180. XFlush(_glfw.x11.display);
  2181. }
  2182. void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
  2183. _GLFWmonitor* monitor,
  2184. int xpos, int ypos,
  2185. int width, int height,
  2186. int refreshRate UNUSED)
  2187. {
  2188. if (window->monitor == monitor)
  2189. {
  2190. if (monitor)
  2191. {
  2192. if (monitor->window == window)
  2193. acquireMonitor(window);
  2194. }
  2195. else
  2196. {
  2197. if (!window->resizable)
  2198. updateNormalHints(window, width, height);
  2199. XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
  2200. xpos, ypos, width, height);
  2201. }
  2202. XFlush(_glfw.x11.display);
  2203. return;
  2204. }
  2205. if (window->monitor)
  2206. releaseMonitor(window);
  2207. _glfwInputWindowMonitor(window, monitor);
  2208. updateNormalHints(window, width, height);
  2209. if (window->monitor)
  2210. {
  2211. if (!_glfwPlatformWindowVisible(window))
  2212. {
  2213. XMapRaised(_glfw.x11.display, window->x11.handle);
  2214. waitForVisibilityNotify(window);
  2215. }
  2216. updateWindowMode(window);
  2217. acquireMonitor(window);
  2218. }
  2219. else
  2220. {
  2221. updateWindowMode(window);
  2222. XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
  2223. xpos, ypos, width, height);
  2224. }
  2225. XFlush(_glfw.x11.display);
  2226. }
  2227. int _glfwPlatformWindowFocused(_GLFWwindow* window)
  2228. {
  2229. Window focused;
  2230. int state;
  2231. XGetInputFocus(_glfw.x11.display, &focused, &state);
  2232. return window->x11.handle == focused;
  2233. }
  2234. int _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)
  2235. {
  2236. return false;
  2237. }
  2238. int _glfwPlatformWindowIconified(_GLFWwindow* window)
  2239. {
  2240. return getWindowState(window) == IconicState;
  2241. }
  2242. int _glfwPlatformWindowVisible(_GLFWwindow* window)
  2243. {
  2244. XWindowAttributes wa;
  2245. XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
  2246. return wa.map_state == IsViewable;
  2247. }
  2248. int _glfwPlatformWindowMaximized(_GLFWwindow* window)
  2249. {
  2250. Atom* states;
  2251. unsigned long i;
  2252. bool maximized = false;
  2253. if (!_glfw.x11.NET_WM_STATE ||
  2254. !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
  2255. !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2256. {
  2257. return maximized;
  2258. }
  2259. const unsigned long count =
  2260. _glfwGetWindowPropertyX11(window->x11.handle,
  2261. _glfw.x11.NET_WM_STATE,
  2262. XA_ATOM,
  2263. (unsigned char**) &states);
  2264. for (i = 0; i < count; i++)
  2265. {
  2266. if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
  2267. states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2268. {
  2269. maximized = true;
  2270. break;
  2271. }
  2272. }
  2273. if (states)
  2274. XFree(states);
  2275. return maximized;
  2276. }
  2277. int _glfwPlatformWindowHovered(_GLFWwindow* window)
  2278. {
  2279. Window w = _glfw.x11.root;
  2280. while (w)
  2281. {
  2282. Window root;
  2283. int rootX, rootY, childX, childY;
  2284. unsigned int mask;
  2285. _glfwGrabErrorHandlerX11();
  2286. const Bool result = XQueryPointer(_glfw.x11.display, w,
  2287. &root, &w, &rootX, &rootY,
  2288. &childX, &childY, &mask);
  2289. _glfwReleaseErrorHandlerX11();
  2290. if (_glfw.x11.errorCode == BadWindow)
  2291. w = _glfw.x11.root;
  2292. else if (!result)
  2293. return false;
  2294. else if (w == window->x11.handle)
  2295. return true;
  2296. }
  2297. return false;
  2298. }
  2299. int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
  2300. {
  2301. if (!window->x11.transparent)
  2302. return false;
  2303. return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
  2304. }
  2305. void _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled UNUSED)
  2306. {
  2307. int width, height;
  2308. _glfwPlatformGetWindowSize(window, &width, &height);
  2309. updateNormalHints(window, width, height);
  2310. }
  2311. void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)
  2312. {
  2313. struct
  2314. {
  2315. unsigned long flags;
  2316. unsigned long functions;
  2317. unsigned long decorations;
  2318. long input_mode;
  2319. unsigned long status;
  2320. } hints = {0};
  2321. hints.flags = MWM_HINTS_DECORATIONS;
  2322. hints.decorations = enabled ? MWM_DECOR_ALL : 0;
  2323. XChangeProperty(_glfw.x11.display, window->x11.handle,
  2324. _glfw.x11.MOTIF_WM_HINTS,
  2325. _glfw.x11.MOTIF_WM_HINTS, 32,
  2326. PropModeReplace,
  2327. (unsigned char*) &hints,
  2328. sizeof(hints) / sizeof(long));
  2329. }
  2330. void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
  2331. {
  2332. if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
  2333. return;
  2334. if (_glfwPlatformWindowVisible(window))
  2335. {
  2336. const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  2337. sendEventToWM(window,
  2338. _glfw.x11.NET_WM_STATE,
  2339. action,
  2340. _glfw.x11.NET_WM_STATE_ABOVE,
  2341. 0, 1, 0);
  2342. }
  2343. else
  2344. {
  2345. Atom* states = NULL;
  2346. unsigned long i, count;
  2347. count = _glfwGetWindowPropertyX11(window->x11.handle,
  2348. _glfw.x11.NET_WM_STATE,
  2349. XA_ATOM,
  2350. (unsigned char**) &states);
  2351. // NOTE: We don't check for failure as this property may not exist yet
  2352. // and that's fine (and we'll create it implicitly with append)
  2353. if (enabled)
  2354. {
  2355. for (i = 0; i < count; i++)
  2356. {
  2357. if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
  2358. break;
  2359. }
  2360. if (i < count)
  2361. return;
  2362. XChangeProperty(_glfw.x11.display, window->x11.handle,
  2363. _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  2364. PropModeAppend,
  2365. (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
  2366. 1);
  2367. }
  2368. else if (states)
  2369. {
  2370. for (i = 0; i < count; i++)
  2371. {
  2372. if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
  2373. break;
  2374. }
  2375. if (i == count)
  2376. return;
  2377. states[i] = states[count - 1];
  2378. count--;
  2379. XChangeProperty(_glfw.x11.display, window->x11.handle,
  2380. _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  2381. PropModeReplace, (unsigned char*) states, count);
  2382. }
  2383. if (states)
  2384. XFree(states);
  2385. }
  2386. XFlush(_glfw.x11.display);
  2387. }
  2388. void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled)
  2389. {
  2390. if (!_glfw.x11.xshape.available)
  2391. return;
  2392. if (enabled)
  2393. {
  2394. Region region = XCreateRegion();
  2395. XShapeCombineRegion(_glfw.x11.display, window->x11.handle,
  2396. ShapeInput, 0, 0, region, ShapeSet);
  2397. XDestroyRegion(region);
  2398. }
  2399. else
  2400. {
  2401. XShapeCombineMask(_glfw.x11.display, window->x11.handle,
  2402. ShapeInput, 0, 0, None, ShapeSet);
  2403. }
  2404. }
  2405. float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
  2406. {
  2407. float opacity = 1.f;
  2408. if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
  2409. {
  2410. CARD32* value = NULL;
  2411. if (_glfwGetWindowPropertyX11(window->x11.handle,
  2412. _glfw.x11.NET_WM_WINDOW_OPACITY,
  2413. XA_CARDINAL,
  2414. (unsigned char**) &value))
  2415. {
  2416. opacity = (float) (*value / (double) 0xffffffffu);
  2417. }
  2418. if (value)
  2419. XFree(value);
  2420. }
  2421. return opacity;
  2422. }
  2423. void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
  2424. {
  2425. const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
  2426. XChangeProperty(_glfw.x11.display, window->x11.handle,
  2427. _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
  2428. PropModeReplace, (unsigned char*) &value, 1);
  2429. }
  2430. static unsigned
  2431. dispatch_x11_queued_events(int num_events) {
  2432. unsigned dispatched = num_events > 0 ? num_events : 0;
  2433. while (num_events-- > 0) {
  2434. XEvent event;
  2435. XNextEvent(_glfw.x11.display, &event);
  2436. processEvent(&event);
  2437. }
  2438. return dispatched;
  2439. }
  2440. static unsigned
  2441. _glfwDispatchX11Events(void) {
  2442. _GLFWwindow* window;
  2443. unsigned dispatched = 0;
  2444. #if defined(__linux__)
  2445. if (_glfw.joysticksInitialized)
  2446. _glfwDetectJoystickConnectionLinux();
  2447. #endif
  2448. dispatched += dispatch_x11_queued_events(XEventsQueued(_glfw.x11.display, QueuedAfterFlush));
  2449. window = _glfw.x11.disabledCursorWindow;
  2450. if (window)
  2451. {
  2452. int width, height;
  2453. _glfwPlatformGetWindowSize(window, &width, &height);
  2454. // NOTE: Re-center the cursor only if it has moved since the last call,
  2455. // to avoid breaking glfwWaitEvents with MotionNotify
  2456. if (window->x11.lastCursorPosX != width / 2 ||
  2457. window->x11.lastCursorPosY != height / 2)
  2458. {
  2459. _glfwPlatformSetCursorPos(window, width / 2.f, height / 2.f);
  2460. }
  2461. }
  2462. XFlush(_glfw.x11.display);
  2463. // XFlush can cause events to be queued, we don't use QueuedAfterFlush here
  2464. // as something might have inserted events into the queue, but we want to guarantee
  2465. // a flush.
  2466. dispatched += dispatch_x11_queued_events(XEventsQueued(_glfw.x11.display, QueuedAlready));
  2467. return dispatched;
  2468. }
  2469. void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled)
  2470. {
  2471. if (!_glfw.x11.xi.available)
  2472. return;
  2473. if (_glfw.x11.disabledCursorWindow != window)
  2474. return;
  2475. if (enabled)
  2476. enableRawMouseMotion(window);
  2477. else
  2478. disableRawMouseMotion(window);
  2479. }
  2480. bool _glfwPlatformRawMouseMotionSupported(void)
  2481. {
  2482. return _glfw.x11.xi.available;
  2483. }
  2484. void _glfwPlatformPollEvents(void)
  2485. {
  2486. _glfwDispatchX11Events();
  2487. handleEvents(0);
  2488. }
  2489. void _glfwPlatformWaitEvents(void)
  2490. {
  2491. monotonic_t timeout = _glfwDispatchX11Events() ? 0 : -1;
  2492. handleEvents(timeout);
  2493. }
  2494. void _glfwPlatformWaitEventsTimeout(monotonic_t timeout)
  2495. {
  2496. if (_glfwDispatchX11Events()) timeout = 0;
  2497. handleEvents(timeout);
  2498. }
  2499. void _glfwPlatformPostEmptyEvent(void)
  2500. {
  2501. wakeupEventLoop(&_glfw.x11.eventLoopData);
  2502. }
  2503. void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
  2504. {
  2505. Window root, child;
  2506. int rootX, rootY, childX, childY;
  2507. unsigned int mask;
  2508. XQueryPointer(_glfw.x11.display, window->x11.handle,
  2509. &root, &child,
  2510. &rootX, &rootY, &childX, &childY,
  2511. &mask);
  2512. if (xpos)
  2513. *xpos = childX;
  2514. if (ypos)
  2515. *ypos = childY;
  2516. }
  2517. void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
  2518. {
  2519. // Store the new position so it can be recognized later
  2520. window->x11.warpCursorPosX = (int) x;
  2521. window->x11.warpCursorPosY = (int) y;
  2522. XWarpPointer(_glfw.x11.display, None, window->x11.handle,
  2523. 0,0,0,0, (int) x, (int) y);
  2524. XFlush(_glfw.x11.display);
  2525. }
  2526. void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
  2527. {
  2528. if (mode == GLFW_CURSOR_DISABLED)
  2529. {
  2530. if (_glfwPlatformWindowFocused(window))
  2531. disableCursor(window);
  2532. }
  2533. else if (_glfw.x11.disabledCursorWindow == window)
  2534. enableCursor(window);
  2535. else
  2536. updateCursorImage(window);
  2537. XFlush(_glfw.x11.display);
  2538. }
  2539. const char* _glfwPlatformGetNativeKeyName(int native_key)
  2540. {
  2541. return glfw_xkb_keysym_name(native_key);
  2542. }
  2543. int _glfwPlatformGetNativeKeyForKey(uint32_t key)
  2544. {
  2545. return glfw_xkb_sym_for_key(key);
  2546. }
  2547. int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
  2548. const GLFWimage* image,
  2549. int xhot, int yhot, int count UNUSED)
  2550. {
  2551. cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
  2552. if (!cursor->x11.handle)
  2553. return false;
  2554. return true;
  2555. }
  2556. static int
  2557. set_cursor_from_font(_GLFWcursor* cursor, int native) {
  2558. cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
  2559. if (!cursor->x11.handle) {
  2560. _glfwInputError(GLFW_PLATFORM_ERROR,
  2561. "X11: Failed to create standard cursor");
  2562. return false;
  2563. }
  2564. return true;
  2565. }
  2566. static bool
  2567. try_cursor_names(_GLFWcursor *cursor, int arg_count, ...) {
  2568. va_list ap;
  2569. va_start(ap, arg_count);
  2570. const char *first_name = "";
  2571. for (int i = 0; i < arg_count; i++) {
  2572. const char *name = va_arg(ap, const char *);
  2573. first_name = name;
  2574. cursor->x11.handle = XcursorLibraryLoadCursor(_glfw.x11.display, name);
  2575. if (cursor->x11.handle) break;
  2576. }
  2577. va_end(ap);
  2578. if (!cursor->x11.handle) {
  2579. _glfwInputError(GLFW_PLATFORM_ERROR,
  2580. "X11: Failed to load standard cursor: %s with %d aliases via Xcursor library", first_name, arg_count);
  2581. return false;
  2582. }
  2583. return true;
  2584. }
  2585. int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape)
  2586. {
  2587. switch(shape) {
  2588. /* start glfw to xc mapping (auto generated by gen-key-constants.py do not edit) */
  2589. case GLFW_DEFAULT_CURSOR: return set_cursor_from_font(cursor, XC_left_ptr);
  2590. case GLFW_TEXT_CURSOR: return set_cursor_from_font(cursor, XC_xterm);
  2591. case GLFW_POINTER_CURSOR: return set_cursor_from_font(cursor, XC_hand2);
  2592. case GLFW_HELP_CURSOR: return set_cursor_from_font(cursor, XC_question_arrow);
  2593. case GLFW_WAIT_CURSOR: return set_cursor_from_font(cursor, XC_clock);
  2594. case GLFW_PROGRESS_CURSOR: return try_cursor_names(cursor, 3, "progress", "half-busy", "left_ptr_watch");
  2595. case GLFW_CROSSHAIR_CURSOR: return set_cursor_from_font(cursor, XC_tcross);
  2596. case GLFW_CELL_CURSOR: return set_cursor_from_font(cursor, XC_plus);
  2597. case GLFW_VERTICAL_TEXT_CURSOR: return try_cursor_names(cursor, 1, "vertical-text");
  2598. case GLFW_MOVE_CURSOR: return set_cursor_from_font(cursor, XC_fleur);
  2599. case GLFW_E_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_right_side);
  2600. case GLFW_NE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_right_corner);
  2601. case GLFW_NW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_left_corner);
  2602. case GLFW_N_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_side);
  2603. case GLFW_SE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_right_corner);
  2604. case GLFW_SW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_left_corner);
  2605. case GLFW_S_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_side);
  2606. case GLFW_W_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_left_side);
  2607. case GLFW_EW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_h_double_arrow);
  2608. case GLFW_NS_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_v_double_arrow);
  2609. case GLFW_NESW_RESIZE_CURSOR: return try_cursor_names(cursor, 3, "nesw-resize", "size_bdiag", "size-bdiag");
  2610. case GLFW_NWSE_RESIZE_CURSOR: return try_cursor_names(cursor, 3, "nwse-resize", "size_fdiag", "size-fdiag");
  2611. case GLFW_ZOOM_IN_CURSOR: return try_cursor_names(cursor, 2, "zoom-in", "zoom_in");
  2612. case GLFW_ZOOM_OUT_CURSOR: return try_cursor_names(cursor, 2, "zoom-out", "zoom_out");
  2613. case GLFW_ALIAS_CURSOR: return try_cursor_names(cursor, 1, "dnd-link");
  2614. case GLFW_COPY_CURSOR: return try_cursor_names(cursor, 1, "dnd-copy");
  2615. case GLFW_NOT_ALLOWED_CURSOR: return try_cursor_names(cursor, 3, "not-allowed", "forbidden", "crossed_circle");
  2616. case GLFW_NO_DROP_CURSOR: return try_cursor_names(cursor, 2, "no-drop", "dnd-no-drop");
  2617. case GLFW_GRAB_CURSOR: return set_cursor_from_font(cursor, XC_hand1);
  2618. case GLFW_GRABBING_CURSOR: return try_cursor_names(cursor, 3, "grabbing", "closedhand", "dnd-none");
  2619. /* end glfw to xc mapping */
  2620. case GLFW_INVALID_CURSOR: return false;
  2621. }
  2622. return false;
  2623. }
  2624. void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
  2625. {
  2626. if (cursor->x11.handle)
  2627. XFreeCursor(_glfw.x11.display, cursor->x11.handle);
  2628. }
  2629. void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor UNUSED)
  2630. {
  2631. if (window->cursorMode == GLFW_CURSOR_NORMAL)
  2632. {
  2633. updateCursorImage(window);
  2634. XFlush(_glfw.x11.display);
  2635. }
  2636. }
  2637. static MimeAtom atom_for_mime(const char *mime) {
  2638. for (size_t i = 0; i < _glfw.x11.mime_atoms.sz; i++) {
  2639. MimeAtom ma = _glfw.x11.mime_atoms.array[i];
  2640. if (strcmp(ma.mime, mime) == 0) {
  2641. return ma;
  2642. }
  2643. }
  2644. MimeAtom ma = {.mime=_glfw_strdup(mime), .atom=XInternAtom(_glfw.x11.display, mime, 0)};
  2645. if (_glfw.x11.mime_atoms.capacity < _glfw.x11.mime_atoms.sz + 1) {
  2646. _glfw.x11.mime_atoms.capacity += 32;
  2647. _glfw.x11.mime_atoms.array = realloc(_glfw.x11.mime_atoms.array, _glfw.x11.mime_atoms.capacity * sizeof(_glfw.x11.mime_atoms.array[0]));
  2648. }
  2649. _glfw.x11.mime_atoms.array[_glfw.x11.mime_atoms.sz++] = ma;
  2650. return ma;
  2651. }
  2652. void _glfwPlatformSetClipboard(GLFWClipboardType t) {
  2653. Atom which = None;
  2654. _GLFWClipboardData *cd = NULL;
  2655. AtomArray *aa = NULL;
  2656. switch (t) {
  2657. case GLFW_CLIPBOARD: which = _glfw.x11.CLIPBOARD; cd = &_glfw.clipboard; aa = &_glfw.x11.clipboard_atoms; break;
  2658. case GLFW_PRIMARY_SELECTION: which = _glfw.x11.PRIMARY; cd = &_glfw.primary; aa = &_glfw.x11.primary_atoms; break;
  2659. }
  2660. XSetSelectionOwner(_glfw.x11.display, which, _glfw.x11.helperWindowHandle, CurrentTime);
  2661. if (XGetSelectionOwner(_glfw.x11.display, which) != _glfw.x11.helperWindowHandle) {
  2662. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to become owner of clipboard selection");
  2663. }
  2664. if (aa->capacity < cd->num_mime_types + 32) {
  2665. aa->capacity = cd->num_mime_types + 32;
  2666. aa->array = reallocarray(aa->array, aa->capacity, sizeof(aa->array[0]));
  2667. }
  2668. aa->sz = 0;
  2669. for (size_t i = 0; i < cd->num_mime_types; i++) {
  2670. MimeAtom *a = aa->array + aa->sz++;
  2671. *a = atom_for_mime(cd->mime_types[i]);
  2672. if (strcmp(cd->mime_types[i], "text/plain") == 0) {
  2673. a = aa->array + aa->sz++;
  2674. a->atom = _glfw.x11.UTF8_STRING;
  2675. a->mime = "text/plain";
  2676. }
  2677. }
  2678. }
  2679. typedef struct chunked_writer {
  2680. char *buf; size_t sz, cap;
  2681. bool is_self_offer;
  2682. } chunked_writer;
  2683. static bool
  2684. write_chunk(void *object, const char *data, size_t sz) {
  2685. chunked_writer *cw = object;
  2686. if (data) {
  2687. if (cw->cap < cw->sz + sz) {
  2688. cw->cap = MAX(cw->cap * 2, cw->sz + 8*sz);
  2689. cw->buf = realloc(cw->buf, cw->cap * sizeof(cw->buf[0]));
  2690. }
  2691. memcpy(cw->buf + cw->sz, data, sz);
  2692. cw->sz += sz;
  2693. } else if (sz == 1) cw->is_self_offer = true;
  2694. return true;
  2695. }
  2696. static void
  2697. get_available_mime_types(Atom which_clipboard, GLFWclipboardwritedatafun write_data, void *object) {
  2698. chunked_writer cw = {0};
  2699. getSelectionString(which_clipboard, &_glfw.x11.TARGETS, 1, write_chunk, &cw, false);
  2700. if (cw.is_self_offer) {
  2701. write_data(object, NULL, 1);
  2702. return;
  2703. }
  2704. size_t count = 0;
  2705. bool ok = true;
  2706. if (cw.buf) {
  2707. Atom *atoms = (Atom*)cw.buf;
  2708. count = cw.sz / sizeof(Atom);
  2709. char **names = calloc(count, sizeof(char*));
  2710. get_atom_names(atoms, count, names);
  2711. for (size_t i = 0; i < count; i++) {
  2712. if (strchr(names[i], '/')) {
  2713. if (ok) ok = write_data(object, names[i], strlen(names[i]));
  2714. } else {
  2715. if (atoms[i] == _glfw.x11.UTF8_STRING || atoms[i] == XA_STRING) {
  2716. if (ok) ok = write_data(object, "text/plain", strlen("text/plain"));
  2717. }
  2718. }
  2719. XFree(names[i]);
  2720. }
  2721. free(cw.buf);
  2722. free(names);
  2723. }
  2724. }
  2725. void
  2726. _glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object) {
  2727. Atom atoms[4], which = clipboard_type == GLFW_PRIMARY_SELECTION ? _glfw.x11.PRIMARY : _glfw.x11.CLIPBOARD;
  2728. if (mime_type == NULL) {
  2729. get_available_mime_types(which, write_data, object);
  2730. return;
  2731. }
  2732. size_t count = 0;
  2733. if (strcmp(mime_type, "text/plain") == 0) {
  2734. // 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
  2735. // 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
  2736. atoms[count++] = _glfw.x11.UTF8_STRING;
  2737. // we need to do this because GTK/GNOME is moronic they convert text/plain to DOS line endings, see
  2738. // https://gitlab.gnome.org/GNOME/gtk/-/issues/2307
  2739. atoms[count++] = atom_for_mime("text/plain;charset=utf-8").atom;
  2740. atoms[count++] = atom_for_mime("text/plain").atom;
  2741. atoms[count++] = XA_STRING;
  2742. } else {
  2743. atoms[count++] = atom_for_mime(mime_type).atom;
  2744. }
  2745. getSelectionString(which, atoms, count, write_data, object, true);
  2746. }
  2747. EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)
  2748. {
  2749. if (_glfw.egl.ANGLE_platform_angle)
  2750. {
  2751. int type = 0;
  2752. if (_glfw.egl.ANGLE_platform_angle_opengl)
  2753. {
  2754. if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
  2755. type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
  2756. }
  2757. if (_glfw.egl.ANGLE_platform_angle_vulkan)
  2758. {
  2759. if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
  2760. type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
  2761. }
  2762. if (type)
  2763. {
  2764. *attribs = calloc(5, sizeof(EGLint));
  2765. (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
  2766. (*attribs)[1] = type;
  2767. (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE;
  2768. (*attribs)[3] = EGL_PLATFORM_X11_EXT;
  2769. (*attribs)[4] = EGL_NONE;
  2770. return EGL_PLATFORM_ANGLE_ANGLE;
  2771. }
  2772. }
  2773. if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_x11)
  2774. return EGL_PLATFORM_X11_EXT;
  2775. return 0;
  2776. }
  2777. EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)
  2778. {
  2779. return _glfw.x11.display;
  2780. }
  2781. EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)
  2782. {
  2783. if (_glfw.egl.platform)
  2784. return &window->x11.handle;
  2785. else
  2786. return (EGLNativeWindowType) window->x11.handle;
  2787. }
  2788. void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
  2789. {
  2790. if (!_glfw.vk.KHR_surface)
  2791. return;
  2792. if (!_glfw.vk.KHR_xcb_surface)
  2793. {
  2794. if (!_glfw.vk.KHR_xlib_surface)
  2795. return;
  2796. }
  2797. extensions[0] = "VK_KHR_surface";
  2798. // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
  2799. // not correctly implementing VK_KHR_xlib_surface
  2800. if (_glfw.vk.KHR_xcb_surface)
  2801. extensions[1] = "VK_KHR_xcb_surface";
  2802. else
  2803. extensions[1] = "VK_KHR_xlib_surface";
  2804. }
  2805. int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
  2806. VkPhysicalDevice device,
  2807. uint32_t queuefamily)
  2808. {
  2809. VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
  2810. _glfw.x11.screen));
  2811. if (_glfw.vk.KHR_xcb_surface)
  2812. {
  2813. PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
  2814. vkGetPhysicalDeviceXcbPresentationSupportKHR =
  2815. (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
  2816. vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
  2817. if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
  2818. {
  2819. _glfwInputError(GLFW_API_UNAVAILABLE,
  2820. "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
  2821. return false;
  2822. }
  2823. xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
  2824. if (!connection)
  2825. {
  2826. _glfwInputError(GLFW_PLATFORM_ERROR,
  2827. "X11: Failed to retrieve XCB connection");
  2828. return false;
  2829. }
  2830. return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
  2831. queuefamily,
  2832. connection,
  2833. visualID);
  2834. }
  2835. else
  2836. {
  2837. PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
  2838. vkGetPhysicalDeviceXlibPresentationSupportKHR =
  2839. (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
  2840. vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
  2841. if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
  2842. {
  2843. _glfwInputError(GLFW_API_UNAVAILABLE,
  2844. "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
  2845. return false;
  2846. }
  2847. return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
  2848. queuefamily,
  2849. _glfw.x11.display,
  2850. visualID);
  2851. }
  2852. }
  2853. VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
  2854. _GLFWwindow* window,
  2855. const VkAllocationCallbacks* allocator,
  2856. VkSurfaceKHR* surface)
  2857. {
  2858. if (_glfw.vk.KHR_xcb_surface)
  2859. {
  2860. VkResult err;
  2861. VkXcbSurfaceCreateInfoKHR sci;
  2862. PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
  2863. xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
  2864. if (!connection)
  2865. {
  2866. _glfwInputError(GLFW_PLATFORM_ERROR,
  2867. "X11: Failed to retrieve XCB connection");
  2868. return VK_ERROR_EXTENSION_NOT_PRESENT;
  2869. }
  2870. vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
  2871. vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
  2872. if (!vkCreateXcbSurfaceKHR)
  2873. {
  2874. _glfwInputError(GLFW_API_UNAVAILABLE,
  2875. "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
  2876. return VK_ERROR_EXTENSION_NOT_PRESENT;
  2877. }
  2878. memset(&sci, 0, sizeof(sci));
  2879. sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
  2880. sci.connection = connection;
  2881. sci.window = window->x11.handle;
  2882. err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
  2883. if (err)
  2884. {
  2885. _glfwInputError(GLFW_PLATFORM_ERROR,
  2886. "X11: Failed to create Vulkan XCB surface: %s",
  2887. _glfwGetVulkanResultString(err));
  2888. }
  2889. return err;
  2890. }
  2891. else
  2892. {
  2893. VkResult err;
  2894. VkXlibSurfaceCreateInfoKHR sci;
  2895. PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
  2896. vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
  2897. vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
  2898. if (!vkCreateXlibSurfaceKHR)
  2899. {
  2900. _glfwInputError(GLFW_API_UNAVAILABLE,
  2901. "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
  2902. return VK_ERROR_EXTENSION_NOT_PRESENT;
  2903. }
  2904. memset(&sci, 0, sizeof(sci));
  2905. sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
  2906. sci.dpy = _glfw.x11.display;
  2907. sci.window = window->x11.handle;
  2908. err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
  2909. if (err)
  2910. {
  2911. _glfwInputError(GLFW_PLATFORM_ERROR,
  2912. "X11: Failed to create Vulkan X11 surface: %s",
  2913. _glfwGetVulkanResultString(err));
  2914. }
  2915. return err;
  2916. }
  2917. }
  2918. void
  2919. _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
  2920. glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, ev);
  2921. }
  2922. int
  2923. _glfwPlatformSetWindowBlur(_GLFWwindow *window, int blur_radius) {
  2924. if (_glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION == None) {
  2925. _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION = XInternAtom(_glfw.x11.display, "_KDE_NET_WM_BLUR_BEHIND_REGION", False);
  2926. }
  2927. if (_glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION != None) {
  2928. uint32_t data = 0;
  2929. if (blur_radius > 0) {
  2930. XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION,
  2931. XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &data, 1);
  2932. } else {
  2933. XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION);
  2934. }
  2935. return 1;
  2936. }
  2937. return 0;
  2938. }
  2939. //////////////////////////////////////////////////////////////////////////
  2940. ////// GLFW native API //////
  2941. //////////////////////////////////////////////////////////////////////////
  2942. GLFWAPI Display* glfwGetX11Display(void)
  2943. {
  2944. _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
  2945. return _glfw.x11.display;
  2946. }
  2947. GLFWAPI unsigned long glfwGetX11Window(GLFWwindow* handle)
  2948. {
  2949. _GLFWwindow* window = (_GLFWwindow*) handle;
  2950. _GLFW_REQUIRE_INIT_OR_RETURN(None);
  2951. return window->x11.handle;
  2952. }
  2953. GLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) {
  2954. return glfw_xkb_keysym_from_name(keyName, caseSensitive);
  2955. }
  2956. GLFWAPI unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data) {
  2957. return glfw_dbus_send_user_notification(n, callback, data);
  2958. }
  2959. GLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) {
  2960. glfw_dbus_set_user_notification_activated_handler(handler);
  2961. }
  2962. GLFWAPI int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)
  2963. {
  2964. _GLFW_REQUIRE_INIT_OR_RETURN(0);
  2965. _GLFWwindow* window = (_GLFWwindow*) handle;
  2966. return XSetCommand(_glfw.x11.display, window->x11.handle, argv, argc);
  2967. }