x11_init.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  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_desktop_settings.h"
  33. #include <X11/Xresource.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <limits.h>
  37. #include <stdio.h>
  38. #include <locale.h>
  39. #include <fcntl.h>
  40. #include <unistd.h>
  41. // Return the atom ID only if it is listed in the specified array
  42. //
  43. static Atom getAtomIfSupported(Atom* supportedAtoms,
  44. unsigned long atomCount,
  45. const char* atomName)
  46. {
  47. const Atom atom = XInternAtom(_glfw.x11.display, atomName, False);
  48. for (unsigned long i = 0; i < atomCount; i++)
  49. {
  50. if (supportedAtoms[i] == atom)
  51. return atom;
  52. }
  53. return None;
  54. }
  55. // Check whether the running window manager is EWMH-compliant
  56. //
  57. static void detectEWMH(void)
  58. {
  59. // First we read the _NET_SUPPORTING_WM_CHECK property on the root window
  60. Window* windowFromRoot = NULL;
  61. if (!_glfwGetWindowPropertyX11(_glfw.x11.root,
  62. _glfw.x11.NET_SUPPORTING_WM_CHECK,
  63. XA_WINDOW,
  64. (unsigned char**) &windowFromRoot))
  65. {
  66. return;
  67. }
  68. _glfwGrabErrorHandlerX11();
  69. // If it exists, it should be the XID of a top-level window
  70. // Then we look for the same property on that window
  71. Window* windowFromChild = NULL;
  72. if (!_glfwGetWindowPropertyX11(*windowFromRoot,
  73. _glfw.x11.NET_SUPPORTING_WM_CHECK,
  74. XA_WINDOW,
  75. (unsigned char**) &windowFromChild))
  76. {
  77. XFree(windowFromRoot);
  78. return;
  79. }
  80. _glfwReleaseErrorHandlerX11();
  81. // If the property exists, it should contain the XID of the window
  82. if (*windowFromRoot != *windowFromChild)
  83. {
  84. XFree(windowFromRoot);
  85. XFree(windowFromChild);
  86. return;
  87. }
  88. XFree(windowFromRoot);
  89. XFree(windowFromChild);
  90. // We are now fairly sure that an EWMH-compliant WM is currently running
  91. // We can now start querying the WM about what features it supports by
  92. // looking in the _NET_SUPPORTED property on the root window
  93. // It should contain a list of supported EWMH protocol and state atoms
  94. Atom* supportedAtoms = NULL;
  95. const unsigned long atomCount =
  96. _glfwGetWindowPropertyX11(_glfw.x11.root,
  97. _glfw.x11.NET_SUPPORTED,
  98. XA_ATOM,
  99. (unsigned char**) &supportedAtoms);
  100. if (!supportedAtoms)
  101. return;
  102. // See which of the atoms we support that are supported by the WM
  103. _glfw.x11.NET_WM_STATE =
  104. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE");
  105. _glfw.x11.NET_WM_STATE_ABOVE =
  106. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE");
  107. _glfw.x11.NET_WM_STATE_FULLSCREEN =
  108. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN");
  109. _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT =
  110. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT");
  111. _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ =
  112. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ");
  113. _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION =
  114. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION");
  115. _glfw.x11.NET_WM_FULLSCREEN_MONITORS =
  116. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS");
  117. _glfw.x11.NET_WM_WINDOW_TYPE =
  118. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE");
  119. _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL =
  120. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL");
  121. _glfw.x11.NET_WM_WINDOW_TYPE_DOCK =
  122. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_DOCK");
  123. _glfw.x11.NET_WORKAREA =
  124. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WORKAREA");
  125. _glfw.x11.NET_CURRENT_DESKTOP =
  126. getAtomIfSupported(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP");
  127. _glfw.x11.NET_ACTIVE_WINDOW =
  128. getAtomIfSupported(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW");
  129. _glfw.x11.NET_FRAME_EXTENTS =
  130. getAtomIfSupported(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS");
  131. _glfw.x11.NET_REQUEST_FRAME_EXTENTS =
  132. getAtomIfSupported(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS");
  133. _glfw.x11.NET_WM_STRUT_PARTIAL =
  134. getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STRUT_PARTIAL");
  135. XFree(supportedAtoms);
  136. }
  137. // Look for and initialize supported X11 extensions
  138. //
  139. static bool initExtensions(void)
  140. {
  141. _glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so.1");
  142. if (_glfw.x11.vidmode.handle)
  143. {
  144. glfw_dlsym(_glfw.x11.vidmode.QueryExtension, _glfw.x11.vidmode.handle, "XF86VidModeQueryExtension");
  145. glfw_dlsym(_glfw.x11.vidmode.GetGammaRamp, _glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp");
  146. glfw_dlsym(_glfw.x11.vidmode.SetGammaRamp, _glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp");
  147. glfw_dlsym(_glfw.x11.vidmode.GetGammaRampSize, _glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize");
  148. _glfw.x11.vidmode.available =
  149. XF86VidModeQueryExtension(_glfw.x11.display,
  150. &_glfw.x11.vidmode.eventBase,
  151. &_glfw.x11.vidmode.errorBase);
  152. }
  153. #if defined(__CYGWIN__)
  154. _glfw.x11.xi.handle = _glfw_dlopen("libXi-6.so");
  155. #else
  156. _glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6");
  157. #endif
  158. if (_glfw.x11.xi.handle)
  159. {
  160. glfw_dlsym(_glfw.x11.xi.QueryVersion, _glfw.x11.xi.handle, "XIQueryVersion");
  161. glfw_dlsym(_glfw.x11.xi.SelectEvents, _glfw.x11.xi.handle, "XISelectEvents");
  162. if (XQueryExtension(_glfw.x11.display,
  163. "XInputExtension",
  164. &_glfw.x11.xi.majorOpcode,
  165. &_glfw.x11.xi.eventBase,
  166. &_glfw.x11.xi.errorBase))
  167. {
  168. _glfw.x11.xi.major = 2;
  169. _glfw.x11.xi.minor = 0;
  170. if (XIQueryVersion(_glfw.x11.display,
  171. &_glfw.x11.xi.major,
  172. &_glfw.x11.xi.minor) == Success)
  173. {
  174. _glfw.x11.xi.available = true;
  175. }
  176. }
  177. }
  178. #if defined(__CYGWIN__)
  179. _glfw.x11.randr.handle = _glfw_dlopen("libXrandr-2.so");
  180. #else
  181. _glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2");
  182. #endif
  183. if (_glfw.x11.randr.handle)
  184. {
  185. glfw_dlsym(_glfw.x11.randr.AllocGamma, _glfw.x11.randr.handle, "XRRAllocGamma");
  186. glfw_dlsym(_glfw.x11.randr.FreeGamma, _glfw.x11.randr.handle, "XRRFreeGamma");
  187. glfw_dlsym(_glfw.x11.randr.FreeCrtcInfo, _glfw.x11.randr.handle, "XRRFreeCrtcInfo");
  188. glfw_dlsym(_glfw.x11.randr.FreeGamma, _glfw.x11.randr.handle, "XRRFreeGamma");
  189. glfw_dlsym(_glfw.x11.randr.FreeOutputInfo, _glfw.x11.randr.handle, "XRRFreeOutputInfo");
  190. glfw_dlsym(_glfw.x11.randr.FreeScreenResources, _glfw.x11.randr.handle, "XRRFreeScreenResources");
  191. glfw_dlsym(_glfw.x11.randr.GetCrtcGamma, _glfw.x11.randr.handle, "XRRGetCrtcGamma");
  192. glfw_dlsym(_glfw.x11.randr.GetCrtcGammaSize, _glfw.x11.randr.handle, "XRRGetCrtcGammaSize");
  193. glfw_dlsym(_glfw.x11.randr.GetCrtcInfo, _glfw.x11.randr.handle, "XRRGetCrtcInfo");
  194. glfw_dlsym(_glfw.x11.randr.GetOutputInfo, _glfw.x11.randr.handle, "XRRGetOutputInfo");
  195. glfw_dlsym(_glfw.x11.randr.GetOutputPrimary, _glfw.x11.randr.handle, "XRRGetOutputPrimary");
  196. glfw_dlsym(_glfw.x11.randr.GetScreenResourcesCurrent, _glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent");
  197. glfw_dlsym(_glfw.x11.randr.QueryExtension, _glfw.x11.randr.handle, "XRRQueryExtension");
  198. glfw_dlsym(_glfw.x11.randr.QueryVersion, _glfw.x11.randr.handle, "XRRQueryVersion");
  199. glfw_dlsym(_glfw.x11.randr.SelectInput, _glfw.x11.randr.handle, "XRRSelectInput");
  200. glfw_dlsym(_glfw.x11.randr.SetCrtcConfig, _glfw.x11.randr.handle, "XRRSetCrtcConfig");
  201. glfw_dlsym(_glfw.x11.randr.SetCrtcGamma, _glfw.x11.randr.handle, "XRRSetCrtcGamma");
  202. glfw_dlsym(_glfw.x11.randr.UpdateConfiguration, _glfw.x11.randr.handle, "XRRUpdateConfiguration");
  203. if (XRRQueryExtension(_glfw.x11.display,
  204. &_glfw.x11.randr.eventBase,
  205. &_glfw.x11.randr.errorBase))
  206. {
  207. if (XRRQueryVersion(_glfw.x11.display,
  208. &_glfw.x11.randr.major,
  209. &_glfw.x11.randr.minor))
  210. {
  211. // The GLFW RandR path requires at least version 1.3
  212. if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3)
  213. _glfw.x11.randr.available = true;
  214. }
  215. else
  216. {
  217. _glfwInputError(GLFW_PLATFORM_ERROR,
  218. "X11: Failed to query RandR version");
  219. }
  220. }
  221. }
  222. if (_glfw.x11.randr.available)
  223. {
  224. XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display,
  225. _glfw.x11.root);
  226. if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0]))
  227. {
  228. // This is likely an older Nvidia driver with broken gamma support
  229. // Flag it as useless and fall back to xf86vm gamma, if available
  230. _glfw.x11.randr.gammaBroken = true;
  231. }
  232. if (!sr->ncrtc)
  233. {
  234. // A system without CRTCs is likely a system with broken RandR
  235. // Disable the RandR monitor path and fall back to core functions
  236. _glfw.x11.randr.monitorBroken = true;
  237. }
  238. XRRFreeScreenResources(sr);
  239. }
  240. if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
  241. {
  242. XRRSelectInput(_glfw.x11.display, _glfw.x11.root,
  243. RROutputChangeNotifyMask);
  244. }
  245. #if defined(__CYGWIN__)
  246. _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor-1.so");
  247. #else
  248. _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1");
  249. #endif
  250. if (_glfw.x11.xcursor.handle)
  251. {
  252. glfw_dlsym(_glfw.x11.xcursor.ImageCreate, _glfw.x11.xcursor.handle, "XcursorImageCreate");
  253. glfw_dlsym(_glfw.x11.xcursor.ImageDestroy, _glfw.x11.xcursor.handle, "XcursorImageDestroy");
  254. glfw_dlsym(_glfw.x11.xcursor.ImageLoadCursor, _glfw.x11.xcursor.handle, "XcursorImageLoadCursor");
  255. }
  256. #if defined(__CYGWIN__)
  257. _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama-1.so");
  258. #else
  259. _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1");
  260. #endif
  261. if (_glfw.x11.xinerama.handle)
  262. {
  263. glfw_dlsym(_glfw.x11.xinerama.IsActive, _glfw.x11.xinerama.handle, "XineramaIsActive");
  264. glfw_dlsym(_glfw.x11.xinerama.QueryExtension, _glfw.x11.xinerama.handle, "XineramaQueryExtension");
  265. glfw_dlsym(_glfw.x11.xinerama.QueryScreens, _glfw.x11.xinerama.handle, "XineramaQueryScreens");
  266. if (XineramaQueryExtension(_glfw.x11.display,
  267. &_glfw.x11.xinerama.major,
  268. &_glfw.x11.xinerama.minor))
  269. {
  270. if (XineramaIsActive(_glfw.x11.display))
  271. _glfw.x11.xinerama.available = true;
  272. }
  273. }
  274. #if defined(__CYGWIN__)
  275. _glfw.x11.xrender.handle = _glfw_dlopen("libXrender-1.so");
  276. #else
  277. _glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1");
  278. #endif
  279. if (_glfw.x11.xrender.handle)
  280. {
  281. glfw_dlsym(_glfw.x11.xrender.QueryExtension, _glfw.x11.xrender.handle, "XRenderQueryExtension");
  282. glfw_dlsym(_glfw.x11.xrender.QueryVersion, _glfw.x11.xrender.handle, "XRenderQueryVersion");
  283. glfw_dlsym(_glfw.x11.xrender.FindVisualFormat, _glfw.x11.xrender.handle, "XRenderFindVisualFormat");
  284. if (XRenderQueryExtension(_glfw.x11.display,
  285. &_glfw.x11.xrender.errorBase,
  286. &_glfw.x11.xrender.eventBase))
  287. {
  288. if (XRenderQueryVersion(_glfw.x11.display,
  289. &_glfw.x11.xrender.major,
  290. &_glfw.x11.xrender.minor))
  291. {
  292. _glfw.x11.xrender.available = true;
  293. }
  294. }
  295. }
  296. #if defined(__CYGWIN__)
  297. _glfw.x11.xshape.handle = _glfw_dlopen("libXext-6.so");
  298. #else
  299. _glfw.x11.xshape.handle = _glfw_dlopen("libXext.so.6");
  300. #endif
  301. if (_glfw.x11.xshape.handle)
  302. {
  303. glfw_dlsym(_glfw.x11.xshape.QueryExtension, _glfw.x11.xshape.handle, "XShapeQueryExtension");
  304. glfw_dlsym(_glfw.x11.xshape.ShapeCombineRegion, _glfw.x11.xshape.handle, "XShapeCombineRegion");
  305. glfw_dlsym(_glfw.x11.xshape.QueryVersion, _glfw.x11.xshape.handle, "XShapeQueryVersion");
  306. if (XShapeQueryExtension(_glfw.x11.display,
  307. &_glfw.x11.xshape.errorBase,
  308. &_glfw.x11.xshape.eventBase))
  309. {
  310. if (XShapeQueryVersion(_glfw.x11.display,
  311. &_glfw.x11.xshape.major,
  312. &_glfw.x11.xshape.minor))
  313. {
  314. _glfw.x11.xshape.available = true;
  315. }
  316. }
  317. }
  318. _glfw.x11.xkb.major = 1;
  319. _glfw.x11.xkb.minor = 0;
  320. _glfw.x11.xkb.available = XkbQueryExtension(_glfw.x11.display,
  321. &_glfw.x11.xkb.majorOpcode,
  322. &_glfw.x11.xkb.eventBase,
  323. &_glfw.x11.xkb.errorBase,
  324. &_glfw.x11.xkb.major,
  325. &_glfw.x11.xkb.minor);
  326. if (!_glfw.x11.xkb.available)
  327. {
  328. _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to load Xkb extension");
  329. return false;
  330. }
  331. Bool supported;
  332. if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported))
  333. {
  334. if (supported)
  335. _glfw.x11.xkb.detectable = true;
  336. }
  337. if (!glfw_xkb_set_x11_events_mask()) return false;
  338. if (!glfw_xkb_create_context(&_glfw.x11.xkb)) return false;
  339. if (!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return false;
  340. if (!glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL)) return false;
  341. // String format atoms
  342. _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False);
  343. _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False);
  344. _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False);
  345. // Custom selection property atom
  346. _glfw.x11.GLFW_SELECTION =
  347. XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False);
  348. // ICCCM standard clipboard atoms
  349. _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False);
  350. _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False);
  351. _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False);
  352. _glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False);
  353. _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False);
  354. // Clipboard manager atoms
  355. _glfw.x11.CLIPBOARD_MANAGER =
  356. XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False);
  357. _glfw.x11.SAVE_TARGETS =
  358. XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False);
  359. // Xdnd (drag and drop) atoms
  360. _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False);
  361. _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False);
  362. _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False);
  363. _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False);
  364. _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False);
  365. _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False);
  366. _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False);
  367. _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False);
  368. _glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False);
  369. // ICCCM, EWMH and Motif window property atoms
  370. // These can be set safely even without WM support
  371. // The EWMH atoms that require WM support are handled in detectEWMH
  372. _glfw.x11.WM_PROTOCOLS =
  373. XInternAtom(_glfw.x11.display, "WM_PROTOCOLS", False);
  374. _glfw.x11.WM_STATE =
  375. XInternAtom(_glfw.x11.display, "WM_STATE", False);
  376. _glfw.x11.WM_DELETE_WINDOW =
  377. XInternAtom(_glfw.x11.display, "WM_DELETE_WINDOW", False);
  378. _glfw.x11.NET_SUPPORTED =
  379. XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", False);
  380. _glfw.x11.NET_SUPPORTING_WM_CHECK =
  381. XInternAtom(_glfw.x11.display, "_NET_SUPPORTING_WM_CHECK", False);
  382. _glfw.x11.NET_WM_ICON =
  383. XInternAtom(_glfw.x11.display, "_NET_WM_ICON", False);
  384. _glfw.x11.NET_WM_PING =
  385. XInternAtom(_glfw.x11.display, "_NET_WM_PING", False);
  386. _glfw.x11.NET_WM_PID =
  387. XInternAtom(_glfw.x11.display, "_NET_WM_PID", False);
  388. _glfw.x11.NET_WM_NAME =
  389. XInternAtom(_glfw.x11.display, "_NET_WM_NAME", False);
  390. _glfw.x11.NET_WM_ICON_NAME =
  391. XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False);
  392. _glfw.x11.NET_WM_BYPASS_COMPOSITOR =
  393. XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False);
  394. _glfw.x11.NET_WM_WINDOW_OPACITY =
  395. XInternAtom(_glfw.x11.display, "_NET_WM_WINDOW_OPACITY", False);
  396. _glfw.x11.MOTIF_WM_HINTS =
  397. XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False);
  398. // The compositing manager selection name contains the screen number
  399. {
  400. char name[32];
  401. snprintf(name, sizeof(name), "_NET_WM_CM_S%u", _glfw.x11.screen);
  402. _glfw.x11.NET_WM_CM_Sx = XInternAtom(_glfw.x11.display, name, False);
  403. }
  404. // Detect whether an EWMH-conformant window manager is running
  405. detectEWMH();
  406. return true;
  407. }
  408. // Retrieve system content scale via folklore heuristics
  409. //
  410. void _glfwGetSystemContentScaleX11(float* xscale, float* yscale, bool bypass_cache)
  411. {
  412. // Start by assuming the default X11 DPI
  413. // NOTE: Some desktop environments (KDE) may remove the Xft.dpi field when it
  414. // would be set to 96, so assume that is the case if we cannot find it
  415. float xdpi = 96.f, ydpi = 96.f;
  416. // NOTE: Basing the scale on Xft.dpi where available should provide the most
  417. // consistent user experience (matches Qt, Gtk, etc), although not
  418. // always the most accurate one
  419. char* rms = NULL;
  420. char* owned_rms = NULL;
  421. if (bypass_cache)
  422. {
  423. _glfwGetWindowPropertyX11(_glfw.x11.root,
  424. _glfw.x11.RESOURCE_MANAGER,
  425. XA_STRING,
  426. (unsigned char**) &owned_rms);
  427. rms = owned_rms;
  428. } else {
  429. rms = XResourceManagerString(_glfw.x11.display);
  430. }
  431. if (rms)
  432. {
  433. XrmDatabase db = XrmGetStringDatabase(rms);
  434. if (db)
  435. {
  436. XrmValue value;
  437. char* type = NULL;
  438. if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value))
  439. {
  440. if (type && strcmp(type, "String") == 0)
  441. xdpi = ydpi = (float)atof(value.addr);
  442. }
  443. XrmDestroyDatabase(db);
  444. }
  445. XFree(owned_rms);
  446. }
  447. *xscale = xdpi / 96.f;
  448. *yscale = ydpi / 96.f;
  449. }
  450. // Create a blank cursor for hidden and disabled cursor modes
  451. //
  452. static Cursor createHiddenCursor(void)
  453. {
  454. unsigned char pixels[16 * 16 * 4] = { 0 };
  455. GLFWimage image = { 16, 16, pixels };
  456. return _glfwCreateCursorX11(&image, 0, 0);
  457. }
  458. // Create a helper window for IPC
  459. //
  460. static Window createHelperWindow(void)
  461. {
  462. XSetWindowAttributes wa;
  463. wa.event_mask = PropertyChangeMask;
  464. return XCreateWindow(_glfw.x11.display, _glfw.x11.root,
  465. 0, 0, 1, 1, 0, 0,
  466. InputOnly,
  467. DefaultVisual(_glfw.x11.display, _glfw.x11.screen),
  468. CWEventMask, &wa);
  469. }
  470. // X error handler
  471. //
  472. static int errorHandler(Display *display, XErrorEvent* event)
  473. {
  474. if (_glfw.x11.display != display)
  475. return 0;
  476. _glfw.x11.errorCode = event->error_code;
  477. return 0;
  478. }
  479. //////////////////////////////////////////////////////////////////////////
  480. ////// GLFW internal API //////
  481. //////////////////////////////////////////////////////////////////////////
  482. // Sets the X error handler callback
  483. //
  484. void _glfwGrabErrorHandlerX11(void)
  485. {
  486. _glfw.x11.errorCode = Success;
  487. XSetErrorHandler(errorHandler);
  488. }
  489. // Clears the X error handler callback
  490. //
  491. void _glfwReleaseErrorHandlerX11(void)
  492. {
  493. // Synchronize to make sure all commands are processed
  494. XSync(_glfw.x11.display, False);
  495. XSetErrorHandler(NULL);
  496. }
  497. // Reports the specified error, appending information about the last X error
  498. //
  499. void _glfwInputErrorX11(int error, const char* message)
  500. {
  501. char buffer[_GLFW_MESSAGE_SIZE];
  502. XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode,
  503. buffer, sizeof(buffer));
  504. _glfwInputError(error, "%s: %s", message, buffer);
  505. }
  506. // Creates a native cursor object from the specified image and hotspot
  507. //
  508. Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot)
  509. {
  510. int i;
  511. Cursor cursor;
  512. if (!_glfw.x11.xcursor.handle)
  513. return None;
  514. XcursorImage* native = XcursorImageCreate(image->width, image->height);
  515. if (native == NULL)
  516. return None;
  517. native->xhot = xhot;
  518. native->yhot = yhot;
  519. unsigned char* source = (unsigned char*) image->pixels;
  520. XcursorPixel* target = native->pixels;
  521. for (i = 0; i < image->width * image->height; i++, target++, source += 4)
  522. {
  523. unsigned int alpha = source[3];
  524. *target = (alpha << 24) |
  525. ((unsigned char) ((source[0] * alpha) / 255) << 16) |
  526. ((unsigned char) ((source[1] * alpha) / 255) << 8) |
  527. ((unsigned char) ((source[2] * alpha) / 255) << 0);
  528. }
  529. cursor = XcursorImageLoadCursor(_glfw.x11.display, native);
  530. XcursorImageDestroy(native);
  531. return cursor;
  532. }
  533. //////////////////////////////////////////////////////////////////////////
  534. ////// GLFW platform API //////
  535. //////////////////////////////////////////////////////////////////////////
  536. GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized) {
  537. return glfw_current_system_color_theme(query_if_unintialized);
  538. }
  539. void _glfwPlatformInputColorScheme(GLFWColorScheme appearance UNUSED) { }
  540. int _glfwPlatformInit(void)
  541. {
  542. XInitThreads();
  543. XrmInitialize();
  544. _glfw.x11.display = XOpenDisplay(NULL);
  545. if (!_glfw.x11.display)
  546. {
  547. const char* display = getenv("DISPLAY");
  548. if (display)
  549. {
  550. _glfwInputError(GLFW_PLATFORM_ERROR,
  551. "X11: Failed to open display %s", display);
  552. }
  553. else
  554. {
  555. _glfwInputError(GLFW_PLATFORM_ERROR,
  556. "X11: The DISPLAY environment variable is missing");
  557. }
  558. return false;
  559. }
  560. if (!initPollData(&_glfw.x11.eventLoopData, ConnectionNumber(_glfw.x11.display))) {
  561. _glfwInputError(GLFW_PLATFORM_ERROR,
  562. "X11: Failed to initialize event loop data");
  563. }
  564. glfw_dbus_init(&_glfw.x11.dbus, &_glfw.x11.eventLoopData);
  565. glfw_initialize_desktop_settings(); // needed for color scheme change notification
  566. _glfw.x11.screen = DefaultScreen(_glfw.x11.display);
  567. _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen);
  568. _glfw.x11.context = XUniqueContext();
  569. _glfw.x11.RESOURCE_MANAGER = XInternAtom(_glfw.x11.display, "RESOURCE_MANAGER", True);
  570. _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION = None;
  571. XSelectInput(_glfw.x11.display, _glfw.x11.root, PropertyChangeMask);
  572. _glfwGetSystemContentScaleX11(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY, false);
  573. if (!initExtensions())
  574. return false;
  575. _glfw.x11.helperWindowHandle = createHelperWindow();
  576. _glfw.x11.hiddenCursorHandle = createHiddenCursor();
  577. _glfwPollMonitorsX11();
  578. return true;
  579. }
  580. void _glfwPlatformTerminate(void)
  581. {
  582. removeAllTimers(&_glfw.x11.eventLoopData);
  583. if (_glfw.x11.helperWindowHandle)
  584. {
  585. if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) ==
  586. _glfw.x11.helperWindowHandle)
  587. {
  588. _glfwPushSelectionToManagerX11();
  589. }
  590. XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle);
  591. _glfw.x11.helperWindowHandle = None;
  592. }
  593. if (_glfw.x11.hiddenCursorHandle)
  594. {
  595. XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle);
  596. _glfw.x11.hiddenCursorHandle = (Cursor) 0;
  597. }
  598. glfw_xkb_release(&_glfw.x11.xkb);
  599. glfw_dbus_terminate(&_glfw.x11.dbus);
  600. if (_glfw.x11.mime_atoms.array) {
  601. for (size_t i = 0; i < _glfw.x11.mime_atoms.sz; i++) {
  602. free((void*)_glfw.x11.mime_atoms.array[i].mime);
  603. }
  604. free(_glfw.x11.mime_atoms.array);
  605. }
  606. if (_glfw.x11.clipboard_atoms.array) { free(_glfw.x11.clipboard_atoms.array); }
  607. if (_glfw.x11.primary_atoms.array) { free(_glfw.x11.primary_atoms.array); }
  608. if (_glfw.x11.display)
  609. {
  610. XCloseDisplay(_glfw.x11.display);
  611. _glfw.x11.display = NULL;
  612. _glfw.x11.eventLoopData.fds[0].fd = -1;
  613. }
  614. if (_glfw.x11.xcursor.handle)
  615. {
  616. _glfw_dlclose(_glfw.x11.xcursor.handle);
  617. _glfw.x11.xcursor.handle = NULL;
  618. }
  619. if (_glfw.x11.randr.handle)
  620. {
  621. _glfw_dlclose(_glfw.x11.randr.handle);
  622. _glfw.x11.randr.handle = NULL;
  623. }
  624. if (_glfw.x11.xinerama.handle)
  625. {
  626. _glfw_dlclose(_glfw.x11.xinerama.handle);
  627. _glfw.x11.xinerama.handle = NULL;
  628. }
  629. if (_glfw.x11.xrender.handle)
  630. {
  631. _glfw_dlclose(_glfw.x11.xrender.handle);
  632. _glfw.x11.xrender.handle = NULL;
  633. }
  634. if (_glfw.x11.vidmode.handle)
  635. {
  636. _glfw_dlclose(_glfw.x11.vidmode.handle);
  637. _glfw.x11.vidmode.handle = NULL;
  638. }
  639. if (_glfw.x11.xi.handle)
  640. {
  641. _glfw_dlclose(_glfw.x11.xi.handle);
  642. _glfw.x11.xi.handle = NULL;
  643. }
  644. // NOTE: These need to be unloaded after XCloseDisplay, as they register
  645. // cleanup callbacks that get called by that function
  646. _glfwTerminateEGL();
  647. _glfwTerminateGLX();
  648. finalizePollData(&_glfw.x11.eventLoopData);
  649. }
  650. const char* _glfwPlatformGetVersionString(void)
  651. {
  652. return _GLFW_VERSION_NUMBER " X11 GLX EGL OSMesa"
  653. #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)
  654. " clock_gettime"
  655. #else
  656. " gettimeofday"
  657. #endif
  658. #if defined(__linux__)
  659. " evdev"
  660. #endif
  661. #if defined(_GLFW_BUILD_DLL)
  662. " shared"
  663. #endif
  664. ;
  665. }
  666. #include "main_loop.h"