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