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