1 /// Translated from C to D
2 module x11_init;
3 
4 extern(C): @nogc: nothrow: __gshared:
5 
6 //========================================================================
7 // GLFW 3.3 X11 - www.glfw.org
8 //------------------------------------------------------------------------
9 // Copyright (c) 2002-2006 Marcus Geelnard
10 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
11 //
12 // This software is provided 'as-is', without any express or implied
13 // warranty. In no event will the authors be held liable for any damages
14 // arising from the use of this software.
15 //
16 // Permission is granted to anyone to use this software for any purpose,
17 // including commercial applications, and to alter it and redistribute it
18 // freely, subject to the following restrictions:
19 //
20 // 1. The origin of this software must not be misrepresented; you must not
21 //    claim that you wrote the original software. If you use this software
22 //    in a product, an acknowledgment in the product documentation would
23 //    be appreciated but is not required.
24 //
25 // 2. Altered source versions must be plainly marked as such, and must not
26 //    be misrepresented as being the original software.
27 //
28 // 3. This notice may not be removed or altered from any source
29 //    distribution.
30 //
31 //========================================================================
32 // It is fine to use C99 in this file because it will not be built with VS
33 //========================================================================
34 
35 import glfw3.internal;
36 
37 version(none) {
38     import x11.Xresource;
39     import x11.extensions.XKBsrv;
40     import x11.XKBlib;
41 }
42 
43 import core.stdc.stdlib;
44 import core.stdc.string;
45 import core.stdc.limits;
46 import core.stdc.stdio;
47 import core.stdc.locale;
48 import core.stdc.config: c_long, c_ulong;
49 
50 // Translate an X11 key code to a GLFW key code.
51 //
52 static int translateKeyCode(int scancode) {
53     int keySym;
54 
55     // Valid key code range is  [8,255], according to the Xlib manual
56     if (scancode < 8 || scancode > 255)
57         return GLFW_KEY_UNKNOWN;
58 
59     if (_glfw.x11.xkb.available)
60     {
61         // Try secondary keysym, for numeric keypad keys
62         // Note: This way we always force "NumLock = ON", which is intentional
63         // since the returned key code should correspond to a physical
64         // location.
65         keySym = cast(int) XkbKeycodeToKeysym(_glfw.x11.display, cast(ubyte) scancode, cast(int) _glfw.x11.xkb.group, 1);
66         switch (keySym)
67         {
68             case XK_KP_0:           return GLFW_KEY_KP_0;
69             case XK_KP_1:           return GLFW_KEY_KP_1;
70             case XK_KP_2:           return GLFW_KEY_KP_2;
71             case XK_KP_3:           return GLFW_KEY_KP_3;
72             case XK_KP_4:           return GLFW_KEY_KP_4;
73             case XK_KP_5:           return GLFW_KEY_KP_5;
74             case XK_KP_6:           return GLFW_KEY_KP_6;
75             case XK_KP_7:           return GLFW_KEY_KP_7;
76             case XK_KP_8:           return GLFW_KEY_KP_8;
77             case XK_KP_9:           return GLFW_KEY_KP_9;
78             case XK_KP_Separator:
79             case XK_KP_Decimal:     return GLFW_KEY_KP_DECIMAL;
80             case XK_KP_Equal:       return GLFW_KEY_KP_EQUAL;
81             case XK_KP_Enter:       return GLFW_KEY_KP_ENTER;
82             default:                break;
83         }
84 
85         // Now try primary keysym for function keys (non-printable keys)
86         // These should not depend on the current keyboard layout
87         keySym = cast(int) XkbKeycodeToKeysym(_glfw.x11.display, cast(ubyte) scancode, _glfw.x11.xkb.group, 0);
88     }
89     else
90     {
91         int dummy;
92         KeySym* keySyms;
93 
94         keySyms = XGetKeyboardMapping(_glfw.x11.display, cast(ubyte) scancode, 1, &dummy);
95         keySym = cast(int) keySyms[0];
96         XFree(keySyms);
97     }
98 
99     switch (keySym)
100     {
101         case XK_Escape:         return GLFW_KEY_ESCAPE;
102         case XK_Tab:            return GLFW_KEY_TAB;
103         case XK_Shift_L:        return GLFW_KEY_LEFT_SHIFT;
104         case XK_Shift_R:        return GLFW_KEY_RIGHT_SHIFT;
105         case XK_Control_L:      return GLFW_KEY_LEFT_CONTROL;
106         case XK_Control_R:      return GLFW_KEY_RIGHT_CONTROL;
107         case XK_Meta_L:
108         case XK_Alt_L:          return GLFW_KEY_LEFT_ALT;
109         case XK_Mode_switch: // Mapped to Alt_R on many keyboards
110         case XK_ISO_Level3_Shift: // AltGr on at least some machines
111         case XK_Meta_R:
112         case XK_Alt_R:          return GLFW_KEY_RIGHT_ALT;
113         case XK_Super_L:        return GLFW_KEY_LEFT_SUPER;
114         case XK_Super_R:        return GLFW_KEY_RIGHT_SUPER;
115         case XK_Menu:           return GLFW_KEY_MENU;
116         case XK_Num_Lock:       return GLFW_KEY_NUM_LOCK;
117         case XK_Caps_Lock:      return GLFW_KEY_CAPS_LOCK;
118         case XK_Print:          return GLFW_KEY_PRINT_SCREEN;
119         case XK_Scroll_Lock:    return GLFW_KEY_SCROLL_LOCK;
120         case XK_Pause:          return GLFW_KEY_PAUSE;
121         case XK_Delete:         return GLFW_KEY_DELETE;
122         case XK_BackSpace:      return GLFW_KEY_BACKSPACE;
123         case XK_Return:         return GLFW_KEY_ENTER;
124         case XK_Home:           return GLFW_KEY_HOME;
125         case XK_End:            return GLFW_KEY_END;
126         case XK_Page_Up:        return GLFW_KEY_PAGE_UP;
127         case XK_Page_Down:      return GLFW_KEY_PAGE_DOWN;
128         case XK_Insert:         return GLFW_KEY_INSERT;
129         case XK_Left:           return GLFW_KEY_LEFT;
130         case XK_Right:          return GLFW_KEY_RIGHT;
131         case XK_Down:           return GLFW_KEY_DOWN;
132         case XK_Up:             return GLFW_KEY_UP;
133         case XK_F1:             return GLFW_KEY_F1;
134         case XK_F2:             return GLFW_KEY_F2;
135         case XK_F3:             return GLFW_KEY_F3;
136         case XK_F4:             return GLFW_KEY_F4;
137         case XK_F5:             return GLFW_KEY_F5;
138         case XK_F6:             return GLFW_KEY_F6;
139         case XK_F7:             return GLFW_KEY_F7;
140         case XK_F8:             return GLFW_KEY_F8;
141         case XK_F9:             return GLFW_KEY_F9;
142         case XK_F10:            return GLFW_KEY_F10;
143         case XK_F11:            return GLFW_KEY_F11;
144         case XK_F12:            return GLFW_KEY_F12;
145         case XK_F13:            return GLFW_KEY_F13;
146         case XK_F14:            return GLFW_KEY_F14;
147         case XK_F15:            return GLFW_KEY_F15;
148         case XK_F16:            return GLFW_KEY_F16;
149         case XK_F17:            return GLFW_KEY_F17;
150         case XK_F18:            return GLFW_KEY_F18;
151         case XK_F19:            return GLFW_KEY_F19;
152         case XK_F20:            return GLFW_KEY_F20;
153         case XK_F21:            return GLFW_KEY_F21;
154         case XK_F22:            return GLFW_KEY_F22;
155         case XK_F23:            return GLFW_KEY_F23;
156         case XK_F24:            return GLFW_KEY_F24;
157         case XK_F25:            return GLFW_KEY_F25;
158 
159         // Numeric keypad
160         case XK_KP_Divide:      return GLFW_KEY_KP_DIVIDE;
161         case XK_KP_Multiply:    return GLFW_KEY_KP_MULTIPLY;
162         case XK_KP_Subtract:    return GLFW_KEY_KP_SUBTRACT;
163         case XK_KP_Add:         return GLFW_KEY_KP_ADD;
164 
165         // These should have been detected in secondary keysym test above!
166         case XK_KP_Insert:      return GLFW_KEY_KP_0;
167         case XK_KP_End:         return GLFW_KEY_KP_1;
168         case XK_KP_Down:        return GLFW_KEY_KP_2;
169         case XK_KP_Page_Down:   return GLFW_KEY_KP_3;
170         case XK_KP_Left:        return GLFW_KEY_KP_4;
171         case XK_KP_Right:       return GLFW_KEY_KP_6;
172         case XK_KP_Home:        return GLFW_KEY_KP_7;
173         case XK_KP_Up:          return GLFW_KEY_KP_8;
174         case XK_KP_Page_Up:     return GLFW_KEY_KP_9;
175         case XK_KP_Delete:      return GLFW_KEY_KP_DECIMAL;
176         case XK_KP_Equal:       return GLFW_KEY_KP_EQUAL;
177         case XK_KP_Enter:       return GLFW_KEY_KP_ENTER;
178 
179         // Last resort: Check for printable keys (should not happen if the XKB
180         // extension is available). This will give a layout dependent mapping
181         // (which is wrong, and we may miss some keys, especially on non-US
182         // keyboards), but it's better than nothing...
183         case XK_a:              return GLFW_KEY_A;
184         case XK_b:              return GLFW_KEY_B;
185         case XK_c:              return GLFW_KEY_C;
186         case XK_d:              return GLFW_KEY_D;
187         case XK_e:              return GLFW_KEY_E;
188         case XK_f:              return GLFW_KEY_F;
189         case XK_g:              return GLFW_KEY_G;
190         case XK_h:              return GLFW_KEY_H;
191         case XK_i:              return GLFW_KEY_I;
192         case XK_j:              return GLFW_KEY_J;
193         case XK_k:              return GLFW_KEY_K;
194         case XK_l:              return GLFW_KEY_L;
195         case XK_m:              return GLFW_KEY_M;
196         case XK_n:              return GLFW_KEY_N;
197         case XK_o:              return GLFW_KEY_O;
198         case XK_p:              return GLFW_KEY_P;
199         case XK_q:              return GLFW_KEY_Q;
200         case XK_r:              return GLFW_KEY_R;
201         case XK_s:              return GLFW_KEY_S;
202         case XK_t:              return GLFW_KEY_T;
203         case XK_u:              return GLFW_KEY_U;
204         case XK_v:              return GLFW_KEY_V;
205         case XK_w:              return GLFW_KEY_W;
206         case XK_x:              return GLFW_KEY_X;
207         case XK_y:              return GLFW_KEY_Y;
208         case XK_z:              return GLFW_KEY_Z;
209         case XK_1:              return GLFW_KEY_1;
210         case XK_2:              return GLFW_KEY_2;
211         case XK_3:              return GLFW_KEY_3;
212         case XK_4:              return GLFW_KEY_4;
213         case XK_5:              return GLFW_KEY_5;
214         case XK_6:              return GLFW_KEY_6;
215         case XK_7:              return GLFW_KEY_7;
216         case XK_8:              return GLFW_KEY_8;
217         case XK_9:              return GLFW_KEY_9;
218         case XK_0:              return GLFW_KEY_0;
219         case XK_space:          return GLFW_KEY_SPACE;
220         case XK_minus:          return GLFW_KEY_MINUS;
221         case XK_equal:          return GLFW_KEY_EQUAL;
222         case XK_bracketleft:    return GLFW_KEY_LEFT_BRACKET;
223         case XK_bracketright:   return GLFW_KEY_RIGHT_BRACKET;
224         case XK_backslash:      return GLFW_KEY_BACKSLASH;
225         case XK_semicolon:      return GLFW_KEY_SEMICOLON;
226         case XK_apostrophe:     return GLFW_KEY_APOSTROPHE;
227         case XK_grave:          return GLFW_KEY_GRAVE_ACCENT;
228         case XK_comma:          return GLFW_KEY_COMMA;
229         case XK_period:         return GLFW_KEY_PERIOD;
230         case XK_slash:          return GLFW_KEY_SLASH;
231         case XK_less:           return GLFW_KEY_WORLD_1; // At least in some layouts...
232         default:                break;
233     }
234 
235     // No matching translation was found
236     return GLFW_KEY_UNKNOWN;
237 }
238 
239 // Create key code translation tables
240 //
241 static void createKeyTables() {
242     int scancode;int key;
243 
244     memset(_glfw.x11.keycodes.ptr, -1, typeof(_glfw.x11.keycodes).sizeof);
245     memset(_glfw.x11.scancodes.ptr, -1, typeof(_glfw.x11.scancodes).sizeof);
246 
247     if (_glfw.x11.xkb.available)
248     {
249         // Use XKB to determine physical key locations independently of the
250         // current keyboard layout
251 
252         char[XkbKeyNameLength + 1] name;
253         XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd);
254         XkbGetNames(_glfw.x11.display, XkbKeyNamesMask, desc);
255 
256         // Find the X11 key code -> GLFW key code mapping
257         for (scancode = desc.min_key_code;  scancode <= desc.max_key_code;  scancode++)
258         {
259             memcpy(name.ptr, desc.names.keys[scancode].name.ptr, XkbKeyNameLength);
260             name[XkbKeyNameLength] = '\0';
261 
262             // Map the key name to a GLFW key code. Note: We only map printable
263             // keys here, and we use the US keyboard layout. The rest of the
264             // keys (function keys) are mapped using traditional KeySym
265             // translations.
266             if (strcmp(name.ptr, "TLDE") == 0) key = GLFW_KEY_GRAVE_ACCENT;
267             else if (strcmp(name.ptr, "AE01") == 0) key = GLFW_KEY_1;
268             else if (strcmp(name.ptr, "AE02") == 0) key = GLFW_KEY_2;
269             else if (strcmp(name.ptr, "AE03") == 0) key = GLFW_KEY_3;
270             else if (strcmp(name.ptr, "AE04") == 0) key = GLFW_KEY_4;
271             else if (strcmp(name.ptr, "AE05") == 0) key = GLFW_KEY_5;
272             else if (strcmp(name.ptr, "AE06") == 0) key = GLFW_KEY_6;
273             else if (strcmp(name.ptr, "AE07") == 0) key = GLFW_KEY_7;
274             else if (strcmp(name.ptr, "AE08") == 0) key = GLFW_KEY_8;
275             else if (strcmp(name.ptr, "AE09") == 0) key = GLFW_KEY_9;
276             else if (strcmp(name.ptr, "AE10") == 0) key = GLFW_KEY_0;
277             else if (strcmp(name.ptr, "AE11") == 0) key = GLFW_KEY_MINUS;
278             else if (strcmp(name.ptr, "AE12") == 0) key = GLFW_KEY_EQUAL;
279             else if (strcmp(name.ptr, "AD01") == 0) key = GLFW_KEY_Q;
280             else if (strcmp(name.ptr, "AD02") == 0) key = GLFW_KEY_W;
281             else if (strcmp(name.ptr, "AD03") == 0) key = GLFW_KEY_E;
282             else if (strcmp(name.ptr, "AD04") == 0) key = GLFW_KEY_R;
283             else if (strcmp(name.ptr, "AD05") == 0) key = GLFW_KEY_T;
284             else if (strcmp(name.ptr, "AD06") == 0) key = GLFW_KEY_Y;
285             else if (strcmp(name.ptr, "AD07") == 0) key = GLFW_KEY_U;
286             else if (strcmp(name.ptr, "AD08") == 0) key = GLFW_KEY_I;
287             else if (strcmp(name.ptr, "AD09") == 0) key = GLFW_KEY_O;
288             else if (strcmp(name.ptr, "AD10") == 0) key = GLFW_KEY_P;
289             else if (strcmp(name.ptr, "AD11") == 0) key = GLFW_KEY_LEFT_BRACKET;
290             else if (strcmp(name.ptr, "AD12") == 0) key = GLFW_KEY_RIGHT_BRACKET;
291             else if (strcmp(name.ptr, "AC01") == 0) key = GLFW_KEY_A;
292             else if (strcmp(name.ptr, "AC02") == 0) key = GLFW_KEY_S;
293             else if (strcmp(name.ptr, "AC03") == 0) key = GLFW_KEY_D;
294             else if (strcmp(name.ptr, "AC04") == 0) key = GLFW_KEY_F;
295             else if (strcmp(name.ptr, "AC05") == 0) key = GLFW_KEY_G;
296             else if (strcmp(name.ptr, "AC06") == 0) key = GLFW_KEY_H;
297             else if (strcmp(name.ptr, "AC07") == 0) key = GLFW_KEY_J;
298             else if (strcmp(name.ptr, "AC08") == 0) key = GLFW_KEY_K;
299             else if (strcmp(name.ptr, "AC09") == 0) key = GLFW_KEY_L;
300             else if (strcmp(name.ptr, "AC10") == 0) key = GLFW_KEY_SEMICOLON;
301             else if (strcmp(name.ptr, "AC11") == 0) key = GLFW_KEY_APOSTROPHE;
302             else if (strcmp(name.ptr, "AB01") == 0) key = GLFW_KEY_Z;
303             else if (strcmp(name.ptr, "AB02") == 0) key = GLFW_KEY_X;
304             else if (strcmp(name.ptr, "AB03") == 0) key = GLFW_KEY_C;
305             else if (strcmp(name.ptr, "AB04") == 0) key = GLFW_KEY_V;
306             else if (strcmp(name.ptr, "AB05") == 0) key = GLFW_KEY_B;
307             else if (strcmp(name.ptr, "AB06") == 0) key = GLFW_KEY_N;
308             else if (strcmp(name.ptr, "AB07") == 0) key = GLFW_KEY_M;
309             else if (strcmp(name.ptr, "AB08") == 0) key = GLFW_KEY_COMMA;
310             else if (strcmp(name.ptr, "AB09") == 0) key = GLFW_KEY_PERIOD;
311             else if (strcmp(name.ptr, "AB10") == 0) key = GLFW_KEY_SLASH;
312             else if (strcmp(name.ptr, "BKSL") == 0) key = GLFW_KEY_BACKSLASH;
313             else if (strcmp(name.ptr, "LSGT") == 0) key = GLFW_KEY_WORLD_1;
314             else key = GLFW_KEY_UNKNOWN;
315 
316             if ((scancode >= 0) && (scancode < 256))
317                 _glfw.x11.keycodes[scancode] = key;
318         }
319 
320         XkbFreeNames(desc, XkbKeyNamesMask, True);
321         XkbFreeKeyboard(desc, 0, True);
322     }
323 
324     for (scancode = 0;  scancode < 256;  scancode++)
325     {
326         // Translate the un-translated key codes using traditional X11 KeySym
327         // lookups
328         if (_glfw.x11.keycodes[scancode] < 0)
329             _glfw.x11.keycodes[scancode] = translateKeyCode(scancode);
330 
331         // Store the reverse translation for faster key name lookup
332         if (_glfw.x11.keycodes[scancode] > 0)
333             _glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode;
334     }
335 }
336 
337 // Check whether the IM has a usable style
338 //
339 static GLFWbool hasUsableInputMethodStyle() {
340     GLFWbool found = GLFW_FALSE;
341     XIMStyles* styles = null;
342 
343     if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, null) != null)
344         return GLFW_FALSE;
345 
346     for (uint i = 0;  i < styles.count_styles;  i++)
347     {
348         if (styles.supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing))
349         {
350             found = GLFW_TRUE;
351             break;
352         }
353     }
354 
355     XFree(styles);
356     return found;
357 }
358 
359 // Check whether the specified atom is supported
360 //
361 static Atom getSupportedAtom(Atom* supportedAtoms, c_ulong atomCount, const(char)* atomName) {
362     const(Atom) atom = XInternAtom(_glfw.x11.display, atomName, False);
363 
364     for (uint i = 0;  i < atomCount;  i++)
365     {
366         if (supportedAtoms[i] == atom)
367             return atom;
368     }
369 
370     return None;
371 }
372 
373 // Check whether the running window manager is EWMH-compliant
374 //
375 static void detectEWMH() {
376     // First we read the _NET_SUPPORTING_WM_CHECK property on the root window
377 
378     Window* windowFromRoot = null;
379     if (!_glfwGetWindowPropertyX11(_glfw.x11.root,
380                                    _glfw.x11.NET_SUPPORTING_WM_CHECK,
381                                    XA_WINDOW,
382                                    cast(ubyte**) &windowFromRoot))
383     {
384         return;
385     }
386 
387     _glfwGrabErrorHandlerX11();
388 
389     // If it exists, it should be the XID of a top-level window
390     // Then we look for the same property on that window
391 
392     Window* windowFromChild = null;
393     if (!_glfwGetWindowPropertyX11(*windowFromRoot,
394                                    _glfw.x11.NET_SUPPORTING_WM_CHECK,
395                                    XA_WINDOW,
396                                    cast(ubyte**) &windowFromChild))
397     {
398         XFree(windowFromRoot);
399         return;
400     }
401 
402     _glfwReleaseErrorHandlerX11();
403 
404     // If the property exists, it should contain the XID of the window
405 
406     if (*windowFromRoot != *windowFromChild)
407     {
408         XFree(windowFromRoot);
409         XFree(windowFromChild);
410         return;
411     }
412 
413     XFree(windowFromRoot);
414     XFree(windowFromChild);
415 
416     // We are now fairly sure that an EWMH-compliant WM is currently running
417     // We can now start querying the WM about what features it supports by
418     // looking in the _NET_SUPPORTED property on the root window
419     // It should contain a list of supported EWMH protocol and state atoms
420 
421     Atom* supportedAtoms = null;
422     c_ulong atomCount = _glfwGetWindowPropertyX11(_glfw.x11.root,
423                                   _glfw.x11.NET_SUPPORTED,
424                                   XA_ATOM,
425                                   cast(ubyte**) &supportedAtoms);
426 
427     // See which of the atoms we support that are supported by the WM
428 
429     _glfw.x11.NET_WM_STATE =
430         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE");
431     _glfw.x11.NET_WM_STATE_ABOVE =
432         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE");
433     _glfw.x11.NET_WM_STATE_FULLSCREEN =
434         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN");
435     _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT =
436         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT");
437     _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ =
438         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ");
439     _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION =
440         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION");
441     _glfw.x11.NET_WM_FULLSCREEN_MONITORS =
442         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS");
443     _glfw.x11.NET_WM_WINDOW_TYPE =
444         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE");
445     _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL =
446         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL");
447     _glfw.x11.NET_WORKAREA =
448         getSupportedAtom(supportedAtoms, atomCount, "_NET_WORKAREA");
449     _glfw.x11.NET_CURRENT_DESKTOP =
450         getSupportedAtom(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP");
451     _glfw.x11.NET_ACTIVE_WINDOW =
452         getSupportedAtom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW");
453     _glfw.x11.NET_FRAME_EXTENTS =
454         getSupportedAtom(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS");
455     _glfw.x11.NET_REQUEST_FRAME_EXTENTS =
456         getSupportedAtom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS");
457 
458     if (supportedAtoms)
459         XFree(supportedAtoms);
460 }
461 
462 // Look for and initialize supported X11 extensions
463 //
464 static GLFWbool initExtensions() {
465     _glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so.1");
466     if (_glfw.x11.vidmode.handle)
467     {
468         _glfw.x11.vidmode.QueryExtension = cast(PFN_XF86VidModeQueryExtension)
469             _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension");
470         _glfw.x11.vidmode.GetGammaRamp = cast(PFN_XF86VidModeGetGammaRamp)
471             _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp");
472         _glfw.x11.vidmode.SetGammaRamp = cast(PFN_XF86VidModeSetGammaRamp)
473             _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp");
474         _glfw.x11.vidmode.GetGammaRampSize = cast(PFN_XF86VidModeGetGammaRampSize)
475             _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize");
476 
477         _glfw.x11.vidmode.available =
478             _glfw.x11.vidmode.QueryExtension(_glfw.x11.display,
479                                       &_glfw.x11.vidmode.eventBase,
480                                       &_glfw.x11.vidmode.errorBase);
481     }
482 
483 version (Cygwin) {
484     _glfw.x11.xi.handle = _glfw_dlopen("libXi-6.so");
485 } else {
486     _glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6");
487 }
488     if (_glfw.x11.xi.handle)
489     {
490         _glfw.x11.xi.QueryVersion = cast(PFN_XIQueryVersion)
491             _glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion");
492         _glfw.x11.xi.SelectEvents = cast(PFN_XISelectEvents)
493             _glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents");
494 
495         if (XQueryExtension(_glfw.x11.display,
496                             "XInputExtension".ptr,
497                             &_glfw.x11.xi.majorOpcode,
498                             &_glfw.x11.xi.eventBase,
499                             &_glfw.x11.xi.errorBase))
500         {
501             _glfw.x11.xi.major = 2;
502             _glfw.x11.xi.minor = 0;
503 
504             if (_glfw.x11.xi.QueryVersion(_glfw.x11.display,
505                                &_glfw.x11.xi.major,
506                                &_glfw.x11.xi.minor) == XErrorCode.Success)
507             {
508                 _glfw.x11.xi.available = GLFW_TRUE;
509             }
510         }
511     }
512 
513 version (Cygwin) {
514     _glfw.x11.randr.handle = _glfw_dlopen("libXrandr-2.so");
515 } else {
516     _glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2");
517 }
518     if (_glfw.x11.randr.handle)
519     {
520         _glfw.x11.randr.AllocGamma = cast(PFN_XRRAllocGamma)
521             _glfw_dlsym(_glfw.x11.randr.handle, "XRRAllocGamma");
522         _glfw.x11.randr.FreeGamma = cast(PFN_XRRFreeGamma)
523             _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma");
524         _glfw.x11.randr.FreeCrtcInfo = cast(PFN_XRRFreeCrtcInfo)
525             _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo");
526         _glfw.x11.randr.FreeGamma = cast(PFN_XRRFreeGamma)
527             _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma");
528         _glfw.x11.randr.FreeOutputInfo = cast(PFN_XRRFreeOutputInfo)
529             _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo");
530         _glfw.x11.randr.FreeScreenResources = cast(PFN_XRRFreeScreenResources)
531             _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources");
532         _glfw.x11.randr.GetCrtcGamma = cast(PFN_XRRGetCrtcGamma)
533             _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma");
534         _glfw.x11.randr.GetCrtcGammaSize = cast(PFN_XRRGetCrtcGammaSize)
535             _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize");
536         _glfw.x11.randr.GetCrtcInfo = cast(PFN_XRRGetCrtcInfo)
537             _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo");
538         _glfw.x11.randr.GetOutputInfo = cast(PFN_XRRGetOutputInfo)
539             _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo");
540         _glfw.x11.randr.GetOutputPrimary = cast(PFN_XRRGetOutputPrimary)
541             _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary");
542         _glfw.x11.randr.GetScreenResourcesCurrent = cast(PFN_XRRGetScreenResourcesCurrent)
543             _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent");
544         _glfw.x11.randr.QueryExtension = cast(PFN_XRRQueryExtension)
545             _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryExtension");
546         _glfw.x11.randr.QueryVersion = cast(PFN_XRRQueryVersion)
547             _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryVersion");
548         _glfw.x11.randr.SelectInput = cast(PFN_XRRSelectInput)
549             _glfw_dlsym(_glfw.x11.randr.handle, "XRRSelectInput");
550         _glfw.x11.randr.SetCrtcConfig = cast(PFN_XRRSetCrtcConfig)
551             _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig");
552         _glfw.x11.randr.SetCrtcGamma = cast(PFN_XRRSetCrtcGamma)
553             _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma");
554         _glfw.x11.randr.UpdateConfiguration = cast(PFN_XRRUpdateConfiguration)
555             _glfw_dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration");
556 
557         if (_glfw.x11.randr.QueryExtension(_glfw.x11.display,
558                               &_glfw.x11.randr.eventBase,
559                               &_glfw.x11.randr.errorBase))
560         {
561             if (_glfw.x11.randr.QueryVersion(_glfw.x11.display,
562                                 &_glfw.x11.randr.major,
563                                 &_glfw.x11.randr.minor))
564             {
565                 // The GLFW RandR path requires at least version 1.3
566                 if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3)
567                     _glfw.x11.randr.available = GLFW_TRUE;
568             }
569             else
570             {
571                 _glfwInputError(GLFW_PLATFORM_ERROR,
572                                 "X11: Failed to query RandR version");
573             }
574         }
575     }
576 
577     if (_glfw.x11.randr.available)
578     {
579         XRRScreenResources* sr = _glfw.x11.randr.GetScreenResourcesCurrent(_glfw.x11.display,
580                                                               _glfw.x11.root);
581 
582         if (!sr.ncrtc || !_glfw.x11.randr.GetCrtcGammaSize(_glfw.x11.display, sr.crtcs[0]))
583         {
584             // This is likely an older Nvidia driver with broken gamma support
585             // Flag it as useless and fall back to xf86vm gamma, if available
586             _glfw.x11.randr.gammaBroken = GLFW_TRUE;
587         }
588 
589         if (!sr.ncrtc)
590         {
591             // A system without CRTCs is likely a system with broken RandR
592             // Disable the RandR monitor path and fall back to core functions
593             _glfw.x11.randr.monitorBroken = GLFW_TRUE;
594         }
595 
596         _glfw.x11.randr.FreeScreenResources(sr);
597     }
598 
599     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
600     {
601         _glfw.x11.randr.SelectInput(_glfw.x11.display, _glfw.x11.root,
602                        RROutputChangeNotifyMask);
603     }
604 
605 version (Cygwin) {
606     _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor-1.so");
607 } else {
608     _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1");
609 }
610     if (_glfw.x11.xcursor.handle)
611     {
612         _glfw.x11.xcursor.ImageCreate = cast(PFN_XcursorImageCreate)
613             _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate");
614         _glfw.x11.xcursor.ImageDestroy = cast(PFN_XcursorImageDestroy)
615             _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy");
616         _glfw.x11.xcursor.ImageLoadCursor = cast(PFN_XcursorImageLoadCursor)
617             _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor");
618     }
619 
620 version (Cygwin) {
621     _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama-1.so");
622 } else {
623     _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1");
624 }
625     if (_glfw.x11.xinerama.handle)
626     {
627         _glfw.x11.xinerama.IsActive = cast(PFN_XineramaIsActive)
628             _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive");
629         _glfw.x11.xinerama.QueryExtension = cast(PFN_XineramaQueryExtension)
630             _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension");
631         _glfw.x11.xinerama.QueryScreens = cast(PFN_XineramaQueryScreens)
632             _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens");
633 
634         if (_glfw.x11.xinerama.QueryExtension(_glfw.x11.display,
635                                    &_glfw.x11.xinerama.major,
636                                    &_glfw.x11.xinerama.minor))
637         {
638             if (_glfw.x11.xinerama.IsActive(_glfw.x11.display))
639                 _glfw.x11.xinerama.available = GLFW_TRUE;
640         }
641     }
642 
643     _glfw.x11.xkb.major = 1;
644     _glfw.x11.xkb.minor = 0;
645     _glfw.x11.xkb.available =
646         XkbQueryExtension(_glfw.x11.display,
647                           &_glfw.x11.xkb.majorOpcode,
648                           &_glfw.x11.xkb.eventBase,
649                           &_glfw.x11.xkb.errorBase,
650                           &_glfw.x11.xkb.major,
651                           &_glfw.x11.xkb.minor);
652 
653     if (_glfw.x11.xkb.available)
654     {
655         Bool supported;
656 
657         if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported))
658         {
659             if (supported)
660                 _glfw.x11.xkb.detectable = GLFW_TRUE;
661         }
662 
663         _glfw.x11.xkb.group = 0;
664         XkbStateRec state;
665         if (XkbGetState(_glfw.x11.display, XkbUseCoreKbd, &state) == XErrorCode.Success)
666         {
667             XkbSelectEventDetails(_glfw.x11.display, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask);
668             _glfw.x11.xkb.group = cast(uint)state.group;
669         }
670     }
671 
672 version (Cygwin) {
673     _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb-1.so");
674 } else {
675     _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so.1");
676 }
677     if (_glfw.x11.x11xcb.handle)
678     {
679         _glfw.x11.x11xcb.GetXCBConnection = cast(PFN_XGetXCBConnection)
680             _glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection");
681     }
682 
683 version (Cygwin) {
684     _glfw.x11.xrender.handle = _glfw_dlopen("libXrender-1.so");
685 } else {
686     _glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1");
687 }
688     if (_glfw.x11.xrender.handle)
689     {
690         _glfw.x11.xrender.QueryExtension = cast(PFN_XRenderQueryExtension)
691             _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension");
692         _glfw.x11.xrender.QueryVersion = cast(PFN_XRenderQueryVersion)
693             _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion");
694         _glfw.x11.xrender.FindVisualFormat = cast(PFN_XRenderFindVisualFormat)
695             _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat");
696 
697         if (_glfw.x11.xrender.QueryExtension(_glfw.x11.display,
698                                   &_glfw.x11.xrender.errorBase,
699                                   &_glfw.x11.xrender.eventBase))
700         {
701             if (_glfw.x11.xrender.QueryVersion(_glfw.x11.display,
702                                     &_glfw.x11.xrender.major,
703                                     &_glfw.x11.xrender.minor))
704             {
705                 _glfw.x11.xrender.available = GLFW_TRUE;
706             }
707         }
708     }
709 
710     // Update the key code LUT
711     // FIXME: We should listen to XkbMapNotify events to track changes to
712     // the keyboard mapping.
713     createKeyTables();
714 
715     // String format atoms
716     _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False);
717     _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False);
718     _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False);
719 
720     // Custom selection property atom
721     _glfw.x11.GLFW_SELECTION =
722         XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False);
723 
724     // ICCCM standard clipboard atoms
725     _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False);
726     _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False);
727     _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False);
728     _glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False);
729     _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False);
730 
731     // Clipboard manager atoms
732     _glfw.x11.CLIPBOARD_MANAGER =
733         XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False);
734     _glfw.x11.SAVE_TARGETS =
735         XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False);
736 
737     // Xdnd (drag and drop) atoms
738     _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False);
739     _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False);
740     _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False);
741     _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False);
742     _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False);
743     _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False);
744     _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False);
745     _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False);
746     _glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False);
747     _glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False);
748 
749     // ICCCM, EWMH and Motif window property atoms
750     // These can be set safely even without WM support
751     // The EWMH atoms that require WM support are handled in detectEWMH
752     _glfw.x11.WM_PROTOCOLS =
753         XInternAtom(_glfw.x11.display, "WM_PROTOCOLS", False);
754     _glfw.x11.WM_STATE =
755         XInternAtom(_glfw.x11.display, "WM_STATE", False);
756     _glfw.x11.WM_DELETE_WINDOW =
757         XInternAtom(_glfw.x11.display, "WM_DELETE_WINDOW", False);
758     _glfw.x11.NET_SUPPORTED =
759         XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", False);
760     _glfw.x11.NET_SUPPORTING_WM_CHECK =
761         XInternAtom(_glfw.x11.display, "_NET_SUPPORTING_WM_CHECK", False);
762     _glfw.x11.NET_WM_ICON =
763         XInternAtom(_glfw.x11.display, "_NET_WM_ICON", False);
764     _glfw.x11.NET_WM_PING =
765         XInternAtom(_glfw.x11.display, "_NET_WM_PING", False);
766     _glfw.x11.NET_WM_PID =
767         XInternAtom(_glfw.x11.display, "_NET_WM_PID", False);
768     _glfw.x11.NET_WM_NAME =
769         XInternAtom(_glfw.x11.display, "_NET_WM_NAME", False);
770     _glfw.x11.NET_WM_ICON_NAME =
771         XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False);
772     _glfw.x11.NET_WM_BYPASS_COMPOSITOR =
773         XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False);
774     _glfw.x11.NET_WM_WINDOW_OPACITY =
775         XInternAtom(_glfw.x11.display, "_NET_WM_WINDOW_OPACITY", False);
776     _glfw.x11.MOTIF_WM_HINTS =
777         XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False);
778 
779     // The compositing manager selection name contains the screen number
780     {
781         char[32] name;
782         snprintf(name.ptr, typeof(name).sizeof, "_NET_WM_CM_S%u", _glfw.x11.screen);
783         _glfw.x11.NET_WM_CM_Sx = XInternAtom(_glfw.x11.display, name.ptr, False);
784     }
785 
786     // Detect whether an EWMH-conformant window manager is running
787     detectEWMH();
788 
789     return GLFW_TRUE;
790 }
791 
792 // Retrieve system content scale via folklore heuristics
793 //
794 static void getSystemContentScale(float* xscale, float* yscale) {
795     // Start by assuming the default X11 DPI
796     // NOTE: Some desktop environments (KDE) may remove the Xft.dpi field when it
797     //       would be set to 96, so assume that is the case if we cannot find it
798     float xdpi = 96.0f;float ydpi = 96.0f;
799 
800     // NOTE: Basing the scale on Xft.dpi where available should provide the most
801     //       consistent user experience (matches Qt, Gtk, etc), although not
802     //       always the most accurate one
803     char* rms = XResourceManagerString(_glfw.x11.display);
804     if (rms)
805     {
806         XrmDatabase db = XrmGetStringDatabase(rms);
807         if (db)
808         {
809             XrmValue value;
810             char* type = null;
811 
812             if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value))
813             {
814                 if (type && strcmp(type, "String") == 0)
815                     xdpi = ydpi = atof(value.addr);
816             }
817 
818             XrmDestroyDatabase(db);
819         }
820     }
821 
822     *xscale = xdpi / 96.0f;
823     *yscale = ydpi / 96.0f;
824 }
825 
826 // Create a blank cursor for hidden and disabled cursor modes
827 //
828 static Cursor createHiddenCursor() {
829     ubyte[16 * 16 * 4] pixels = 0;
830     GLFWimage image = GLFWimage(16, 16, pixels.ptr);
831     return _glfwCreateCursorX11(&image, 0, 0);
832 }
833 
834 // Create a helper window for IPC
835 //
836 static Window createHelperWindow() {
837     XSetWindowAttributes wa;
838     wa.event_mask = PropertyChangeMask;
839 
840     return XCreateWindow(_glfw.x11.display, _glfw.x11.root,
841                          0, 0, 1, 1, 0, 0,
842                          InputOnly,
843                          DefaultVisual(_glfw.x11.display, _glfw.x11.screen),
844                          CWEventMask, &wa);
845 }
846 
847 // X error handler
848 //
849 static int errorHandler(Display* display, XErrorEvent* event) {
850     _glfw.x11.errorCode = event.error_code;
851     return 0;
852 }
853 
854 
855 //////////////////////////////////////////////////////////////////////////
856 //////                       GLFW internal API                      //////
857 //////////////////////////////////////////////////////////////////////////
858 
859 // Sets the X error handler callback
860 //
861 void _glfwGrabErrorHandlerX11() {
862     _glfw.x11.errorCode = XErrorCode.Success;
863     XSetErrorHandler(&errorHandler);
864 }
865 
866 // Clears the X error handler callback
867 //
868 void _glfwReleaseErrorHandlerX11() {
869     // Synchronize to make sure all commands are processed
870     XSync(_glfw.x11.display, False);
871     XSetErrorHandler(null);
872 }
873 
874 // Reports the specified error, appending information about the last X error
875 //
876 void _glfwInputErrorX11(int error, const(char)* message) {
877     char[_GLFW_MESSAGE_SIZE] buffer;
878     XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode,
879                   buffer.ptr, typeof(buffer).sizeof);
880 
881     _glfwInputError(error, "%s: %s", message, buffer.ptr);
882 }
883 
884 // Creates a native cursor object from the specified image and hotspot
885 //
886 Cursor _glfwCreateCursorX11(const(GLFWimage)* image, int xhot, int yhot) {
887     int i;
888     Cursor cursor;
889 
890     if (!_glfw.x11.xcursor.handle)
891         return None;
892 
893     XcursorImage* native = _glfw.x11.xcursor.ImageCreate(image.width, image.height);
894     if (native == null)
895         return None;
896 
897     native.xhot = xhot;
898     native.yhot = yhot;
899 
900     ubyte* source = cast(ubyte*) image.pixels;
901     XcursorPixel* target = native.pixels;
902 
903     for (i = 0;  i < image.width * image.height;  i++, target++, source += 4)
904     {
905         uint alpha = source[3];
906 
907         *target = (alpha << 24) |
908                   (cast(ubyte) ((source[0] * alpha) / 255) << 16) |
909                   (cast(ubyte) ((source[1] * alpha) / 255) <<  8) |
910                   (cast(ubyte) ((source[2] * alpha) / 255) <<  0);
911     }
912 
913     cursor = _glfw.x11.xcursor.ImageLoadCursor(_glfw.x11.display, native);
914     _glfw.x11.xcursor.ImageDestroy(native);
915 
916     return cursor;
917 }
918 
919 
920 //////////////////////////////////////////////////////////////////////////
921 //////                       GLFW platform API                      //////
922 //////////////////////////////////////////////////////////////////////////
923 
924 int _glfwPlatformInit() {
925 version (X_HAVE_UTF8_STRING) {} else {
926     // HACK: If the current locale is "C" and the Xlib UTF-8 functions are
927     //       unavailable, apply the environment's locale in the hope that it's
928     //       both available and not "C"
929     //       This is done because the "C" locale breaks wide character input,
930     //       which is what we fall back on when UTF-8 support is missing
931     if (strcmp(setlocale(LC_CTYPE, null), "C") == 0)
932         setlocale(LC_CTYPE, "");
933 }
934 
935     XInitThreads();
936     XrmInitialize();
937 
938     _glfw.x11.display = XOpenDisplay(null);
939     if (!_glfw.x11.display)
940     {
941         const(char)* display = getenv("DISPLAY");
942         if (display)
943         {
944             _glfwInputError(GLFW_PLATFORM_ERROR,
945                             "X11: Failed to open display %s", display);
946         }
947         else
948         {
949             _glfwInputError(GLFW_PLATFORM_ERROR,
950                             "X11: The DISPLAY environment variable is missing");
951         }
952 
953         return GLFW_FALSE;
954     }
955 
956     _glfw.x11.screen = DefaultScreen(_glfw.x11.display);
957     _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen);
958     _glfw.x11.context = XUniqueContext();
959 
960     getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY);
961 
962     if (!initExtensions())
963         return GLFW_FALSE;
964 
965     _glfw.x11.helperWindowHandle = createHelperWindow();
966     _glfw.x11.hiddenCursorHandle = createHiddenCursor();
967 
968     if (XSupportsLocale())
969     {
970         XSetLocaleModifiers("");
971 
972         _glfw.x11.im = XOpenIM(_glfw.x11.display, null, null, null);
973         if (_glfw.x11.im)
974         {
975             if (!hasUsableInputMethodStyle())
976             {
977                 XCloseIM(_glfw.x11.im);
978                 _glfw.x11.im = null;
979             }
980         }
981     }
982 
983     version (linux) {
984         if (!_glfwInitJoysticksLinux())
985             return GLFW_FALSE;
986     }
987 
988     _glfwInitTimerPOSIX();
989 
990     _glfwPollMonitorsX11();
991     return GLFW_TRUE;
992 }
993 
994 void _glfwPlatformTerminate() {
995     if (_glfw.x11.helperWindowHandle)
996     {
997         if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) ==
998             _glfw.x11.helperWindowHandle)
999         {
1000             _glfwPushSelectionToManagerX11();
1001         }
1002 
1003         XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle);
1004         _glfw.x11.helperWindowHandle = None;
1005     }
1006 
1007     if (_glfw.x11.hiddenCursorHandle)
1008     {
1009         XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle);
1010         _glfw.x11.hiddenCursorHandle = cast(Cursor) 0;
1011     }
1012 
1013     free(_glfw.x11.primarySelectionString);
1014     free(_glfw.x11.clipboardString);
1015 
1016     if (_glfw.x11.im)
1017     {
1018         XCloseIM(_glfw.x11.im);
1019         _glfw.x11.im = null;
1020     }
1021 
1022     if (_glfw.x11.display)
1023     {
1024         XCloseDisplay(_glfw.x11.display);
1025         _glfw.x11.display = null;
1026     }
1027 
1028     if (_glfw.x11.x11xcb.handle)
1029     {
1030         _glfw_dlclose(_glfw.x11.x11xcb.handle);
1031         _glfw.x11.x11xcb.handle = null;
1032     }
1033 
1034     if (_glfw.x11.xcursor.handle)
1035     {
1036         _glfw_dlclose(_glfw.x11.xcursor.handle);
1037         _glfw.x11.xcursor.handle = null;
1038     }
1039 
1040     if (_glfw.x11.randr.handle)
1041     {
1042         _glfw_dlclose(_glfw.x11.randr.handle);
1043         _glfw.x11.randr.handle = null;
1044     }
1045 
1046     if (_glfw.x11.xinerama.handle)
1047     {
1048         _glfw_dlclose(_glfw.x11.xinerama.handle);
1049         _glfw.x11.xinerama.handle = null;
1050     }
1051 
1052     if (_glfw.x11.xrender.handle)
1053     {
1054         _glfw_dlclose(_glfw.x11.xrender.handle);
1055         _glfw.x11.xrender.handle = null;
1056     }
1057 
1058     if (_glfw.x11.vidmode.handle)
1059     {
1060         _glfw_dlclose(_glfw.x11.vidmode.handle);
1061         _glfw.x11.vidmode.handle = null;
1062     }
1063 
1064     if (_glfw.x11.xi.handle)
1065     {
1066         _glfw_dlclose(_glfw.x11.xi.handle);
1067         _glfw.x11.xi.handle = null;
1068     }
1069 
1070     // NOTE: These need to be unloaded after XCloseDisplay, as they register
1071     //       cleanup callbacks that get called by that function
1072     _glfwTerminateEGL();
1073     _glfwTerminateGLX();
1074 
1075     version (linux) {
1076         _glfwTerminateJoysticksLinux();
1077     }
1078 }
1079 
1080 const(char)* _glfwPlatformGetVersionString() {
1081     version (linux) {
1082         enum evdev = " evdev";
1083     } else {
1084         enum evdev = "";
1085     }
1086     version (_GLFW_BUILD_DLL) {
1087         enum dllStr = " shared";
1088     } else {
1089         enum dllStr = "";
1090     }
1091     return _GLFW_VERSION_NUMBER ~ " X11 GLX EGL OSMesa" ~ evdev ~ dllStr;
1092 }