• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //========================================================================
2 // GLFW 3.2 X11 - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2002-2006 Marcus Geelnard
5 // Copyright (c) 2006-2016 Camilla Berglund <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.publicKeys, -1, sizeof(_glfw.x11.publicKeys));
236     memset(_glfw.x11.nativeKeys, -1, sizeof(_glfw.x11.nativeKeys));
237 
238     if (_glfw.x11.xkb.available)
239     {
240         // Use XKB to determine physical key locations independently of the current
241         // 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.publicKeys[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.publicKeys[scancode] < 0)
320             _glfw.x11.publicKeys[scancode] = translateKeyCode(scancode);
321 
322         // Store the reverse translation for faster key name lookup
323         if (_glfw.x11.publicKeys[scancode] > 0)
324             _glfw.x11.nativeKeys[_glfw.x11.publicKeys[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) != 1)
388     {
389         if (windowFromRoot)
390             XFree(windowFromRoot);
391         return;
392     }
393 
394     _glfwGrabErrorHandlerX11();
395 
396     // It should be the ID of a child window (of the root)
397     // Then we look for the same property on the child window
398     if (_glfwGetWindowPropertyX11(*windowFromRoot,
399                                   supportingWmCheck,
400                                   XA_WINDOW,
401                                   (unsigned char**) &windowFromChild) != 1)
402     {
403         XFree(windowFromRoot);
404         if (windowFromChild)
405             XFree(windowFromChild);
406         return;
407     }
408 
409     _glfwReleaseErrorHandlerX11();
410 
411     // It should be the ID of that same child window
412     if (*windowFromRoot != *windowFromChild)
413     {
414         XFree(windowFromRoot);
415         XFree(windowFromChild);
416         return;
417     }
418 
419     XFree(windowFromRoot);
420     XFree(windowFromChild);
421 
422     // We are now fairly sure that an EWMH-compliant window manager is running
423 
424     Atom* supportedAtoms;
425     unsigned long atomCount;
426 
427     // Now we need to check the _NET_SUPPORTED property of the root window
428     // It should be a list of supported WM protocol and state atoms
429     atomCount = _glfwGetWindowPropertyX11(_glfw.x11.root,
430                                           wmSupported,
431                                           XA_ATOM,
432                                           (unsigned char**) &supportedAtoms);
433 
434     // See which of the atoms we support that are supported by the WM
435     _glfw.x11.NET_WM_STATE =
436         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE");
437     _glfw.x11.NET_WM_STATE_ABOVE =
438         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE");
439     _glfw.x11.NET_WM_STATE_FULLSCREEN =
440         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN");
441     _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT =
442         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT");
443     _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ =
444         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ");
445     _glfw.x11.NET_WM_FULLSCREEN_MONITORS =
446         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS");
447     _glfw.x11.NET_WM_WINDOW_TYPE =
448         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE");
449     _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL =
450         getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL");
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     XFree(supportedAtoms);
459 }
460 
461 // Initialize X11 display and look for supported X11 extensions
462 //
initExtensions(void)463 static GLFWbool initExtensions(void)
464 {
465 #if defined(_GLFW_HAS_XF86VM)
466     // Check for XF86VidMode extension
467     _glfw.x11.vidmode.available =
468         XF86VidModeQueryExtension(_glfw.x11.display,
469                                   &_glfw.x11.vidmode.eventBase,
470                                   &_glfw.x11.vidmode.errorBase);
471 #endif /*_GLFW_HAS_XF86VM*/
472 
473     // Check for RandR extension
474     if (XRRQueryExtension(_glfw.x11.display,
475                           &_glfw.x11.randr.eventBase,
476                           &_glfw.x11.randr.errorBase))
477     {
478         if (XRRQueryVersion(_glfw.x11.display,
479                             &_glfw.x11.randr.major,
480                             &_glfw.x11.randr.minor))
481         {
482             // The GLFW RandR path requires at least version 1.3
483             if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3)
484                 _glfw.x11.randr.available = GLFW_TRUE;
485         }
486         else
487         {
488             _glfwInputError(GLFW_PLATFORM_ERROR,
489                             "X11: Failed to query RandR version");
490         }
491     }
492 
493     if (_glfw.x11.randr.available)
494     {
495         XRRScreenResources* sr = XRRGetScreenResources(_glfw.x11.display,
496                                                        _glfw.x11.root);
497 
498         if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0]))
499         {
500             // This is either a headless system or an older Nvidia binary driver
501             // with broken gamma support
502             // Flag it as useless and fall back to Xf86VidMode gamma, if
503             // available
504             _glfwInputError(GLFW_PLATFORM_ERROR,
505                             "X11: RandR gamma ramp support seems broken");
506             _glfw.x11.randr.gammaBroken = GLFW_TRUE;
507         }
508 
509         XRRFreeScreenResources(sr);
510 
511         XRRSelectInput(_glfw.x11.display, _glfw.x11.root,
512                        RROutputChangeNotifyMask);
513     }
514 
515     if (XineramaQueryExtension(_glfw.x11.display,
516                                &_glfw.x11.xinerama.major,
517                                &_glfw.x11.xinerama.minor))
518     {
519         if (XineramaIsActive(_glfw.x11.display))
520             _glfw.x11.xinerama.available = GLFW_TRUE;
521     }
522 
523     // Check if Xkb is supported on this display
524     _glfw.x11.xkb.major = 1;
525     _glfw.x11.xkb.minor = 0;
526     _glfw.x11.xkb.available =
527         XkbQueryExtension(_glfw.x11.display,
528                           &_glfw.x11.xkb.majorOpcode,
529                           &_glfw.x11.xkb.eventBase,
530                           &_glfw.x11.xkb.errorBase,
531                           &_glfw.x11.xkb.major,
532                           &_glfw.x11.xkb.minor);
533 
534     if (_glfw.x11.xkb.available)
535     {
536         Bool supported;
537 
538         if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported))
539         {
540             if (supported)
541                 _glfw.x11.xkb.detectable = GLFW_TRUE;
542         }
543     }
544 
545     _glfw.x11.x11xcb.handle = dlopen("libX11-xcb.so", RTLD_LAZY | RTLD_GLOBAL);
546     if (_glfw.x11.x11xcb.handle)
547     {
548         _glfw.x11.x11xcb.XGetXCBConnection = (XGETXCBCONNECTION_T)
549             dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection");
550     }
551 
552     // Update the key code LUT
553     // FIXME: We should listen to XkbMapNotify events to track changes to
554     // the keyboard mapping.
555     createKeyTables();
556 
557     // Detect whether an EWMH-conformant window manager is running
558     detectEWMH();
559 
560     // String format atoms
561     _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False);
562     _glfw.x11.UTF8_STRING =
563         XInternAtom(_glfw.x11.display, "UTF8_STRING", False);
564     _glfw.x11.COMPOUND_STRING =
565         XInternAtom(_glfw.x11.display, "COMPOUND_STRING", False);
566     _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False);
567 
568     // Custom selection property atom
569     _glfw.x11.GLFW_SELECTION =
570         XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False);
571 
572     // ICCCM standard clipboard atoms
573     _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False);
574     _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False);
575     _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False);
576 
577     // Clipboard manager atoms
578     _glfw.x11.CLIPBOARD_MANAGER =
579         XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False);
580     _glfw.x11.SAVE_TARGETS =
581         XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False);
582 
583     // Xdnd (drag and drop) atoms
584     _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False);
585     _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False);
586     _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False);
587     _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False);
588     _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False);
589     _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False);
590     _glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", False);
591     _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False);
592     _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False);
593 
594     // ICCCM, EWMH and Motif window property atoms
595     // These can be set safely even without WM support
596     // The EWMH atoms that require WM support are handled in detectEWMH
597     _glfw.x11.WM_PROTOCOLS =
598         XInternAtom(_glfw.x11.display, "WM_PROTOCOLS", False);
599     _glfw.x11.WM_STATE =
600         XInternAtom(_glfw.x11.display, "WM_STATE", False);
601     _glfw.x11.WM_DELETE_WINDOW =
602         XInternAtom(_glfw.x11.display, "WM_DELETE_WINDOW", False);
603     _glfw.x11.NET_WM_ICON =
604         XInternAtom(_glfw.x11.display, "_NET_WM_ICON", False);
605     _glfw.x11.NET_WM_PING =
606         XInternAtom(_glfw.x11.display, "_NET_WM_PING", False);
607     _glfw.x11.NET_WM_PID =
608         XInternAtom(_glfw.x11.display, "_NET_WM_PID", False);
609     _glfw.x11.NET_WM_NAME =
610         XInternAtom(_glfw.x11.display, "_NET_WM_NAME", False);
611     _glfw.x11.NET_WM_ICON_NAME =
612         XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False);
613     _glfw.x11.NET_WM_BYPASS_COMPOSITOR =
614         XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False);
615     _glfw.x11.MOTIF_WM_HINTS =
616         XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False);
617 
618     return GLFW_TRUE;
619 }
620 
621 // Create a blank cursor for hidden and disabled cursor modes
622 //
createHiddenCursor(void)623 static Cursor createHiddenCursor(void)
624 {
625     unsigned char pixels[16 * 16 * 4];
626     GLFWimage image = { 16, 16, pixels };
627 
628     memset(pixels, 0, sizeof(pixels));
629 
630     return _glfwCreateCursorX11(&image, 0, 0);
631 }
632 
633 // X error handler
634 //
errorHandler(Display * display,XErrorEvent * event)635 static int errorHandler(Display *display, XErrorEvent* event)
636 {
637     _glfw.x11.errorCode = event->error_code;
638     return 0;
639 }
640 
641 
642 //////////////////////////////////////////////////////////////////////////
643 //////                       GLFW internal API                      //////
644 //////////////////////////////////////////////////////////////////////////
645 
646 // Sets the X error handler callback
647 //
_glfwGrabErrorHandlerX11(void)648 void _glfwGrabErrorHandlerX11(void)
649 {
650     _glfw.x11.errorCode = Success;
651     XSetErrorHandler(errorHandler);
652 }
653 
654 // Clears the X error handler callback
655 //
_glfwReleaseErrorHandlerX11(void)656 void _glfwReleaseErrorHandlerX11(void)
657 {
658     // Synchronize to make sure all commands are processed
659     XSync(_glfw.x11.display, False);
660     XSetErrorHandler(NULL);
661 }
662 
663 // Reports the specified error, appending information about the last X error
664 //
_glfwInputErrorX11(int error,const char * message)665 void _glfwInputErrorX11(int error, const char* message)
666 {
667     char buffer[8192];
668     XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode,
669                   buffer, sizeof(buffer));
670 
671     _glfwInputError(error, "%s: %s", message, buffer);
672 }
673 
674 // Creates a native cursor object from the specified image and hotspot
675 //
_glfwCreateCursorX11(const GLFWimage * image,int xhot,int yhot)676 Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot)
677 {
678     int i;
679     Cursor cursor;
680 
681     XcursorImage* native = XcursorImageCreate(image->width, image->height);
682     if (native == NULL)
683         return None;
684 
685     native->xhot = xhot;
686     native->yhot = yhot;
687 
688     unsigned char* source = (unsigned char*) image->pixels;
689     XcursorPixel* target = native->pixels;
690 
691     for (i = 0;  i < image->width * image->height;  i++, target++, source += 4)
692     {
693         unsigned int alpha = source[3];
694 
695         *target = (alpha << 24) |
696                   ((unsigned char) ((source[0] * alpha) / 255) << 16) |
697                   ((unsigned char) ((source[1] * alpha) / 255) <<  8) |
698                   ((unsigned char) ((source[2] * alpha) / 255) <<  0);
699     }
700 
701     cursor = XcursorImageLoadCursor(_glfw.x11.display, native);
702     XcursorImageDestroy(native);
703 
704     return cursor;
705 }
706 
707 
708 //////////////////////////////////////////////////////////////////////////
709 //////                       GLFW platform API                      //////
710 //////////////////////////////////////////////////////////////////////////
711 
_glfwPlatformInit(void)712 int _glfwPlatformInit(void)
713 {
714 #if !defined(X_HAVE_UTF8_STRING)
715     // HACK: If the current locale is C, apply the environment's locale
716     //       This is done because the C locale breaks wide character input
717     if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0)
718         setlocale(LC_CTYPE, "");
719 #endif
720 
721     XInitThreads();
722 
723     _glfw.x11.display = XOpenDisplay(NULL);
724     if (!_glfw.x11.display)
725     {
726         const char* display = getenv("DISPLAY");
727         if (display)
728         {
729             _glfwInputError(GLFW_PLATFORM_ERROR,
730                             "X11: Failed to open display %s", display);
731         }
732         else
733         {
734             _glfwInputError(GLFW_PLATFORM_ERROR,
735                             "X11: The DISPLAY environment variable is missing");
736         }
737 
738         return GLFW_FALSE;
739     }
740 
741     _glfw.x11.screen = DefaultScreen(_glfw.x11.display);
742     _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen);
743     _glfw.x11.context = XUniqueContext();
744 
745     if (!initExtensions())
746         return GLFW_FALSE;
747 
748     _glfw.x11.cursor = createHiddenCursor();
749 
750     if (XSupportsLocale())
751     {
752         XSetLocaleModifiers("");
753 
754         _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL);
755         if (_glfw.x11.im)
756         {
757             if (!hasUsableInputMethodStyle())
758             {
759                 XCloseIM(_glfw.x11.im);
760                 _glfw.x11.im = NULL;
761             }
762         }
763     }
764 
765     if (!_glfwInitThreadLocalStoragePOSIX())
766         return GLFW_FALSE;
767 
768     if (!_glfwInitJoysticksLinux())
769         return GLFW_FALSE;
770 
771     _glfwInitTimerPOSIX();
772 
773     return GLFW_TRUE;
774 }
775 
_glfwPlatformTerminate(void)776 void _glfwPlatformTerminate(void)
777 {
778     if (_glfw.x11.x11xcb.handle)
779     {
780         dlclose(_glfw.x11.x11xcb.handle);
781         _glfw.x11.x11xcb.handle = NULL;
782     }
783 
784     if (_glfw.x11.cursor)
785     {
786         XFreeCursor(_glfw.x11.display, _glfw.x11.cursor);
787         _glfw.x11.cursor = (Cursor) 0;
788     }
789 
790     free(_glfw.x11.clipboardString);
791 
792     if (_glfw.x11.im)
793     {
794         XCloseIM(_glfw.x11.im);
795         _glfw.x11.im = NULL;
796     }
797 
798     _glfwTerminateEGL();
799 
800     if (_glfw.x11.display)
801     {
802         XCloseDisplay(_glfw.x11.display);
803         _glfw.x11.display = NULL;
804     }
805 
806     // NOTE: This needs to be done after XCloseDisplay, as libGL registers
807     //       cleanup callbacks that get called by it
808     _glfwTerminateGLX();
809 
810     _glfwTerminateJoysticksLinux();
811     _glfwTerminateThreadLocalStoragePOSIX();
812 }
813 
_glfwPlatformGetVersionString(void)814 const char* _glfwPlatformGetVersionString(void)
815 {
816     return _GLFW_VERSION_NUMBER " X11 GLX EGL"
817 #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)
818         " clock_gettime"
819 #else
820         " gettimeofday"
821 #endif
822 #if defined(__linux__)
823         " /dev/js"
824 #endif
825 #if defined(_GLFW_HAS_XF86VM)
826         " Xf86vm"
827 #endif
828 #if defined(_GLFW_BUILD_DLL)
829         " shared"
830 #endif
831         ;
832 }
833 
834