zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

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