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