x11_init.cc (38638B)
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 // 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 // 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 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.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 // 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 // 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 // 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 // 462 static GLFWbool initExtensions(void) 463 { 464 _glfw.x11.vidmode.handle = dlopen("libXxf86vm.so.1", RTLD_LAZY | RTLD_GLOBAL); 465 if (_glfw.x11.vidmode.handle) 466 { 467 _glfw.x11.vidmode.QueryExtension = (PFN_XF86VidModeQueryExtension) 468 dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension"); 469 _glfw.x11.vidmode.GetGammaRamp = (PFN_XF86VidModeGetGammaRamp) 470 dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp"); 471 _glfw.x11.vidmode.SetGammaRamp = (PFN_XF86VidModeSetGammaRamp) 472 dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp"); 473 _glfw.x11.vidmode.GetGammaRampSize = (PFN_XF86VidModeGetGammaRampSize) 474 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 _glfw.x11.xi.handle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_GLOBAL); 483 if (_glfw.x11.xi.handle) 484 { 485 _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) 486 dlsym(_glfw.x11.xi.handle, "XIQueryVersion"); 487 _glfw.x11.xi.SelectEvents = (PFN_XISelectEvents) 488 dlsym(_glfw.x11.xi.handle, "XISelectEvents"); 489 490 if (XQueryExtension(_glfw.x11.display, 491 "XInputExtension", 492 &_glfw.x11.xi.majorOpcode, 493 &_glfw.x11.xi.eventBase, 494 &_glfw.x11.xi.errorBase)) 495 { 496 _glfw.x11.xi.major = 2; 497 _glfw.x11.xi.minor = 0; 498 499 if (XIQueryVersion(_glfw.x11.display, 500 &_glfw.x11.xi.major, 501 &_glfw.x11.xi.minor) == Success) 502 { 503 _glfw.x11.xi.available = GLFW_TRUE; 504 } 505 } 506 } 507 508 _glfw.x11.randr.handle = dlopen("libXrandr.so.2", RTLD_LAZY | RTLD_GLOBAL); 509 if (_glfw.x11.randr.handle) 510 { 511 _glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma) 512 dlsym(_glfw.x11.randr.handle, "XRRAllocGamma"); 513 _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) 514 dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); 515 _glfw.x11.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo) 516 dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo"); 517 _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) 518 dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); 519 _glfw.x11.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo) 520 dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo"); 521 _glfw.x11.randr.FreeScreenResources = (PFN_XRRFreeScreenResources) 522 dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources"); 523 _glfw.x11.randr.GetCrtcGamma = (PFN_XRRGetCrtcGamma) 524 dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma"); 525 _glfw.x11.randr.GetCrtcGammaSize = (PFN_XRRGetCrtcGammaSize) 526 dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize"); 527 _glfw.x11.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo) 528 dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo"); 529 _glfw.x11.randr.GetOutputInfo = (PFN_XRRGetOutputInfo) 530 dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo"); 531 _glfw.x11.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary) 532 dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary"); 533 _glfw.x11.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent) 534 dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent"); 535 _glfw.x11.randr.QueryExtension = (PFN_XRRQueryExtension) 536 dlsym(_glfw.x11.randr.handle, "XRRQueryExtension"); 537 _glfw.x11.randr.QueryVersion = (PFN_XRRQueryVersion) 538 dlsym(_glfw.x11.randr.handle, "XRRQueryVersion"); 539 _glfw.x11.randr.SelectInput = (PFN_XRRSelectInput) 540 dlsym(_glfw.x11.randr.handle, "XRRSelectInput"); 541 _glfw.x11.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig) 542 dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig"); 543 _glfw.x11.randr.SetCrtcGamma = (PFN_XRRSetCrtcGamma) 544 dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma"); 545 _glfw.x11.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration) 546 dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration"); 547 548 if (XRRQueryExtension(_glfw.x11.display, 549 &_glfw.x11.randr.eventBase, 550 &_glfw.x11.randr.errorBase)) 551 { 552 if (XRRQueryVersion(_glfw.x11.display, 553 &_glfw.x11.randr.major, 554 &_glfw.x11.randr.minor)) 555 { 556 // The GLFW RandR path requires at least version 1.3 557 if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3) 558 _glfw.x11.randr.available = GLFW_TRUE; 559 } 560 else 561 { 562 _glfwInputError(GLFW_PLATFORM_ERROR, 563 "X11: Failed to query RandR version"); 564 } 565 } 566 } 567 568 if (_glfw.x11.randr.available) 569 { 570 XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, 571 _glfw.x11.root); 572 573 if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0])) 574 { 575 // This is likely an older Nvidia driver with broken gamma support 576 // Flag it as useless and fall back to xf86vm gamma, if available 577 _glfw.x11.randr.gammaBroken = GLFW_TRUE; 578 } 579 580 if (!sr->ncrtc) 581 { 582 // A system without CRTCs is likely a system with broken RandR 583 // Disable the RandR monitor path and fall back to core functions 584 _glfw.x11.randr.monitorBroken = GLFW_TRUE; 585 } 586 587 XRRFreeScreenResources(sr); 588 } 589 590 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) 591 { 592 XRRSelectInput(_glfw.x11.display, _glfw.x11.root, 593 RROutputChangeNotifyMask); 594 } 595 596 _glfw.x11.xcursor.handle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_GLOBAL); 597 if (_glfw.x11.xcursor.handle) 598 { 599 _glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate) 600 dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate"); 601 _glfw.x11.xcursor.ImageDestroy = (PFN_XcursorImageDestroy) 602 dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); 603 _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) 604 dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); 605 } 606 607 _glfw.x11.xinerama.handle = dlopen("libXinerama.so.1", RTLD_LAZY | RTLD_GLOBAL); 608 if (_glfw.x11.xinerama.handle) 609 { 610 _glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive) 611 dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive"); 612 _glfw.x11.xinerama.QueryExtension = (PFN_XineramaQueryExtension) 613 dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension"); 614 _glfw.x11.xinerama.QueryScreens = (PFN_XineramaQueryScreens) 615 dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens"); 616 617 if (XineramaQueryExtension(_glfw.x11.display, 618 &_glfw.x11.xinerama.major, 619 &_glfw.x11.xinerama.minor)) 620 { 621 if (XineramaIsActive(_glfw.x11.display)) 622 _glfw.x11.xinerama.available = GLFW_TRUE; 623 } 624 } 625 626 _glfw.x11.xkb.major = 1; 627 _glfw.x11.xkb.minor = 0; 628 _glfw.x11.xkb.available = 629 XkbQueryExtension(_glfw.x11.display, 630 &_glfw.x11.xkb.majorOpcode, 631 &_glfw.x11.xkb.eventBase, 632 &_glfw.x11.xkb.errorBase, 633 &_glfw.x11.xkb.major, 634 &_glfw.x11.xkb.minor); 635 636 if (_glfw.x11.xkb.available) 637 { 638 Bool supported; 639 640 if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported)) 641 { 642 if (supported) 643 _glfw.x11.xkb.detectable = GLFW_TRUE; 644 } 645 } 646 647 _glfw.x11.x11xcb.handle = dlopen("libX11-xcb.so.1", RTLD_LAZY | RTLD_GLOBAL); 648 if (_glfw.x11.x11xcb.handle) 649 { 650 _glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection) 651 dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); 652 } 653 654 // Update the key code LUT 655 // FIXME: We should listen to XkbMapNotify events to track changes to 656 // the keyboard mapping. 657 createKeyTables(); 658 659 // Detect whether an EWMH-conformant window manager is running 660 detectEWMH(); 661 662 // String format atoms 663 _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); 664 _glfw.x11.UTF8_STRING = 665 XInternAtom(_glfw.x11.display, "UTF8_STRING", False); 666 _glfw.x11.COMPOUND_STRING = 667 XInternAtom(_glfw.x11.display, "COMPOUND_STRING", False); 668 _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); 669 670 // Custom selection property atom 671 _glfw.x11.GLFW_SELECTION = 672 XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False); 673 674 // ICCCM standard clipboard atoms 675 _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); 676 _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); 677 _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False); 678 _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); 679 680 // Clipboard manager atoms 681 _glfw.x11.CLIPBOARD_MANAGER = 682 XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False); 683 _glfw.x11.SAVE_TARGETS = 684 XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False); 685 686 // Xdnd (drag and drop) atoms 687 _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False); 688 _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False); 689 _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False); 690 _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); 691 _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); 692 _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False); 693 _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False); 694 _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False); 695 _glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False); 696 _glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False); 697 698 // ICCCM, EWMH and Motif window property atoms 699 // These can be set safely even without WM support 700 // The EWMH atoms that require WM support are handled in detectEWMH 701 _glfw.x11.WM_PROTOCOLS = 702 XInternAtom(_glfw.x11.display, "WM_PROTOCOLS", False); 703 _glfw.x11.WM_STATE = 704 XInternAtom(_glfw.x11.display, "WM_STATE", False); 705 _glfw.x11.WM_DELETE_WINDOW = 706 XInternAtom(_glfw.x11.display, "WM_DELETE_WINDOW", False); 707 _glfw.x11.NET_WM_ICON = 708 XInternAtom(_glfw.x11.display, "_NET_WM_ICON", False); 709 _glfw.x11.NET_WM_PING = 710 XInternAtom(_glfw.x11.display, "_NET_WM_PING", False); 711 _glfw.x11.NET_WM_PID = 712 XInternAtom(_glfw.x11.display, "_NET_WM_PID", False); 713 _glfw.x11.NET_WM_NAME = 714 XInternAtom(_glfw.x11.display, "_NET_WM_NAME", False); 715 _glfw.x11.NET_WM_ICON_NAME = 716 XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False); 717 _glfw.x11.NET_WM_BYPASS_COMPOSITOR = 718 XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False); 719 _glfw.x11.MOTIF_WM_HINTS = 720 XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); 721 722 return GLFW_TRUE; 723 } 724 725 // Create a blank cursor for hidden and disabled cursor modes 726 // 727 static Cursor createHiddenCursor(void) 728 { 729 unsigned char pixels[16 * 16 * 4] = { 0 }; 730 GLFWimage image = { 16, 16, pixels }; 731 return _glfwCreateCursorX11(&image, 0, 0); 732 } 733 734 // Create a helper window for IPC 735 // 736 static Window createHelperWindow(void) 737 { 738 XSetWindowAttributes wa; 739 wa.event_mask = PropertyChangeMask; 740 741 return XCreateWindow(_glfw.x11.display, _glfw.x11.root, 742 0, 0, 1, 1, 0, 0, 743 InputOnly, 744 DefaultVisual(_glfw.x11.display, _glfw.x11.screen), 745 CWEventMask, &wa); 746 } 747 748 // X error handler 749 // 750 static int errorHandler(Display *display, XErrorEvent* event) 751 { 752 _glfw.x11.errorCode = event->error_code; 753 return 0; 754 } 755 756 757 ////////////////////////////////////////////////////////////////////////// 758 ////// GLFW internal API ////// 759 ////////////////////////////////////////////////////////////////////////// 760 761 // Sets the X error handler callback 762 // 763 void _glfwGrabErrorHandlerX11(void) 764 { 765 _glfw.x11.errorCode = Success; 766 XSetErrorHandler(errorHandler); 767 } 768 769 // Clears the X error handler callback 770 // 771 void _glfwReleaseErrorHandlerX11(void) 772 { 773 // Synchronize to make sure all commands are processed 774 XSync(_glfw.x11.display, False); 775 XSetErrorHandler(NULL); 776 } 777 778 // Reports the specified error, appending information about the last X error 779 // 780 void _glfwInputErrorX11(int error, const char* message) 781 { 782 char buffer[_GLFW_MESSAGE_SIZE]; 783 XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode, 784 buffer, sizeof(buffer)); 785 786 _glfwInputError(error, "%s: %s", message, buffer); 787 } 788 789 // Creates a native cursor object from the specified image and hotspot 790 // 791 Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) 792 { 793 int i; 794 Cursor cursor; 795 796 if (!_glfw.x11.xcursor.handle) 797 return None; 798 799 XcursorImage* native = XcursorImageCreate(image->width, image->height); 800 if (native == NULL) 801 return None; 802 803 native->xhot = xhot; 804 native->yhot = yhot; 805 806 unsigned char* source = (unsigned char*) image->pixels; 807 XcursorPixel* target = native->pixels; 808 809 for (i = 0; i < image->width * image->height; i++, target++, source += 4) 810 { 811 unsigned int alpha = source[3]; 812 813 *target = (alpha << 24) | 814 ((unsigned char) ((source[0] * alpha) / 255) << 16) | 815 ((unsigned char) ((source[1] * alpha) / 255) << 8) | 816 ((unsigned char) ((source[2] * alpha) / 255) << 0); 817 } 818 819 cursor = XcursorImageLoadCursor(_glfw.x11.display, native); 820 XcursorImageDestroy(native); 821 822 return cursor; 823 } 824 825 826 ////////////////////////////////////////////////////////////////////////// 827 ////// GLFW platform API ////// 828 ////////////////////////////////////////////////////////////////////////// 829 830 int _glfwPlatformInit(void) 831 { 832 #if !defined(X_HAVE_UTF8_STRING) 833 // HACK: If the current locale is "C" and the Xlib UTF-8 functions are 834 // unavailable, apply the environment's locale in the hope that it's 835 // both available and not "C" 836 // This is done because the "C" locale breaks wide character input, 837 // which is what we fall back on when UTF-8 support is missing 838 if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) 839 setlocale(LC_CTYPE, ""); 840 #endif 841 842 XInitThreads(); 843 844 _glfw.x11.display = XOpenDisplay(NULL); 845 if (!_glfw.x11.display) 846 { 847 const char* display = getenv("DISPLAY"); 848 if (display) 849 { 850 _glfwInputError(GLFW_PLATFORM_ERROR, 851 "X11: Failed to open display %s", display); 852 } 853 else 854 { 855 _glfwInputError(GLFW_PLATFORM_ERROR, 856 "X11: The DISPLAY environment variable is missing"); 857 } 858 859 return GLFW_FALSE; 860 } 861 862 _glfw.x11.screen = DefaultScreen(_glfw.x11.display); 863 _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); 864 _glfw.x11.context = XUniqueContext(); 865 _glfw.x11.helperWindowHandle = createHelperWindow(); 866 _glfw.x11.hiddenCursorHandle = createHiddenCursor(); 867 868 if (!initExtensions()) 869 return GLFW_FALSE; 870 871 if (XSupportsLocale()) 872 { 873 XSetLocaleModifiers(""); 874 875 _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL); 876 if (_glfw.x11.im) 877 { 878 if (!hasUsableInputMethodStyle()) 879 { 880 XCloseIM(_glfw.x11.im); 881 _glfw.x11.im = NULL; 882 } 883 } 884 } 885 886 #if defined(__linux__) 887 if (!_glfwInitJoysticksLinux()) 888 return GLFW_FALSE; 889 #endif 890 891 _glfwInitTimerPOSIX(); 892 893 _glfwPollMonitorsX11(); 894 return GLFW_TRUE; 895 } 896 897 void _glfwPlatformTerminate(void) 898 { 899 if (_glfw.x11.helperWindowHandle) 900 { 901 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == 902 _glfw.x11.helperWindowHandle) 903 { 904 _glfwPushSelectionToManagerX11(); 905 } 906 907 XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle); 908 _glfw.x11.helperWindowHandle = None; 909 } 910 911 if (_glfw.x11.hiddenCursorHandle) 912 { 913 XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle); 914 _glfw.x11.hiddenCursorHandle = (Cursor) 0; 915 } 916 917 free(_glfw.x11.primarySelectionString); 918 free(_glfw.x11.clipboardString); 919 920 if (_glfw.x11.im) 921 { 922 XCloseIM(_glfw.x11.im); 923 _glfw.x11.im = NULL; 924 } 925 926 _glfwTerminateEGL(); 927 928 if (_glfw.x11.display) 929 { 930 XCloseDisplay(_glfw.x11.display); 931 _glfw.x11.display = NULL; 932 } 933 934 if (_glfw.x11.x11xcb.handle) 935 { 936 dlclose(_glfw.x11.x11xcb.handle); 937 _glfw.x11.x11xcb.handle = NULL; 938 } 939 940 if (_glfw.x11.xcursor.handle) 941 { 942 dlclose(_glfw.x11.xcursor.handle); 943 _glfw.x11.xcursor.handle = NULL; 944 } 945 946 if (_glfw.x11.randr.handle) 947 { 948 dlclose(_glfw.x11.randr.handle); 949 _glfw.x11.randr.handle = NULL; 950 } 951 952 if (_glfw.x11.xinerama.handle) 953 { 954 dlclose(_glfw.x11.xinerama.handle); 955 _glfw.x11.xinerama.handle = NULL; 956 } 957 958 // NOTE: This needs to be done after XCloseDisplay, as libGL registers 959 // cleanup callbacks that get called by it 960 _glfwTerminateGLX(); 961 962 #if defined(__linux__) 963 _glfwTerminateJoysticksLinux(); 964 #endif 965 } 966 967 const char* _glfwPlatformGetVersionString(void) 968 { 969 return _GLFW_VERSION_NUMBER " X11 GLX EGL" 970 #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) 971 " clock_gettime" 972 #else 973 " gettimeofday" 974 #endif 975 #if defined(__linux__) 976 " evdev" 977 #endif 978 #if defined(_GLFW_BUILD_DLL) 979 " shared" 980 #endif 981 ; 982 } 983