revolver

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

x11_window.c (102783B)


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