twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

x11_init.c (43120B)


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