medfall

A super great game engine
Log | Files | Refs

x11_init.cc (38638B)


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