x11_init.c 27 KB

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