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