volviewer

Volumetric Data Toy Viewer
git clone anongit@rnpnr.xyz:volviewer.git
Log | Files | Refs | Feed | LICENSE

x11_window.c (106419B)


      1 //========================================================================
      2 // GLFW 3.4 X11 (modified for raylib) - www.glfw.org; www.raylib.com
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2002-2006 Marcus Geelnard
      5 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
      6 // Copyright (c) 2024 M374LX <wilsalx@gmail.com>
      7 //
      8 // This software is provided 'as-is', without any express or implied
      9 // warranty. In no event will the authors be held liable for any damages
     10 // arising from the use of this software.
     11 //
     12 // Permission is granted to anyone to use this software for any purpose,
     13 // including commercial applications, and to alter it and redistribute it
     14 // freely, subject to the following restrictions:
     15 //
     16 // 1. The origin of this software must not be misrepresented; you must not
     17 //    claim that you wrote the original software. If you use this software
     18 //    in a product, an acknowledgment in the product documentation would
     19 //    be appreciated but is not required.
     20 //
     21 // 2. Altered source versions must be plainly marked as such, and must not
     22 //    be misrepresented as being the original software.
     23 //
     24 // 3. This notice may not be removed or altered from any source
     25 //    distribution.
     26 //
     27 //========================================================================
     28 
     29 #include "internal.h"
     30 
     31 #if defined(_GLFW_X11)
     32 
     33 #include <X11/cursorfont.h>
     34 #include <X11/Xmd.h>
     35 
     36 #include <poll.h>
     37 
     38 #include <string.h>
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <limits.h>
     42 #include <errno.h>
     43 #include <assert.h>
     44 
     45 // Action for EWMH client messages
     46 #define _NET_WM_STATE_REMOVE        0
     47 #define _NET_WM_STATE_ADD           1
     48 #define _NET_WM_STATE_TOGGLE        2
     49 
     50 // Additional mouse button names for XButtonEvent
     51 #define Button6            6
     52 #define Button7            7
     53 
     54 // Motif WM hints flags
     55 #define MWM_HINTS_DECORATIONS   2
     56 #define MWM_DECOR_ALL           1
     57 
     58 #define _GLFW_XDND_VERSION 5
     59 
     60 // Wait for event data to arrive on the X11 display socket
     61 // This avoids blocking other threads via the per-display Xlib lock that also
     62 // covers GLX functions
     63 //
     64 static GLFWbool waitForX11Event(double* timeout)
     65 {
     66     struct pollfd fd = { ConnectionNumber(_glfw.x11.display), POLLIN };
     67 
     68     while (!XPending(_glfw.x11.display))
     69     {
     70         if (!_glfwPollPOSIX(&fd, 1, timeout))
     71             return GLFW_FALSE;
     72     }
     73 
     74     return GLFW_TRUE;
     75 }
     76 
     77 // Wait for event data to arrive on any event file descriptor
     78 // This avoids blocking other threads via the per-display Xlib lock that also
     79 // covers GLX functions
     80 //
     81 static GLFWbool waitForAnyEvent(double* timeout)
     82 {
     83     enum { XLIB_FD, PIPE_FD, INOTIFY_FD };
     84     struct pollfd fds[] =
     85     {
     86         [XLIB_FD] = { ConnectionNumber(_glfw.x11.display), POLLIN },
     87         [PIPE_FD] = { _glfw.x11.emptyEventPipe[0], POLLIN },
     88         [INOTIFY_FD] = { -1, POLLIN }
     89     };
     90 
     91 #if defined(GLFW_BUILD_LINUX_JOYSTICK)
     92     if (_glfw.joysticksInitialized)
     93         fds[INOTIFY_FD].fd = _glfw.linjs.inotify;
     94 #endif
     95 
     96     while (!XPending(_glfw.x11.display))
     97     {
     98         if (!_glfwPollPOSIX(fds, sizeof(fds) / sizeof(fds[0]), timeout))
     99             return GLFW_FALSE;
    100 
    101         for (int i = 1; i < sizeof(fds) / sizeof(fds[0]); i++)
    102         {
    103             if (fds[i].revents & POLLIN)
    104                 return GLFW_TRUE;
    105         }
    106     }
    107 
    108     return GLFW_TRUE;
    109 }
    110 
    111 // Writes a byte to the empty event pipe
    112 //
    113 static void writeEmptyEvent(void)
    114 {
    115     for (;;)
    116     {
    117         const char byte = 0;
    118         const ssize_t result = write(_glfw.x11.emptyEventPipe[1], &byte, 1);
    119         if (result == 1 || (result == -1 && errno != EINTR))
    120             break;
    121     }
    122 }
    123 
    124 // Drains available data from the empty event pipe
    125 //
    126 static void drainEmptyEvents(void)
    127 {
    128     for (;;)
    129     {
    130         char dummy[64];
    131         const ssize_t result = read(_glfw.x11.emptyEventPipe[0], dummy, sizeof(dummy));
    132         if (result == -1 && errno != EINTR)
    133             break;
    134     }
    135 }
    136 
    137 // Waits until a VisibilityNotify event arrives for the specified window or the
    138 // timeout period elapses (ICCCM section 4.2.2)
    139 //
    140 static GLFWbool waitForVisibilityNotify(_GLFWwindow* window)
    141 {
    142     XEvent dummy;
    143     double timeout = 0.1;
    144 
    145     while (!XCheckTypedWindowEvent(_glfw.x11.display,
    146                                    window->x11.handle,
    147                                    VisibilityNotify,
    148                                    &dummy))
    149     {
    150         if (!waitForX11Event(&timeout))
    151             return GLFW_FALSE;
    152     }
    153 
    154     return GLFW_TRUE;
    155 }
    156 
    157 // Returns whether the window is iconified
    158 //
    159 static int getWindowState(_GLFWwindow* window)
    160 {
    161     int result = WithdrawnState;
    162     struct {
    163         CARD32 state;
    164         Window icon;
    165     } *state = NULL;
    166 
    167     if (_glfwGetWindowPropertyX11(window->x11.handle,
    168                                   _glfw.x11.WM_STATE,
    169                                   _glfw.x11.WM_STATE,
    170                                   (unsigned char**) &state) >= 2)
    171     {
    172         result = state->state;
    173     }
    174 
    175     if (state)
    176         XFree(state);
    177 
    178     return result;
    179 }
    180 
    181 // Returns whether the event is a selection event
    182 //
    183 static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer)
    184 {
    185     if (event->xany.window != _glfw.x11.helperWindowHandle)
    186         return False;
    187 
    188     return event->type == SelectionRequest ||
    189            event->type == SelectionNotify ||
    190            event->type == SelectionClear;
    191 }
    192 
    193 // Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
    194 //
    195 static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer)
    196 {
    197     _GLFWwindow* window = (_GLFWwindow*) pointer;
    198     return event->type == PropertyNotify &&
    199            event->xproperty.state == PropertyNewValue &&
    200            event->xproperty.window == window->x11.handle &&
    201            event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
    202 }
    203 
    204 // Returns whether it is a property event for the specified selection transfer
    205 //
    206 static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer)
    207 {
    208     XEvent* notification = (XEvent*) pointer;
    209     return event->type == PropertyNotify &&
    210            event->xproperty.state == PropertyNewValue &&
    211            event->xproperty.window == notification->xselection.requestor &&
    212            event->xproperty.atom == notification->xselection.property;
    213 }
    214 
    215 // Translates an X event modifier state mask
    216 //
    217 static int translateState(int state)
    218 {
    219     int mods = 0;
    220 
    221     if (state & ShiftMask)
    222         mods |= GLFW_MOD_SHIFT;
    223     if (state & ControlMask)
    224         mods |= GLFW_MOD_CONTROL;
    225     if (state & Mod1Mask)
    226         mods |= GLFW_MOD_ALT;
    227     if (state & Mod4Mask)
    228         mods |= GLFW_MOD_SUPER;
    229     if (state & LockMask)
    230         mods |= GLFW_MOD_CAPS_LOCK;
    231     if (state & Mod2Mask)
    232         mods |= GLFW_MOD_NUM_LOCK;
    233 
    234     return mods;
    235 }
    236 
    237 // Translates an X11 key code to a GLFW key token
    238 //
    239 static int translateKeyX11(int scancode)
    240 {
    241     // Use the pre-filled LUT (see createKeyTablesX11() in x11_init.c)
    242     if (scancode < 0 || scancode > 255)
    243         return GLFW_KEY_UNKNOWN;
    244 
    245     return _glfw.x11.keycodes[scancode];
    246 }
    247 
    248 // Sends an EWMH or ICCCM event to the window manager
    249 //
    250 static void sendEventToWM(_GLFWwindow* window, Atom type,
    251                           long a, long b, long c, long d, long e)
    252 {
    253     XEvent event = { ClientMessage };
    254     event.xclient.window = window->x11.handle;
    255     event.xclient.format = 32; // Data is 32-bit longs
    256     event.xclient.message_type = type;
    257     event.xclient.data.l[0] = a;
    258     event.xclient.data.l[1] = b;
    259     event.xclient.data.l[2] = c;
    260     event.xclient.data.l[3] = d;
    261     event.xclient.data.l[4] = e;
    262 
    263     XSendEvent(_glfw.x11.display, _glfw.x11.root,
    264                False,
    265                SubstructureNotifyMask | SubstructureRedirectMask,
    266                &event);
    267 }
    268 
    269 // Updates the normal hints according to the window settings
    270 //
    271 static void updateNormalHints(_GLFWwindow* window, int width, int height)
    272 {
    273     XSizeHints* hints = XAllocSizeHints();
    274 
    275     long supplied;
    276     XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied);
    277 
    278     hints->flags &= ~(PMinSize | PMaxSize | PAspect);
    279 
    280     if (!window->monitor)
    281     {
    282         if (window->resizable)
    283         {
    284             if (window->minwidth != GLFW_DONT_CARE &&
    285                 window->minheight != GLFW_DONT_CARE)
    286             {
    287                 hints->flags |= PMinSize;
    288                 hints->min_width = window->minwidth;
    289                 hints->min_height = window->minheight;
    290             }
    291 
    292             if (window->maxwidth != GLFW_DONT_CARE &&
    293                 window->maxheight != GLFW_DONT_CARE)
    294             {
    295                 hints->flags |= PMaxSize;
    296                 hints->max_width = window->maxwidth;
    297                 hints->max_height = window->maxheight;
    298             }
    299 
    300             if (window->numer != GLFW_DONT_CARE &&
    301                 window->denom != GLFW_DONT_CARE)
    302             {
    303                 hints->flags |= PAspect;
    304                 hints->min_aspect.x = hints->max_aspect.x = window->numer;
    305                 hints->min_aspect.y = hints->max_aspect.y = window->denom;
    306             }
    307         }
    308         else
    309         {
    310             hints->flags |= (PMinSize | PMaxSize);
    311             hints->min_width  = hints->max_width  = width;
    312             hints->min_height = hints->max_height = height;
    313         }
    314     }
    315 
    316     XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
    317     XFree(hints);
    318 }
    319 
    320 // Updates the full screen status of the window
    321 //
    322 static void updateWindowMode(_GLFWwindow* window)
    323 {
    324     if (window->monitor)
    325     {
    326         if (_glfw.x11.xinerama.available &&
    327             _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
    328         {
    329             sendEventToWM(window,
    330                           _glfw.x11.NET_WM_FULLSCREEN_MONITORS,
    331                           window->monitor->x11.index,
    332                           window->monitor->x11.index,
    333                           window->monitor->x11.index,
    334                           window->monitor->x11.index,
    335                           0);
    336         }
    337 
    338         if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
    339         {
    340             sendEventToWM(window,
    341                           _glfw.x11.NET_WM_STATE,
    342                           _NET_WM_STATE_ADD,
    343                           _glfw.x11.NET_WM_STATE_FULLSCREEN,
    344                           0, 1, 0);
    345         }
    346         else
    347         {
    348             // This is the butcher's way of removing window decorations
    349             // Setting the override-redirect attribute on a window makes the
    350             // window manager ignore the window completely (ICCCM, section 4)
    351             // The good thing is that this makes undecorated full screen windows
    352             // easy to do; the bad thing is that we have to do everything
    353             // manually and some things (like iconify/restore) won't work at
    354             // all, as those are tasks usually performed by the window manager
    355 
    356             XSetWindowAttributes attributes;
    357             attributes.override_redirect = True;
    358             XChangeWindowAttributes(_glfw.x11.display,
    359                                     window->x11.handle,
    360                                     CWOverrideRedirect,
    361                                     &attributes);
    362 
    363             window->x11.overrideRedirect = GLFW_TRUE;
    364         }
    365 
    366         // Enable compositor bypass
    367         if (!window->x11.transparent)
    368         {
    369             const unsigned long value = 1;
    370 
    371             XChangeProperty(_glfw.x11.display,  window->x11.handle,
    372                             _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
    373                             PropModeReplace, (unsigned char*) &value, 1);
    374         }
    375     }
    376     else
    377     {
    378         if (_glfw.x11.xinerama.available &&
    379             _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
    380         {
    381             XDeleteProperty(_glfw.x11.display, window->x11.handle,
    382                             _glfw.x11.NET_WM_FULLSCREEN_MONITORS);
    383         }
    384 
    385         if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
    386         {
    387             sendEventToWM(window,
    388                           _glfw.x11.NET_WM_STATE,
    389                           _NET_WM_STATE_REMOVE,
    390                           _glfw.x11.NET_WM_STATE_FULLSCREEN,
    391                           0, 1, 0);
    392         }
    393         else
    394         {
    395             XSetWindowAttributes attributes;
    396             attributes.override_redirect = False;
    397             XChangeWindowAttributes(_glfw.x11.display,
    398                                     window->x11.handle,
    399                                     CWOverrideRedirect,
    400                                     &attributes);
    401 
    402             window->x11.overrideRedirect = GLFW_FALSE;
    403         }
    404 
    405         // Disable compositor bypass
    406         if (!window->x11.transparent)
    407         {
    408             XDeleteProperty(_glfw.x11.display, window->x11.handle,
    409                             _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
    410         }
    411     }
    412 }
    413 
    414 // Decode a Unicode code point from a UTF-8 stream
    415 // Based on cutef8 by Jeff Bezanson (Public Domain)
    416 //
    417 static uint32_t decodeUTF8(const char** s)
    418 {
    419     uint32_t codepoint = 0, count = 0;
    420     static const uint32_t offsets[] =
    421     {
    422         0x00000000u, 0x00003080u, 0x000e2080u,
    423         0x03c82080u, 0xfa082080u, 0x82082080u
    424     };
    425 
    426     do
    427     {
    428         codepoint = (codepoint << 6) + (unsigned char) **s;
    429         (*s)++;
    430         count++;
    431     } while ((**s & 0xc0) == 0x80);
    432 
    433     assert(count <= 6);
    434     return codepoint - offsets[count - 1];
    435 }
    436 
    437 // Convert the specified Latin-1 string to UTF-8
    438 //
    439 static char* convertLatin1toUTF8(const char* source)
    440 {
    441     size_t size = 1;
    442     const char* sp;
    443 
    444     for (sp = source;  *sp;  sp++)
    445         size += (*sp & 0x80) ? 2 : 1;
    446 
    447     char* target = _glfw_calloc(size, 1);
    448     char* tp = target;
    449 
    450     for (sp = source;  *sp;  sp++)
    451         tp += _glfwEncodeUTF8(tp, *sp);
    452 
    453     return target;
    454 }
    455 
    456 // Updates the cursor image according to its cursor mode
    457 //
    458 static void updateCursorImage(_GLFWwindow* window)
    459 {
    460     if (window->cursorMode == GLFW_CURSOR_NORMAL ||
    461         window->cursorMode == GLFW_CURSOR_CAPTURED)
    462     {
    463         if (window->cursor)
    464         {
    465             XDefineCursor(_glfw.x11.display, window->x11.handle,
    466                           window->cursor->x11.handle);
    467         }
    468         else
    469             XUndefineCursor(_glfw.x11.display, window->x11.handle);
    470     }
    471     else
    472     {
    473         XDefineCursor(_glfw.x11.display, window->x11.handle,
    474                       _glfw.x11.hiddenCursorHandle);
    475     }
    476 }
    477 
    478 // Grabs the cursor and confines it to the window
    479 //
    480 static void captureCursor(_GLFWwindow* window)
    481 {
    482     XGrabPointer(_glfw.x11.display, window->x11.handle, True,
    483                  ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
    484                  GrabModeAsync, GrabModeAsync,
    485                  window->x11.handle,
    486                  None,
    487                  CurrentTime);
    488 }
    489 
    490 // Ungrabs the cursor
    491 //
    492 static void releaseCursor(void)
    493 {
    494     XUngrabPointer(_glfw.x11.display, CurrentTime);
    495 }
    496 
    497 // Enable XI2 raw mouse motion events
    498 //
    499 static void enableRawMouseMotion(_GLFWwindow* window)
    500 {
    501     XIEventMask em;
    502     unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
    503 
    504     em.deviceid = XIAllMasterDevices;
    505     em.mask_len = sizeof(mask);
    506     em.mask = mask;
    507     XISetMask(mask, XI_RawMotion);
    508 
    509     XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
    510 }
    511 
    512 // Disable XI2 raw mouse motion events
    513 //
    514 static void disableRawMouseMotion(_GLFWwindow* window)
    515 {
    516     XIEventMask em;
    517     unsigned char mask[] = { 0 };
    518 
    519     em.deviceid = XIAllMasterDevices;
    520     em.mask_len = sizeof(mask);
    521     em.mask = mask;
    522 
    523     XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
    524 }
    525 
    526 // Apply disabled cursor mode to a focused window
    527 //
    528 static void disableCursor(_GLFWwindow* window)
    529 {
    530     if (window->rawMouseMotion)
    531         enableRawMouseMotion(window);
    532 
    533     _glfw.x11.disabledCursorWindow = window;
    534     _glfwGetCursorPosX11(window,
    535                          &_glfw.x11.restoreCursorPosX,
    536                          &_glfw.x11.restoreCursorPosY);
    537     updateCursorImage(window);
    538     _glfwCenterCursorInContentArea(window);
    539     captureCursor(window);
    540 }
    541 
    542 // Exit disabled cursor mode for the specified window
    543 //
    544 static void enableCursor(_GLFWwindow* window)
    545 {
    546     if (window->rawMouseMotion)
    547         disableRawMouseMotion(window);
    548 
    549     _glfw.x11.disabledCursorWindow = NULL;
    550     releaseCursor();
    551     _glfwSetCursorPosX11(window,
    552                          _glfw.x11.restoreCursorPosX,
    553                          _glfw.x11.restoreCursorPosY);
    554     updateCursorImage(window);
    555 }
    556 
    557 // Clear its handle when the input context has been destroyed
    558 //
    559 static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData)
    560 {
    561     _GLFWwindow* window = (_GLFWwindow*) clientData;
    562     window->x11.ic = NULL;
    563 }
    564 
    565 // Create the X11 window (and its colormap)
    566 //
    567 static GLFWbool createNativeWindow(_GLFWwindow* window,
    568                                    const _GLFWwndconfig* wndconfig,
    569                                    Visual* visual, int depth)
    570 {
    571     int width = wndconfig->width;
    572     int height = wndconfig->height;
    573 
    574     if (wndconfig->scaleToMonitor)
    575     {
    576         width *= _glfw.x11.contentScaleX;
    577         height *= _glfw.x11.contentScaleY;
    578     }
    579 
    580     int xpos = 0, ypos = 0;
    581 
    582     if (wndconfig->xpos != GLFW_ANY_POSITION && wndconfig->ypos != GLFW_ANY_POSITION)
    583     {
    584         xpos = wndconfig->xpos;
    585         ypos = wndconfig->ypos;
    586     }
    587 
    588     // Create a colormap based on the visual used by the current context
    589     window->x11.colormap = XCreateColormap(_glfw.x11.display,
    590                                            _glfw.x11.root,
    591                                            visual,
    592                                            AllocNone);
    593 
    594     window->x11.transparent = _glfwIsVisualTransparentX11(visual);
    595 
    596     XSetWindowAttributes wa = { 0 };
    597     wa.colormap = window->x11.colormap;
    598     wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
    599                     PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
    600                     ExposureMask | FocusChangeMask | VisibilityChangeMask |
    601                     EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
    602 
    603     _glfwGrabErrorHandlerX11();
    604 
    605     window->x11.parent = _glfw.x11.root;
    606     window->x11.handle = XCreateWindow(_glfw.x11.display,
    607                                        _glfw.x11.root,
    608                                        xpos, ypos,
    609                                        width, height,
    610                                        0,      // Border width
    611                                        depth,  // Color depth
    612                                        InputOutput,
    613                                        visual,
    614                                        CWBorderPixel | CWColormap | CWEventMask,
    615                                        &wa);
    616 
    617     _glfwReleaseErrorHandlerX11();
    618 
    619     if (!window->x11.handle)
    620     {
    621         _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
    622                            "X11: Failed to create window");
    623         return GLFW_FALSE;
    624     }
    625 
    626     XSaveContext(_glfw.x11.display,
    627                  window->x11.handle,
    628                  _glfw.x11.context,
    629                  (XPointer) window);
    630 
    631     if (!wndconfig->decorated)
    632         _glfwSetWindowDecoratedX11(window, GLFW_FALSE);
    633 
    634     if (_glfw.x11.NET_WM_STATE && !window->monitor)
    635     {
    636         Atom states[3];
    637         int count = 0;
    638 
    639         if (wndconfig->floating)
    640         {
    641             if (_glfw.x11.NET_WM_STATE_ABOVE)
    642                 states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
    643         }
    644 
    645         if (wndconfig->maximized)
    646         {
    647             if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
    648                 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
    649             {
    650                 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
    651                 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
    652                 window->x11.maximized = GLFW_TRUE;
    653             }
    654         }
    655 
    656         if (count)
    657         {
    658             XChangeProperty(_glfw.x11.display, window->x11.handle,
    659                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
    660                             PropModeReplace, (unsigned char*) states, count);
    661         }
    662     }
    663 
    664     // Declare the WM protocols supported by GLFW
    665     {
    666         Atom protocols[] =
    667         {
    668             _glfw.x11.WM_DELETE_WINDOW,
    669             _glfw.x11.NET_WM_PING
    670         };
    671 
    672         XSetWMProtocols(_glfw.x11.display, window->x11.handle,
    673                         protocols, sizeof(protocols) / sizeof(Atom));
    674     }
    675 
    676     // Declare our PID
    677     {
    678         const long pid = getpid();
    679 
    680         XChangeProperty(_glfw.x11.display,  window->x11.handle,
    681                         _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
    682                         PropModeReplace,
    683                         (unsigned char*) &pid, 1);
    684     }
    685 
    686     if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
    687     {
    688         Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
    689         XChangeProperty(_glfw.x11.display,  window->x11.handle,
    690                         _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
    691                         PropModeReplace, (unsigned char*) &type, 1);
    692     }
    693 
    694     // Set ICCCM WM_HINTS property
    695     {
    696         XWMHints* hints = XAllocWMHints();
    697         if (!hints)
    698         {
    699             _glfwInputError(GLFW_OUT_OF_MEMORY,
    700                             "X11: Failed to allocate WM hints");
    701             return GLFW_FALSE;
    702         }
    703 
    704         hints->flags = StateHint;
    705         hints->initial_state = NormalState;
    706 
    707         XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
    708         XFree(hints);
    709     }
    710 
    711     // Set ICCCM WM_NORMAL_HINTS property
    712     {
    713         XSizeHints* hints = XAllocSizeHints();
    714         if (!hints)
    715         {
    716             _glfwInputError(GLFW_OUT_OF_MEMORY, "X11: Failed to allocate size hints");
    717             return GLFW_FALSE;
    718         }
    719 
    720         if (!wndconfig->resizable)
    721         {
    722             hints->flags |= (PMinSize | PMaxSize);
    723             hints->min_width  = hints->max_width  = width;
    724             hints->min_height = hints->max_height = height;
    725         }
    726 
    727         // HACK: Explicitly setting PPosition to any value causes some WMs, notably
    728         //       Compiz and Metacity, to honor the position of unmapped windows
    729         if (wndconfig->xpos != GLFW_ANY_POSITION && wndconfig->ypos != GLFW_ANY_POSITION)
    730         {
    731             hints->flags |= PPosition;
    732             hints->x = 0;
    733             hints->y = 0;
    734         }
    735 
    736         hints->flags |= PWinGravity;
    737         hints->win_gravity = StaticGravity;
    738 
    739         XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
    740         XFree(hints);
    741     }
    742 
    743     // Set ICCCM WM_CLASS property
    744     {
    745         XClassHint* hint = XAllocClassHint();
    746 
    747         if (strlen(wndconfig->x11.instanceName) &&
    748             strlen(wndconfig->x11.className))
    749         {
    750             hint->res_name = (char*) wndconfig->x11.instanceName;
    751             hint->res_class = (char*) wndconfig->x11.className;
    752         }
    753         else
    754         {
    755             const char* resourceName = getenv("RESOURCE_NAME");
    756             if (resourceName && strlen(resourceName))
    757                 hint->res_name = (char*) resourceName;
    758             else if (strlen(wndconfig->title))
    759                 hint->res_name = (char*) wndconfig->title;
    760             else
    761                 hint->res_name = (char*) "glfw-application";
    762 
    763             if (strlen(wndconfig->title))
    764                 hint->res_class = (char*) wndconfig->title;
    765             else
    766                 hint->res_class = (char*) "GLFW-Application";
    767         }
    768 
    769         XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
    770         XFree(hint);
    771     }
    772 
    773     // Announce support for Xdnd (drag and drop)
    774     {
    775         const Atom version = _GLFW_XDND_VERSION;
    776         XChangeProperty(_glfw.x11.display, window->x11.handle,
    777                         _glfw.x11.XdndAware, XA_ATOM, 32,
    778                         PropModeReplace, (unsigned char*) &version, 1);
    779     }
    780 
    781     if (_glfw.x11.im)
    782         _glfwCreateInputContextX11(window);
    783 
    784     _glfwSetWindowTitleX11(window, wndconfig->title);
    785     _glfwGetWindowPosX11(window, &window->x11.xpos, &window->x11.ypos);
    786     _glfwGetWindowSizeX11(window, &window->x11.width, &window->x11.height);
    787 
    788     return GLFW_TRUE;
    789 }
    790 
    791 // Set the specified property to the selection converted to the requested target
    792 //
    793 static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
    794 {
    795     char* selectionString = NULL;
    796     const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING };
    797     const int formatCount = sizeof(formats) / sizeof(formats[0]);
    798 
    799     if (request->selection == _glfw.x11.PRIMARY)
    800         selectionString = _glfw.x11.primarySelectionString;
    801     else
    802         selectionString = _glfw.x11.clipboardString;
    803 
    804     if (request->property == None)
    805     {
    806         // The requester is a legacy client (ICCCM section 2.2)
    807         // We don't support legacy clients, so fail here
    808         return None;
    809     }
    810 
    811     if (request->target == _glfw.x11.TARGETS)
    812     {
    813         // The list of supported targets was requested
    814 
    815         const Atom targets[] = { _glfw.x11.TARGETS,
    816                                  _glfw.x11.MULTIPLE,
    817                                  _glfw.x11.UTF8_STRING,
    818                                  XA_STRING };
    819 
    820         XChangeProperty(_glfw.x11.display,
    821                         request->requestor,
    822                         request->property,
    823                         XA_ATOM,
    824                         32,
    825                         PropModeReplace,
    826                         (unsigned char*) targets,
    827                         sizeof(targets) / sizeof(targets[0]));
    828 
    829         return request->property;
    830     }
    831 
    832     if (request->target == _glfw.x11.MULTIPLE)
    833     {
    834         // Multiple conversions were requested
    835 
    836         Atom* targets;
    837         const unsigned long count =
    838             _glfwGetWindowPropertyX11(request->requestor,
    839                                       request->property,
    840                                       _glfw.x11.ATOM_PAIR,
    841                                       (unsigned char**) &targets);
    842 
    843         for (unsigned long i = 0;  i < count;  i += 2)
    844         {
    845             int j;
    846 
    847             for (j = 0;  j < formatCount;  j++)
    848             {
    849                 if (targets[i] == formats[j])
    850                     break;
    851             }
    852 
    853             if (j < formatCount)
    854             {
    855                 XChangeProperty(_glfw.x11.display,
    856                                 request->requestor,
    857                                 targets[i + 1],
    858                                 targets[i],
    859                                 8,
    860                                 PropModeReplace,
    861                                 (unsigned char *) selectionString,
    862                                 strlen(selectionString));
    863             }
    864             else
    865                 targets[i + 1] = None;
    866         }
    867 
    868         XChangeProperty(_glfw.x11.display,
    869                         request->requestor,
    870                         request->property,
    871                         _glfw.x11.ATOM_PAIR,
    872                         32,
    873                         PropModeReplace,
    874                         (unsigned char*) targets,
    875                         count);
    876 
    877         XFree(targets);
    878 
    879         return request->property;
    880     }
    881 
    882     if (request->target == _glfw.x11.SAVE_TARGETS)
    883     {
    884         // The request is a check whether we support SAVE_TARGETS
    885         // It should be handled as a no-op side effect target
    886 
    887         XChangeProperty(_glfw.x11.display,
    888                         request->requestor,
    889                         request->property,
    890                         _glfw.x11.NULL_,
    891                         32,
    892                         PropModeReplace,
    893                         NULL,
    894                         0);
    895 
    896         return request->property;
    897     }
    898 
    899     // Conversion to a data target was requested
    900 
    901     for (int i = 0;  i < formatCount;  i++)
    902     {
    903         if (request->target == formats[i])
    904         {
    905             // The requested target is one we support
    906 
    907             XChangeProperty(_glfw.x11.display,
    908                             request->requestor,
    909                             request->property,
    910                             request->target,
    911                             8,
    912                             PropModeReplace,
    913                             (unsigned char *) selectionString,
    914                             strlen(selectionString));
    915 
    916             return request->property;
    917         }
    918     }
    919 
    920     // The requested target is not supported
    921 
    922     return None;
    923 }
    924 
    925 static void handleSelectionRequest(XEvent* event)
    926 {
    927     const XSelectionRequestEvent* request = &event->xselectionrequest;
    928 
    929     XEvent reply = { SelectionNotify };
    930     reply.xselection.property = writeTargetToProperty(request);
    931     reply.xselection.display = request->display;
    932     reply.xselection.requestor = request->requestor;
    933     reply.xselection.selection = request->selection;
    934     reply.xselection.target = request->target;
    935     reply.xselection.time = request->time;
    936 
    937     XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
    938 }
    939 
    940 static const char* getSelectionString(Atom selection)
    941 {
    942     char** selectionString = NULL;
    943     const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING };
    944     const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
    945 
    946     if (selection == _glfw.x11.PRIMARY)
    947         selectionString = &_glfw.x11.primarySelectionString;
    948     else
    949         selectionString = &_glfw.x11.clipboardString;
    950 
    951     if (XGetSelectionOwner(_glfw.x11.display, selection) ==
    952         _glfw.x11.helperWindowHandle)
    953     {
    954         // Instead of doing a large number of X round-trips just to put this
    955         // string into a window property and then read it back, just return it
    956         return *selectionString;
    957     }
    958 
    959     _glfw_free(*selectionString);
    960     *selectionString = NULL;
    961 
    962     for (size_t i = 0;  i < targetCount;  i++)
    963     {
    964         char* data;
    965         Atom actualType;
    966         int actualFormat;
    967         unsigned long itemCount, bytesAfter;
    968         XEvent notification, dummy;
    969 
    970         XConvertSelection(_glfw.x11.display,
    971                           selection,
    972                           targets[i],
    973                           _glfw.x11.GLFW_SELECTION,
    974                           _glfw.x11.helperWindowHandle,
    975                           CurrentTime);
    976 
    977         while (!XCheckTypedWindowEvent(_glfw.x11.display,
    978                                        _glfw.x11.helperWindowHandle,
    979                                        SelectionNotify,
    980                                        &notification))
    981         {
    982             waitForX11Event(NULL);
    983         }
    984 
    985         if (notification.xselection.property == None)
    986             continue;
    987 
    988         XCheckIfEvent(_glfw.x11.display,
    989                       &dummy,
    990                       isSelPropNewValueNotify,
    991                       (XPointer) &notification);
    992 
    993         XGetWindowProperty(_glfw.x11.display,
    994                            notification.xselection.requestor,
    995                            notification.xselection.property,
    996                            0,
    997                            LONG_MAX,
    998                            True,
    999                            AnyPropertyType,
   1000                            &actualType,
   1001                            &actualFormat,
   1002                            &itemCount,
   1003                            &bytesAfter,
   1004                            (unsigned char**) &data);
   1005 
   1006         if (actualType == _glfw.x11.INCR)
   1007         {
   1008             size_t size = 1;
   1009             char* string = NULL;
   1010 
   1011             for (;;)
   1012             {
   1013                 while (!XCheckIfEvent(_glfw.x11.display,
   1014                                       &dummy,
   1015                                       isSelPropNewValueNotify,
   1016                                       (XPointer) &notification))
   1017                 {
   1018                     waitForX11Event(NULL);
   1019                 }
   1020 
   1021                 XFree(data);
   1022                 XGetWindowProperty(_glfw.x11.display,
   1023                                    notification.xselection.requestor,
   1024                                    notification.xselection.property,
   1025                                    0,
   1026                                    LONG_MAX,
   1027                                    True,
   1028                                    AnyPropertyType,
   1029                                    &actualType,
   1030                                    &actualFormat,
   1031                                    &itemCount,
   1032                                    &bytesAfter,
   1033                                    (unsigned char**) &data);
   1034 
   1035                 if (itemCount)
   1036                 {
   1037                     size += itemCount;
   1038                     string = _glfw_realloc(string, size);
   1039                     string[size - itemCount - 1] = '\0';
   1040                     strcat(string, data);
   1041                 }
   1042 
   1043                 if (!itemCount)
   1044                 {
   1045                     if (string)
   1046                     {
   1047                         if (targets[i] == XA_STRING)
   1048                         {
   1049                             *selectionString = convertLatin1toUTF8(string);
   1050                             _glfw_free(string);
   1051                         }
   1052                         else
   1053                             *selectionString = string;
   1054                     }
   1055 
   1056                     break;
   1057                 }
   1058             }
   1059         }
   1060         else if (actualType == targets[i])
   1061         {
   1062             if (targets[i] == XA_STRING)
   1063                 *selectionString = convertLatin1toUTF8(data);
   1064             else
   1065                 *selectionString = _glfw_strdup(data);
   1066         }
   1067 
   1068         XFree(data);
   1069 
   1070         if (*selectionString)
   1071             break;
   1072     }
   1073 
   1074     if (!*selectionString)
   1075     {
   1076         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
   1077                         "X11: Failed to convert selection to string");
   1078     }
   1079 
   1080     return *selectionString;
   1081 }
   1082 
   1083 // Make the specified window and its video mode active on its monitor
   1084 //
   1085 static void acquireMonitorX11(_GLFWwindow* window)
   1086 {
   1087     if (_glfw.x11.saver.count == 0)
   1088     {
   1089         // Remember old screen saver settings
   1090         XGetScreenSaver(_glfw.x11.display,
   1091                         &_glfw.x11.saver.timeout,
   1092                         &_glfw.x11.saver.interval,
   1093                         &_glfw.x11.saver.blanking,
   1094                         &_glfw.x11.saver.exposure);
   1095 
   1096         // Disable screen saver
   1097         XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
   1098                         DefaultExposures);
   1099     }
   1100 
   1101     if (!window->monitor->window)
   1102         _glfw.x11.saver.count++;
   1103 
   1104     _glfwSetVideoModeX11(window->monitor, &window->videoMode);
   1105 
   1106     if (window->x11.overrideRedirect)
   1107     {
   1108         int xpos, ypos;
   1109         GLFWvidmode mode;
   1110 
   1111         // Manually position the window over its monitor
   1112         _glfwGetMonitorPosX11(window->monitor, &xpos, &ypos);
   1113         _glfwGetVideoModeX11(window->monitor, &mode);
   1114 
   1115         XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   1116                           xpos, ypos, mode.width, mode.height);
   1117     }
   1118 
   1119     _glfwInputMonitorWindow(window->monitor, window);
   1120 }
   1121 
   1122 // Remove the window and restore the original video mode
   1123 //
   1124 static void releaseMonitorX11(_GLFWwindow* window)
   1125 {
   1126     if (window->monitor->window != window)
   1127         return;
   1128 
   1129     _glfwInputMonitorWindow(window->monitor, NULL);
   1130     _glfwRestoreVideoModeX11(window->monitor);
   1131 
   1132     _glfw.x11.saver.count--;
   1133 
   1134     if (_glfw.x11.saver.count == 0)
   1135     {
   1136         // Restore old screen saver settings
   1137         XSetScreenSaver(_glfw.x11.display,
   1138                         _glfw.x11.saver.timeout,
   1139                         _glfw.x11.saver.interval,
   1140                         _glfw.x11.saver.blanking,
   1141                         _glfw.x11.saver.exposure);
   1142     }
   1143 }
   1144 
   1145 // Process the specified X event
   1146 //
   1147 static void processEvent(XEvent *event)
   1148 {
   1149     int keycode = 0;
   1150     Bool filtered = False;
   1151 
   1152     // HACK: Save scancode as some IMs clear the field in XFilterEvent
   1153     if (event->type == KeyPress || event->type == KeyRelease)
   1154         keycode = event->xkey.keycode;
   1155 
   1156     filtered = XFilterEvent(event, None);
   1157 
   1158     if (_glfw.x11.randr.available)
   1159     {
   1160         if (event->type == _glfw.x11.randr.eventBase + RRNotify)
   1161         {
   1162             XRRUpdateConfiguration(event);
   1163             _glfwPollMonitorsX11();
   1164             return;
   1165         }
   1166     }
   1167 
   1168     if (_glfw.x11.xkb.available)
   1169     {
   1170         if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode)
   1171         {
   1172             if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify &&
   1173                 (((XkbEvent*) event)->state.changed & XkbGroupStateMask))
   1174             {
   1175                 _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group;
   1176             }
   1177 
   1178             return;
   1179         }
   1180     }
   1181 
   1182     if (event->type == GenericEvent)
   1183     {
   1184         if (_glfw.x11.xi.available)
   1185         {
   1186             _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
   1187 
   1188             if (window &&
   1189                 window->rawMouseMotion &&
   1190                 event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
   1191                 XGetEventData(_glfw.x11.display, &event->xcookie) &&
   1192                 event->xcookie.evtype == XI_RawMotion)
   1193             {
   1194                 XIRawEvent* re = event->xcookie.data;
   1195                 if (re->valuators.mask_len)
   1196                 {
   1197                     const double* values = re->raw_values;
   1198                     double xpos = window->virtualCursorPosX;
   1199                     double ypos = window->virtualCursorPosY;
   1200 
   1201                     if (XIMaskIsSet(re->valuators.mask, 0))
   1202                     {
   1203                         xpos += *values;
   1204                         values++;
   1205                     }
   1206 
   1207                     if (XIMaskIsSet(re->valuators.mask, 1))
   1208                         ypos += *values;
   1209 
   1210                     _glfwInputCursorPos(window, xpos, ypos);
   1211                 }
   1212             }
   1213 
   1214             XFreeEventData(_glfw.x11.display, &event->xcookie);
   1215         }
   1216 
   1217         return;
   1218     }
   1219 
   1220     if (event->type == SelectionRequest)
   1221     {
   1222         handleSelectionRequest(event);
   1223         return;
   1224     }
   1225 
   1226     _GLFWwindow* window = NULL;
   1227     if (XFindContext(_glfw.x11.display,
   1228                      event->xany.window,
   1229                      _glfw.x11.context,
   1230                      (XPointer*) &window) != 0)
   1231     {
   1232         // This is an event for a window that has already been destroyed
   1233         return;
   1234     }
   1235 
   1236     switch (event->type)
   1237     {
   1238         case ReparentNotify:
   1239         {
   1240             window->x11.parent = event->xreparent.parent;
   1241             return;
   1242         }
   1243 
   1244         case KeyPress:
   1245         {
   1246             const int key = translateKeyX11(keycode);
   1247             const int mods = translateState(event->xkey.state);
   1248             const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
   1249 
   1250             if (window->x11.ic)
   1251             {
   1252                 // HACK: Do not report the key press events duplicated by XIM
   1253                 //       Duplicate key releases are filtered out implicitly by
   1254                 //       the GLFW key repeat logic in _glfwInputKey
   1255                 //       A timestamp per key is used to handle simultaneous keys
   1256                 // NOTE: Always allow the first event for each key through
   1257                 //       (the server never sends a timestamp of zero)
   1258                 // NOTE: Timestamp difference is compared to handle wrap-around
   1259                 Time diff = event->xkey.time - window->x11.keyPressTimes[keycode];
   1260                 if (diff == event->xkey.time || (diff > 0 && diff < ((Time)1 << 31)))
   1261                 {
   1262                     if (keycode)
   1263                         _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
   1264 
   1265                     window->x11.keyPressTimes[keycode] = event->xkey.time;
   1266                 }
   1267 
   1268                 if (!filtered)
   1269                 {
   1270                     int count;
   1271                     Status status;
   1272                     char buffer[100];
   1273                     char* chars = buffer;
   1274 
   1275                     count = Xutf8LookupString(window->x11.ic,
   1276                                               &event->xkey,
   1277                                               buffer, sizeof(buffer) - 1,
   1278                                               NULL, &status);
   1279 
   1280                     if (status == XBufferOverflow)
   1281                     {
   1282                         chars = _glfw_calloc(count + 1, 1);
   1283                         count = Xutf8LookupString(window->x11.ic,
   1284                                                   &event->xkey,
   1285                                                   chars, count,
   1286                                                   NULL, &status);
   1287                     }
   1288 
   1289                     if (status == XLookupChars || status == XLookupBoth)
   1290                     {
   1291                         const char* c = chars;
   1292                         chars[count] = '\0';
   1293                         while (c - chars < count)
   1294                             _glfwInputChar(window, decodeUTF8(&c), mods, plain);
   1295                     }
   1296 
   1297                     if (chars != buffer)
   1298                         _glfw_free(chars);
   1299                 }
   1300             }
   1301             else
   1302             {
   1303                 KeySym keysym;
   1304                 XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
   1305 
   1306                 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
   1307 
   1308                 const uint32_t codepoint = _glfwKeySym2Unicode(keysym);
   1309                 if (codepoint != GLFW_INVALID_CODEPOINT)
   1310                     _glfwInputChar(window, codepoint, mods, plain);
   1311             }
   1312 
   1313             return;
   1314         }
   1315 
   1316         case KeyRelease:
   1317         {
   1318             const int key = translateKeyX11(keycode);
   1319             const int mods = translateState(event->xkey.state);
   1320 
   1321             if (!_glfw.x11.xkb.detectable)
   1322             {
   1323                 // HACK: Key repeat events will arrive as KeyRelease/KeyPress
   1324                 //       pairs with similar or identical time stamps
   1325                 //       The key repeat logic in _glfwInputKey expects only key
   1326                 //       presses to repeat, so detect and discard release events
   1327                 if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
   1328                 {
   1329                     XEvent next;
   1330                     XPeekEvent(_glfw.x11.display, &next);
   1331 
   1332                     if (next.type == KeyPress &&
   1333                         next.xkey.window == event->xkey.window &&
   1334                         next.xkey.keycode == keycode)
   1335                     {
   1336                         // HACK: The time of repeat events sometimes doesn't
   1337                         //       match that of the press event, so add an
   1338                         //       epsilon
   1339                         //       Toshiyuki Takahashi can press a button
   1340                         //       16 times per second so it's fairly safe to
   1341                         //       assume that no human is pressing the key 50
   1342                         //       times per second (value is ms)
   1343                         if ((next.xkey.time - event->xkey.time) < 20)
   1344                         {
   1345                             // This is very likely a server-generated key repeat
   1346                             // event, so ignore it
   1347                             return;
   1348                         }
   1349                     }
   1350                 }
   1351             }
   1352 
   1353             _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
   1354             return;
   1355         }
   1356 
   1357         case ButtonPress:
   1358         {
   1359             const int mods = translateState(event->xbutton.state);
   1360 
   1361             if (event->xbutton.button == Button1)
   1362                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
   1363             else if (event->xbutton.button == Button2)
   1364                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
   1365             else if (event->xbutton.button == Button3)
   1366                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
   1367 
   1368             // Modern X provides scroll events as mouse button presses
   1369             else if (event->xbutton.button == Button4)
   1370                 _glfwInputScroll(window, 0.0, 1.0);
   1371             else if (event->xbutton.button == Button5)
   1372                 _glfwInputScroll(window, 0.0, -1.0);
   1373             else if (event->xbutton.button == Button6)
   1374                 _glfwInputScroll(window, 1.0, 0.0);
   1375             else if (event->xbutton.button == Button7)
   1376                 _glfwInputScroll(window, -1.0, 0.0);
   1377 
   1378             else
   1379             {
   1380                 // Additional buttons after 7 are treated as regular buttons
   1381                 // We subtract 4 to fill the gap left by scroll input above
   1382                 _glfwInputMouseClick(window,
   1383                                      event->xbutton.button - Button1 - 4,
   1384                                      GLFW_PRESS,
   1385                                      mods);
   1386             }
   1387 
   1388             return;
   1389         }
   1390 
   1391         case ButtonRelease:
   1392         {
   1393             const int mods = translateState(event->xbutton.state);
   1394 
   1395             if (event->xbutton.button == Button1)
   1396             {
   1397                 _glfwInputMouseClick(window,
   1398                                      GLFW_MOUSE_BUTTON_LEFT,
   1399                                      GLFW_RELEASE,
   1400                                      mods);
   1401             }
   1402             else if (event->xbutton.button == Button2)
   1403             {
   1404                 _glfwInputMouseClick(window,
   1405                                      GLFW_MOUSE_BUTTON_MIDDLE,
   1406                                      GLFW_RELEASE,
   1407                                      mods);
   1408             }
   1409             else if (event->xbutton.button == Button3)
   1410             {
   1411                 _glfwInputMouseClick(window,
   1412                                      GLFW_MOUSE_BUTTON_RIGHT,
   1413                                      GLFW_RELEASE,
   1414                                      mods);
   1415             }
   1416             else if (event->xbutton.button > Button7)
   1417             {
   1418                 // Additional buttons after 7 are treated as regular buttons
   1419                 // We subtract 4 to fill the gap left by scroll input above
   1420                 _glfwInputMouseClick(window,
   1421                                      event->xbutton.button - Button1 - 4,
   1422                                      GLFW_RELEASE,
   1423                                      mods);
   1424             }
   1425 
   1426             return;
   1427         }
   1428 
   1429         case EnterNotify:
   1430         {
   1431             // XEnterWindowEvent is XCrossingEvent
   1432             const int x = event->xcrossing.x;
   1433             const int y = event->xcrossing.y;
   1434 
   1435             // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
   1436             //       ignore the defined cursor for hidden cursor mode
   1437             if (window->cursorMode == GLFW_CURSOR_HIDDEN)
   1438                 updateCursorImage(window);
   1439 
   1440             _glfwInputCursorEnter(window, GLFW_TRUE);
   1441             _glfwInputCursorPos(window, x, y);
   1442 
   1443             window->x11.lastCursorPosX = x;
   1444             window->x11.lastCursorPosY = y;
   1445             return;
   1446         }
   1447 
   1448         case LeaveNotify:
   1449         {
   1450             _glfwInputCursorEnter(window, GLFW_FALSE);
   1451             return;
   1452         }
   1453 
   1454         case MotionNotify:
   1455         {
   1456             const int x = event->xmotion.x;
   1457             const int y = event->xmotion.y;
   1458 
   1459             if (x != window->x11.warpCursorPosX ||
   1460                 y != window->x11.warpCursorPosY)
   1461             {
   1462                 // The cursor was moved by something other than GLFW
   1463 
   1464                 if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1465                 {
   1466                     if (_glfw.x11.disabledCursorWindow != window)
   1467                         return;
   1468                     if (window->rawMouseMotion)
   1469                         return;
   1470 
   1471                     const int dx = x - window->x11.lastCursorPosX;
   1472                     const int dy = y - window->x11.lastCursorPosY;
   1473 
   1474                     _glfwInputCursorPos(window,
   1475                                         window->virtualCursorPosX + dx,
   1476                                         window->virtualCursorPosY + dy);
   1477                 }
   1478                 else
   1479                     _glfwInputCursorPos(window, x, y);
   1480             }
   1481 
   1482             window->x11.lastCursorPosX = x;
   1483             window->x11.lastCursorPosY = y;
   1484             return;
   1485         }
   1486 
   1487         case ConfigureNotify:
   1488         {
   1489             if (event->xconfigure.width != window->x11.width ||
   1490                 event->xconfigure.height != window->x11.height)
   1491             {
   1492                 window->x11.width = event->xconfigure.width;
   1493                 window->x11.height = event->xconfigure.height;
   1494 
   1495                 _glfwInputFramebufferSize(window,
   1496                                           event->xconfigure.width,
   1497                                           event->xconfigure.height);
   1498 
   1499                 _glfwInputWindowSize(window,
   1500                                      event->xconfigure.width,
   1501                                      event->xconfigure.height);
   1502             }
   1503 
   1504             int xpos = event->xconfigure.x;
   1505             int ypos = event->xconfigure.y;
   1506 
   1507             // NOTE: ConfigureNotify events from the server are in local
   1508             //       coordinates, so if we are reparented we need to translate
   1509             //       the position into root (screen) coordinates
   1510             if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
   1511             {
   1512                 _glfwGrabErrorHandlerX11();
   1513 
   1514                 Window dummy;
   1515                 XTranslateCoordinates(_glfw.x11.display,
   1516                                       window->x11.parent,
   1517                                       _glfw.x11.root,
   1518                                       xpos, ypos,
   1519                                       &xpos, &ypos,
   1520                                       &dummy);
   1521 
   1522                 _glfwReleaseErrorHandlerX11();
   1523                 if (_glfw.x11.errorCode == BadWindow)
   1524                     return;
   1525             }
   1526 
   1527             if (xpos != window->x11.xpos || ypos != window->x11.ypos)
   1528             {
   1529                 window->x11.xpos = xpos;
   1530                 window->x11.ypos = ypos;
   1531 
   1532                 _glfwInputWindowPos(window, xpos, ypos);
   1533             }
   1534 
   1535             return;
   1536         }
   1537 
   1538         case ClientMessage:
   1539         {
   1540             // Custom client message, probably from the window manager
   1541 
   1542             if (filtered)
   1543                 return;
   1544 
   1545             if (event->xclient.message_type == None)
   1546                 return;
   1547 
   1548             if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
   1549             {
   1550                 const Atom protocol = event->xclient.data.l[0];
   1551                 if (protocol == None)
   1552                     return;
   1553 
   1554                 if (protocol == _glfw.x11.WM_DELETE_WINDOW)
   1555                 {
   1556                     // The window manager was asked to close the window, for
   1557                     // example by the user pressing a 'close' window decoration
   1558                     // button
   1559                     _glfwInputWindowCloseRequest(window);
   1560                 }
   1561                 else if (protocol == _glfw.x11.NET_WM_PING)
   1562                 {
   1563                     // The window manager is pinging the application to ensure
   1564                     // it's still responding to events
   1565 
   1566                     XEvent reply = *event;
   1567                     reply.xclient.window = _glfw.x11.root;
   1568 
   1569                     XSendEvent(_glfw.x11.display, _glfw.x11.root,
   1570                                False,
   1571                                SubstructureNotifyMask | SubstructureRedirectMask,
   1572                                &reply);
   1573                 }
   1574             }
   1575             else if (event->xclient.message_type == _glfw.x11.XdndEnter)
   1576             {
   1577                 // A drag operation has entered the window
   1578                 unsigned long count;
   1579                 Atom* formats = NULL;
   1580                 const GLFWbool list = event->xclient.data.l[1] & 1;
   1581 
   1582                 _glfw.x11.xdnd.source  = event->xclient.data.l[0];
   1583                 _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
   1584                 _glfw.x11.xdnd.format  = None;
   1585 
   1586                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1587                     return;
   1588 
   1589                 if (list)
   1590                 {
   1591                     count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
   1592                                                       _glfw.x11.XdndTypeList,
   1593                                                       XA_ATOM,
   1594                                                       (unsigned char**) &formats);
   1595                 }
   1596                 else
   1597                 {
   1598                     count = 3;
   1599                     formats = (Atom*) event->xclient.data.l + 2;
   1600                 }
   1601 
   1602                 for (unsigned int i = 0;  i < count;  i++)
   1603                 {
   1604                     if (formats[i] == _glfw.x11.text_uri_list)
   1605                     {
   1606                         _glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
   1607                         break;
   1608                     }
   1609                 }
   1610 
   1611                 if (list && formats)
   1612                     XFree(formats);
   1613             }
   1614             else if (event->xclient.message_type == _glfw.x11.XdndDrop)
   1615             {
   1616                 // The drag operation has finished by dropping on the window
   1617                 Time time = CurrentTime;
   1618 
   1619                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1620                     return;
   1621 
   1622                 if (_glfw.x11.xdnd.format)
   1623                 {
   1624                     if (_glfw.x11.xdnd.version >= 1)
   1625                         time = event->xclient.data.l[2];
   1626 
   1627                     // Request the chosen format from the source window
   1628                     XConvertSelection(_glfw.x11.display,
   1629                                       _glfw.x11.XdndSelection,
   1630                                       _glfw.x11.xdnd.format,
   1631                                       _glfw.x11.XdndSelection,
   1632                                       window->x11.handle,
   1633                                       time);
   1634                 }
   1635                 else if (_glfw.x11.xdnd.version >= 2)
   1636                 {
   1637                     XEvent reply = { ClientMessage };
   1638                     reply.xclient.window = _glfw.x11.xdnd.source;
   1639                     reply.xclient.message_type = _glfw.x11.XdndFinished;
   1640                     reply.xclient.format = 32;
   1641                     reply.xclient.data.l[0] = window->x11.handle;
   1642                     reply.xclient.data.l[1] = 0; // The drag was rejected
   1643                     reply.xclient.data.l[2] = None;
   1644 
   1645                     XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1646                                False, NoEventMask, &reply);
   1647                     XFlush(_glfw.x11.display);
   1648                 }
   1649             }
   1650             else if (event->xclient.message_type == _glfw.x11.XdndPosition)
   1651             {
   1652                 // The drag operation has moved over the window
   1653                 const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
   1654                 const int yabs = (event->xclient.data.l[2]) & 0xffff;
   1655                 Window dummy;
   1656                 int xpos, ypos;
   1657 
   1658                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1659                     return;
   1660 
   1661                 XTranslateCoordinates(_glfw.x11.display,
   1662                                       _glfw.x11.root,
   1663                                       window->x11.handle,
   1664                                       xabs, yabs,
   1665                                       &xpos, &ypos,
   1666                                       &dummy);
   1667 
   1668                 _glfwInputCursorPos(window, xpos, ypos);
   1669 
   1670                 XEvent reply = { ClientMessage };
   1671                 reply.xclient.window = _glfw.x11.xdnd.source;
   1672                 reply.xclient.message_type = _glfw.x11.XdndStatus;
   1673                 reply.xclient.format = 32;
   1674                 reply.xclient.data.l[0] = window->x11.handle;
   1675                 reply.xclient.data.l[2] = 0; // Specify an empty rectangle
   1676                 reply.xclient.data.l[3] = 0;
   1677 
   1678                 if (_glfw.x11.xdnd.format)
   1679                 {
   1680                     // Reply that we are ready to copy the dragged data
   1681                     reply.xclient.data.l[1] = 1; // Accept with no rectangle
   1682                     if (_glfw.x11.xdnd.version >= 2)
   1683                         reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
   1684                 }
   1685 
   1686                 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1687                            False, NoEventMask, &reply);
   1688                 XFlush(_glfw.x11.display);
   1689             }
   1690 
   1691             return;
   1692         }
   1693 
   1694         case SelectionNotify:
   1695         {
   1696             if (event->xselection.property == _glfw.x11.XdndSelection)
   1697             {
   1698                 // The converted data from the drag operation has arrived
   1699                 char* data;
   1700                 const unsigned long result =
   1701                     _glfwGetWindowPropertyX11(event->xselection.requestor,
   1702                                               event->xselection.property,
   1703                                               event->xselection.target,
   1704                                               (unsigned char**) &data);
   1705 
   1706                 if (result)
   1707                 {
   1708                     int count;
   1709                     char** paths = _glfwParseUriList(data, &count);
   1710 
   1711                     _glfwInputDrop(window, count, (const char**) paths);
   1712 
   1713                     for (int i = 0;  i < count;  i++)
   1714                         _glfw_free(paths[i]);
   1715                     _glfw_free(paths);
   1716                 }
   1717 
   1718                 if (data)
   1719                     XFree(data);
   1720 
   1721                 if (_glfw.x11.xdnd.version >= 2)
   1722                 {
   1723                     XEvent reply = { ClientMessage };
   1724                     reply.xclient.window = _glfw.x11.xdnd.source;
   1725                     reply.xclient.message_type = _glfw.x11.XdndFinished;
   1726                     reply.xclient.format = 32;
   1727                     reply.xclient.data.l[0] = window->x11.handle;
   1728                     reply.xclient.data.l[1] = result;
   1729                     reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
   1730 
   1731                     XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1732                                False, NoEventMask, &reply);
   1733                     XFlush(_glfw.x11.display);
   1734                 }
   1735             }
   1736 
   1737             return;
   1738         }
   1739 
   1740         case FocusIn:
   1741         {
   1742             if (event->xfocus.mode == NotifyGrab ||
   1743                 event->xfocus.mode == NotifyUngrab)
   1744             {
   1745                 // Ignore focus events from popup indicator windows, window menu
   1746                 // key chords and window dragging
   1747                 return;
   1748             }
   1749 
   1750             if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1751                 disableCursor(window);
   1752             else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
   1753                 captureCursor(window);
   1754 
   1755             if (window->x11.ic)
   1756                 XSetICFocus(window->x11.ic);
   1757 
   1758             _glfwInputWindowFocus(window, GLFW_TRUE);
   1759             return;
   1760         }
   1761 
   1762         case FocusOut:
   1763         {
   1764             if (event->xfocus.mode == NotifyGrab ||
   1765                 event->xfocus.mode == NotifyUngrab)
   1766             {
   1767                 // Ignore focus events from popup indicator windows, window menu
   1768                 // key chords and window dragging
   1769                 return;
   1770             }
   1771 
   1772             if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1773                 enableCursor(window);
   1774             else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
   1775                 releaseCursor();
   1776 
   1777             if (window->x11.ic)
   1778                 XUnsetICFocus(window->x11.ic);
   1779 
   1780             if (window->monitor && window->autoIconify)
   1781                 _glfwIconifyWindowX11(window);
   1782 
   1783             _glfwInputWindowFocus(window, GLFW_FALSE);
   1784             return;
   1785         }
   1786 
   1787         case Expose:
   1788         {
   1789             _glfwInputWindowDamage(window);
   1790             return;
   1791         }
   1792 
   1793         case PropertyNotify:
   1794         {
   1795             if (event->xproperty.state != PropertyNewValue)
   1796                 return;
   1797 
   1798             if (event->xproperty.atom == _glfw.x11.WM_STATE)
   1799             {
   1800                 const int state = getWindowState(window);
   1801                 if (state != IconicState && state != NormalState)
   1802                     return;
   1803 
   1804                 const GLFWbool iconified = (state == IconicState);
   1805                 if (window->x11.iconified != iconified)
   1806                 {
   1807                     if (window->monitor)
   1808                     {
   1809                         if (iconified)
   1810                             releaseMonitorX11(window);
   1811                         else
   1812                             acquireMonitorX11(window);
   1813                     }
   1814 
   1815                     window->x11.iconified = iconified;
   1816                     _glfwInputWindowIconify(window, iconified);
   1817                 }
   1818             }
   1819             else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
   1820             {
   1821                 const GLFWbool maximized = _glfwWindowMaximizedX11(window);
   1822                 if (window->x11.maximized != maximized)
   1823                 {
   1824                     window->x11.maximized = maximized;
   1825                     _glfwInputWindowMaximize(window, maximized);
   1826                 }
   1827             }
   1828 
   1829             return;
   1830         }
   1831 
   1832         case DestroyNotify:
   1833             return;
   1834     }
   1835 }
   1836 
   1837 
   1838 //////////////////////////////////////////////////////////////////////////
   1839 //////                       GLFW internal API                      //////
   1840 //////////////////////////////////////////////////////////////////////////
   1841 
   1842 // Retrieve a single window property of the specified type
   1843 // Inspired by fghGetWindowProperty from freeglut
   1844 //
   1845 unsigned long _glfwGetWindowPropertyX11(Window window,
   1846                                         Atom property,
   1847                                         Atom type,
   1848                                         unsigned char** value)
   1849 {
   1850     Atom actualType;
   1851     int actualFormat;
   1852     unsigned long itemCount, bytesAfter;
   1853 
   1854     XGetWindowProperty(_glfw.x11.display,
   1855                        window,
   1856                        property,
   1857                        0,
   1858                        LONG_MAX,
   1859                        False,
   1860                        type,
   1861                        &actualType,
   1862                        &actualFormat,
   1863                        &itemCount,
   1864                        &bytesAfter,
   1865                        value);
   1866 
   1867     return itemCount;
   1868 }
   1869 
   1870 GLFWbool _glfwIsVisualTransparentX11(Visual* visual)
   1871 {
   1872     if (!_glfw.x11.xrender.available)
   1873         return GLFW_FALSE;
   1874 
   1875     XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
   1876     return pf && pf->direct.alphaMask;
   1877 }
   1878 
   1879 // Push contents of our selection to clipboard manager
   1880 //
   1881 void _glfwPushSelectionToManagerX11(void)
   1882 {
   1883     XConvertSelection(_glfw.x11.display,
   1884                       _glfw.x11.CLIPBOARD_MANAGER,
   1885                       _glfw.x11.SAVE_TARGETS,
   1886                       None,
   1887                       _glfw.x11.helperWindowHandle,
   1888                       CurrentTime);
   1889 
   1890     for (;;)
   1891     {
   1892         XEvent event;
   1893 
   1894         while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
   1895         {
   1896             switch (event.type)
   1897             {
   1898                 case SelectionRequest:
   1899                     handleSelectionRequest(&event);
   1900                     break;
   1901 
   1902                 case SelectionNotify:
   1903                 {
   1904                     if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
   1905                     {
   1906                         // This means one of two things; either the selection
   1907                         // was not owned, which means there is no clipboard
   1908                         // manager, or the transfer to the clipboard manager has
   1909                         // completed
   1910                         // In either case, it means we are done here
   1911                         return;
   1912                     }
   1913 
   1914                     break;
   1915                 }
   1916             }
   1917         }
   1918 
   1919         waitForX11Event(NULL);
   1920     }
   1921 }
   1922 
   1923 void _glfwCreateInputContextX11(_GLFWwindow* window)
   1924 {
   1925     XIMCallback callback;
   1926     callback.callback = (XIMProc) inputContextDestroyCallback;
   1927     callback.client_data = (XPointer) window;
   1928 
   1929     window->x11.ic = XCreateIC(_glfw.x11.im,
   1930                                XNInputStyle,
   1931                                XIMPreeditNothing | XIMStatusNothing,
   1932                                XNClientWindow,
   1933                                window->x11.handle,
   1934                                XNFocusWindow,
   1935                                window->x11.handle,
   1936                                XNDestroyCallback,
   1937                                &callback,
   1938                                NULL);
   1939 
   1940     if (window->x11.ic)
   1941     {
   1942         XWindowAttributes attribs;
   1943         XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
   1944 
   1945         unsigned long filter = 0;
   1946         if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL)
   1947         {
   1948             XSelectInput(_glfw.x11.display,
   1949                          window->x11.handle,
   1950                          attribs.your_event_mask | filter);
   1951         }
   1952     }
   1953 }
   1954 
   1955 
   1956 //////////////////////////////////////////////////////////////////////////
   1957 //////                       GLFW platform API                      //////
   1958 //////////////////////////////////////////////////////////////////////////
   1959 
   1960 GLFWbool _glfwCreateWindowX11(_GLFWwindow* window,
   1961                               const _GLFWwndconfig* wndconfig,
   1962                               const _GLFWctxconfig* ctxconfig,
   1963                               const _GLFWfbconfig* fbconfig)
   1964 {
   1965     Visual* visual = NULL;
   1966     int depth;
   1967 
   1968     if (ctxconfig->client != GLFW_NO_API)
   1969     {
   1970         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
   1971         {
   1972             if (!_glfwInitGLX())
   1973                 return GLFW_FALSE;
   1974             if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
   1975                 return GLFW_FALSE;
   1976         }
   1977         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
   1978         {
   1979             if (!_glfwInitEGL())
   1980                 return GLFW_FALSE;
   1981             if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
   1982                 return GLFW_FALSE;
   1983         }
   1984         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   1985         {
   1986             if (!_glfwInitOSMesa())
   1987                 return GLFW_FALSE;
   1988         }
   1989     }
   1990 
   1991     if (!visual)
   1992     {
   1993         visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
   1994         depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
   1995     }
   1996 
   1997     if (!createNativeWindow(window, wndconfig, visual, depth))
   1998         return GLFW_FALSE;
   1999 
   2000     if (ctxconfig->client != GLFW_NO_API)
   2001     {
   2002         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
   2003         {
   2004             if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
   2005                 return GLFW_FALSE;
   2006         }
   2007         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
   2008         {
   2009             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
   2010                 return GLFW_FALSE;
   2011         }
   2012         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   2013         {
   2014             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
   2015                 return GLFW_FALSE;
   2016         }
   2017 
   2018         if (!_glfwRefreshContextAttribs(window, ctxconfig))
   2019             return GLFW_FALSE;
   2020     }
   2021 
   2022     if (wndconfig->mousePassthrough)
   2023         _glfwSetWindowMousePassthroughX11(window, GLFW_TRUE);
   2024 
   2025     if (window->monitor)
   2026     {
   2027         _glfwShowWindowX11(window);
   2028         updateWindowMode(window);
   2029         acquireMonitorX11(window);
   2030 
   2031         if (wndconfig->centerCursor)
   2032             _glfwCenterCursorInContentArea(window);
   2033     }
   2034     else
   2035     {
   2036         if (wndconfig->visible)
   2037         {
   2038             _glfwShowWindowX11(window);
   2039             if (wndconfig->focused)
   2040                 _glfwFocusWindowX11(window);
   2041         }
   2042     }
   2043 
   2044     XFlush(_glfw.x11.display);
   2045     return GLFW_TRUE;
   2046 }
   2047 
   2048 void _glfwDestroyWindowX11(_GLFWwindow* window)
   2049 {
   2050     if (_glfw.x11.disabledCursorWindow == window)
   2051         enableCursor(window);
   2052 
   2053     if (window->monitor)
   2054         releaseMonitorX11(window);
   2055 
   2056     if (window->x11.ic)
   2057     {
   2058         XDestroyIC(window->x11.ic);
   2059         window->x11.ic = NULL;
   2060     }
   2061 
   2062     if (window->context.destroy)
   2063         window->context.destroy(window);
   2064 
   2065     if (window->x11.handle)
   2066     {
   2067         XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
   2068         XUnmapWindow(_glfw.x11.display, window->x11.handle);
   2069         XDestroyWindow(_glfw.x11.display, window->x11.handle);
   2070         window->x11.handle = (Window) 0;
   2071     }
   2072 
   2073     if (window->x11.colormap)
   2074     {
   2075         XFreeColormap(_glfw.x11.display, window->x11.colormap);
   2076         window->x11.colormap = (Colormap) 0;
   2077     }
   2078 
   2079     XFlush(_glfw.x11.display);
   2080 }
   2081 
   2082 void _glfwSetWindowTitleX11(_GLFWwindow* window, const char* title)
   2083 {
   2084     if (_glfw.x11.xlib.utf8)
   2085     {
   2086         Xutf8SetWMProperties(_glfw.x11.display,
   2087                              window->x11.handle,
   2088                              title, title,
   2089                              NULL, 0,
   2090                              NULL, NULL, NULL);
   2091     }
   2092 
   2093     XChangeProperty(_glfw.x11.display,  window->x11.handle,
   2094                     _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
   2095                     PropModeReplace,
   2096                     (unsigned char*) title, strlen(title));
   2097 
   2098     XChangeProperty(_glfw.x11.display,  window->x11.handle,
   2099                     _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
   2100                     PropModeReplace,
   2101                     (unsigned char*) title, strlen(title));
   2102 
   2103     XFlush(_glfw.x11.display);
   2104 }
   2105 
   2106 void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* images)
   2107 {
   2108     if (count)
   2109     {
   2110         int longCount = 0;
   2111 
   2112         for (int i = 0;  i < count;  i++)
   2113             longCount += 2 + images[i].width * images[i].height;
   2114 
   2115         unsigned long* icon = _glfw_calloc(longCount, sizeof(unsigned long));
   2116         unsigned long* target = icon;
   2117 
   2118         for (int i = 0;  i < count;  i++)
   2119         {
   2120             *target++ = images[i].width;
   2121             *target++ = images[i].height;
   2122 
   2123             for (int j = 0;  j < images[i].width * images[i].height;  j++)
   2124             {
   2125                 *target++ = (((unsigned long) images[i].pixels[j * 4 + 0]) << 16) |
   2126                             (((unsigned long) images[i].pixels[j * 4 + 1]) <<  8) |
   2127                             (((unsigned long) images[i].pixels[j * 4 + 2]) <<  0) |
   2128                             (((unsigned long) images[i].pixels[j * 4 + 3]) << 24);
   2129             }
   2130         }
   2131 
   2132         // NOTE: XChangeProperty expects 32-bit values like the image data above to be
   2133         //       placed in the 32 least significant bits of individual longs.  This is
   2134         //       true even if long is 64-bit and a WM protocol calls for "packed" data.
   2135         //       This is because of a historical mistake that then became part of the Xlib
   2136         //       ABI.  Xlib will pack these values into a regular array of 32-bit values
   2137         //       before sending it over the wire.
   2138         XChangeProperty(_glfw.x11.display, window->x11.handle,
   2139                         _glfw.x11.NET_WM_ICON,
   2140                         XA_CARDINAL, 32,
   2141                         PropModeReplace,
   2142                         (unsigned char*) icon,
   2143                         longCount);
   2144 
   2145         _glfw_free(icon);
   2146     }
   2147     else
   2148     {
   2149         XDeleteProperty(_glfw.x11.display, window->x11.handle,
   2150                         _glfw.x11.NET_WM_ICON);
   2151     }
   2152 
   2153     XFlush(_glfw.x11.display);
   2154 }
   2155 
   2156 void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos)
   2157 {
   2158     Window dummy;
   2159     int x, y;
   2160 
   2161     XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
   2162                           0, 0, &x, &y, &dummy);
   2163 
   2164     if (xpos)
   2165         *xpos = x;
   2166     if (ypos)
   2167         *ypos = y;
   2168 }
   2169 
   2170 void _glfwSetWindowPosX11(_GLFWwindow* window, int xpos, int ypos)
   2171 {
   2172     // HACK: Explicitly setting PPosition to any value causes some WMs, notably
   2173     //       Compiz and Metacity, to honor the position of unmapped windows
   2174     if (!_glfwWindowVisibleX11(window))
   2175     {
   2176         long supplied;
   2177         XSizeHints* hints = XAllocSizeHints();
   2178 
   2179         if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
   2180         {
   2181             hints->flags |= PPosition;
   2182             hints->x = hints->y = 0;
   2183 
   2184             XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
   2185         }
   2186 
   2187         XFree(hints);
   2188     }
   2189 
   2190     XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
   2191     XFlush(_glfw.x11.display);
   2192 }
   2193 
   2194 void _glfwGetWindowSizeX11(_GLFWwindow* window, int* width, int* height)
   2195 {
   2196     XWindowAttributes attribs;
   2197     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
   2198 
   2199     if (width)
   2200         *width = attribs.width;
   2201     if (height)
   2202         *height = attribs.height;
   2203 }
   2204 
   2205 void _glfwSetWindowSizeX11(_GLFWwindow* window, int width, int height)
   2206 {
   2207     if (window->monitor)
   2208     {
   2209         if (window->monitor->window == window)
   2210             acquireMonitorX11(window);
   2211     }
   2212     else
   2213     {
   2214         if (!window->resizable)
   2215             updateNormalHints(window, width, height);
   2216 
   2217         XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
   2218     }
   2219 
   2220     XFlush(_glfw.x11.display);
   2221 }
   2222 
   2223 void _glfwSetWindowSizeLimitsX11(_GLFWwindow* window,
   2224                                  int minwidth, int minheight,
   2225                                  int maxwidth, int maxheight)
   2226 {
   2227     int width, height;
   2228     _glfwGetWindowSizeX11(window, &width, &height);
   2229     updateNormalHints(window, width, height);
   2230     XFlush(_glfw.x11.display);
   2231 }
   2232 
   2233 void _glfwSetWindowAspectRatioX11(_GLFWwindow* window, int numer, int denom)
   2234 {
   2235     int width, height;
   2236     _glfwGetWindowSizeX11(window, &width, &height);
   2237     updateNormalHints(window, width, height);
   2238     XFlush(_glfw.x11.display);
   2239 }
   2240 
   2241 void _glfwGetFramebufferSizeX11(_GLFWwindow* window, int* width, int* height)
   2242 {
   2243     _glfwGetWindowSizeX11(window, width, height);
   2244 }
   2245 
   2246 void _glfwGetWindowFrameSizeX11(_GLFWwindow* window,
   2247                                 int* left, int* top,
   2248                                 int* right, int* bottom)
   2249 {
   2250     long* extents = NULL;
   2251 
   2252     if (window->monitor || !window->decorated)
   2253         return;
   2254 
   2255     if (_glfw.x11.NET_FRAME_EXTENTS == None)
   2256         return;
   2257 
   2258     if (!_glfwWindowVisibleX11(window) &&
   2259         _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
   2260     {
   2261         XEvent event;
   2262         double timeout = 0.5;
   2263 
   2264         // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
   2265         // function before the window is mapped
   2266         sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
   2267                       0, 0, 0, 0, 0);
   2268 
   2269         // HACK: Use a timeout because earlier versions of some window managers
   2270         //       (at least Unity, Fluxbox and Xfwm) failed to send the reply
   2271         //       They have been fixed but broken versions are still in the wild
   2272         //       If you are affected by this and your window manager is NOT
   2273         //       listed above, PLEASE report it to their and our issue trackers
   2274         while (!XCheckIfEvent(_glfw.x11.display,
   2275                               &event,
   2276                               isFrameExtentsEvent,
   2277                               (XPointer) window))
   2278         {
   2279             if (!waitForX11Event(&timeout))
   2280             {
   2281                 _glfwInputError(GLFW_PLATFORM_ERROR,
   2282                                 "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
   2283                 return;
   2284             }
   2285         }
   2286     }
   2287 
   2288     if (_glfwGetWindowPropertyX11(window->x11.handle,
   2289                                   _glfw.x11.NET_FRAME_EXTENTS,
   2290                                   XA_CARDINAL,
   2291                                   (unsigned char**) &extents) == 4)
   2292     {
   2293         if (left)
   2294             *left = extents[0];
   2295         if (top)
   2296             *top = extents[2];
   2297         if (right)
   2298             *right = extents[1];
   2299         if (bottom)
   2300             *bottom = extents[3];
   2301     }
   2302 
   2303     if (extents)
   2304         XFree(extents);
   2305 }
   2306 
   2307 void _glfwGetWindowContentScaleX11(_GLFWwindow* window, float* xscale, float* yscale)
   2308 {
   2309     if (xscale)
   2310         *xscale = _glfw.x11.contentScaleX;
   2311     if (yscale)
   2312         *yscale = _glfw.x11.contentScaleY;
   2313 }
   2314 
   2315 void _glfwIconifyWindowX11(_GLFWwindow* window)
   2316 {
   2317     if (window->x11.overrideRedirect)
   2318     {
   2319         // Override-redirect windows cannot be iconified or restored, as those
   2320         // tasks are performed by the window manager
   2321         _glfwInputError(GLFW_PLATFORM_ERROR,
   2322                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
   2323         return;
   2324     }
   2325 
   2326     XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
   2327     XFlush(_glfw.x11.display);
   2328 }
   2329 
   2330 void _glfwRestoreWindowX11(_GLFWwindow* window)
   2331 {
   2332     if (window->x11.overrideRedirect)
   2333     {
   2334         // Override-redirect windows cannot be iconified or restored, as those
   2335         // tasks are performed by the window manager
   2336         _glfwInputError(GLFW_PLATFORM_ERROR,
   2337                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
   2338         return;
   2339     }
   2340 
   2341     if (_glfwWindowIconifiedX11(window))
   2342     {
   2343         XMapWindow(_glfw.x11.display, window->x11.handle);
   2344         waitForVisibilityNotify(window);
   2345     }
   2346     else if (_glfwWindowVisibleX11(window))
   2347     {
   2348         if (_glfw.x11.NET_WM_STATE &&
   2349             _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
   2350             _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2351         {
   2352             sendEventToWM(window,
   2353                           _glfw.x11.NET_WM_STATE,
   2354                           _NET_WM_STATE_REMOVE,
   2355                           _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2356                           _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
   2357                           1, 0);
   2358         }
   2359     }
   2360 
   2361     XFlush(_glfw.x11.display);
   2362 }
   2363 
   2364 void _glfwMaximizeWindowX11(_GLFWwindow* window)
   2365 {
   2366     if (!_glfw.x11.NET_WM_STATE ||
   2367         !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2368         !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2369     {
   2370         return;
   2371     }
   2372 
   2373     if (_glfwWindowVisibleX11(window))
   2374     {
   2375         sendEventToWM(window,
   2376                     _glfw.x11.NET_WM_STATE,
   2377                     _NET_WM_STATE_ADD,
   2378                     _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2379                     _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
   2380                     1, 0);
   2381     }
   2382     else
   2383     {
   2384         Atom* states = NULL;
   2385         unsigned long count =
   2386             _glfwGetWindowPropertyX11(window->x11.handle,
   2387                                       _glfw.x11.NET_WM_STATE,
   2388                                       XA_ATOM,
   2389                                       (unsigned char**) &states);
   2390 
   2391         // NOTE: We don't check for failure as this property may not exist yet
   2392         //       and that's fine (and we'll create it implicitly with append)
   2393 
   2394         Atom missing[2] =
   2395         {
   2396             _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2397             _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
   2398         };
   2399         unsigned long missingCount = 2;
   2400 
   2401         for (unsigned long i = 0;  i < count;  i++)
   2402         {
   2403             for (unsigned long j = 0;  j < missingCount;  j++)
   2404             {
   2405                 if (states[i] == missing[j])
   2406                 {
   2407                     missing[j] = missing[missingCount - 1];
   2408                     missingCount--;
   2409                 }
   2410             }
   2411         }
   2412 
   2413         if (states)
   2414             XFree(states);
   2415 
   2416         if (!missingCount)
   2417             return;
   2418 
   2419         XChangeProperty(_glfw.x11.display, window->x11.handle,
   2420                         _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2421                         PropModeAppend,
   2422                         (unsigned char*) missing,
   2423                         missingCount);
   2424     }
   2425 
   2426     XFlush(_glfw.x11.display);
   2427 }
   2428 
   2429 void _glfwShowWindowX11(_GLFWwindow* window)
   2430 {
   2431     if (_glfwWindowVisibleX11(window))
   2432         return;
   2433 
   2434     XMapWindow(_glfw.x11.display, window->x11.handle);
   2435     waitForVisibilityNotify(window);
   2436 }
   2437 
   2438 void _glfwHideWindowX11(_GLFWwindow* window)
   2439 {
   2440     XUnmapWindow(_glfw.x11.display, window->x11.handle);
   2441     XFlush(_glfw.x11.display);
   2442 }
   2443 
   2444 void _glfwRequestWindowAttentionX11(_GLFWwindow* window)
   2445 {
   2446     if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
   2447         return;
   2448 
   2449     sendEventToWM(window,
   2450                   _glfw.x11.NET_WM_STATE,
   2451                   _NET_WM_STATE_ADD,
   2452                   _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
   2453                   0, 1, 0);
   2454 }
   2455 
   2456 void _glfwFocusWindowX11(_GLFWwindow* window)
   2457 {
   2458     if (_glfw.x11.NET_ACTIVE_WINDOW)
   2459         sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
   2460     else if (_glfwWindowVisibleX11(window))
   2461     {
   2462         XRaiseWindow(_glfw.x11.display, window->x11.handle);
   2463         XSetInputFocus(_glfw.x11.display, window->x11.handle,
   2464                        RevertToParent, CurrentTime);
   2465     }
   2466 
   2467     XFlush(_glfw.x11.display);
   2468 }
   2469 
   2470 void _glfwSetWindowMonitorX11(_GLFWwindow* window,
   2471                               _GLFWmonitor* monitor,
   2472                               int xpos, int ypos,
   2473                               int width, int height,
   2474                               int refreshRate)
   2475 {
   2476     if (window->monitor == monitor)
   2477     {
   2478         if (monitor)
   2479         {
   2480             if (monitor->window == window)
   2481                 acquireMonitorX11(window);
   2482         }
   2483         else
   2484         {
   2485             if (!window->resizable)
   2486                 updateNormalHints(window, width, height);
   2487 
   2488             XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   2489                               xpos, ypos, width, height);
   2490         }
   2491 
   2492         XFlush(_glfw.x11.display);
   2493         return;
   2494     }
   2495 
   2496     if (window->monitor)
   2497     {
   2498         _glfwSetWindowDecoratedX11(window, window->decorated);
   2499         _glfwSetWindowFloatingX11(window, window->floating);
   2500         releaseMonitorX11(window);
   2501     }
   2502 
   2503     _glfwInputWindowMonitor(window, monitor);
   2504     updateNormalHints(window, width, height);
   2505 
   2506     if (window->monitor)
   2507     {
   2508         if (!_glfwWindowVisibleX11(window))
   2509         {
   2510             XMapRaised(_glfw.x11.display, window->x11.handle);
   2511             waitForVisibilityNotify(window);
   2512         }
   2513 
   2514         updateWindowMode(window);
   2515         acquireMonitorX11(window);
   2516     }
   2517     else
   2518     {
   2519         updateWindowMode(window);
   2520         XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   2521                           xpos, ypos, width, height);
   2522     }
   2523 
   2524     XFlush(_glfw.x11.display);
   2525 }
   2526 
   2527 GLFWbool _glfwWindowFocusedX11(_GLFWwindow* window)
   2528 {
   2529     Window focused;
   2530     int state;
   2531 
   2532     XGetInputFocus(_glfw.x11.display, &focused, &state);
   2533     return window->x11.handle == focused;
   2534 }
   2535 
   2536 GLFWbool _glfwWindowIconifiedX11(_GLFWwindow* window)
   2537 {
   2538     return getWindowState(window) == IconicState;
   2539 }
   2540 
   2541 GLFWbool _glfwWindowVisibleX11(_GLFWwindow* window)
   2542 {
   2543     XWindowAttributes wa;
   2544     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
   2545     return wa.map_state == IsViewable;
   2546 }
   2547 
   2548 GLFWbool _glfwWindowMaximizedX11(_GLFWwindow* window)
   2549 {
   2550     Atom* states;
   2551     GLFWbool maximized = GLFW_FALSE;
   2552 
   2553     if (!_glfw.x11.NET_WM_STATE ||
   2554         !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2555         !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2556     {
   2557         return maximized;
   2558     }
   2559 
   2560     const unsigned long count =
   2561         _glfwGetWindowPropertyX11(window->x11.handle,
   2562                                   _glfw.x11.NET_WM_STATE,
   2563                                   XA_ATOM,
   2564                                   (unsigned char**) &states);
   2565 
   2566     for (unsigned long i = 0;  i < count;  i++)
   2567     {
   2568         if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2569             states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2570         {
   2571             maximized = GLFW_TRUE;
   2572             break;
   2573         }
   2574     }
   2575 
   2576     if (states)
   2577         XFree(states);
   2578 
   2579     return maximized;
   2580 }
   2581 
   2582 GLFWbool _glfwWindowHoveredX11(_GLFWwindow* window)
   2583 {
   2584     Window w = _glfw.x11.root;
   2585     while (w)
   2586     {
   2587         Window root;
   2588         int rootX, rootY, childX, childY;
   2589         unsigned int mask;
   2590 
   2591         _glfwGrabErrorHandlerX11();
   2592 
   2593         const Bool result = XQueryPointer(_glfw.x11.display, w,
   2594                                           &root, &w, &rootX, &rootY,
   2595                                           &childX, &childY, &mask);
   2596 
   2597         _glfwReleaseErrorHandlerX11();
   2598 
   2599         if (_glfw.x11.errorCode == BadWindow)
   2600             w = _glfw.x11.root;
   2601         else if (!result)
   2602             return GLFW_FALSE;
   2603         else if (w == window->x11.handle)
   2604             return GLFW_TRUE;
   2605     }
   2606 
   2607     return GLFW_FALSE;
   2608 }
   2609 
   2610 GLFWbool _glfwFramebufferTransparentX11(_GLFWwindow* window)
   2611 {
   2612     if (!window->x11.transparent)
   2613         return GLFW_FALSE;
   2614 
   2615     return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
   2616 }
   2617 
   2618 void _glfwSetWindowResizableX11(_GLFWwindow* window, GLFWbool enabled)
   2619 {
   2620     int width, height;
   2621     _glfwGetWindowSizeX11(window, &width, &height);
   2622     updateNormalHints(window, width, height);
   2623 }
   2624 
   2625 void _glfwSetWindowDecoratedX11(_GLFWwindow* window, GLFWbool enabled)
   2626 {
   2627     struct
   2628     {
   2629         unsigned long flags;
   2630         unsigned long functions;
   2631         unsigned long decorations;
   2632         long input_mode;
   2633         unsigned long status;
   2634     } hints = {0};
   2635 
   2636     hints.flags = MWM_HINTS_DECORATIONS;
   2637     hints.decorations = enabled ? MWM_DECOR_ALL : 0;
   2638 
   2639     XChangeProperty(_glfw.x11.display, window->x11.handle,
   2640                     _glfw.x11.MOTIF_WM_HINTS,
   2641                     _glfw.x11.MOTIF_WM_HINTS, 32,
   2642                     PropModeReplace,
   2643                     (unsigned char*) &hints,
   2644                     sizeof(hints) / sizeof(long));
   2645 }
   2646 
   2647 void _glfwSetWindowFloatingX11(_GLFWwindow* window, GLFWbool enabled)
   2648 {
   2649     if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
   2650         return;
   2651 
   2652     if (_glfwWindowVisibleX11(window))
   2653     {
   2654         const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
   2655         sendEventToWM(window,
   2656                       _glfw.x11.NET_WM_STATE,
   2657                       action,
   2658                       _glfw.x11.NET_WM_STATE_ABOVE,
   2659                       0, 1, 0);
   2660     }
   2661     else
   2662     {
   2663         Atom* states = NULL;
   2664         const unsigned long count =
   2665             _glfwGetWindowPropertyX11(window->x11.handle,
   2666                                       _glfw.x11.NET_WM_STATE,
   2667                                       XA_ATOM,
   2668                                       (unsigned char**) &states);
   2669 
   2670         // NOTE: We don't check for failure as this property may not exist yet
   2671         //       and that's fine (and we'll create it implicitly with append)
   2672 
   2673         if (enabled)
   2674         {
   2675             unsigned long i;
   2676 
   2677             for (i = 0;  i < count;  i++)
   2678             {
   2679                 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
   2680                     break;
   2681             }
   2682 
   2683             if (i == count)
   2684             {
   2685                 XChangeProperty(_glfw.x11.display, window->x11.handle,
   2686                                 _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2687                                 PropModeAppend,
   2688                                 (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
   2689                                 1);
   2690             }
   2691         }
   2692         else if (states)
   2693         {
   2694             for (unsigned long i = 0;  i < count;  i++)
   2695             {
   2696                 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
   2697                 {
   2698                     states[i] = states[count - 1];
   2699                     XChangeProperty(_glfw.x11.display, window->x11.handle,
   2700                                     _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2701                                     PropModeReplace, (unsigned char*) states, count - 1);
   2702                     break;
   2703                 }
   2704             }
   2705         }
   2706 
   2707         if (states)
   2708             XFree(states);
   2709     }
   2710 
   2711     XFlush(_glfw.x11.display);
   2712 }
   2713 
   2714 void _glfwSetWindowMousePassthroughX11(_GLFWwindow* window, GLFWbool enabled)
   2715 {
   2716     if (!_glfw.x11.xshape.available)
   2717         return;
   2718 
   2719     if (enabled)
   2720     {
   2721         Region region = XCreateRegion();
   2722         XShapeCombineRegion(_glfw.x11.display, window->x11.handle,
   2723                             ShapeInput, 0, 0, region, ShapeSet);
   2724         XDestroyRegion(region);
   2725     }
   2726     else
   2727     {
   2728         XShapeCombineMask(_glfw.x11.display, window->x11.handle,
   2729                           ShapeInput, 0, 0, None, ShapeSet);
   2730     }
   2731 }
   2732 
   2733 float _glfwGetWindowOpacityX11(_GLFWwindow* window)
   2734 {
   2735     float opacity = 1.f;
   2736 
   2737     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
   2738     {
   2739         CARD32* value = NULL;
   2740 
   2741         if (_glfwGetWindowPropertyX11(window->x11.handle,
   2742                                       _glfw.x11.NET_WM_WINDOW_OPACITY,
   2743                                       XA_CARDINAL,
   2744                                       (unsigned char**) &value))
   2745         {
   2746             opacity = (float) (*value / (double) 0xffffffffu);
   2747         }
   2748 
   2749         if (value)
   2750             XFree(value);
   2751     }
   2752 
   2753     return opacity;
   2754 }
   2755 
   2756 void _glfwSetWindowOpacityX11(_GLFWwindow* window, float opacity)
   2757 {
   2758     const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
   2759     XChangeProperty(_glfw.x11.display, window->x11.handle,
   2760                     _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
   2761                     PropModeReplace, (unsigned char*) &value, 1);
   2762 }
   2763 
   2764 void _glfwSetRawMouseMotionX11(_GLFWwindow *window, GLFWbool enabled)
   2765 {
   2766     if (!_glfw.x11.xi.available)
   2767         return;
   2768 
   2769     if (_glfw.x11.disabledCursorWindow != window)
   2770         return;
   2771 
   2772     if (enabled)
   2773         enableRawMouseMotion(window);
   2774     else
   2775         disableRawMouseMotion(window);
   2776 }
   2777 
   2778 GLFWbool _glfwRawMouseMotionSupportedX11(void)
   2779 {
   2780     return _glfw.x11.xi.available;
   2781 }
   2782 
   2783 void _glfwPollEventsX11(void)
   2784 {
   2785     drainEmptyEvents();
   2786 
   2787 #if defined(GLFW_BUILD_LINUX_JOYSTICK)
   2788     if (_glfw.joysticksInitialized)
   2789         _glfwDetectJoystickConnectionLinux();
   2790 #endif
   2791     XPending(_glfw.x11.display);
   2792 
   2793     while (QLength(_glfw.x11.display))
   2794     {
   2795         XEvent event;
   2796         XNextEvent(_glfw.x11.display, &event);
   2797         processEvent(&event);
   2798     }
   2799 
   2800     _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
   2801     if (window)
   2802     {
   2803         int width, height;
   2804         _glfwGetWindowSizeX11(window, &width, &height);
   2805 
   2806         // NOTE: Re-center the cursor only if it has moved since the last call,
   2807         //       to avoid breaking glfwWaitEvents with MotionNotify
   2808         if (window->x11.lastCursorPosX != width / 2 ||
   2809             window->x11.lastCursorPosY != height / 2)
   2810         {
   2811             _glfwSetCursorPosX11(window, width / 2, height / 2);
   2812         }
   2813     }
   2814 
   2815     XFlush(_glfw.x11.display);
   2816 }
   2817 
   2818 void _glfwWaitEventsX11(void)
   2819 {
   2820     waitForAnyEvent(NULL);
   2821     _glfwPollEventsX11();
   2822 }
   2823 
   2824 void _glfwWaitEventsTimeoutX11(double timeout)
   2825 {
   2826     waitForAnyEvent(&timeout);
   2827     _glfwPollEventsX11();
   2828 }
   2829 
   2830 void _glfwPostEmptyEventX11(void)
   2831 {
   2832     writeEmptyEvent();
   2833 }
   2834 
   2835 void _glfwGetCursorPosX11(_GLFWwindow* window, double* xpos, double* ypos)
   2836 {
   2837     Window root, child;
   2838     int rootX, rootY, childX, childY;
   2839     unsigned int mask;
   2840 
   2841     XQueryPointer(_glfw.x11.display, window->x11.handle,
   2842                   &root, &child,
   2843                   &rootX, &rootY, &childX, &childY,
   2844                   &mask);
   2845 
   2846     if (xpos)
   2847         *xpos = childX;
   2848     if (ypos)
   2849         *ypos = childY;
   2850 }
   2851 
   2852 void _glfwSetCursorPosX11(_GLFWwindow* window, double x, double y)
   2853 {
   2854     // Store the new position so it can be recognized later
   2855     window->x11.warpCursorPosX = (int) x;
   2856     window->x11.warpCursorPosY = (int) y;
   2857 
   2858     XWarpPointer(_glfw.x11.display, None, window->x11.handle,
   2859                  0,0,0,0, (int) x, (int) y);
   2860     XFlush(_glfw.x11.display);
   2861 }
   2862 
   2863 void _glfwSetCursorModeX11(_GLFWwindow* window, int mode)
   2864 {
   2865     if (_glfwWindowFocusedX11(window))
   2866     {
   2867         if (mode == GLFW_CURSOR_DISABLED)
   2868         {
   2869             _glfwGetCursorPosX11(window,
   2870                                  &_glfw.x11.restoreCursorPosX,
   2871                                  &_glfw.x11.restoreCursorPosY);
   2872             _glfwCenterCursorInContentArea(window);
   2873             if (window->rawMouseMotion)
   2874                 enableRawMouseMotion(window);
   2875         }
   2876         else if (_glfw.x11.disabledCursorWindow == window)
   2877         {
   2878             if (window->rawMouseMotion)
   2879                 disableRawMouseMotion(window);
   2880         }
   2881 
   2882         if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED)
   2883             captureCursor(window);
   2884         else
   2885             releaseCursor();
   2886 
   2887         if (mode == GLFW_CURSOR_DISABLED)
   2888             _glfw.x11.disabledCursorWindow = window;
   2889         else if (_glfw.x11.disabledCursorWindow == window)
   2890         {
   2891             _glfw.x11.disabledCursorWindow = NULL;
   2892             _glfwSetCursorPosX11(window,
   2893                                  _glfw.x11.restoreCursorPosX,
   2894                                  _glfw.x11.restoreCursorPosY);
   2895         }
   2896     }
   2897 
   2898     updateCursorImage(window);
   2899     XFlush(_glfw.x11.display);
   2900 }
   2901 
   2902 const char* _glfwGetScancodeNameX11(int scancode)
   2903 {
   2904     if (!_glfw.x11.xkb.available)
   2905         return NULL;
   2906 
   2907     if (scancode < 0 || scancode > 0xff)
   2908     {
   2909         _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode);
   2910         return NULL;
   2911     }
   2912 
   2913     const int key = _glfw.x11.keycodes[scancode];
   2914     if (key == GLFW_KEY_UNKNOWN)
   2915         return NULL;
   2916 
   2917     const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display,
   2918                                              scancode, _glfw.x11.xkb.group, 0);
   2919     if (keysym == NoSymbol)
   2920         return NULL;
   2921 
   2922     const uint32_t codepoint = _glfwKeySym2Unicode(keysym);
   2923     if (codepoint == GLFW_INVALID_CODEPOINT)
   2924         return NULL;
   2925 
   2926     const size_t count = _glfwEncodeUTF8(_glfw.x11.keynames[key], codepoint);
   2927     if (count == 0)
   2928         return NULL;
   2929 
   2930     _glfw.x11.keynames[key][count] = '\0';
   2931     return _glfw.x11.keynames[key];
   2932 }
   2933 
   2934 int _glfwGetKeyScancodeX11(int key)
   2935 {
   2936     return _glfw.x11.scancodes[key];
   2937 }
   2938 
   2939 GLFWbool _glfwCreateCursorX11(_GLFWcursor* cursor,
   2940                               const GLFWimage* image,
   2941                               int xhot, int yhot)
   2942 {
   2943     cursor->x11.handle = _glfwCreateNativeCursorX11(image, xhot, yhot);
   2944     if (!cursor->x11.handle)
   2945         return GLFW_FALSE;
   2946 
   2947     return GLFW_TRUE;
   2948 }
   2949 
   2950 GLFWbool _glfwCreateStandardCursorX11(_GLFWcursor* cursor, int shape)
   2951 {
   2952     if (_glfw.x11.xcursor.handle)
   2953     {
   2954         char* theme = XcursorGetTheme(_glfw.x11.display);
   2955         if (theme)
   2956         {
   2957             const int size = XcursorGetDefaultSize(_glfw.x11.display);
   2958             const char* name = NULL;
   2959 
   2960             switch (shape)
   2961             {
   2962                 case GLFW_ARROW_CURSOR:
   2963                     name = "default";
   2964                     break;
   2965                 case GLFW_IBEAM_CURSOR:
   2966                     name = "text";
   2967                     break;
   2968                 case GLFW_CROSSHAIR_CURSOR:
   2969                     name = "crosshair";
   2970                     break;
   2971                 case GLFW_POINTING_HAND_CURSOR:
   2972                     name = "pointer";
   2973                     break;
   2974                 case GLFW_RESIZE_EW_CURSOR:
   2975                     name = "ew-resize";
   2976                     break;
   2977                 case GLFW_RESIZE_NS_CURSOR:
   2978                     name = "ns-resize";
   2979                     break;
   2980                 case GLFW_RESIZE_NWSE_CURSOR:
   2981                     name = "nwse-resize";
   2982                     break;
   2983                 case GLFW_RESIZE_NESW_CURSOR:
   2984                     name = "nesw-resize";
   2985                     break;
   2986                 case GLFW_RESIZE_ALL_CURSOR:
   2987                     name = "all-scroll";
   2988                     break;
   2989                 case GLFW_NOT_ALLOWED_CURSOR:
   2990                     name = "not-allowed";
   2991                     break;
   2992             }
   2993 
   2994             XcursorImage* image = XcursorLibraryLoadImage(name, theme, size);
   2995             if (image)
   2996             {
   2997                 cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image);
   2998                 XcursorImageDestroy(image);
   2999             }
   3000         }
   3001     }
   3002 
   3003     if (!cursor->x11.handle)
   3004     {
   3005         unsigned int native = 0;
   3006 
   3007         switch (shape)
   3008         {
   3009             case GLFW_ARROW_CURSOR:
   3010                 native = XC_left_ptr;
   3011                 break;
   3012             case GLFW_IBEAM_CURSOR:
   3013                 native = XC_xterm;
   3014                 break;
   3015             case GLFW_CROSSHAIR_CURSOR:
   3016                 native = XC_crosshair;
   3017                 break;
   3018             case GLFW_POINTING_HAND_CURSOR:
   3019                 native = XC_hand2;
   3020                 break;
   3021             case GLFW_RESIZE_EW_CURSOR:
   3022                 native = XC_sb_h_double_arrow;
   3023                 break;
   3024             case GLFW_RESIZE_NS_CURSOR:
   3025                 native = XC_sb_v_double_arrow;
   3026                 break;
   3027             case GLFW_RESIZE_ALL_CURSOR:
   3028                 native = XC_fleur;
   3029                 break;
   3030             default:
   3031                 _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
   3032                                 "X11: Standard cursor shape unavailable");
   3033                 return GLFW_FALSE;
   3034         }
   3035 
   3036         cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
   3037         if (!cursor->x11.handle)
   3038         {
   3039             _glfwInputError(GLFW_PLATFORM_ERROR,
   3040                             "X11: Failed to create standard cursor");
   3041             return GLFW_FALSE;
   3042         }
   3043     }
   3044 
   3045     return GLFW_TRUE;
   3046 }
   3047 
   3048 void _glfwDestroyCursorX11(_GLFWcursor* cursor)
   3049 {
   3050     if (cursor->x11.handle)
   3051         XFreeCursor(_glfw.x11.display, cursor->x11.handle);
   3052 }
   3053 
   3054 void _glfwSetCursorX11(_GLFWwindow* window, _GLFWcursor* cursor)
   3055 {
   3056     if (window->cursorMode == GLFW_CURSOR_NORMAL ||
   3057         window->cursorMode == GLFW_CURSOR_CAPTURED)
   3058     {
   3059         updateCursorImage(window);
   3060         XFlush(_glfw.x11.display);
   3061     }
   3062 }
   3063 
   3064 void _glfwSetClipboardStringX11(const char* string)
   3065 {
   3066     char* copy = _glfw_strdup(string);
   3067     _glfw_free(_glfw.x11.clipboardString);
   3068     _glfw.x11.clipboardString = copy;
   3069 
   3070     XSetSelectionOwner(_glfw.x11.display,
   3071                        _glfw.x11.CLIPBOARD,
   3072                        _glfw.x11.helperWindowHandle,
   3073                        CurrentTime);
   3074 
   3075     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
   3076         _glfw.x11.helperWindowHandle)
   3077     {
   3078         _glfwInputError(GLFW_PLATFORM_ERROR,
   3079                         "X11: Failed to become owner of clipboard selection");
   3080     }
   3081 }
   3082 
   3083 const char* _glfwGetClipboardStringX11(void)
   3084 {
   3085     return getSelectionString(_glfw.x11.CLIPBOARD);
   3086 }
   3087 
   3088 EGLenum _glfwGetEGLPlatformX11(EGLint** attribs)
   3089 {
   3090     if (_glfw.egl.ANGLE_platform_angle)
   3091     {
   3092         int type = 0;
   3093 
   3094         if (_glfw.egl.ANGLE_platform_angle_opengl)
   3095         {
   3096             if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
   3097                 type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
   3098         }
   3099 
   3100         if (_glfw.egl.ANGLE_platform_angle_vulkan)
   3101         {
   3102             if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
   3103                 type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
   3104         }
   3105 
   3106         if (type)
   3107         {
   3108             *attribs = _glfw_calloc(5, sizeof(EGLint));
   3109             (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
   3110             (*attribs)[1] = type;
   3111             (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE;
   3112             (*attribs)[3] = EGL_PLATFORM_X11_EXT;
   3113             (*attribs)[4] = EGL_NONE;
   3114             return EGL_PLATFORM_ANGLE_ANGLE;
   3115         }
   3116     }
   3117 
   3118     if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_x11)
   3119         return EGL_PLATFORM_X11_EXT;
   3120 
   3121     return 0;
   3122 }
   3123 
   3124 EGLNativeDisplayType _glfwGetEGLNativeDisplayX11(void)
   3125 {
   3126     return _glfw.x11.display;
   3127 }
   3128 
   3129 EGLNativeWindowType _glfwGetEGLNativeWindowX11(_GLFWwindow* window)
   3130 {
   3131     if (_glfw.egl.platform)
   3132         return &window->x11.handle;
   3133     else
   3134         return (EGLNativeWindowType) window->x11.handle;
   3135 }
   3136 
   3137 void _glfwGetRequiredInstanceExtensionsX11(char** extensions)
   3138 {
   3139     if (!_glfw.vk.KHR_surface)
   3140         return;
   3141 
   3142     if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
   3143     {
   3144         if (!_glfw.vk.KHR_xlib_surface)
   3145             return;
   3146     }
   3147 
   3148     extensions[0] = "VK_KHR_surface";
   3149 
   3150     // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
   3151     //       not correctly implementing VK_KHR_xlib_surface
   3152     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   3153         extensions[1] = "VK_KHR_xcb_surface";
   3154     else
   3155         extensions[1] = "VK_KHR_xlib_surface";
   3156 }
   3157 
   3158 GLFWbool _glfwGetPhysicalDevicePresentationSupportX11(VkInstance instance,
   3159                                                       VkPhysicalDevice device,
   3160                                                       uint32_t queuefamily)
   3161 {
   3162     VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
   3163                                                           _glfw.x11.screen));
   3164 
   3165     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   3166     {
   3167         PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
   3168             vkGetPhysicalDeviceXcbPresentationSupportKHR =
   3169             (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
   3170             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
   3171         if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
   3172         {
   3173             _glfwInputError(GLFW_API_UNAVAILABLE,
   3174                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
   3175             return GLFW_FALSE;
   3176         }
   3177 
   3178         xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
   3179         if (!connection)
   3180         {
   3181             _glfwInputError(GLFW_PLATFORM_ERROR,
   3182                             "X11: Failed to retrieve XCB connection");
   3183             return GLFW_FALSE;
   3184         }
   3185 
   3186         return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
   3187                                                             queuefamily,
   3188                                                             connection,
   3189                                                             visualID);
   3190     }
   3191     else
   3192     {
   3193         PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
   3194             vkGetPhysicalDeviceXlibPresentationSupportKHR =
   3195             (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
   3196             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
   3197         if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
   3198         {
   3199             _glfwInputError(GLFW_API_UNAVAILABLE,
   3200                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
   3201             return GLFW_FALSE;
   3202         }
   3203 
   3204         return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
   3205                                                              queuefamily,
   3206                                                              _glfw.x11.display,
   3207                                                              visualID);
   3208     }
   3209 }
   3210 
   3211 VkResult _glfwCreateWindowSurfaceX11(VkInstance instance,
   3212                                      _GLFWwindow* window,
   3213                                      const VkAllocationCallbacks* allocator,
   3214                                      VkSurfaceKHR* surface)
   3215 {
   3216     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   3217     {
   3218         VkResult err;
   3219         VkXcbSurfaceCreateInfoKHR sci;
   3220         PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
   3221 
   3222         xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
   3223         if (!connection)
   3224         {
   3225             _glfwInputError(GLFW_PLATFORM_ERROR,
   3226                             "X11: Failed to retrieve XCB connection");
   3227             return VK_ERROR_EXTENSION_NOT_PRESENT;
   3228         }
   3229 
   3230         vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
   3231             vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
   3232         if (!vkCreateXcbSurfaceKHR)
   3233         {
   3234             _glfwInputError(GLFW_API_UNAVAILABLE,
   3235                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
   3236             return VK_ERROR_EXTENSION_NOT_PRESENT;
   3237         }
   3238 
   3239         memset(&sci, 0, sizeof(sci));
   3240         sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
   3241         sci.connection = connection;
   3242         sci.window = window->x11.handle;
   3243 
   3244         err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
   3245         if (err)
   3246         {
   3247             _glfwInputError(GLFW_PLATFORM_ERROR,
   3248                             "X11: Failed to create Vulkan XCB surface: %s",
   3249                             _glfwGetVulkanResultString(err));
   3250         }
   3251 
   3252         return err;
   3253     }
   3254     else
   3255     {
   3256         VkResult err;
   3257         VkXlibSurfaceCreateInfoKHR sci;
   3258         PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
   3259 
   3260         vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
   3261             vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
   3262         if (!vkCreateXlibSurfaceKHR)
   3263         {
   3264             _glfwInputError(GLFW_API_UNAVAILABLE,
   3265                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
   3266             return VK_ERROR_EXTENSION_NOT_PRESENT;
   3267         }
   3268 
   3269         memset(&sci, 0, sizeof(sci));
   3270         sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
   3271         sci.dpy = _glfw.x11.display;
   3272         sci.window = window->x11.handle;
   3273 
   3274         err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
   3275         if (err)
   3276         {
   3277             _glfwInputError(GLFW_PLATFORM_ERROR,
   3278                             "X11: Failed to create Vulkan X11 surface: %s",
   3279                             _glfwGetVulkanResultString(err));
   3280         }
   3281 
   3282         return err;
   3283     }
   3284 }
   3285 
   3286 
   3287 //////////////////////////////////////////////////////////////////////////
   3288 //////                        GLFW native API                       //////
   3289 //////////////////////////////////////////////////////////////////////////
   3290 
   3291 GLFWAPI Display* glfwGetX11Display(void)
   3292 {
   3293     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   3294 
   3295     if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
   3296     {
   3297         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
   3298         return NULL;
   3299     }
   3300 
   3301     return _glfw.x11.display;
   3302 }
   3303 
   3304 GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
   3305 {
   3306     _GLFWwindow* window = (_GLFWwindow*) handle;
   3307     _GLFW_REQUIRE_INIT_OR_RETURN(None);
   3308 
   3309     if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
   3310     {
   3311         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
   3312         return None;
   3313     }
   3314 
   3315     return window->x11.handle;
   3316 }
   3317 
   3318 GLFWAPI void glfwSetX11SelectionString(const char* string)
   3319 {
   3320     _GLFW_REQUIRE_INIT();
   3321 
   3322     if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
   3323     {
   3324         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
   3325         return;
   3326     }
   3327 
   3328     _glfw_free(_glfw.x11.primarySelectionString);
   3329     _glfw.x11.primarySelectionString = _glfw_strdup(string);
   3330 
   3331     XSetSelectionOwner(_glfw.x11.display,
   3332                        _glfw.x11.PRIMARY,
   3333                        _glfw.x11.helperWindowHandle,
   3334                        CurrentTime);
   3335 
   3336     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
   3337         _glfw.x11.helperWindowHandle)
   3338     {
   3339         _glfwInputError(GLFW_PLATFORM_ERROR,
   3340                         "X11: Failed to become owner of primary selection");
   3341     }
   3342 }
   3343 
   3344 GLFWAPI const char* glfwGetX11SelectionString(void)
   3345 {
   3346     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   3347 
   3348     if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
   3349     {
   3350         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
   3351         return NULL;
   3352     }
   3353 
   3354     return getSelectionString(_glfw.x11.PRIMARY);
   3355 }
   3356 
   3357 #endif // _GLFW_X11
   3358