x11_init.c 27 KB

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