medfall

A super great game engine
Log | Files | Refs

x11_window.cc (91042B)


      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/cursorfont.h>
     31 #include <X11/Xmd.h>
     32 
     33 #include <sys/select.h>
     34 
     35 #include <string.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <limits.h>
     39 #include <errno.h>
     40 #include <assert.h>
     41 
     42 // Action for EWMH client messages
     43 #define _NET_WM_STATE_REMOVE        0
     44 #define _NET_WM_STATE_ADD           1
     45 #define _NET_WM_STATE_TOGGLE        2
     46 
     47 // Additional mouse button names for XButtonEvent
     48 #define Button6            6
     49 #define Button7            7
     50 
     51 #define _GLFW_XDND_VERSION 5
     52 
     53 
     54 // Wait for data to arrive using select
     55 // This avoids blocking other threads via the per-display Xlib lock that also
     56 // covers GLX functions
     57 //
     58 static GLFWbool waitForEvent(double* timeout)
     59 {
     60     fd_set fds;
     61     const int fd = ConnectionNumber(_glfw.x11.display);
     62     int count = fd + 1;
     63 
     64 #if defined(__linux__)
     65     if (_glfw.linjs.inotify > fd)
     66         count = _glfw.linjs.inotify + 1;
     67 #endif
     68     for (;;)
     69     {
     70         FD_ZERO(&fds);
     71         FD_SET(fd, &fds);
     72 #if defined(__linux__)
     73         if (_glfw.linjs.inotify > 0)
     74             FD_SET(_glfw.linjs.inotify, &fds);
     75 #endif
     76 
     77         if (timeout)
     78         {
     79             const long seconds = (long) *timeout;
     80             const long microseconds = (long) ((*timeout - seconds) * 1e6);
     81             struct timeval tv = { seconds, microseconds };
     82             const uint64_t base = _glfwPlatformGetTimerValue();
     83 
     84             const int result = select(count, &fds, NULL, NULL, &tv);
     85             const int error = errno;
     86 
     87             *timeout -= (_glfwPlatformGetTimerValue() - base) /
     88                 (double) _glfwPlatformGetTimerFrequency();
     89 
     90             if (result > 0)
     91                 return GLFW_TRUE;
     92             if ((result == -1 && error == EINTR) || *timeout <= 0.0)
     93                 return GLFW_FALSE;
     94         }
     95         else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR)
     96             return GLFW_TRUE;
     97     }
     98 }
     99 
    100 // Waits until a VisibilityNotify event arrives for the specified window or the
    101 // timeout period elapses (ICCCM section 4.2.2)
    102 //
    103 static GLFWbool waitForVisibilityNotify(_GLFWwindow* window)
    104 {
    105     XEvent dummy;
    106     double timeout = 0.1;
    107 
    108     while (!XCheckTypedWindowEvent(_glfw.x11.display,
    109                                    window->x11.handle,
    110                                    VisibilityNotify,
    111                                    &dummy))
    112     {
    113         if (!waitForEvent(&timeout))
    114             return GLFW_FALSE;
    115     }
    116 
    117     return GLFW_TRUE;
    118 }
    119 
    120 // Returns whether the window is iconified
    121 //
    122 static int getWindowState(_GLFWwindow* window)
    123 {
    124     int result = WithdrawnState;
    125     struct {
    126         CARD32 state;
    127         Window icon;
    128     } *state = NULL;
    129 
    130     if (_glfwGetWindowPropertyX11(window->x11.handle,
    131                                   _glfw.x11.WM_STATE,
    132                                   _glfw.x11.WM_STATE,
    133                                   (unsigned char**) &state) >= 2)
    134     {
    135         result = state->state;
    136     }
    137 
    138     if (state)
    139         XFree(state);
    140 
    141     return result;
    142 }
    143 
    144 // Returns whether the event is a selection event
    145 //
    146 static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer)
    147 {
    148     if (event->xany.window != _glfw.x11.helperWindowHandle)
    149         return False;
    150 
    151     return event->type == SelectionRequest ||
    152            event->type == SelectionNotify ||
    153            event->type == SelectionClear;
    154 }
    155 
    156 // Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
    157 //
    158 static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer)
    159 {
    160     _GLFWwindow* window = (_GLFWwindow*) pointer;
    161     return event->type == PropertyNotify &&
    162            event->xproperty.state == PropertyNewValue &&
    163            event->xproperty.window == window->x11.handle &&
    164            event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
    165 }
    166 
    167 // Translates a GLFW standard cursor to a font cursor shape
    168 //
    169 static int translateCursorShape(int shape)
    170 {
    171     switch (shape)
    172     {
    173         case GLFW_ARROW_CURSOR:
    174             return XC_left_ptr;
    175         case GLFW_IBEAM_CURSOR:
    176             return XC_xterm;
    177         case GLFW_CROSSHAIR_CURSOR:
    178             return XC_crosshair;
    179         case GLFW_HAND_CURSOR:
    180             return XC_hand1;
    181         case GLFW_HRESIZE_CURSOR:
    182             return XC_sb_h_double_arrow;
    183         case GLFW_VRESIZE_CURSOR:
    184             return XC_sb_v_double_arrow;
    185     }
    186 
    187     return 0;
    188 }
    189 
    190 // Translates an X event modifier state mask
    191 //
    192 static int translateState(int state)
    193 {
    194     int mods = 0;
    195 
    196     if (state & ShiftMask)
    197         mods |= GLFW_MOD_SHIFT;
    198     if (state & ControlMask)
    199         mods |= GLFW_MOD_CONTROL;
    200     if (state & Mod1Mask)
    201         mods |= GLFW_MOD_ALT;
    202     if (state & Mod4Mask)
    203         mods |= GLFW_MOD_SUPER;
    204 
    205     return mods;
    206 }
    207 
    208 // Translates an X11 key code to a GLFW key token
    209 //
    210 static int translateKey(int scancode)
    211 {
    212     // Use the pre-filled LUT (see createKeyTables() in x11_init.c)
    213     if (scancode < 0 || scancode > 255)
    214         return GLFW_KEY_UNKNOWN;
    215 
    216     return _glfw.x11.keycodes[scancode];
    217 }
    218 
    219 // Return the GLFW window corresponding to the specified X11 window
    220 //
    221 static _GLFWwindow* findWindowByHandle(Window handle)
    222 {
    223     _GLFWwindow* window;
    224 
    225     if (XFindContext(_glfw.x11.display,
    226                      handle,
    227                      _glfw.x11.context,
    228                      (XPointer*) &window) != 0)
    229     {
    230         return NULL;
    231     }
    232 
    233     return window;
    234 }
    235 
    236 // Sends an EWMH or ICCCM event to the window manager
    237 //
    238 static void sendEventToWM(_GLFWwindow* window, Atom type,
    239                           long a, long b, long c, long d, long e)
    240 {
    241     XEvent event;
    242     memset(&event, 0, sizeof(event));
    243 
    244     event.type = ClientMessage;
    245     event.xclient.window = window->x11.handle;
    246     event.xclient.format = 32; // Data is 32-bit longs
    247     event.xclient.message_type = type;
    248     event.xclient.data.l[0] = a;
    249     event.xclient.data.l[1] = b;
    250     event.xclient.data.l[2] = c;
    251     event.xclient.data.l[3] = d;
    252     event.xclient.data.l[4] = e;
    253 
    254     XSendEvent(_glfw.x11.display, _glfw.x11.root,
    255                False,
    256                SubstructureNotifyMask | SubstructureRedirectMask,
    257                &event);
    258 }
    259 
    260 // Updates the normal hints according to the window settings
    261 //
    262 static void updateNormalHints(_GLFWwindow* window, int width, int height)
    263 {
    264     XSizeHints* hints = XAllocSizeHints();
    265 
    266     if (!window->monitor)
    267     {
    268         if (window->resizable)
    269         {
    270             if (window->minwidth != GLFW_DONT_CARE &&
    271                 window->minheight != GLFW_DONT_CARE)
    272             {
    273                 hints->flags |= PMinSize;
    274                 hints->min_width = window->minwidth;
    275                 hints->min_height = window->minheight;
    276             }
    277 
    278             if (window->maxwidth != GLFW_DONT_CARE &&
    279                 window->maxheight != GLFW_DONT_CARE)
    280             {
    281                 hints->flags |= PMaxSize;
    282                 hints->max_width = window->maxwidth;
    283                 hints->max_height = window->maxheight;
    284             }
    285 
    286             if (window->numer != GLFW_DONT_CARE &&
    287                 window->denom != GLFW_DONT_CARE)
    288             {
    289                 hints->flags |= PAspect;
    290                 hints->min_aspect.x = hints->max_aspect.x = window->numer;
    291                 hints->min_aspect.y = hints->max_aspect.y = window->denom;
    292             }
    293         }
    294         else
    295         {
    296             hints->flags |= (PMinSize | PMaxSize);
    297             hints->min_width  = hints->max_width  = width;
    298             hints->min_height = hints->max_height = height;
    299         }
    300     }
    301 
    302     hints->flags |= PWinGravity;
    303     hints->win_gravity = StaticGravity;
    304 
    305     XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
    306     XFree(hints);
    307 }
    308 
    309 // Updates the full screen status of the window
    310 //
    311 static void updateWindowMode(_GLFWwindow* window)
    312 {
    313     if (window->monitor)
    314     {
    315         if (_glfw.x11.xinerama.available &&
    316             _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
    317         {
    318             sendEventToWM(window,
    319                           _glfw.x11.NET_WM_FULLSCREEN_MONITORS,
    320                           window->monitor->x11.index,
    321                           window->monitor->x11.index,
    322                           window->monitor->x11.index,
    323                           window->monitor->x11.index,
    324                           0);
    325         }
    326 
    327         if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
    328         {
    329             sendEventToWM(window,
    330                           _glfw.x11.NET_WM_STATE,
    331                           _NET_WM_STATE_ADD,
    332                           _glfw.x11.NET_WM_STATE_FULLSCREEN,
    333                           0, 1, 0);
    334         }
    335         else
    336         {
    337             // This is the butcher's way of removing window decorations
    338             // Setting the override-redirect attribute on a window makes the
    339             // window manager ignore the window completely (ICCCM, section 4)
    340             // The good thing is that this makes undecorated full screen windows
    341             // easy to do; the bad thing is that we have to do everything
    342             // manually and some things (like iconify/restore) won't work at
    343             // all, as those are tasks usually performed by the window manager
    344 
    345             XSetWindowAttributes attributes;
    346             attributes.override_redirect = True;
    347             XChangeWindowAttributes(_glfw.x11.display,
    348                                     window->x11.handle,
    349                                     CWOverrideRedirect,
    350                                     &attributes);
    351 
    352             window->x11.overrideRedirect = GLFW_TRUE;
    353         }
    354 
    355         // Enable compositor bypass
    356         {
    357             const unsigned long value = 1;
    358 
    359             XChangeProperty(_glfw.x11.display,  window->x11.handle,
    360                             _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
    361                             PropModeReplace, (unsigned char*) &value, 1);
    362         }
    363     }
    364     else
    365     {
    366         if (_glfw.x11.xinerama.available &&
    367             _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
    368         {
    369             XDeleteProperty(_glfw.x11.display, window->x11.handle,
    370                             _glfw.x11.NET_WM_FULLSCREEN_MONITORS);
    371         }
    372 
    373         if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
    374         {
    375             sendEventToWM(window,
    376                           _glfw.x11.NET_WM_STATE,
    377                           _NET_WM_STATE_REMOVE,
    378                           _glfw.x11.NET_WM_STATE_FULLSCREEN,
    379                           0, 1, 0);
    380         }
    381         else
    382         {
    383             XSetWindowAttributes attributes;
    384             attributes.override_redirect = False;
    385             XChangeWindowAttributes(_glfw.x11.display,
    386                                     window->x11.handle,
    387                                     CWOverrideRedirect,
    388                                     &attributes);
    389 
    390             window->x11.overrideRedirect = GLFW_FALSE;
    391         }
    392 
    393         // Disable compositor bypass
    394         {
    395             XDeleteProperty(_glfw.x11.display, window->x11.handle,
    396                             _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
    397         }
    398     }
    399 }
    400 
    401 // Splits and translates a text/uri-list into separate file paths
    402 // NOTE: This function destroys the provided string
    403 //
    404 static char** parseUriList(char* text, int* count)
    405 {
    406     const char* prefix = "file://";
    407     char** paths = NULL;
    408     char* line;
    409 
    410     *count = 0;
    411 
    412     while ((line = strtok(text, "\r\n")))
    413     {
    414         text = NULL;
    415 
    416         if (line[0] == '#')
    417             continue;
    418 
    419         if (strncmp(line, prefix, strlen(prefix)) == 0)
    420         {
    421             line += strlen(prefix);
    422             // TODO: Validate hostname
    423             while (*line != '/')
    424                 line++;
    425         }
    426 
    427         (*count)++;
    428 
    429         char* path = calloc(strlen(line) + 1, 1);
    430         paths = realloc(paths, *count * sizeof(char*));
    431         paths[*count - 1] = path;
    432 
    433         while (*line)
    434         {
    435             if (line[0] == '%' && line[1] && line[2])
    436             {
    437                 const char digits[3] = { line[1], line[2], '\0' };
    438                 *path = strtol(digits, NULL, 16);
    439                 line += 2;
    440             }
    441             else
    442                 *path = *line;
    443 
    444             path++;
    445             line++;
    446         }
    447     }
    448 
    449     return paths;
    450 }
    451 
    452 // Centers the cursor over the window client area
    453 //
    454 static void centerCursor(_GLFWwindow* window)
    455 {
    456     int width, height;
    457     _glfwPlatformGetWindowSize(window, &width, &height);
    458     _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
    459 }
    460 
    461 // Updates the cursor image according to its cursor mode
    462 //
    463 static void updateCursorImage(_GLFWwindow* window)
    464 {
    465     if (window->cursorMode == GLFW_CURSOR_NORMAL)
    466     {
    467         if (window->cursor)
    468         {
    469             XDefineCursor(_glfw.x11.display, window->x11.handle,
    470                           window->cursor->x11.handle);
    471         }
    472         else
    473             XUndefineCursor(_glfw.x11.display, window->x11.handle);
    474     }
    475     else
    476     {
    477         XDefineCursor(_glfw.x11.display, window->x11.handle,
    478                       _glfw.x11.hiddenCursorHandle);
    479     }
    480 }
    481 
    482 // Create the X11 window (and its colormap)
    483 //
    484 static GLFWbool createNativeWindow(_GLFWwindow* window,
    485                                    const _GLFWwndconfig* wndconfig,
    486                                    Visual* visual, int depth)
    487 {
    488     // Create a colormap based on the visual used by the current context
    489     window->x11.colormap = XCreateColormap(_glfw.x11.display,
    490                                            _glfw.x11.root,
    491                                            visual,
    492                                            AllocNone);
    493 
    494     // Create the actual window
    495     {
    496         XSetWindowAttributes wa;
    497         const unsigned long wamask = CWBorderPixel | CWColormap | CWEventMask;
    498 
    499         wa.colormap = window->x11.colormap;
    500         wa.border_pixel = 0;
    501         wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
    502                         PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
    503                         ExposureMask | FocusChangeMask | VisibilityChangeMask |
    504                         EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
    505 
    506         _glfwGrabErrorHandlerX11();
    507 
    508         window->x11.handle = XCreateWindow(_glfw.x11.display,
    509                                            _glfw.x11.root,
    510                                            0, 0,
    511                                            wndconfig->width, wndconfig->height,
    512                                            0,      // Border width
    513                                            depth,  // Color depth
    514                                            InputOutput,
    515                                            visual,
    516                                            wamask,
    517                                            &wa);
    518 
    519         _glfwReleaseErrorHandlerX11();
    520 
    521         if (!window->x11.handle)
    522         {
    523             _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
    524                                "X11: Failed to create window");
    525             return GLFW_FALSE;
    526         }
    527 
    528         XSaveContext(_glfw.x11.display,
    529                      window->x11.handle,
    530                      _glfw.x11.context,
    531                      (XPointer) window);
    532     }
    533 
    534     if (!wndconfig->decorated)
    535         _glfwPlatformSetWindowDecorated(window, GLFW_FALSE);
    536 
    537     if (_glfw.x11.NET_WM_STATE && !window->monitor)
    538     {
    539         Atom states[3];
    540         int count = 0;
    541 
    542         if (wndconfig->floating)
    543         {
    544             if (_glfw.x11.NET_WM_STATE_ABOVE)
    545                 states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
    546         }
    547 
    548         if (wndconfig->maximized)
    549         {
    550             if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
    551                 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
    552             {
    553                 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
    554                 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
    555                 window->x11.maximized = GLFW_TRUE;
    556             }
    557         }
    558 
    559         if (count)
    560         {
    561             XChangeProperty(_glfw.x11.display, window->x11.handle,
    562                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
    563                             PropModeReplace, (unsigned char*) &states, count);
    564         }
    565     }
    566 
    567     // Declare the WM protocols supported by GLFW
    568     {
    569         Atom protocols[] =
    570         {
    571             _glfw.x11.WM_DELETE_WINDOW,
    572             _glfw.x11.NET_WM_PING
    573         };
    574 
    575         XSetWMProtocols(_glfw.x11.display, window->x11.handle,
    576                         protocols, sizeof(protocols) / sizeof(Atom));
    577     }
    578 
    579     // Declare our PID
    580     {
    581         const long pid = getpid();
    582 
    583         XChangeProperty(_glfw.x11.display,  window->x11.handle,
    584                         _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
    585                         PropModeReplace,
    586                         (unsigned char*) &pid, 1);
    587     }
    588 
    589     if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
    590     {
    591         Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
    592         XChangeProperty(_glfw.x11.display,  window->x11.handle,
    593                         _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
    594                         PropModeReplace, (unsigned char*) &type, 1);
    595     }
    596 
    597     // Set ICCCM WM_HINTS property
    598     {
    599         XWMHints* hints = XAllocWMHints();
    600         if (!hints)
    601         {
    602             _glfwInputError(GLFW_OUT_OF_MEMORY,
    603                             "X11: Failed to allocate WM hints");
    604             return GLFW_FALSE;
    605         }
    606 
    607         hints->flags = StateHint;
    608         hints->initial_state = NormalState;
    609 
    610         XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
    611         XFree(hints);
    612     }
    613 
    614     updateNormalHints(window, wndconfig->width, wndconfig->height);
    615 
    616     // Set ICCCM WM_CLASS property
    617     {
    618         XClassHint* hint = XAllocClassHint();
    619 
    620         if (strlen(_glfw.hints.init.x11.className) &&
    621             strlen(_glfw.hints.init.x11.classClass))
    622         {
    623             hint->res_name = (char*) _glfw.hints.init.x11.className;
    624             hint->res_class = (char*) _glfw.hints.init.x11.classClass;
    625         }
    626         else if (strlen(wndconfig->title))
    627         {
    628             hint->res_name = (char*) wndconfig->title;
    629             hint->res_class = (char*) wndconfig->title;
    630         }
    631         else
    632         {
    633             hint->res_name = (char*) "glfw-application";
    634             hint->res_class = (char*) "GLFW-Application";
    635         }
    636 
    637         XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
    638         XFree(hint);
    639     }
    640 
    641     // Announce support for Xdnd (drag and drop)
    642     {
    643         const Atom version = _GLFW_XDND_VERSION;
    644         XChangeProperty(_glfw.x11.display, window->x11.handle,
    645                         _glfw.x11.XdndAware, XA_ATOM, 32,
    646                         PropModeReplace, (unsigned char*) &version, 1);
    647     }
    648 
    649     _glfwPlatformSetWindowTitle(window, wndconfig->title);
    650 
    651     if (_glfw.x11.im)
    652     {
    653         window->x11.ic = XCreateIC(_glfw.x11.im,
    654                                    XNInputStyle,
    655                                    XIMPreeditNothing | XIMStatusNothing,
    656                                    XNClientWindow,
    657                                    window->x11.handle,
    658                                    XNFocusWindow,
    659                                    window->x11.handle,
    660                                    NULL);
    661     }
    662 
    663     _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
    664     _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
    665 
    666     return GLFW_TRUE;
    667 }
    668 
    669 // Set the specified property to the selection converted to the requested target
    670 //
    671 static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
    672 {
    673     int i;
    674     char* selectionString = NULL;
    675     const Atom formats[] = { _glfw.x11.UTF8_STRING,
    676                              _glfw.x11.COMPOUND_STRING,
    677                              XA_STRING };
    678     const int formatCount = sizeof(formats) / sizeof(formats[0]);
    679 
    680     if (request->selection == _glfw.x11.PRIMARY)
    681         selectionString = _glfw.x11.primarySelectionString;
    682     else
    683         selectionString = _glfw.x11.clipboardString;
    684 
    685     if (request->property == None)
    686     {
    687         // The requester is a legacy client (ICCCM section 2.2)
    688         // We don't support legacy clients, so fail here
    689         return None;
    690     }
    691 
    692     if (request->target == _glfw.x11.TARGETS)
    693     {
    694         // The list of supported targets was requested
    695 
    696         const Atom targets[] = { _glfw.x11.TARGETS,
    697                                  _glfw.x11.MULTIPLE,
    698                                  _glfw.x11.UTF8_STRING,
    699                                  _glfw.x11.COMPOUND_STRING,
    700                                  XA_STRING };
    701 
    702         XChangeProperty(_glfw.x11.display,
    703                         request->requestor,
    704                         request->property,
    705                         XA_ATOM,
    706                         32,
    707                         PropModeReplace,
    708                         (unsigned char*) targets,
    709                         sizeof(targets) / sizeof(targets[0]));
    710 
    711         return request->property;
    712     }
    713 
    714     if (request->target == _glfw.x11.MULTIPLE)
    715     {
    716         // Multiple conversions were requested
    717 
    718         Atom* targets;
    719         unsigned long i, count;
    720 
    721         count = _glfwGetWindowPropertyX11(request->requestor,
    722                                           request->property,
    723                                           _glfw.x11.ATOM_PAIR,
    724                                           (unsigned char**) &targets);
    725 
    726         for (i = 0;  i < count;  i += 2)
    727         {
    728             int j;
    729 
    730             for (j = 0;  j < formatCount;  j++)
    731             {
    732                 if (targets[i] == formats[j])
    733                     break;
    734             }
    735 
    736             if (j < formatCount)
    737             {
    738                 XChangeProperty(_glfw.x11.display,
    739                                 request->requestor,
    740                                 targets[i + 1],
    741                                 targets[i],
    742                                 8,
    743                                 PropModeReplace,
    744                                 (unsigned char *) selectionString,
    745                                 strlen(selectionString));
    746             }
    747             else
    748                 targets[i + 1] = None;
    749         }
    750 
    751         XChangeProperty(_glfw.x11.display,
    752                         request->requestor,
    753                         request->property,
    754                         _glfw.x11.ATOM_PAIR,
    755                         32,
    756                         PropModeReplace,
    757                         (unsigned char*) targets,
    758                         count);
    759 
    760         XFree(targets);
    761 
    762         return request->property;
    763     }
    764 
    765     if (request->target == _glfw.x11.SAVE_TARGETS)
    766     {
    767         // The request is a check whether we support SAVE_TARGETS
    768         // It should be handled as a no-op side effect target
    769 
    770         XChangeProperty(_glfw.x11.display,
    771                         request->requestor,
    772                         request->property,
    773                         _glfw.x11.NULL_,
    774                         32,
    775                         PropModeReplace,
    776                         NULL,
    777                         0);
    778 
    779         return request->property;
    780     }
    781 
    782     // Conversion to a data target was requested
    783 
    784     for (i = 0;  i < formatCount;  i++)
    785     {
    786         if (request->target == formats[i])
    787         {
    788             // The requested target is one we support
    789 
    790             XChangeProperty(_glfw.x11.display,
    791                             request->requestor,
    792                             request->property,
    793                             request->target,
    794                             8,
    795                             PropModeReplace,
    796                             (unsigned char *) selectionString,
    797                             strlen(selectionString));
    798 
    799             return request->property;
    800         }
    801     }
    802 
    803     // The requested target is not supported
    804 
    805     return None;
    806 }
    807 
    808 static void handleSelectionClear(XEvent* event)
    809 {
    810     if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
    811     {
    812         free(_glfw.x11.primarySelectionString);
    813         _glfw.x11.primarySelectionString = NULL;
    814     }
    815     else
    816     {
    817         free(_glfw.x11.clipboardString);
    818         _glfw.x11.clipboardString = NULL;
    819     }
    820 }
    821 
    822 static void handleSelectionRequest(XEvent* event)
    823 {
    824     const XSelectionRequestEvent* request = &event->xselectionrequest;
    825 
    826     XEvent reply;
    827     memset(&reply, 0, sizeof(reply));
    828 
    829     reply.xselection.property = writeTargetToProperty(request);
    830     reply.xselection.type = SelectionNotify;
    831     reply.xselection.display = request->display;
    832     reply.xselection.requestor = request->requestor;
    833     reply.xselection.selection = request->selection;
    834     reply.xselection.target = request->target;
    835     reply.xselection.time = request->time;
    836 
    837     XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
    838 }
    839 
    840 static const char* getSelectionString(Atom selection)
    841 {
    842     size_t i;
    843     char** selectionString = NULL;
    844     const Atom formats[] = { _glfw.x11.UTF8_STRING,
    845                              _glfw.x11.COMPOUND_STRING,
    846                              XA_STRING };
    847     const size_t formatCount = sizeof(formats) / sizeof(formats[0]);
    848 
    849     if (selection == _glfw.x11.PRIMARY)
    850         selectionString = &_glfw.x11.primarySelectionString;
    851     else
    852         selectionString = &_glfw.x11.clipboardString;
    853 
    854     if (XGetSelectionOwner(_glfw.x11.display, selection) ==
    855         _glfw.x11.helperWindowHandle)
    856     {
    857         // Instead of doing a large number of X round-trips just to put this
    858         // string into a window property and then read it back, just return it
    859         return *selectionString;
    860     }
    861 
    862     free(*selectionString);
    863     *selectionString = NULL;
    864 
    865     for (i = 0;  i < formatCount;  i++)
    866     {
    867         char* data;
    868         XEvent event;
    869 
    870         XConvertSelection(_glfw.x11.display,
    871                           selection,
    872                           formats[i],
    873                           _glfw.x11.GLFW_SELECTION,
    874                           _glfw.x11.helperWindowHandle,
    875                           CurrentTime);
    876 
    877         while (!XCheckTypedWindowEvent(_glfw.x11.display,
    878                                        _glfw.x11.helperWindowHandle,
    879                                        SelectionNotify,
    880                                        &event))
    881         {
    882             waitForEvent(NULL);
    883         }
    884 
    885         if (event.xselection.property == None)
    886             continue;
    887 
    888         if (_glfwGetWindowPropertyX11(event.xselection.requestor,
    889                                       event.xselection.property,
    890                                       event.xselection.target,
    891                                       (unsigned char**) &data))
    892         {
    893             *selectionString = strdup(data);
    894         }
    895 
    896         if (data)
    897             XFree(data);
    898 
    899         XDeleteProperty(_glfw.x11.display,
    900                         event.xselection.requestor,
    901                         event.xselection.property);
    902 
    903         if (*selectionString)
    904             break;
    905     }
    906 
    907     if (!*selectionString)
    908     {
    909         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
    910                         "X11: Failed to convert clipboard to string");
    911     }
    912 
    913     return *selectionString;
    914 }
    915 
    916 // Make the specified window and its video mode active on its monitor
    917 //
    918 static GLFWbool acquireMonitor(_GLFWwindow* window)
    919 {
    920     GLFWbool status;
    921 
    922     if (_glfw.x11.saver.count == 0)
    923     {
    924         // Remember old screen saver settings
    925         XGetScreenSaver(_glfw.x11.display,
    926                         &_glfw.x11.saver.timeout,
    927                         &_glfw.x11.saver.interval,
    928                         &_glfw.x11.saver.blanking,
    929                         &_glfw.x11.saver.exposure);
    930 
    931         // Disable screen saver
    932         XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
    933                         DefaultExposures);
    934     }
    935 
    936     if (!window->monitor->window)
    937         _glfw.x11.saver.count++;
    938 
    939     status = _glfwSetVideoModeX11(window->monitor, &window->videoMode);
    940 
    941     if (window->x11.overrideRedirect)
    942     {
    943         int xpos, ypos;
    944         GLFWvidmode mode;
    945 
    946         // Manually position the window over its monitor
    947         _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
    948         _glfwPlatformGetVideoMode(window->monitor, &mode);
    949 
    950         XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
    951                           xpos, ypos, mode.width, mode.height);
    952     }
    953 
    954     _glfwInputMonitorWindow(window->monitor, window);
    955     return status;
    956 }
    957 
    958 // Remove the window and restore the original video mode
    959 //
    960 static void releaseMonitor(_GLFWwindow* window)
    961 {
    962     if (window->monitor->window != window)
    963         return;
    964 
    965     _glfwInputMonitorWindow(window->monitor, NULL);
    966     _glfwRestoreVideoModeX11(window->monitor);
    967 
    968     _glfw.x11.saver.count--;
    969 
    970     if (_glfw.x11.saver.count == 0)
    971     {
    972         // Restore old screen saver settings
    973         XSetScreenSaver(_glfw.x11.display,
    974                         _glfw.x11.saver.timeout,
    975                         _glfw.x11.saver.interval,
    976                         _glfw.x11.saver.blanking,
    977                         _glfw.x11.saver.exposure);
    978     }
    979 }
    980 
    981 // Encode a Unicode code point to a UTF-8 stream
    982 // Based on cutef8 by Jeff Bezanson (Public Domain)
    983 //
    984 static size_t encodeUTF8(char* s, unsigned int ch)
    985 {
    986     size_t count = 0;
    987 
    988     if (ch < 0x80)
    989         s[count++] = (char) ch;
    990     else if (ch < 0x800)
    991     {
    992         s[count++] = (ch >> 6) | 0xc0;
    993         s[count++] = (ch & 0x3f) | 0x80;
    994     }
    995     else if (ch < 0x10000)
    996     {
    997         s[count++] = (ch >> 12) | 0xe0;
    998         s[count++] = ((ch >> 6) & 0x3f) | 0x80;
    999         s[count++] = (ch & 0x3f) | 0x80;
   1000     }
   1001     else if (ch < 0x110000)
   1002     {
   1003         s[count++] = (ch >> 18) | 0xf0;
   1004         s[count++] = ((ch >> 12) & 0x3f) | 0x80;
   1005         s[count++] = ((ch >> 6) & 0x3f) | 0x80;
   1006         s[count++] = (ch & 0x3f) | 0x80;
   1007     }
   1008 
   1009     return count;
   1010 }
   1011 
   1012 // Decode a Unicode code point from a UTF-8 stream
   1013 // Based on cutef8 by Jeff Bezanson (Public Domain)
   1014 //
   1015 #if defined(X_HAVE_UTF8_STRING)
   1016 static unsigned int decodeUTF8(const char** s)
   1017 {
   1018     unsigned int ch = 0, count = 0;
   1019     static const unsigned int offsets[] =
   1020     {
   1021         0x00000000u, 0x00003080u, 0x000e2080u,
   1022         0x03c82080u, 0xfa082080u, 0x82082080u
   1023     };
   1024 
   1025     do
   1026     {
   1027         ch = (ch << 6) + (unsigned char) **s;
   1028         (*s)++;
   1029         count++;
   1030     } while ((**s & 0xc0) == 0x80);
   1031 
   1032     assert(count <= 6);
   1033     return ch - offsets[count - 1];
   1034 }
   1035 #endif /*X_HAVE_UTF8_STRING*/
   1036 
   1037 // Process the specified X event
   1038 //
   1039 static void processEvent(XEvent *event)
   1040 {
   1041     _GLFWwindow* window = NULL;
   1042     int keycode = 0;
   1043     Bool filtered = False;
   1044 
   1045     // HACK: Save scancode as some IMs clear the field in XFilterEvent
   1046     if (event->type == KeyPress || event->type == KeyRelease)
   1047         keycode = event->xkey.keycode;
   1048 
   1049     if (_glfw.x11.im)
   1050         filtered = XFilterEvent(event, None);
   1051 
   1052     if (_glfw.x11.randr.available)
   1053     {
   1054         if (event->type == _glfw.x11.randr.eventBase + RRNotify)
   1055         {
   1056             XRRUpdateConfiguration(event);
   1057             _glfwPollMonitorsX11();
   1058             return;
   1059         }
   1060     }
   1061 
   1062     if (event->type == GenericEvent)
   1063     {
   1064         if (_glfw.x11.xi.available)
   1065         {
   1066             _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
   1067 
   1068             if (window &&
   1069                 event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
   1070                 XGetEventData(_glfw.x11.display, &event->xcookie) &&
   1071                 event->xcookie.evtype == XI_RawMotion)
   1072             {
   1073                 XIRawEvent* re = event->xcookie.data;
   1074                 if (re->valuators.mask_len)
   1075                 {
   1076                     const double* values = re->raw_values;
   1077                     double xpos = window->virtualCursorPosX;
   1078                     double ypos = window->virtualCursorPosY;
   1079 
   1080                     if (XIMaskIsSet(re->valuators.mask, 0))
   1081                     {
   1082                         xpos += *values;
   1083                         values++;
   1084                     }
   1085 
   1086                     if (XIMaskIsSet(re->valuators.mask, 1))
   1087                         ypos += *values;
   1088 
   1089                     _glfwInputCursorPos(window, xpos, ypos);
   1090                 }
   1091             }
   1092 
   1093             XFreeEventData(_glfw.x11.display, &event->xcookie);
   1094         }
   1095 
   1096         return;
   1097     }
   1098 
   1099     if (event->type == SelectionClear)
   1100     {
   1101         handleSelectionClear(event);
   1102         return;
   1103     }
   1104     else if (event->type == SelectionRequest)
   1105     {
   1106         handleSelectionRequest(event);
   1107         return;
   1108     }
   1109 
   1110     window = findWindowByHandle(event->xany.window);
   1111     if (window == NULL)
   1112     {
   1113         // This is an event for a window that has already been destroyed
   1114         return;
   1115     }
   1116 
   1117     switch (event->type)
   1118     {
   1119         case KeyPress:
   1120         {
   1121             const int key = translateKey(keycode);
   1122             const int mods = translateState(event->xkey.state);
   1123             const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
   1124 
   1125             if (window->x11.ic)
   1126             {
   1127                 // HACK: Ignore duplicate key press events generated by ibus
   1128                 //       These have the same timestamp as the original event
   1129                 //       Corresponding release events are filtered out
   1130                 //       implicitly by the GLFW key repeat logic
   1131                 if (window->x11.lastKeyTime < event->xkey.time)
   1132                 {
   1133                     if (keycode)
   1134                         _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
   1135 
   1136                     window->x11.lastKeyTime = event->xkey.time;
   1137                 }
   1138 
   1139                 if (!filtered)
   1140                 {
   1141                     int count;
   1142                     Status status;
   1143 #if defined(X_HAVE_UTF8_STRING)
   1144                     char buffer[100];
   1145                     char* chars = buffer;
   1146 
   1147                     count = Xutf8LookupString(window->x11.ic,
   1148                                               &event->xkey,
   1149                                               buffer, sizeof(buffer) - 1,
   1150                                               NULL, &status);
   1151 
   1152                     if (status == XBufferOverflow)
   1153                     {
   1154                         chars = calloc(count + 1, 1);
   1155                         count = Xutf8LookupString(window->x11.ic,
   1156                                                   &event->xkey,
   1157                                                   chars, count,
   1158                                                   NULL, &status);
   1159                     }
   1160 
   1161                     if (status == XLookupChars || status == XLookupBoth)
   1162                     {
   1163                         const char* c = chars;
   1164                         chars[count] = '\0';
   1165                         while (c - chars < count)
   1166                             _glfwInputChar(window, decodeUTF8(&c), mods, plain);
   1167                     }
   1168 #else /*X_HAVE_UTF8_STRING*/
   1169                     wchar_t buffer[16];
   1170                     wchar_t* chars = buffer;
   1171 
   1172                     count = XwcLookupString(window->x11.ic,
   1173                                             &event->xkey,
   1174                                             buffer, sizeof(buffer) / sizeof(wchar_t),
   1175                                             NULL, &status);
   1176 
   1177                     if (status == XBufferOverflow)
   1178                     {
   1179                         chars = calloc(count, sizeof(wchar_t));
   1180                         count = XwcLookupString(window->x11.ic,
   1181                                                 &event->xkey,
   1182                                                 chars, count,
   1183                                                 NULL, &status);
   1184                     }
   1185 
   1186                     if (status == XLookupChars || status == XLookupBoth)
   1187                     {
   1188                         int i;
   1189                         for (i = 0;  i < count;  i++)
   1190                             _glfwInputChar(window, chars[i], mods, plain);
   1191                     }
   1192 #endif /*X_HAVE_UTF8_STRING*/
   1193 
   1194                     if (chars != buffer)
   1195                         free(chars);
   1196                 }
   1197             }
   1198             else
   1199             {
   1200                 KeySym keysym;
   1201                 XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
   1202 
   1203                 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
   1204 
   1205                 const long character = _glfwKeySym2Unicode(keysym);
   1206                 if (character != -1)
   1207                     _glfwInputChar(window, character, mods, plain);
   1208             }
   1209 
   1210             return;
   1211         }
   1212 
   1213         case KeyRelease:
   1214         {
   1215             const int key = translateKey(keycode);
   1216             const int mods = translateState(event->xkey.state);
   1217 
   1218             if (!_glfw.x11.xkb.detectable)
   1219             {
   1220                 // HACK: Key repeat events will arrive as KeyRelease/KeyPress
   1221                 //       pairs with similar or identical time stamps
   1222                 //       The key repeat logic in _glfwInputKey expects only key
   1223                 //       presses to repeat, so detect and discard release events
   1224                 if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
   1225                 {
   1226                     XEvent next;
   1227                     XPeekEvent(_glfw.x11.display, &next);
   1228 
   1229                     if (next.type == KeyPress &&
   1230                         next.xkey.window == event->xkey.window &&
   1231                         next.xkey.keycode == keycode)
   1232                     {
   1233                         // HACK: The time of repeat events sometimes doesn't
   1234                         //       match that of the press event, so add an
   1235                         //       epsilon
   1236                         //       Toshiyuki Takahashi can press a button
   1237                         //       16 times per second so it's fairly safe to
   1238                         //       assume that no human is pressing the key 50
   1239                         //       times per second (value is ms)
   1240                         if ((next.xkey.time - event->xkey.time) < 20)
   1241                         {
   1242                             // This is very likely a server-generated key repeat
   1243                             // event, so ignore it
   1244                             return;
   1245                         }
   1246                     }
   1247                 }
   1248             }
   1249 
   1250             _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
   1251             return;
   1252         }
   1253 
   1254         case ButtonPress:
   1255         {
   1256             const int mods = translateState(event->xbutton.state);
   1257 
   1258             if (event->xbutton.button == Button1)
   1259                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
   1260             else if (event->xbutton.button == Button2)
   1261                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
   1262             else if (event->xbutton.button == Button3)
   1263                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
   1264 
   1265             // Modern X provides scroll events as mouse button presses
   1266             else if (event->xbutton.button == Button4)
   1267                 _glfwInputScroll(window, 0.0, 1.0);
   1268             else if (event->xbutton.button == Button5)
   1269                 _glfwInputScroll(window, 0.0, -1.0);
   1270             else if (event->xbutton.button == Button6)
   1271                 _glfwInputScroll(window, 1.0, 0.0);
   1272             else if (event->xbutton.button == Button7)
   1273                 _glfwInputScroll(window, -1.0, 0.0);
   1274 
   1275             else
   1276             {
   1277                 // Additional buttons after 7 are treated as regular buttons
   1278                 // We subtract 4 to fill the gap left by scroll input above
   1279                 _glfwInputMouseClick(window,
   1280                                      event->xbutton.button - Button1 - 4,
   1281                                      GLFW_PRESS,
   1282                                      mods);
   1283             }
   1284 
   1285             return;
   1286         }
   1287 
   1288         case ButtonRelease:
   1289         {
   1290             const int mods = translateState(event->xbutton.state);
   1291 
   1292             if (event->xbutton.button == Button1)
   1293             {
   1294                 _glfwInputMouseClick(window,
   1295                                      GLFW_MOUSE_BUTTON_LEFT,
   1296                                      GLFW_RELEASE,
   1297                                      mods);
   1298             }
   1299             else if (event->xbutton.button == Button2)
   1300             {
   1301                 _glfwInputMouseClick(window,
   1302                                      GLFW_MOUSE_BUTTON_MIDDLE,
   1303                                      GLFW_RELEASE,
   1304                                      mods);
   1305             }
   1306             else if (event->xbutton.button == Button3)
   1307             {
   1308                 _glfwInputMouseClick(window,
   1309                                      GLFW_MOUSE_BUTTON_RIGHT,
   1310                                      GLFW_RELEASE,
   1311                                      mods);
   1312             }
   1313             else if (event->xbutton.button > Button7)
   1314             {
   1315                 // Additional buttons after 7 are treated as regular buttons
   1316                 // We subtract 4 to fill the gap left by scroll input above
   1317                 _glfwInputMouseClick(window,
   1318                                      event->xbutton.button - Button1 - 4,
   1319                                      GLFW_RELEASE,
   1320                                      mods);
   1321             }
   1322 
   1323             return;
   1324         }
   1325 
   1326         case EnterNotify:
   1327         {
   1328             // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
   1329             //       ignore the defined cursor for hidden cursor mode
   1330             if (window->cursorMode == GLFW_CURSOR_HIDDEN)
   1331                 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_HIDDEN);
   1332 
   1333             _glfwInputCursorEnter(window, GLFW_TRUE);
   1334             return;
   1335         }
   1336 
   1337         case LeaveNotify:
   1338         {
   1339             _glfwInputCursorEnter(window, GLFW_FALSE);
   1340             return;
   1341         }
   1342 
   1343         case MotionNotify:
   1344         {
   1345             const int x = event->xmotion.x;
   1346             const int y = event->xmotion.y;
   1347 
   1348             if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY)
   1349             {
   1350                 // The cursor was moved by something other than GLFW
   1351 
   1352                 if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1353                 {
   1354                     if (_glfw.x11.disabledCursorWindow != window)
   1355                         return;
   1356                     if (_glfw.x11.xi.available)
   1357                         return;
   1358 
   1359                     const int dx = x - window->x11.lastCursorPosX;
   1360                     const int dy = y - window->x11.lastCursorPosY;
   1361 
   1362                     _glfwInputCursorPos(window,
   1363                                         window->virtualCursorPosX + dx,
   1364                                         window->virtualCursorPosY + dy);
   1365                 }
   1366                 else
   1367                     _glfwInputCursorPos(window, x, y);
   1368             }
   1369 
   1370             window->x11.lastCursorPosX = x;
   1371             window->x11.lastCursorPosY = y;
   1372             return;
   1373         }
   1374 
   1375         case ConfigureNotify:
   1376         {
   1377             if (event->xconfigure.width != window->x11.width ||
   1378                 event->xconfigure.height != window->x11.height)
   1379             {
   1380                 _glfwInputFramebufferSize(window,
   1381                                           event->xconfigure.width,
   1382                                           event->xconfigure.height);
   1383 
   1384                 _glfwInputWindowSize(window,
   1385                                      event->xconfigure.width,
   1386                                      event->xconfigure.height);
   1387 
   1388                 window->x11.width = event->xconfigure.width;
   1389                 window->x11.height = event->xconfigure.height;
   1390             }
   1391 
   1392             if (event->xconfigure.x != window->x11.xpos ||
   1393                 event->xconfigure.y != window->x11.ypos)
   1394             {
   1395                 if (window->x11.overrideRedirect || event->xany.send_event)
   1396                 {
   1397                     _glfwInputWindowPos(window,
   1398                                         event->xconfigure.x,
   1399                                         event->xconfigure.y);
   1400 
   1401                     window->x11.xpos = event->xconfigure.x;
   1402                     window->x11.ypos = event->xconfigure.y;
   1403                 }
   1404             }
   1405 
   1406             return;
   1407         }
   1408 
   1409         case ClientMessage:
   1410         {
   1411             // Custom client message, probably from the window manager
   1412 
   1413             if (filtered)
   1414                 return;
   1415 
   1416             if (event->xclient.message_type == None)
   1417                 return;
   1418 
   1419             if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
   1420             {
   1421                 const Atom protocol = event->xclient.data.l[0];
   1422                 if (protocol == None)
   1423                     return;
   1424 
   1425                 if (protocol == _glfw.x11.WM_DELETE_WINDOW)
   1426                 {
   1427                     // The window manager was asked to close the window, for example by
   1428                     // the user pressing a 'close' window decoration button
   1429                     _glfwInputWindowCloseRequest(window);
   1430                 }
   1431                 else if (protocol == _glfw.x11.NET_WM_PING)
   1432                 {
   1433                     // The window manager is pinging the application to ensure it's
   1434                     // still responding to events
   1435 
   1436                     XEvent reply = *event;
   1437                     reply.xclient.window = _glfw.x11.root;
   1438 
   1439                     XSendEvent(_glfw.x11.display, _glfw.x11.root,
   1440                                False,
   1441                                SubstructureNotifyMask | SubstructureRedirectMask,
   1442                                &reply);
   1443                 }
   1444             }
   1445             else if (event->xclient.message_type == _glfw.x11.XdndEnter)
   1446             {
   1447                 // A drag operation has entered the window
   1448                 unsigned long i, count;
   1449                 Atom* formats = NULL;
   1450                 const GLFWbool list = event->xclient.data.l[1] & 1;
   1451 
   1452                 _glfw.x11.xdnd.source  = event->xclient.data.l[0];
   1453                 _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
   1454                 _glfw.x11.xdnd.format  = None;
   1455 
   1456                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1457                     return;
   1458 
   1459                 if (list)
   1460                 {
   1461                     count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
   1462                                                       _glfw.x11.XdndTypeList,
   1463                                                       XA_ATOM,
   1464                                                       (unsigned char**) &formats);
   1465                 }
   1466                 else
   1467                 {
   1468                     count = 3;
   1469                     formats = (Atom*) event->xclient.data.l + 2;
   1470                 }
   1471 
   1472                 for (i = 0;  i < count;  i++)
   1473                 {
   1474                     if (formats[i] == _glfw.x11.text_uri_list)
   1475                     {
   1476                         _glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
   1477                         break;
   1478                     }
   1479                 }
   1480 
   1481                 if (list && formats)
   1482                     XFree(formats);
   1483             }
   1484             else if (event->xclient.message_type == _glfw.x11.XdndDrop)
   1485             {
   1486                 // The drag operation has finished by dropping on the window
   1487                 Time time = CurrentTime;
   1488 
   1489                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1490                     return;
   1491 
   1492                 if (_glfw.x11.xdnd.format)
   1493                 {
   1494                     if (_glfw.x11.xdnd.version >= 1)
   1495                         time = event->xclient.data.l[2];
   1496 
   1497                     // Request the chosen format from the source window
   1498                     XConvertSelection(_glfw.x11.display,
   1499                                       _glfw.x11.XdndSelection,
   1500                                       _glfw.x11.xdnd.format,
   1501                                       _glfw.x11.XdndSelection,
   1502                                       window->x11.handle,
   1503                                       time);
   1504                 }
   1505                 else if (_glfw.x11.xdnd.version >= 2)
   1506                 {
   1507                     XEvent reply;
   1508                     memset(&reply, 0, sizeof(reply));
   1509 
   1510                     reply.type = ClientMessage;
   1511                     reply.xclient.window = _glfw.x11.xdnd.source;
   1512                     reply.xclient.message_type = _glfw.x11.XdndFinished;
   1513                     reply.xclient.format = 32;
   1514                     reply.xclient.data.l[0] = window->x11.handle;
   1515                     reply.xclient.data.l[1] = 0; // The drag was rejected
   1516                     reply.xclient.data.l[2] = None;
   1517 
   1518                     XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1519                                False, NoEventMask, &reply);
   1520                     XFlush(_glfw.x11.display);
   1521                 }
   1522             }
   1523             else if (event->xclient.message_type == _glfw.x11.XdndPosition)
   1524             {
   1525                 // The drag operation has moved over the window
   1526                 const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
   1527                 const int yabs = (event->xclient.data.l[2]) & 0xffff;
   1528                 Window dummy;
   1529                 int xpos, ypos;
   1530 
   1531                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1532                     return;
   1533 
   1534                 XTranslateCoordinates(_glfw.x11.display,
   1535                                       _glfw.x11.root,
   1536                                       window->x11.handle,
   1537                                       xabs, yabs,
   1538                                       &xpos, &ypos,
   1539                                       &dummy);
   1540 
   1541                 _glfwInputCursorPos(window, xpos, ypos);
   1542 
   1543                 XEvent reply;
   1544                 memset(&reply, 0, sizeof(reply));
   1545 
   1546                 reply.type = ClientMessage;
   1547                 reply.xclient.window = _glfw.x11.xdnd.source;
   1548                 reply.xclient.message_type = _glfw.x11.XdndStatus;
   1549                 reply.xclient.format = 32;
   1550                 reply.xclient.data.l[0] = window->x11.handle;
   1551                 reply.xclient.data.l[2] = 0; // Specify an empty rectangle
   1552                 reply.xclient.data.l[3] = 0;
   1553 
   1554                 if (_glfw.x11.xdnd.format)
   1555                 {
   1556                     // Reply that we are ready to copy the dragged data
   1557                     reply.xclient.data.l[1] = 1; // Accept with no rectangle
   1558                     if (_glfw.x11.xdnd.version >= 2)
   1559                         reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
   1560                 }
   1561 
   1562                 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1563                            False, NoEventMask, &reply);
   1564                 XFlush(_glfw.x11.display);
   1565             }
   1566 
   1567             return;
   1568         }
   1569 
   1570         case SelectionNotify:
   1571         {
   1572             if (event->xselection.property == _glfw.x11.XdndSelection)
   1573             {
   1574                 // The converted data from the drag operation has arrived
   1575                 char* data;
   1576                 const unsigned long result =
   1577                     _glfwGetWindowPropertyX11(event->xselection.requestor,
   1578                                               event->xselection.property,
   1579                                               event->xselection.target,
   1580                                               (unsigned char**) &data);
   1581 
   1582                 if (result)
   1583                 {
   1584                     int i, count;
   1585                     char** paths = parseUriList(data, &count);
   1586 
   1587                     _glfwInputDrop(window, count, (const char**) paths);
   1588 
   1589                     for (i = 0;  i < count;  i++)
   1590                         free(paths[i]);
   1591                     free(paths);
   1592                 }
   1593 
   1594                 if (data)
   1595                     XFree(data);
   1596 
   1597                 if (_glfw.x11.xdnd.version >= 2)
   1598                 {
   1599                     XEvent reply;
   1600                     memset(&reply, 0, sizeof(reply));
   1601 
   1602                     reply.type = ClientMessage;
   1603                     reply.xclient.window = _glfw.x11.xdnd.source;
   1604                     reply.xclient.message_type = _glfw.x11.XdndFinished;
   1605                     reply.xclient.format = 32;
   1606                     reply.xclient.data.l[0] = window->x11.handle;
   1607                     reply.xclient.data.l[1] = result;
   1608                     reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
   1609 
   1610                     XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1611                                False, NoEventMask, &reply);
   1612                     XFlush(_glfw.x11.display);
   1613                 }
   1614             }
   1615 
   1616             return;
   1617         }
   1618 
   1619         case FocusIn:
   1620         {
   1621             if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1622                 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
   1623 
   1624             if (event->xfocus.mode == NotifyGrab ||
   1625                 event->xfocus.mode == NotifyUngrab)
   1626             {
   1627                 // Ignore focus events from popup indicator windows, window menu
   1628                 // key chords and window dragging
   1629                 return;
   1630             }
   1631 
   1632             if (window->x11.ic)
   1633                 XSetICFocus(window->x11.ic);
   1634 
   1635             _glfwInputWindowFocus(window, GLFW_TRUE);
   1636             return;
   1637         }
   1638 
   1639         case FocusOut:
   1640         {
   1641             if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1642                 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
   1643 
   1644             if (event->xfocus.mode == NotifyGrab ||
   1645                 event->xfocus.mode == NotifyUngrab)
   1646             {
   1647                 // Ignore focus events from popup indicator windows, window menu
   1648                 // key chords and window dragging
   1649                 return;
   1650             }
   1651 
   1652             if (window->x11.ic)
   1653                 XUnsetICFocus(window->x11.ic);
   1654 
   1655             if (window->monitor && window->autoIconify)
   1656                 _glfwPlatformIconifyWindow(window);
   1657 
   1658             _glfwInputWindowFocus(window, GLFW_FALSE);
   1659             return;
   1660         }
   1661 
   1662         case Expose:
   1663         {
   1664             _glfwInputWindowDamage(window);
   1665             return;
   1666         }
   1667 
   1668         case PropertyNotify:
   1669         {
   1670             if (event->xproperty.state != PropertyNewValue)
   1671                 return;
   1672 
   1673             if (event->xproperty.atom == _glfw.x11.WM_STATE)
   1674             {
   1675                 const int state = getWindowState(window);
   1676                 if (state != IconicState && state != NormalState)
   1677                     return;
   1678 
   1679                 const GLFWbool iconified = (state == IconicState);
   1680                 if (window->x11.iconified != iconified)
   1681                 {
   1682                     if (window->monitor)
   1683                     {
   1684                         if (iconified)
   1685                             releaseMonitor(window);
   1686                         else
   1687                             acquireMonitor(window);
   1688                     }
   1689 
   1690                     window->x11.iconified = iconified;
   1691                     _glfwInputWindowIconify(window, iconified);
   1692                 }
   1693             }
   1694             else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
   1695             {
   1696                 const GLFWbool maximized = _glfwPlatformWindowMaximized(window);
   1697                 if (window->x11.maximized != maximized)
   1698                 {
   1699                     window->x11.maximized = maximized;
   1700                     _glfwInputWindowMaximize(window, maximized);
   1701                 }
   1702             }
   1703 
   1704             return;
   1705         }
   1706 
   1707         case DestroyNotify:
   1708             return;
   1709     }
   1710 }
   1711 
   1712 
   1713 //////////////////////////////////////////////////////////////////////////
   1714 //////                       GLFW internal API                      //////
   1715 //////////////////////////////////////////////////////////////////////////
   1716 
   1717 // Retrieve a single window property of the specified type
   1718 // Inspired by fghGetWindowProperty from freeglut
   1719 //
   1720 unsigned long _glfwGetWindowPropertyX11(Window window,
   1721                                         Atom property,
   1722                                         Atom type,
   1723                                         unsigned char** value)
   1724 {
   1725     Atom actualType;
   1726     int actualFormat;
   1727     unsigned long itemCount, bytesAfter;
   1728 
   1729     XGetWindowProperty(_glfw.x11.display,
   1730                        window,
   1731                        property,
   1732                        0,
   1733                        LONG_MAX,
   1734                        False,
   1735                        type,
   1736                        &actualType,
   1737                        &actualFormat,
   1738                        &itemCount,
   1739                        &bytesAfter,
   1740                        value);
   1741 
   1742     return itemCount;
   1743 }
   1744 
   1745 // Push contents of our selection to clipboard manager
   1746 //
   1747 void _glfwPushSelectionToManagerX11(void)
   1748 {
   1749     XConvertSelection(_glfw.x11.display,
   1750                       _glfw.x11.CLIPBOARD_MANAGER,
   1751                       _glfw.x11.SAVE_TARGETS,
   1752                       None,
   1753                       _glfw.x11.helperWindowHandle,
   1754                       CurrentTime);
   1755 
   1756     for (;;)
   1757     {
   1758         XEvent event;
   1759 
   1760         while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
   1761         {
   1762             switch (event.type)
   1763             {
   1764                 case SelectionRequest:
   1765                     handleSelectionRequest(&event);
   1766                     break;
   1767 
   1768                 case SelectionClear:
   1769                     handleSelectionClear(&event);
   1770                     break;
   1771 
   1772                 case SelectionNotify:
   1773                 {
   1774                     if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
   1775                     {
   1776                         // This means one of two things; either the selection was
   1777                         // not owned, which means there is no clipboard manager, or
   1778                         // the transfer to the clipboard manager has completed
   1779                         // In either case, it means we are done here
   1780                         return;
   1781                     }
   1782 
   1783                     break;
   1784                 }
   1785             }
   1786         }
   1787 
   1788         waitForEvent(NULL);
   1789     }
   1790 }
   1791 
   1792 
   1793 //////////////////////////////////////////////////////////////////////////
   1794 //////                       GLFW platform API                      //////
   1795 //////////////////////////////////////////////////////////////////////////
   1796 
   1797 int _glfwPlatformCreateWindow(_GLFWwindow* window,
   1798                               const _GLFWwndconfig* wndconfig,
   1799                               const _GLFWctxconfig* ctxconfig,
   1800                               const _GLFWfbconfig* fbconfig)
   1801 {
   1802     Visual* visual;
   1803     int depth;
   1804 
   1805     if (ctxconfig->client != GLFW_NO_API)
   1806     {
   1807         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
   1808         {
   1809             if (!_glfwInitGLX())
   1810                 return GLFW_FALSE;
   1811             if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth))
   1812                 return GLFW_FALSE;
   1813         }
   1814         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
   1815         {
   1816             if (!_glfwInitEGL())
   1817                 return GLFW_FALSE;
   1818             if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth))
   1819                 return GLFW_FALSE;
   1820         }
   1821         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   1822         {
   1823             if (!_glfwInitOSMesa())
   1824                 return GLFW_FALSE;
   1825         }
   1826     }
   1827 
   1828     if (ctxconfig->client == GLFW_NO_API ||
   1829         ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   1830     {
   1831         visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
   1832         depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
   1833     }
   1834 
   1835     if (!createNativeWindow(window, wndconfig, visual, depth))
   1836         return GLFW_FALSE;
   1837 
   1838     if (ctxconfig->client != GLFW_NO_API)
   1839     {
   1840         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
   1841         {
   1842             if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
   1843                 return GLFW_FALSE;
   1844         }
   1845         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
   1846         {
   1847             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
   1848                 return GLFW_FALSE;
   1849         }
   1850         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   1851         {
   1852             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
   1853                 return GLFW_FALSE;
   1854         }
   1855     }
   1856 
   1857     if (window->monitor)
   1858     {
   1859         _glfwPlatformShowWindow(window);
   1860         updateWindowMode(window);
   1861         if (!acquireMonitor(window))
   1862             return GLFW_FALSE;
   1863 
   1864         if (wndconfig->centerCursor)
   1865             centerCursor(window);
   1866     }
   1867 
   1868     XFlush(_glfw.x11.display);
   1869     return GLFW_TRUE;
   1870 }
   1871 
   1872 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
   1873 {
   1874     if (_glfw.x11.disabledCursorWindow == window)
   1875         _glfw.x11.disabledCursorWindow = NULL;
   1876 
   1877     if (window->monitor)
   1878         releaseMonitor(window);
   1879 
   1880     if (window->x11.ic)
   1881     {
   1882         XDestroyIC(window->x11.ic);
   1883         window->x11.ic = NULL;
   1884     }
   1885 
   1886     if (window->context.destroy)
   1887         window->context.destroy(window);
   1888 
   1889     if (window->x11.handle)
   1890     {
   1891         XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
   1892         XUnmapWindow(_glfw.x11.display, window->x11.handle);
   1893         XDestroyWindow(_glfw.x11.display, window->x11.handle);
   1894         window->x11.handle = (Window) 0;
   1895     }
   1896 
   1897     if (window->x11.colormap)
   1898     {
   1899         XFreeColormap(_glfw.x11.display, window->x11.colormap);
   1900         window->x11.colormap = (Colormap) 0;
   1901     }
   1902 
   1903     XFlush(_glfw.x11.display);
   1904 }
   1905 
   1906 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
   1907 {
   1908 #if defined(X_HAVE_UTF8_STRING)
   1909     Xutf8SetWMProperties(_glfw.x11.display,
   1910                          window->x11.handle,
   1911                          title, title,
   1912                          NULL, 0,
   1913                          NULL, NULL, NULL);
   1914 #else
   1915     // This may be a slightly better fallback than using XStoreName and
   1916     // XSetIconName, which always store their arguments using STRING
   1917     XmbSetWMProperties(_glfw.x11.display,
   1918                        window->x11.handle,
   1919                        title, title,
   1920                        NULL, 0,
   1921                        NULL, NULL, NULL);
   1922 #endif
   1923 
   1924     XChangeProperty(_glfw.x11.display,  window->x11.handle,
   1925                     _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
   1926                     PropModeReplace,
   1927                     (unsigned char*) title, strlen(title));
   1928 
   1929     XChangeProperty(_glfw.x11.display,  window->x11.handle,
   1930                     _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
   1931                     PropModeReplace,
   1932                     (unsigned char*) title, strlen(title));
   1933 
   1934     XFlush(_glfw.x11.display);
   1935 }
   1936 
   1937 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
   1938                                 int count, const GLFWimage* images)
   1939 {
   1940     if (count)
   1941     {
   1942         int i, j, longCount = 0;
   1943 
   1944         for (i = 0;  i < count;  i++)
   1945             longCount += 2 + images[i].width * images[i].height;
   1946 
   1947         long* icon = calloc(longCount, sizeof(long));
   1948         long* target = icon;
   1949 
   1950         for (i = 0;  i < count;  i++)
   1951         {
   1952             *target++ = images[i].width;
   1953             *target++ = images[i].height;
   1954 
   1955             for (j = 0;  j < images[i].width * images[i].height;  j++)
   1956             {
   1957                 *target++ = (images[i].pixels[j * 4 + 0] << 16) |
   1958                             (images[i].pixels[j * 4 + 1] <<  8) |
   1959                             (images[i].pixels[j * 4 + 2] <<  0) |
   1960                             (images[i].pixels[j * 4 + 3] << 24);
   1961             }
   1962         }
   1963 
   1964         XChangeProperty(_glfw.x11.display, window->x11.handle,
   1965                         _glfw.x11.NET_WM_ICON,
   1966                         XA_CARDINAL, 32,
   1967                         PropModeReplace,
   1968                         (unsigned char*) icon,
   1969                         longCount);
   1970 
   1971         free(icon);
   1972     }
   1973     else
   1974     {
   1975         XDeleteProperty(_glfw.x11.display, window->x11.handle,
   1976                         _glfw.x11.NET_WM_ICON);
   1977     }
   1978 
   1979     XFlush(_glfw.x11.display);
   1980 }
   1981 
   1982 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
   1983 {
   1984     Window dummy;
   1985     int x, y;
   1986 
   1987     XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
   1988                           0, 0, &x, &y, &dummy);
   1989 
   1990     if (xpos)
   1991         *xpos = x;
   1992     if (ypos)
   1993         *ypos = y;
   1994 }
   1995 
   1996 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
   1997 {
   1998     // HACK: Explicitly setting PPosition to any value causes some WMs, notably
   1999     //       Compiz and Metacity, to honor the position of unmapped windows
   2000     if (!_glfwPlatformWindowVisible(window))
   2001     {
   2002         long supplied;
   2003         XSizeHints* hints = XAllocSizeHints();
   2004 
   2005         if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
   2006         {
   2007             hints->flags |= PPosition;
   2008             hints->x = hints->y = 0;
   2009 
   2010             XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
   2011         }
   2012 
   2013         XFree(hints);
   2014     }
   2015 
   2016     XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
   2017     XFlush(_glfw.x11.display);
   2018 }
   2019 
   2020 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
   2021 {
   2022     XWindowAttributes attribs;
   2023     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
   2024 
   2025     if (width)
   2026         *width = attribs.width;
   2027     if (height)
   2028         *height = attribs.height;
   2029 }
   2030 
   2031 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
   2032 {
   2033     if (window->monitor)
   2034     {
   2035         if (window->monitor->window == window)
   2036             acquireMonitor(window);
   2037     }
   2038     else
   2039     {
   2040         if (!window->resizable)
   2041             updateNormalHints(window, width, height);
   2042 
   2043         XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
   2044     }
   2045 
   2046     XFlush(_glfw.x11.display);
   2047 }
   2048 
   2049 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
   2050                                       int minwidth, int minheight,
   2051                                       int maxwidth, int maxheight)
   2052 {
   2053     int width, height;
   2054     _glfwPlatformGetWindowSize(window, &width, &height);
   2055     updateNormalHints(window, width, height);
   2056     XFlush(_glfw.x11.display);
   2057 }
   2058 
   2059 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
   2060 {
   2061     int width, height;
   2062     _glfwPlatformGetWindowSize(window, &width, &height);
   2063     updateNormalHints(window, width, height);
   2064     XFlush(_glfw.x11.display);
   2065 }
   2066 
   2067 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
   2068 {
   2069     _glfwPlatformGetWindowSize(window, width, height);
   2070 }
   2071 
   2072 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
   2073                                      int* left, int* top,
   2074                                      int* right, int* bottom)
   2075 {
   2076     long* extents = NULL;
   2077 
   2078     if (window->monitor || !window->decorated)
   2079         return;
   2080 
   2081     if (_glfw.x11.NET_FRAME_EXTENTS == None)
   2082         return;
   2083 
   2084     if (!_glfwPlatformWindowVisible(window) &&
   2085         _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
   2086     {
   2087         XEvent event;
   2088         double timeout = 0.5;
   2089 
   2090         // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
   2091         // function before the window is mapped
   2092         sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
   2093                       0, 0, 0, 0, 0);
   2094 
   2095         // HACK: Use a timeout because earlier versions of some window managers
   2096         //       (at least Unity, Fluxbox and Xfwm) failed to send the reply
   2097         //       They have been fixed but broken versions are still in the wild
   2098         //       If you are affected by this and your window manager is NOT
   2099         //       listed above, PLEASE report it to their and our issue trackers
   2100         while (!XCheckIfEvent(_glfw.x11.display,
   2101                               &event,
   2102                               isFrameExtentsEvent,
   2103                               (XPointer) window))
   2104         {
   2105             if (!waitForEvent(&timeout))
   2106             {
   2107                 _glfwInputError(GLFW_PLATFORM_ERROR,
   2108                                 "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
   2109                 return;
   2110             }
   2111         }
   2112     }
   2113 
   2114     if (_glfwGetWindowPropertyX11(window->x11.handle,
   2115                                   _glfw.x11.NET_FRAME_EXTENTS,
   2116                                   XA_CARDINAL,
   2117                                   (unsigned char**) &extents) == 4)
   2118     {
   2119         if (left)
   2120             *left = extents[0];
   2121         if (top)
   2122             *top = extents[2];
   2123         if (right)
   2124             *right = extents[1];
   2125         if (bottom)
   2126             *bottom = extents[3];
   2127     }
   2128 
   2129     if (extents)
   2130         XFree(extents);
   2131 }
   2132 
   2133 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
   2134 {
   2135     if (window->x11.overrideRedirect)
   2136     {
   2137         // Override-redirect windows cannot be iconified or restored, as those
   2138         // tasks are performed by the window manager
   2139         _glfwInputError(GLFW_PLATFORM_ERROR,
   2140                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
   2141         return;
   2142     }
   2143 
   2144     XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
   2145     XFlush(_glfw.x11.display);
   2146 }
   2147 
   2148 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
   2149 {
   2150     if (window->x11.overrideRedirect)
   2151     {
   2152         // Override-redirect windows cannot be iconified or restored, as those
   2153         // tasks are performed by the window manager
   2154         _glfwInputError(GLFW_PLATFORM_ERROR,
   2155                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
   2156         return;
   2157     }
   2158 
   2159     if (_glfwPlatformWindowIconified(window))
   2160     {
   2161         XMapWindow(_glfw.x11.display, window->x11.handle);
   2162         waitForVisibilityNotify(window);
   2163     }
   2164     else if (_glfwPlatformWindowVisible(window))
   2165     {
   2166         if (_glfw.x11.NET_WM_STATE &&
   2167             _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
   2168             _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2169         {
   2170             sendEventToWM(window,
   2171                           _glfw.x11.NET_WM_STATE,
   2172                           _NET_WM_STATE_REMOVE,
   2173                           _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2174                           _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
   2175                           1, 0);
   2176         }
   2177     }
   2178 
   2179     XFlush(_glfw.x11.display);
   2180 }
   2181 
   2182 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
   2183 {
   2184     if (_glfw.x11.NET_WM_STATE &&
   2185         _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
   2186         _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2187     {
   2188         sendEventToWM(window,
   2189                       _glfw.x11.NET_WM_STATE,
   2190                       _NET_WM_STATE_ADD,
   2191                       _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2192                       _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
   2193                       1, 0);
   2194         XFlush(_glfw.x11.display);
   2195     }
   2196 }
   2197 
   2198 void _glfwPlatformShowWindow(_GLFWwindow* window)
   2199 {
   2200     if (_glfwPlatformWindowVisible(window))
   2201         return;
   2202 
   2203     XMapWindow(_glfw.x11.display, window->x11.handle);
   2204     waitForVisibilityNotify(window);
   2205 }
   2206 
   2207 void _glfwPlatformHideWindow(_GLFWwindow* window)
   2208 {
   2209     XUnmapWindow(_glfw.x11.display, window->x11.handle);
   2210     XFlush(_glfw.x11.display);
   2211 }
   2212 
   2213 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
   2214 {
   2215     sendEventToWM(window,
   2216                   _glfw.x11.NET_WM_STATE,
   2217                   _NET_WM_STATE_ADD,
   2218                   _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
   2219                   0, 1, 0);
   2220 }
   2221 
   2222 void _glfwPlatformFocusWindow(_GLFWwindow* window)
   2223 {
   2224     if (_glfw.x11.NET_ACTIVE_WINDOW)
   2225         sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
   2226     else
   2227     {
   2228         XRaiseWindow(_glfw.x11.display, window->x11.handle);
   2229         XSetInputFocus(_glfw.x11.display, window->x11.handle,
   2230                        RevertToParent, CurrentTime);
   2231     }
   2232 
   2233     XFlush(_glfw.x11.display);
   2234 }
   2235 
   2236 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
   2237                                    _GLFWmonitor* monitor,
   2238                                    int xpos, int ypos,
   2239                                    int width, int height,
   2240                                    int refreshRate)
   2241 {
   2242     if (window->monitor == monitor)
   2243     {
   2244         if (monitor)
   2245         {
   2246             if (monitor->window == window)
   2247                 acquireMonitor(window);
   2248         }
   2249         else
   2250         {
   2251             XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   2252                               xpos, ypos, width, height);
   2253         }
   2254 
   2255         return;
   2256     }
   2257 
   2258     if (window->monitor)
   2259         releaseMonitor(window);
   2260 
   2261     _glfwInputWindowMonitorChange(window, monitor);
   2262     updateNormalHints(window, width, height);
   2263     updateWindowMode(window);
   2264 
   2265     if (window->monitor)
   2266     {
   2267         XMapRaised(_glfw.x11.display, window->x11.handle);
   2268         if (waitForVisibilityNotify(window))
   2269             acquireMonitor(window);
   2270     }
   2271     else
   2272     {
   2273         XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   2274                           xpos, ypos, width, height);
   2275     }
   2276 
   2277     XFlush(_glfw.x11.display);
   2278 }
   2279 
   2280 int _glfwPlatformWindowFocused(_GLFWwindow* window)
   2281 {
   2282     Window focused;
   2283     int state;
   2284 
   2285     XGetInputFocus(_glfw.x11.display, &focused, &state);
   2286     return window->x11.handle == focused;
   2287 }
   2288 
   2289 int _glfwPlatformWindowIconified(_GLFWwindow* window)
   2290 {
   2291     return getWindowState(window) == IconicState;
   2292 }
   2293 
   2294 int _glfwPlatformWindowVisible(_GLFWwindow* window)
   2295 {
   2296     XWindowAttributes wa;
   2297     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
   2298     return wa.map_state == IsViewable;
   2299 }
   2300 
   2301 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
   2302 {
   2303     Atom* states;
   2304     unsigned long i;
   2305     GLFWbool maximized = GLFW_FALSE;
   2306     const unsigned long count =
   2307         _glfwGetWindowPropertyX11(window->x11.handle,
   2308                                   _glfw.x11.NET_WM_STATE,
   2309                                   XA_ATOM,
   2310                                   (unsigned char**) &states);
   2311 
   2312     for (i = 0;  i < count;  i++)
   2313     {
   2314         if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2315             states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2316         {
   2317             maximized = GLFW_TRUE;
   2318             break;
   2319         }
   2320     }
   2321 
   2322     if (states)
   2323         XFree(states);
   2324 
   2325     return maximized;
   2326 }
   2327 
   2328 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
   2329 {
   2330     int width, height;
   2331     _glfwPlatformGetWindowSize(window, &width, &height);
   2332     updateNormalHints(window, width, height);
   2333 }
   2334 
   2335 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
   2336 {
   2337     if (enabled)
   2338     {
   2339         XDeleteProperty(_glfw.x11.display,
   2340                         window->x11.handle,
   2341                         _glfw.x11.MOTIF_WM_HINTS);
   2342     }
   2343     else
   2344     {
   2345         struct
   2346         {
   2347             unsigned long flags;
   2348             unsigned long functions;
   2349             unsigned long decorations;
   2350             long input_mode;
   2351             unsigned long status;
   2352         } hints;
   2353 
   2354         hints.flags = 2;       // Set decorations
   2355         hints.decorations = 0; // No decorations
   2356 
   2357         XChangeProperty(_glfw.x11.display, window->x11.handle,
   2358                         _glfw.x11.MOTIF_WM_HINTS,
   2359                         _glfw.x11.MOTIF_WM_HINTS, 32,
   2360                         PropModeReplace,
   2361                         (unsigned char*) &hints,
   2362                         sizeof(hints) / sizeof(long));
   2363     }
   2364 }
   2365 
   2366 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
   2367 {
   2368     if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
   2369         return;
   2370 
   2371     if (_glfwPlatformWindowVisible(window))
   2372     {
   2373         const Atom action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
   2374         sendEventToWM(window,
   2375                       _glfw.x11.NET_WM_STATE,
   2376                       action,
   2377                       _glfw.x11.NET_WM_STATE_ABOVE,
   2378                       0, 1, 0);
   2379     }
   2380     else
   2381     {
   2382         Atom* states;
   2383         unsigned long i, count;
   2384 
   2385         count = _glfwGetWindowPropertyX11(window->x11.handle,
   2386                                           _glfw.x11.NET_WM_STATE,
   2387                                           XA_ATOM,
   2388                                           (unsigned char**) &states);
   2389         if (!states)
   2390             return;
   2391 
   2392         if (enabled)
   2393         {
   2394             for (i = 0;  i < count;  i++)
   2395             {
   2396                 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
   2397                     break;
   2398             }
   2399 
   2400             if (i == count)
   2401             {
   2402                 XChangeProperty(_glfw.x11.display, window->x11.handle,
   2403                                 _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2404                                 PropModeAppend,
   2405                                 (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
   2406                                 1);
   2407             }
   2408         }
   2409         else
   2410         {
   2411             for (i = 0;  i < count;  i++)
   2412             {
   2413                 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
   2414                 {
   2415                     states[i] = states[count - 1];
   2416                     count--;
   2417                 }
   2418             }
   2419 
   2420             XChangeProperty(_glfw.x11.display, window->x11.handle,
   2421                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2422                             PropModeReplace, (unsigned char*) &states, count);
   2423         }
   2424 
   2425         XFree(states);
   2426     }
   2427 
   2428     XFlush(_glfw.x11.display);
   2429 }
   2430 
   2431 void _glfwPlatformPollEvents(void)
   2432 {
   2433     _GLFWwindow* window;
   2434 
   2435 #if defined(__linux__)
   2436     _glfwDetectJoystickConnectionLinux();
   2437 #endif
   2438     int count = XPending(_glfw.x11.display);
   2439     while (count--)
   2440     {
   2441         XEvent event;
   2442         XNextEvent(_glfw.x11.display, &event);
   2443         processEvent(&event);
   2444     }
   2445 
   2446     window = _glfw.x11.disabledCursorWindow;
   2447     if (window)
   2448     {
   2449         int width, height;
   2450         _glfwPlatformGetWindowSize(window, &width, &height);
   2451 
   2452         // NOTE: Re-center the cursor only if it has moved since the last call,
   2453         //       to avoid breaking glfwWaitEvents with MotionNotify
   2454         if (window->x11.lastCursorPosX != width / 2 ||
   2455             window->x11.lastCursorPosY != height / 2)
   2456         {
   2457             _glfwPlatformSetCursorPos(window, width / 2, height / 2);
   2458         }
   2459     }
   2460 
   2461     XFlush(_glfw.x11.display);
   2462 }
   2463 
   2464 void _glfwPlatformWaitEvents(void)
   2465 {
   2466     while (!XPending(_glfw.x11.display))
   2467         waitForEvent(NULL);
   2468 
   2469     _glfwPlatformPollEvents();
   2470 }
   2471 
   2472 void _glfwPlatformWaitEventsTimeout(double timeout)
   2473 {
   2474     while (!XPending(_glfw.x11.display))
   2475     {
   2476         if (!waitForEvent(&timeout))
   2477             break;
   2478     }
   2479 
   2480     _glfwPlatformPollEvents();
   2481 }
   2482 
   2483 void _glfwPlatformPostEmptyEvent(void)
   2484 {
   2485     XEvent event;
   2486 
   2487     memset(&event, 0, sizeof(event));
   2488     event.type = ClientMessage;
   2489     event.xclient.window = _glfw.x11.helperWindowHandle;
   2490     event.xclient.format = 32; // Data is 32-bit longs
   2491     event.xclient.message_type = _glfw.x11.NULL_;
   2492 
   2493     XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event);
   2494     XFlush(_glfw.x11.display);
   2495 }
   2496 
   2497 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
   2498 {
   2499     Window root, child;
   2500     int rootX, rootY, childX, childY;
   2501     unsigned int mask;
   2502 
   2503     XQueryPointer(_glfw.x11.display, window->x11.handle,
   2504                   &root, &child,
   2505                   &rootX, &rootY, &childX, &childY,
   2506                   &mask);
   2507 
   2508     if (xpos)
   2509         *xpos = childX;
   2510     if (ypos)
   2511         *ypos = childY;
   2512 }
   2513 
   2514 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
   2515 {
   2516     // Store the new position so it can be recognized later
   2517     window->x11.warpCursorPosX = (int) x;
   2518     window->x11.warpCursorPosY = (int) y;
   2519 
   2520     XWarpPointer(_glfw.x11.display, None, window->x11.handle,
   2521                  0,0,0,0, (int) x, (int) y);
   2522     XFlush(_glfw.x11.display);
   2523 }
   2524 
   2525 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
   2526 {
   2527     if (mode == GLFW_CURSOR_DISABLED)
   2528     {
   2529         if (_glfw.x11.xi.available)
   2530         {
   2531             XIEventMask em;
   2532             unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
   2533 
   2534             em.deviceid = XIAllMasterDevices;
   2535             em.mask_len = sizeof(mask);
   2536             em.mask = mask;
   2537             XISetMask(mask, XI_RawMotion);
   2538 
   2539             XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
   2540         }
   2541 
   2542         _glfw.x11.disabledCursorWindow = window;
   2543         _glfwPlatformGetCursorPos(window,
   2544                                   &_glfw.x11.restoreCursorPosX,
   2545                                   &_glfw.x11.restoreCursorPosY);
   2546         centerCursor(window);
   2547         XGrabPointer(_glfw.x11.display, window->x11.handle, True,
   2548                      ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
   2549                      GrabModeAsync, GrabModeAsync,
   2550                      window->x11.handle,
   2551                      _glfw.x11.hiddenCursorHandle,
   2552                      CurrentTime);
   2553     }
   2554     else if (_glfw.x11.disabledCursorWindow == window)
   2555     {
   2556         if (_glfw.x11.xi.available)
   2557         {
   2558             XIEventMask em;
   2559             unsigned char mask[] = { 0 };
   2560 
   2561             em.deviceid = XIAllMasterDevices;
   2562             em.mask_len = sizeof(mask);
   2563             em.mask = mask;
   2564 
   2565             XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
   2566         }
   2567 
   2568         _glfw.x11.disabledCursorWindow = NULL;
   2569         XUngrabPointer(_glfw.x11.display, CurrentTime);
   2570         _glfwPlatformSetCursorPos(window,
   2571                                   _glfw.x11.restoreCursorPosX,
   2572                                   _glfw.x11.restoreCursorPosY);
   2573     }
   2574 
   2575     updateCursorImage(window);
   2576     XFlush(_glfw.x11.display);
   2577 }
   2578 
   2579 const char* _glfwPlatformGetScancodeName(int scancode)
   2580 {
   2581     if (!_glfw.x11.xkb.available)
   2582         return NULL;
   2583 
   2584     const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
   2585     if (keysym == NoSymbol)
   2586         return NULL;
   2587 
   2588     const long ch = _glfwKeySym2Unicode(keysym);
   2589     if (ch == -1)
   2590         return NULL;
   2591 
   2592     const size_t count = encodeUTF8(_glfw.x11.keyName, (unsigned int) ch);
   2593     if (count == 0)
   2594         return NULL;
   2595 
   2596     _glfw.x11.keyName[count] = '\0';
   2597     return _glfw.x11.keyName;
   2598 }
   2599 
   2600 int _glfwPlatformGetKeyScancode(int key)
   2601 {
   2602     return _glfw.x11.scancodes[key];
   2603 }
   2604 
   2605 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
   2606                               const GLFWimage* image,
   2607                               int xhot, int yhot)
   2608 {
   2609     cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
   2610     if (!cursor->x11.handle)
   2611         return GLFW_FALSE;
   2612 
   2613     return GLFW_TRUE;
   2614 }
   2615 
   2616 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
   2617 {
   2618     cursor->x11.handle = XCreateFontCursor(_glfw.x11.display,
   2619                                            translateCursorShape(shape));
   2620     if (!cursor->x11.handle)
   2621     {
   2622         _glfwInputError(GLFW_PLATFORM_ERROR,
   2623                         "X11: Failed to create standard cursor");
   2624         return GLFW_FALSE;
   2625     }
   2626 
   2627     return GLFW_TRUE;
   2628 }
   2629 
   2630 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
   2631 {
   2632     if (cursor->x11.handle)
   2633         XFreeCursor(_glfw.x11.display, cursor->x11.handle);
   2634 }
   2635 
   2636 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
   2637 {
   2638     if (window->cursorMode == GLFW_CURSOR_NORMAL)
   2639     {
   2640         updateCursorImage(window);
   2641         XFlush(_glfw.x11.display);
   2642     }
   2643 }
   2644 
   2645 void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
   2646 {
   2647     free(_glfw.x11.clipboardString);
   2648     _glfw.x11.clipboardString = strdup(string);
   2649 
   2650     XSetSelectionOwner(_glfw.x11.display,
   2651                        _glfw.x11.CLIPBOARD,
   2652                        _glfw.x11.helperWindowHandle,
   2653                        CurrentTime);
   2654 
   2655     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
   2656         _glfw.x11.helperWindowHandle)
   2657     {
   2658         _glfwInputError(GLFW_PLATFORM_ERROR,
   2659                         "X11: Failed to become owner of clipboard selection");
   2660     }
   2661 }
   2662 
   2663 const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
   2664 {
   2665     return getSelectionString(_glfw.x11.CLIPBOARD);
   2666 }
   2667 
   2668 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
   2669 {
   2670     if (!_glfw.vk.KHR_surface)
   2671         return;
   2672 
   2673     if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
   2674     {
   2675         if (!_glfw.vk.KHR_xlib_surface)
   2676             return;
   2677     }
   2678 
   2679     extensions[0] = "VK_KHR_surface";
   2680 
   2681     // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
   2682     //       not correctly implementing VK_KHR_xlib_surface
   2683     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   2684         extensions[1] = "VK_KHR_xcb_surface";
   2685     else
   2686         extensions[1] = "VK_KHR_xlib_surface";
   2687 }
   2688 
   2689 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
   2690                                                       VkPhysicalDevice device,
   2691                                                       uint32_t queuefamily)
   2692 {
   2693     VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
   2694                                                           _glfw.x11.screen));
   2695 
   2696     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   2697     {
   2698         PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR =
   2699             (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
   2700             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
   2701         if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
   2702         {
   2703             _glfwInputError(GLFW_API_UNAVAILABLE,
   2704                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
   2705             return GLFW_FALSE;
   2706         }
   2707 
   2708         xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
   2709         if (!connection)
   2710         {
   2711             _glfwInputError(GLFW_PLATFORM_ERROR,
   2712                             "X11: Failed to retrieve XCB connection");
   2713             return GLFW_FALSE;
   2714         }
   2715 
   2716         return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
   2717                                                             queuefamily,
   2718                                                             connection,
   2719                                                             visualID);
   2720     }
   2721     else
   2722     {
   2723         PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR =
   2724             (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
   2725             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
   2726         if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
   2727         {
   2728             _glfwInputError(GLFW_API_UNAVAILABLE,
   2729                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
   2730             return GLFW_FALSE;
   2731         }
   2732 
   2733         return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
   2734                                                              queuefamily,
   2735                                                              _glfw.x11.display,
   2736                                                              visualID);
   2737     }
   2738 }
   2739 
   2740 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
   2741                                           _GLFWwindow* window,
   2742                                           const VkAllocationCallbacks* allocator,
   2743                                           VkSurfaceKHR* surface)
   2744 {
   2745     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   2746     {
   2747         VkResult err;
   2748         VkXcbSurfaceCreateInfoKHR sci;
   2749         PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
   2750 
   2751         xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
   2752         if (!connection)
   2753         {
   2754             _glfwInputError(GLFW_PLATFORM_ERROR,
   2755                             "X11: Failed to retrieve XCB connection");
   2756             return VK_ERROR_EXTENSION_NOT_PRESENT;
   2757         }
   2758 
   2759         vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
   2760             vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
   2761         if (!vkCreateXcbSurfaceKHR)
   2762         {
   2763             _glfwInputError(GLFW_API_UNAVAILABLE,
   2764                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
   2765             return VK_ERROR_EXTENSION_NOT_PRESENT;
   2766         }
   2767 
   2768         memset(&sci, 0, sizeof(sci));
   2769         sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
   2770         sci.connection = connection;
   2771         sci.window = window->x11.handle;
   2772 
   2773         err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
   2774         if (err)
   2775         {
   2776             _glfwInputError(GLFW_PLATFORM_ERROR,
   2777                             "X11: Failed to create Vulkan XCB surface: %s",
   2778                             _glfwGetVulkanResultString(err));
   2779         }
   2780 
   2781         return err;
   2782     }
   2783     else
   2784     {
   2785         VkResult err;
   2786         VkXlibSurfaceCreateInfoKHR sci;
   2787         PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
   2788 
   2789         vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
   2790             vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
   2791         if (!vkCreateXlibSurfaceKHR)
   2792         {
   2793             _glfwInputError(GLFW_API_UNAVAILABLE,
   2794                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
   2795             return VK_ERROR_EXTENSION_NOT_PRESENT;
   2796         }
   2797 
   2798         memset(&sci, 0, sizeof(sci));
   2799         sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
   2800         sci.dpy = _glfw.x11.display;
   2801         sci.window = window->x11.handle;
   2802 
   2803         err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
   2804         if (err)
   2805         {
   2806             _glfwInputError(GLFW_PLATFORM_ERROR,
   2807                             "X11: Failed to create Vulkan X11 surface: %s",
   2808                             _glfwGetVulkanResultString(err));
   2809         }
   2810 
   2811         return err;
   2812     }
   2813 }
   2814 
   2815 
   2816 //////////////////////////////////////////////////////////////////////////
   2817 //////                        GLFW native API                       //////
   2818 //////////////////////////////////////////////////////////////////////////
   2819 
   2820 GLFWAPI Display* glfwGetX11Display(void)
   2821 {
   2822     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   2823     return _glfw.x11.display;
   2824 }
   2825 
   2826 GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
   2827 {
   2828     _GLFWwindow* window = (_GLFWwindow*) handle;
   2829     _GLFW_REQUIRE_INIT_OR_RETURN(None);
   2830     return window->x11.handle;
   2831 }
   2832 
   2833 GLFWAPI void glfwSetX11SelectionString(const char* string)
   2834 {
   2835     _GLFW_REQUIRE_INIT();
   2836 
   2837     free(_glfw.x11.primarySelectionString);
   2838     _glfw.x11.primarySelectionString = strdup(string);
   2839 
   2840     XSetSelectionOwner(_glfw.x11.display,
   2841                        _glfw.x11.PRIMARY,
   2842                        _glfw.x11.helperWindowHandle,
   2843                        CurrentTime);
   2844 
   2845     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
   2846         _glfw.x11.helperWindowHandle)
   2847     {
   2848         _glfwInputError(GLFW_PLATFORM_ERROR,
   2849                         "X11: Failed to become owner of primary selection");
   2850     }
   2851 }
   2852 
   2853 GLFWAPI const char* glfwGetX11SelectionString(void)
   2854 {
   2855     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   2856     return getSelectionString(_glfw.x11.PRIMARY);
   2857 }
   2858