zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

x11_window.c (101173B)


      1 //========================================================================
      2 // GLFW 3.3 X11 - www.glfw.org
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2002-2006 Marcus Geelnard
      5 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
      6 //
      7 // This software is provided 'as-is', without any express or implied
      8 // warranty. In no event will the authors be held liable for any damages
      9 // arising from the use of this software.
     10 //
     11 // Permission is granted to anyone to use this software for any purpose,
     12 // including commercial applications, and to alter it and redistribute it
     13 // freely, subject to the following restrictions:
     14 //
     15 // 1. The origin of this software must not be misrepresented; you must not
     16 //    claim that you wrote the original software. If you use this software
     17 //    in a product, an acknowledgment in the product documentation would
     18 //    be appreciated but is not required.
     19 //
     20 // 2. Altered source versions must be plainly marked as such, and must not
     21 //    be misrepresented as being the original software.
     22 //
     23 // 3. This notice may not be removed or altered from any source
     24 //    distribution.
     25 //
     26 //========================================================================
     27 // It is fine to use C99 in this file because it will not be built with VS
     28 //========================================================================
     29 
     30 #include "internal.h"
     31 
     32 #include <X11/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 // Create the X11 window (and its colormap)
    594 //
    595 static GLFWbool createNativeWindow(_GLFWwindow* window,
    596                                    const _GLFWwndconfig* wndconfig,
    597                                    Visual* visual, int depth)
    598 {
    599     int width = wndconfig->width;
    600     int height = wndconfig->height;
    601 
    602     if (wndconfig->scaleToMonitor)
    603     {
    604         width *= _glfw.x11.contentScaleX;
    605         height *= _glfw.x11.contentScaleY;
    606     }
    607 
    608     // Create a colormap based on the visual used by the current context
    609     window->x11.colormap = XCreateColormap(_glfw.x11.display,
    610                                            _glfw.x11.root,
    611                                            visual,
    612                                            AllocNone);
    613 
    614     window->x11.transparent = _glfwIsVisualTransparentX11(visual);
    615 
    616     XSetWindowAttributes wa = { 0 };
    617     wa.colormap = window->x11.colormap;
    618     wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
    619                     PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
    620                     ExposureMask | FocusChangeMask | VisibilityChangeMask |
    621                     EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
    622 
    623     _glfwGrabErrorHandlerX11();
    624 
    625     window->x11.parent = _glfw.x11.root;
    626     window->x11.handle = XCreateWindow(_glfw.x11.display,
    627                                        _glfw.x11.root,
    628                                        0, 0,   // Position
    629                                        width, height,
    630                                        0,      // Border width
    631                                        depth,  // Color depth
    632                                        InputOutput,
    633                                        visual,
    634                                        CWBorderPixel | CWColormap | CWEventMask,
    635                                        &wa);
    636 
    637     _glfwReleaseErrorHandlerX11();
    638 
    639     if (!window->x11.handle)
    640     {
    641         _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
    642                            "X11: Failed to create window");
    643         return GLFW_FALSE;
    644     }
    645 
    646     XSaveContext(_glfw.x11.display,
    647                  window->x11.handle,
    648                  _glfw.x11.context,
    649                  (XPointer) window);
    650 
    651     if (!wndconfig->decorated)
    652         _glfwPlatformSetWindowDecorated(window, GLFW_FALSE);
    653 
    654     if (_glfw.x11.NET_WM_STATE && !window->monitor)
    655     {
    656         Atom states[3];
    657         int count = 0;
    658 
    659         if (wndconfig->floating)
    660         {
    661             if (_glfw.x11.NET_WM_STATE_ABOVE)
    662                 states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
    663         }
    664 
    665         if (wndconfig->maximized)
    666         {
    667             if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
    668                 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
    669             {
    670                 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
    671                 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
    672                 window->x11.maximized = GLFW_TRUE;
    673             }
    674         }
    675 
    676         if (count)
    677         {
    678             XChangeProperty(_glfw.x11.display, window->x11.handle,
    679                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
    680                             PropModeReplace, (unsigned char*) states, count);
    681         }
    682     }
    683 
    684     // Declare the WM protocols supported by GLFW
    685     {
    686         Atom protocols[] =
    687         {
    688             _glfw.x11.WM_DELETE_WINDOW,
    689             _glfw.x11.NET_WM_PING
    690         };
    691 
    692         XSetWMProtocols(_glfw.x11.display, window->x11.handle,
    693                         protocols, sizeof(protocols) / sizeof(Atom));
    694     }
    695 
    696     // Declare our PID
    697     {
    698         const long pid = getpid();
    699 
    700         XChangeProperty(_glfw.x11.display,  window->x11.handle,
    701                         _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
    702                         PropModeReplace,
    703                         (unsigned char*) &pid, 1);
    704     }
    705 
    706     if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
    707     {
    708         Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
    709         XChangeProperty(_glfw.x11.display,  window->x11.handle,
    710                         _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
    711                         PropModeReplace, (unsigned char*) &type, 1);
    712     }
    713 
    714     // Set ICCCM WM_HINTS property
    715     {
    716         XWMHints* hints = XAllocWMHints();
    717         if (!hints)
    718         {
    719             _glfwInputError(GLFW_OUT_OF_MEMORY,
    720                             "X11: Failed to allocate WM hints");
    721             return GLFW_FALSE;
    722         }
    723 
    724         hints->flags = StateHint;
    725         hints->initial_state = NormalState;
    726 
    727         XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
    728         XFree(hints);
    729     }
    730 
    731     updateNormalHints(window, width, height);
    732 
    733     // Set ICCCM WM_CLASS property
    734     {
    735         XClassHint* hint = XAllocClassHint();
    736 
    737         if (strlen(wndconfig->x11.instanceName) &&
    738             strlen(wndconfig->x11.className))
    739         {
    740             hint->res_name = (char*) wndconfig->x11.instanceName;
    741             hint->res_class = (char*) wndconfig->x11.className;
    742         }
    743         else
    744         {
    745             const char* resourceName = getenv("RESOURCE_NAME");
    746             if (resourceName && strlen(resourceName))
    747                 hint->res_name = (char*) resourceName;
    748             else if (strlen(wndconfig->title))
    749                 hint->res_name = (char*) wndconfig->title;
    750             else
    751                 hint->res_name = (char*) "glfw-application";
    752 
    753             if (strlen(wndconfig->title))
    754                 hint->res_class = (char*) wndconfig->title;
    755             else
    756                 hint->res_class = (char*) "GLFW-Application";
    757         }
    758 
    759         XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
    760         XFree(hint);
    761     }
    762 
    763     // Announce support for Xdnd (drag and drop)
    764     {
    765         const Atom version = _GLFW_XDND_VERSION;
    766         XChangeProperty(_glfw.x11.display, window->x11.handle,
    767                         _glfw.x11.XdndAware, XA_ATOM, 32,
    768                         PropModeReplace, (unsigned char*) &version, 1);
    769     }
    770 
    771     _glfwPlatformSetWindowTitle(window, wndconfig->title);
    772 
    773     if (_glfw.x11.im)
    774     {
    775         window->x11.ic = XCreateIC(_glfw.x11.im,
    776                                    XNInputStyle,
    777                                    XIMPreeditNothing | XIMStatusNothing,
    778                                    XNClientWindow,
    779                                    window->x11.handle,
    780                                    XNFocusWindow,
    781                                    window->x11.handle,
    782                                    NULL);
    783     }
    784 
    785     if (window->x11.ic)
    786     {
    787         unsigned long filter = 0;
    788         if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL)
    789             XSelectInput(_glfw.x11.display, window->x11.handle, wa.event_mask | filter);
    790     }
    791 
    792     _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
    793     _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
    794 
    795     return GLFW_TRUE;
    796 }
    797 
    798 // Set the specified property to the selection converted to the requested target
    799 //
    800 static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
    801 {
    802     int i;
    803     char* selectionString = NULL;
    804     const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING };
    805     const int formatCount = sizeof(formats) / sizeof(formats[0]);
    806 
    807     if (request->selection == _glfw.x11.PRIMARY)
    808         selectionString = _glfw.x11.primarySelectionString;
    809     else
    810         selectionString = _glfw.x11.clipboardString;
    811 
    812     if (request->property == None)
    813     {
    814         // The requester is a legacy client (ICCCM section 2.2)
    815         // We don't support legacy clients, so fail here
    816         return None;
    817     }
    818 
    819     if (request->target == _glfw.x11.TARGETS)
    820     {
    821         // The list of supported targets was requested
    822 
    823         const Atom targets[] = { _glfw.x11.TARGETS,
    824                                  _glfw.x11.MULTIPLE,
    825                                  _glfw.x11.UTF8_STRING,
    826                                  XA_STRING };
    827 
    828         XChangeProperty(_glfw.x11.display,
    829                         request->requestor,
    830                         request->property,
    831                         XA_ATOM,
    832                         32,
    833                         PropModeReplace,
    834                         (unsigned char*) targets,
    835                         sizeof(targets) / sizeof(targets[0]));
    836 
    837         return request->property;
    838     }
    839 
    840     if (request->target == _glfw.x11.MULTIPLE)
    841     {
    842         // Multiple conversions were requested
    843 
    844         Atom* targets;
    845         unsigned long i, count;
    846 
    847         count = _glfwGetWindowPropertyX11(request->requestor,
    848                                           request->property,
    849                                           _glfw.x11.ATOM_PAIR,
    850                                           (unsigned char**) &targets);
    851 
    852         for (i = 0;  i < count;  i += 2)
    853         {
    854             int j;
    855 
    856             for (j = 0;  j < formatCount;  j++)
    857             {
    858                 if (targets[i] == formats[j])
    859                     break;
    860             }
    861 
    862             if (j < formatCount)
    863             {
    864                 XChangeProperty(_glfw.x11.display,
    865                                 request->requestor,
    866                                 targets[i + 1],
    867                                 targets[i],
    868                                 8,
    869                                 PropModeReplace,
    870                                 (unsigned char *) selectionString,
    871                                 strlen(selectionString));
    872             }
    873             else
    874                 targets[i + 1] = None;
    875         }
    876 
    877         XChangeProperty(_glfw.x11.display,
    878                         request->requestor,
    879                         request->property,
    880                         _glfw.x11.ATOM_PAIR,
    881                         32,
    882                         PropModeReplace,
    883                         (unsigned char*) targets,
    884                         count);
    885 
    886         XFree(targets);
    887 
    888         return request->property;
    889     }
    890 
    891     if (request->target == _glfw.x11.SAVE_TARGETS)
    892     {
    893         // The request is a check whether we support SAVE_TARGETS
    894         // It should be handled as a no-op side effect target
    895 
    896         XChangeProperty(_glfw.x11.display,
    897                         request->requestor,
    898                         request->property,
    899                         _glfw.x11.NULL_,
    900                         32,
    901                         PropModeReplace,
    902                         NULL,
    903                         0);
    904 
    905         return request->property;
    906     }
    907 
    908     // Conversion to a data target was requested
    909 
    910     for (i = 0;  i < formatCount;  i++)
    911     {
    912         if (request->target == formats[i])
    913         {
    914             // The requested target is one we support
    915 
    916             XChangeProperty(_glfw.x11.display,
    917                             request->requestor,
    918                             request->property,
    919                             request->target,
    920                             8,
    921                             PropModeReplace,
    922                             (unsigned char *) selectionString,
    923                             strlen(selectionString));
    924 
    925             return request->property;
    926         }
    927     }
    928 
    929     // The requested target is not supported
    930 
    931     return None;
    932 }
    933 
    934 static void handleSelectionClear(XEvent* event)
    935 {
    936     if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
    937     {
    938         free(_glfw.x11.primarySelectionString);
    939         _glfw.x11.primarySelectionString = NULL;
    940     }
    941     else
    942     {
    943         free(_glfw.x11.clipboardString);
    944         _glfw.x11.clipboardString = NULL;
    945     }
    946 }
    947 
    948 static void handleSelectionRequest(XEvent* event)
    949 {
    950     const XSelectionRequestEvent* request = &event->xselectionrequest;
    951 
    952     XEvent reply = { SelectionNotify };
    953     reply.xselection.property = writeTargetToProperty(request);
    954     reply.xselection.display = request->display;
    955     reply.xselection.requestor = request->requestor;
    956     reply.xselection.selection = request->selection;
    957     reply.xselection.target = request->target;
    958     reply.xselection.time = request->time;
    959 
    960     XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
    961 }
    962 
    963 static const char* getSelectionString(Atom selection)
    964 {
    965     char** selectionString = NULL;
    966     const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING };
    967     const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
    968 
    969     if (selection == _glfw.x11.PRIMARY)
    970         selectionString = &_glfw.x11.primarySelectionString;
    971     else
    972         selectionString = &_glfw.x11.clipboardString;
    973 
    974     if (XGetSelectionOwner(_glfw.x11.display, selection) ==
    975         _glfw.x11.helperWindowHandle)
    976     {
    977         // Instead of doing a large number of X round-trips just to put this
    978         // string into a window property and then read it back, just return it
    979         return *selectionString;
    980     }
    981 
    982     free(*selectionString);
    983     *selectionString = NULL;
    984 
    985     for (size_t i = 0;  i < targetCount;  i++)
    986     {
    987         char* data;
    988         Atom actualType;
    989         int actualFormat;
    990         unsigned long itemCount, bytesAfter;
    991         XEvent notification, dummy;
    992 
    993         XConvertSelection(_glfw.x11.display,
    994                           selection,
    995                           targets[i],
    996                           _glfw.x11.GLFW_SELECTION,
    997                           _glfw.x11.helperWindowHandle,
    998                           CurrentTime);
    999 
   1000         while (!XCheckTypedWindowEvent(_glfw.x11.display,
   1001                                        _glfw.x11.helperWindowHandle,
   1002                                        SelectionNotify,
   1003                                        &notification))
   1004         {
   1005             waitForEvent(NULL);
   1006         }
   1007 
   1008         if (notification.xselection.property == None)
   1009             continue;
   1010 
   1011         XCheckIfEvent(_glfw.x11.display,
   1012                       &dummy,
   1013                       isSelPropNewValueNotify,
   1014                       (XPointer) &notification);
   1015 
   1016         XGetWindowProperty(_glfw.x11.display,
   1017                            notification.xselection.requestor,
   1018                            notification.xselection.property,
   1019                            0,
   1020                            LONG_MAX,
   1021                            True,
   1022                            AnyPropertyType,
   1023                            &actualType,
   1024                            &actualFormat,
   1025                            &itemCount,
   1026                            &bytesAfter,
   1027                            (unsigned char**) &data);
   1028 
   1029         if (actualType == _glfw.x11.INCR)
   1030         {
   1031             size_t size = 1;
   1032             char* string = NULL;
   1033 
   1034             for (;;)
   1035             {
   1036                 while (!XCheckIfEvent(_glfw.x11.display,
   1037                                       &dummy,
   1038                                       isSelPropNewValueNotify,
   1039                                       (XPointer) &notification))
   1040                 {
   1041                     waitForEvent(NULL);
   1042                 }
   1043 
   1044                 XFree(data);
   1045                 XGetWindowProperty(_glfw.x11.display,
   1046                                    notification.xselection.requestor,
   1047                                    notification.xselection.property,
   1048                                    0,
   1049                                    LONG_MAX,
   1050                                    True,
   1051                                    AnyPropertyType,
   1052                                    &actualType,
   1053                                    &actualFormat,
   1054                                    &itemCount,
   1055                                    &bytesAfter,
   1056                                    (unsigned char**) &data);
   1057 
   1058                 if (itemCount)
   1059                 {
   1060                     size += itemCount;
   1061                     string = realloc(string, size);
   1062                     string[size - itemCount - 1] = '\0';
   1063                     strcat(string, data);
   1064                 }
   1065 
   1066                 if (!itemCount)
   1067                 {
   1068                     if (targets[i] == XA_STRING)
   1069                     {
   1070                         *selectionString = convertLatin1toUTF8(string);
   1071                         free(string);
   1072                     }
   1073                     else
   1074                         *selectionString = string;
   1075 
   1076                     break;
   1077                 }
   1078             }
   1079         }
   1080         else if (actualType == targets[i])
   1081         {
   1082             if (targets[i] == XA_STRING)
   1083                 *selectionString = convertLatin1toUTF8(data);
   1084             else
   1085                 *selectionString = _glfw_strdup(data);
   1086         }
   1087 
   1088         XFree(data);
   1089 
   1090         if (*selectionString)
   1091             break;
   1092     }
   1093 
   1094     if (!*selectionString)
   1095     {
   1096         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
   1097                         "X11: Failed to convert selection to string");
   1098     }
   1099 
   1100     return *selectionString;
   1101 }
   1102 
   1103 // Make the specified window and its video mode active on its monitor
   1104 //
   1105 static void acquireMonitor(_GLFWwindow* window)
   1106 {
   1107     if (_glfw.x11.saver.count == 0)
   1108     {
   1109         // Remember old screen saver settings
   1110         XGetScreenSaver(_glfw.x11.display,
   1111                         &_glfw.x11.saver.timeout,
   1112                         &_glfw.x11.saver.interval,
   1113                         &_glfw.x11.saver.blanking,
   1114                         &_glfw.x11.saver.exposure);
   1115 
   1116         // Disable screen saver
   1117         XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
   1118                         DefaultExposures);
   1119     }
   1120 
   1121     if (!window->monitor->window)
   1122         _glfw.x11.saver.count++;
   1123 
   1124     _glfwSetVideoModeX11(window->monitor, &window->videoMode);
   1125 
   1126     if (window->x11.overrideRedirect)
   1127     {
   1128         int xpos, ypos;
   1129         GLFWvidmode mode;
   1130 
   1131         // Manually position the window over its monitor
   1132         _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
   1133         _glfwPlatformGetVideoMode(window->monitor, &mode);
   1134 
   1135         XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   1136                           xpos, ypos, mode.width, mode.height);
   1137     }
   1138 
   1139     _glfwInputMonitorWindow(window->monitor, window);
   1140 }
   1141 
   1142 // Remove the window and restore the original video mode
   1143 //
   1144 static void releaseMonitor(_GLFWwindow* window)
   1145 {
   1146     if (window->monitor->window != window)
   1147         return;
   1148 
   1149     _glfwInputMonitorWindow(window->monitor, NULL);
   1150     _glfwRestoreVideoModeX11(window->monitor);
   1151 
   1152     _glfw.x11.saver.count--;
   1153 
   1154     if (_glfw.x11.saver.count == 0)
   1155     {
   1156         // Restore old screen saver settings
   1157         XSetScreenSaver(_glfw.x11.display,
   1158                         _glfw.x11.saver.timeout,
   1159                         _glfw.x11.saver.interval,
   1160                         _glfw.x11.saver.blanking,
   1161                         _glfw.x11.saver.exposure);
   1162     }
   1163 }
   1164 
   1165 // Process the specified X event
   1166 //
   1167 static void processEvent(XEvent *event)
   1168 {
   1169     int keycode = 0;
   1170     Bool filtered = False;
   1171 
   1172     // HACK: Save scancode as some IMs clear the field in XFilterEvent
   1173     if (event->type == KeyPress || event->type == KeyRelease)
   1174         keycode = event->xkey.keycode;
   1175 
   1176     if (_glfw.x11.im)
   1177         filtered = XFilterEvent(event, None);
   1178 
   1179     if (_glfw.x11.randr.available)
   1180     {
   1181         if (event->type == _glfw.x11.randr.eventBase + RRNotify)
   1182         {
   1183             XRRUpdateConfiguration(event);
   1184             _glfwPollMonitorsX11();
   1185             return;
   1186         }
   1187     }
   1188 
   1189     if (_glfw.x11.xkb.available)
   1190     {
   1191         if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode)
   1192         {
   1193             if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify &&
   1194                 (((XkbEvent*) event)->state.changed & XkbGroupStateMask))
   1195             {
   1196                 _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group;
   1197             }
   1198 
   1199             return;
   1200         }
   1201     }
   1202 
   1203     if (event->type == GenericEvent)
   1204     {
   1205         if (_glfw.x11.xi.available)
   1206         {
   1207             _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
   1208 
   1209             if (window &&
   1210                 window->rawMouseMotion &&
   1211                 event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
   1212                 XGetEventData(_glfw.x11.display, &event->xcookie) &&
   1213                 event->xcookie.evtype == XI_RawMotion)
   1214             {
   1215                 XIRawEvent* re = event->xcookie.data;
   1216                 if (re->valuators.mask_len)
   1217                 {
   1218                     const double* values = re->raw_values;
   1219                     double xpos = window->virtualCursorPosX;
   1220                     double ypos = window->virtualCursorPosY;
   1221 
   1222                     if (XIMaskIsSet(re->valuators.mask, 0))
   1223                     {
   1224                         xpos += *values;
   1225                         values++;
   1226                     }
   1227 
   1228                     if (XIMaskIsSet(re->valuators.mask, 1))
   1229                         ypos += *values;
   1230 
   1231                     _glfwInputCursorPos(window, xpos, ypos);
   1232                 }
   1233             }
   1234 
   1235             XFreeEventData(_glfw.x11.display, &event->xcookie);
   1236         }
   1237 
   1238         return;
   1239     }
   1240 
   1241     if (event->type == SelectionClear)
   1242     {
   1243         handleSelectionClear(event);
   1244         return;
   1245     }
   1246     else if (event->type == SelectionRequest)
   1247     {
   1248         handleSelectionRequest(event);
   1249         return;
   1250     }
   1251 
   1252     _GLFWwindow* window = NULL;
   1253     if (XFindContext(_glfw.x11.display,
   1254                      event->xany.window,
   1255                      _glfw.x11.context,
   1256                      (XPointer*) &window) != 0)
   1257     {
   1258         // This is an event for a window that has already been destroyed
   1259         return;
   1260     }
   1261 
   1262     switch (event->type)
   1263     {
   1264         case ReparentNotify:
   1265         {
   1266             window->x11.parent = event->xreparent.parent;
   1267             return;
   1268         }
   1269 
   1270         case KeyPress:
   1271         {
   1272             const int key = translateKey(keycode);
   1273             const int mods = translateState(event->xkey.state);
   1274             const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
   1275 
   1276             if (window->x11.ic)
   1277             {
   1278                 // HACK: Do not report the key press events duplicated by XIM
   1279                 //       Duplicate key releases are filtered out implicitly by
   1280                 //       the GLFW key repeat logic in _glfwInputKey
   1281                 //       A timestamp per key is used to handle simultaneous keys
   1282                 // NOTE: Always allow the first event for each key through
   1283                 //       (the server never sends a timestamp of zero)
   1284                 // NOTE: Timestamp difference is compared to handle wrap-around
   1285                 Time diff = event->xkey.time - window->x11.keyPressTimes[keycode];
   1286                 if (diff == event->xkey.time || (diff > 0 && diff < (1 << 31)))
   1287                 {
   1288                     if (keycode)
   1289                         _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
   1290 
   1291                     window->x11.keyPressTimes[keycode] = event->xkey.time;
   1292                 }
   1293 
   1294                 if (!filtered)
   1295                 {
   1296                     int count;
   1297                     Status status;
   1298 #if defined(X_HAVE_UTF8_STRING)
   1299                     char buffer[100];
   1300                     char* chars = buffer;
   1301 
   1302                     count = Xutf8LookupString(window->x11.ic,
   1303                                               &event->xkey,
   1304                                               buffer, sizeof(buffer) - 1,
   1305                                               NULL, &status);
   1306 
   1307                     if (status == XBufferOverflow)
   1308                     {
   1309                         chars = calloc(count + 1, 1);
   1310                         count = Xutf8LookupString(window->x11.ic,
   1311                                                   &event->xkey,
   1312                                                   chars, count,
   1313                                                   NULL, &status);
   1314                     }
   1315 
   1316                     if (status == XLookupChars || status == XLookupBoth)
   1317                     {
   1318                         const char* c = chars;
   1319                         chars[count] = '\0';
   1320                         while (c - chars < count)
   1321                             _glfwInputChar(window, decodeUTF8(&c), mods, plain);
   1322                     }
   1323 #else /*X_HAVE_UTF8_STRING*/
   1324                     wchar_t buffer[16];
   1325                     wchar_t* chars = buffer;
   1326 
   1327                     count = XwcLookupString(window->x11.ic,
   1328                                             &event->xkey,
   1329                                             buffer,
   1330                                             sizeof(buffer) / sizeof(wchar_t),
   1331                                             NULL,
   1332                                             &status);
   1333 
   1334                     if (status == XBufferOverflow)
   1335                     {
   1336                         chars = calloc(count, sizeof(wchar_t));
   1337                         count = XwcLookupString(window->x11.ic,
   1338                                                 &event->xkey,
   1339                                                 chars, count,
   1340                                                 NULL, &status);
   1341                     }
   1342 
   1343                     if (status == XLookupChars || status == XLookupBoth)
   1344                     {
   1345                         int i;
   1346                         for (i = 0;  i < count;  i++)
   1347                             _glfwInputChar(window, chars[i], mods, plain);
   1348                     }
   1349 #endif /*X_HAVE_UTF8_STRING*/
   1350 
   1351                     if (chars != buffer)
   1352                         free(chars);
   1353                 }
   1354             }
   1355             else
   1356             {
   1357                 KeySym keysym;
   1358                 XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
   1359 
   1360                 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
   1361 
   1362                 const long character = _glfwKeySym2Unicode(keysym);
   1363                 if (character != -1)
   1364                     _glfwInputChar(window, character, mods, plain);
   1365             }
   1366 
   1367             return;
   1368         }
   1369 
   1370         case KeyRelease:
   1371         {
   1372             const int key = translateKey(keycode);
   1373             const int mods = translateState(event->xkey.state);
   1374 
   1375             if (!_glfw.x11.xkb.detectable)
   1376             {
   1377                 // HACK: Key repeat events will arrive as KeyRelease/KeyPress
   1378                 //       pairs with similar or identical time stamps
   1379                 //       The key repeat logic in _glfwInputKey expects only key
   1380                 //       presses to repeat, so detect and discard release events
   1381                 if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
   1382                 {
   1383                     XEvent next;
   1384                     XPeekEvent(_glfw.x11.display, &next);
   1385 
   1386                     if (next.type == KeyPress &&
   1387                         next.xkey.window == event->xkey.window &&
   1388                         next.xkey.keycode == keycode)
   1389                     {
   1390                         // HACK: The time of repeat events sometimes doesn't
   1391                         //       match that of the press event, so add an
   1392                         //       epsilon
   1393                         //       Toshiyuki Takahashi can press a button
   1394                         //       16 times per second so it's fairly safe to
   1395                         //       assume that no human is pressing the key 50
   1396                         //       times per second (value is ms)
   1397                         if ((next.xkey.time - event->xkey.time) < 20)
   1398                         {
   1399                             // This is very likely a server-generated key repeat
   1400                             // event, so ignore it
   1401                             return;
   1402                         }
   1403                     }
   1404                 }
   1405             }
   1406 
   1407             _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
   1408             return;
   1409         }
   1410 
   1411         case ButtonPress:
   1412         {
   1413             const int mods = translateState(event->xbutton.state);
   1414 
   1415             if (event->xbutton.button == Button1)
   1416                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
   1417             else if (event->xbutton.button == Button2)
   1418                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
   1419             else if (event->xbutton.button == Button3)
   1420                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
   1421 
   1422             // Modern X provides scroll events as mouse button presses
   1423             else if (event->xbutton.button == Button4)
   1424                 _glfwInputScroll(window, 0.0, 1.0);
   1425             else if (event->xbutton.button == Button5)
   1426                 _glfwInputScroll(window, 0.0, -1.0);
   1427             else if (event->xbutton.button == Button6)
   1428                 _glfwInputScroll(window, 1.0, 0.0);
   1429             else if (event->xbutton.button == Button7)
   1430                 _glfwInputScroll(window, -1.0, 0.0);
   1431 
   1432             else
   1433             {
   1434                 // Additional buttons after 7 are treated as regular buttons
   1435                 // We subtract 4 to fill the gap left by scroll input above
   1436                 _glfwInputMouseClick(window,
   1437                                      event->xbutton.button - Button1 - 4,
   1438                                      GLFW_PRESS,
   1439                                      mods);
   1440             }
   1441 
   1442             return;
   1443         }
   1444 
   1445         case ButtonRelease:
   1446         {
   1447             const int mods = translateState(event->xbutton.state);
   1448 
   1449             if (event->xbutton.button == Button1)
   1450             {
   1451                 _glfwInputMouseClick(window,
   1452                                      GLFW_MOUSE_BUTTON_LEFT,
   1453                                      GLFW_RELEASE,
   1454                                      mods);
   1455             }
   1456             else if (event->xbutton.button == Button2)
   1457             {
   1458                 _glfwInputMouseClick(window,
   1459                                      GLFW_MOUSE_BUTTON_MIDDLE,
   1460                                      GLFW_RELEASE,
   1461                                      mods);
   1462             }
   1463             else if (event->xbutton.button == Button3)
   1464             {
   1465                 _glfwInputMouseClick(window,
   1466                                      GLFW_MOUSE_BUTTON_RIGHT,
   1467                                      GLFW_RELEASE,
   1468                                      mods);
   1469             }
   1470             else if (event->xbutton.button > Button7)
   1471             {
   1472                 // Additional buttons after 7 are treated as regular buttons
   1473                 // We subtract 4 to fill the gap left by scroll input above
   1474                 _glfwInputMouseClick(window,
   1475                                      event->xbutton.button - Button1 - 4,
   1476                                      GLFW_RELEASE,
   1477                                      mods);
   1478             }
   1479 
   1480             return;
   1481         }
   1482 
   1483         case EnterNotify:
   1484         {
   1485             // XEnterWindowEvent is XCrossingEvent
   1486             const int x = event->xcrossing.x;
   1487             const int y = event->xcrossing.y;
   1488 
   1489             // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
   1490             //       ignore the defined cursor for hidden cursor mode
   1491             if (window->cursorMode == GLFW_CURSOR_HIDDEN)
   1492                 updateCursorImage(window);
   1493 
   1494             _glfwInputCursorEnter(window, GLFW_TRUE);
   1495             _glfwInputCursorPos(window, x, y);
   1496 
   1497             window->x11.lastCursorPosX = x;
   1498             window->x11.lastCursorPosY = y;
   1499             return;
   1500         }
   1501 
   1502         case LeaveNotify:
   1503         {
   1504             _glfwInputCursorEnter(window, GLFW_FALSE);
   1505             return;
   1506         }
   1507 
   1508         case MotionNotify:
   1509         {
   1510             const int x = event->xmotion.x;
   1511             const int y = event->xmotion.y;
   1512 
   1513             if (x != window->x11.warpCursorPosX ||
   1514                 y != window->x11.warpCursorPosY)
   1515             {
   1516                 // The cursor was moved by something other than GLFW
   1517 
   1518                 if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1519                 {
   1520                     if (_glfw.x11.disabledCursorWindow != window)
   1521                         return;
   1522                     if (window->rawMouseMotion)
   1523                         return;
   1524 
   1525                     const int dx = x - window->x11.lastCursorPosX;
   1526                     const int dy = y - window->x11.lastCursorPosY;
   1527 
   1528                     _glfwInputCursorPos(window,
   1529                                         window->virtualCursorPosX + dx,
   1530                                         window->virtualCursorPosY + dy);
   1531                 }
   1532                 else
   1533                     _glfwInputCursorPos(window, x, y);
   1534             }
   1535 
   1536             window->x11.lastCursorPosX = x;
   1537             window->x11.lastCursorPosY = y;
   1538             return;
   1539         }
   1540 
   1541         case ConfigureNotify:
   1542         {
   1543             if (event->xconfigure.width != window->x11.width ||
   1544                 event->xconfigure.height != window->x11.height)
   1545             {
   1546                 _glfwInputFramebufferSize(window,
   1547                                           event->xconfigure.width,
   1548                                           event->xconfigure.height);
   1549 
   1550                 _glfwInputWindowSize(window,
   1551                                      event->xconfigure.width,
   1552                                      event->xconfigure.height);
   1553 
   1554                 window->x11.width = event->xconfigure.width;
   1555                 window->x11.height = event->xconfigure.height;
   1556             }
   1557 
   1558             int xpos = event->xconfigure.x;
   1559             int ypos = event->xconfigure.y;
   1560 
   1561             // NOTE: ConfigureNotify events from the server are in local
   1562             //       coordinates, so if we are reparented we need to translate
   1563             //       the position into root (screen) coordinates
   1564             if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
   1565             {
   1566                 _glfwGrabErrorHandlerX11();
   1567 
   1568                 Window dummy;
   1569                 XTranslateCoordinates(_glfw.x11.display,
   1570                                       window->x11.parent,
   1571                                       _glfw.x11.root,
   1572                                       xpos, ypos,
   1573                                       &xpos, &ypos,
   1574                                       &dummy);
   1575 
   1576                 _glfwReleaseErrorHandlerX11();
   1577                 if (_glfw.x11.errorCode == BadWindow)
   1578                     return;
   1579             }
   1580 
   1581             if (xpos != window->x11.xpos || ypos != window->x11.ypos)
   1582             {
   1583                 _glfwInputWindowPos(window, xpos, ypos);
   1584                 window->x11.xpos = xpos;
   1585                 window->x11.ypos = ypos;
   1586             }
   1587 
   1588             return;
   1589         }
   1590 
   1591         case ClientMessage:
   1592         {
   1593             // Custom client message, probably from the window manager
   1594 
   1595             if (filtered)
   1596                 return;
   1597 
   1598             if (event->xclient.message_type == None)
   1599                 return;
   1600 
   1601             if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
   1602             {
   1603                 const Atom protocol = event->xclient.data.l[0];
   1604                 if (protocol == None)
   1605                     return;
   1606 
   1607                 if (protocol == _glfw.x11.WM_DELETE_WINDOW)
   1608                 {
   1609                     // The window manager was asked to close the window, for
   1610                     // example by the user pressing a 'close' window decoration
   1611                     // button
   1612                     _glfwInputWindowCloseRequest(window);
   1613                 }
   1614                 else if (protocol == _glfw.x11.NET_WM_PING)
   1615                 {
   1616                     // The window manager is pinging the application to ensure
   1617                     // it's still responding to events
   1618 
   1619                     XEvent reply = *event;
   1620                     reply.xclient.window = _glfw.x11.root;
   1621 
   1622                     XSendEvent(_glfw.x11.display, _glfw.x11.root,
   1623                                False,
   1624                                SubstructureNotifyMask | SubstructureRedirectMask,
   1625                                &reply);
   1626                 }
   1627             }
   1628             else if (event->xclient.message_type == _glfw.x11.XdndEnter)
   1629             {
   1630                 // A drag operation has entered the window
   1631                 unsigned long i, count;
   1632                 Atom* formats = NULL;
   1633                 const GLFWbool list = event->xclient.data.l[1] & 1;
   1634 
   1635                 _glfw.x11.xdnd.source  = event->xclient.data.l[0];
   1636                 _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
   1637                 _glfw.x11.xdnd.format  = None;
   1638 
   1639                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1640                     return;
   1641 
   1642                 if (list)
   1643                 {
   1644                     count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
   1645                                                       _glfw.x11.XdndTypeList,
   1646                                                       XA_ATOM,
   1647                                                       (unsigned char**) &formats);
   1648                 }
   1649                 else
   1650                 {
   1651                     count = 3;
   1652                     formats = (Atom*) event->xclient.data.l + 2;
   1653                 }
   1654 
   1655                 for (i = 0;  i < count;  i++)
   1656                 {
   1657                     if (formats[i] == _glfw.x11.text_uri_list)
   1658                     {
   1659                         _glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
   1660                         break;
   1661                     }
   1662                 }
   1663 
   1664                 if (list && formats)
   1665                     XFree(formats);
   1666             }
   1667             else if (event->xclient.message_type == _glfw.x11.XdndDrop)
   1668             {
   1669                 // The drag operation has finished by dropping on the window
   1670                 Time time = CurrentTime;
   1671 
   1672                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1673                     return;
   1674 
   1675                 if (_glfw.x11.xdnd.format)
   1676                 {
   1677                     if (_glfw.x11.xdnd.version >= 1)
   1678                         time = event->xclient.data.l[2];
   1679 
   1680                     // Request the chosen format from the source window
   1681                     XConvertSelection(_glfw.x11.display,
   1682                                       _glfw.x11.XdndSelection,
   1683                                       _glfw.x11.xdnd.format,
   1684                                       _glfw.x11.XdndSelection,
   1685                                       window->x11.handle,
   1686                                       time);
   1687                 }
   1688                 else if (_glfw.x11.xdnd.version >= 2)
   1689                 {
   1690                     XEvent reply = { ClientMessage };
   1691                     reply.xclient.window = _glfw.x11.xdnd.source;
   1692                     reply.xclient.message_type = _glfw.x11.XdndFinished;
   1693                     reply.xclient.format = 32;
   1694                     reply.xclient.data.l[0] = window->x11.handle;
   1695                     reply.xclient.data.l[1] = 0; // The drag was rejected
   1696                     reply.xclient.data.l[2] = None;
   1697 
   1698                     XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1699                                False, NoEventMask, &reply);
   1700                     XFlush(_glfw.x11.display);
   1701                 }
   1702             }
   1703             else if (event->xclient.message_type == _glfw.x11.XdndPosition)
   1704             {
   1705                 // The drag operation has moved over the window
   1706                 const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
   1707                 const int yabs = (event->xclient.data.l[2]) & 0xffff;
   1708                 Window dummy;
   1709                 int xpos, ypos;
   1710 
   1711                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1712                     return;
   1713 
   1714                 XTranslateCoordinates(_glfw.x11.display,
   1715                                       _glfw.x11.root,
   1716                                       window->x11.handle,
   1717                                       xabs, yabs,
   1718                                       &xpos, &ypos,
   1719                                       &dummy);
   1720 
   1721                 _glfwInputCursorPos(window, xpos, ypos);
   1722 
   1723                 XEvent reply = { ClientMessage };
   1724                 reply.xclient.window = _glfw.x11.xdnd.source;
   1725                 reply.xclient.message_type = _glfw.x11.XdndStatus;
   1726                 reply.xclient.format = 32;
   1727                 reply.xclient.data.l[0] = window->x11.handle;
   1728                 reply.xclient.data.l[2] = 0; // Specify an empty rectangle
   1729                 reply.xclient.data.l[3] = 0;
   1730 
   1731                 if (_glfw.x11.xdnd.format)
   1732                 {
   1733                     // Reply that we are ready to copy the dragged data
   1734                     reply.xclient.data.l[1] = 1; // Accept with no rectangle
   1735                     if (_glfw.x11.xdnd.version >= 2)
   1736                         reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
   1737                 }
   1738 
   1739                 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1740                            False, NoEventMask, &reply);
   1741                 XFlush(_glfw.x11.display);
   1742             }
   1743 
   1744             return;
   1745         }
   1746 
   1747         case SelectionNotify:
   1748         {
   1749             if (event->xselection.property == _glfw.x11.XdndSelection)
   1750             {
   1751                 // The converted data from the drag operation has arrived
   1752                 char* data;
   1753                 const unsigned long result =
   1754                     _glfwGetWindowPropertyX11(event->xselection.requestor,
   1755                                               event->xselection.property,
   1756                                               event->xselection.target,
   1757                                               (unsigned char**) &data);
   1758 
   1759                 if (result)
   1760                 {
   1761                     int i, count;
   1762                     char** paths = parseUriList(data, &count);
   1763 
   1764                     _glfwInputDrop(window, count, (const char**) paths);
   1765 
   1766                     for (i = 0;  i < count;  i++)
   1767                         free(paths[i]);
   1768                     free(paths);
   1769                 }
   1770 
   1771                 if (data)
   1772                     XFree(data);
   1773 
   1774                 if (_glfw.x11.xdnd.version >= 2)
   1775                 {
   1776                     XEvent reply = { ClientMessage };
   1777                     reply.xclient.window = _glfw.x11.xdnd.source;
   1778                     reply.xclient.message_type = _glfw.x11.XdndFinished;
   1779                     reply.xclient.format = 32;
   1780                     reply.xclient.data.l[0] = window->x11.handle;
   1781                     reply.xclient.data.l[1] = result;
   1782                     reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
   1783 
   1784                     XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1785                                False, NoEventMask, &reply);
   1786                     XFlush(_glfw.x11.display);
   1787                 }
   1788             }
   1789 
   1790             return;
   1791         }
   1792 
   1793         case FocusIn:
   1794         {
   1795             if (event->xfocus.mode == NotifyGrab ||
   1796                 event->xfocus.mode == NotifyUngrab)
   1797             {
   1798                 // Ignore focus events from popup indicator windows, window menu
   1799                 // key chords and window dragging
   1800                 return;
   1801             }
   1802 
   1803             if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1804                 disableCursor(window);
   1805 
   1806             if (window->x11.ic)
   1807                 XSetICFocus(window->x11.ic);
   1808 
   1809             _glfwInputWindowFocus(window, GLFW_TRUE);
   1810             return;
   1811         }
   1812 
   1813         case FocusOut:
   1814         {
   1815             if (event->xfocus.mode == NotifyGrab ||
   1816                 event->xfocus.mode == NotifyUngrab)
   1817             {
   1818                 // Ignore focus events from popup indicator windows, window menu
   1819                 // key chords and window dragging
   1820                 return;
   1821             }
   1822 
   1823             if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1824                 enableCursor(window);
   1825 
   1826             if (window->x11.ic)
   1827                 XUnsetICFocus(window->x11.ic);
   1828 
   1829             if (window->monitor && window->autoIconify)
   1830                 _glfwPlatformIconifyWindow(window);
   1831 
   1832             _glfwInputWindowFocus(window, GLFW_FALSE);
   1833             return;
   1834         }
   1835 
   1836         case Expose:
   1837         {
   1838             _glfwInputWindowDamage(window);
   1839             return;
   1840         }
   1841 
   1842         case PropertyNotify:
   1843         {
   1844             if (event->xproperty.state != PropertyNewValue)
   1845                 return;
   1846 
   1847             if (event->xproperty.atom == _glfw.x11.WM_STATE)
   1848             {
   1849                 const int state = getWindowState(window);
   1850                 if (state != IconicState && state != NormalState)
   1851                     return;
   1852 
   1853                 const GLFWbool iconified = (state == IconicState);
   1854                 if (window->x11.iconified != iconified)
   1855                 {
   1856                     if (window->monitor)
   1857                     {
   1858                         if (iconified)
   1859                             releaseMonitor(window);
   1860                         else
   1861                             acquireMonitor(window);
   1862                     }
   1863 
   1864                     window->x11.iconified = iconified;
   1865                     _glfwInputWindowIconify(window, iconified);
   1866                 }
   1867             }
   1868             else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
   1869             {
   1870                 const GLFWbool maximized = _glfwPlatformWindowMaximized(window);
   1871                 if (window->x11.maximized != maximized)
   1872                 {
   1873                     window->x11.maximized = maximized;
   1874                     _glfwInputWindowMaximize(window, maximized);
   1875                 }
   1876             }
   1877 
   1878             return;
   1879         }
   1880 
   1881         case DestroyNotify:
   1882             return;
   1883     }
   1884 }
   1885 
   1886 
   1887 //////////////////////////////////////////////////////////////////////////
   1888 //////                       GLFW internal API                      //////
   1889 //////////////////////////////////////////////////////////////////////////
   1890 
   1891 // Retrieve a single window property of the specified type
   1892 // Inspired by fghGetWindowProperty from freeglut
   1893 //
   1894 unsigned long _glfwGetWindowPropertyX11(Window window,
   1895                                         Atom property,
   1896                                         Atom type,
   1897                                         unsigned char** value)
   1898 {
   1899     Atom actualType;
   1900     int actualFormat;
   1901     unsigned long itemCount, bytesAfter;
   1902 
   1903     XGetWindowProperty(_glfw.x11.display,
   1904                        window,
   1905                        property,
   1906                        0,
   1907                        LONG_MAX,
   1908                        False,
   1909                        type,
   1910                        &actualType,
   1911                        &actualFormat,
   1912                        &itemCount,
   1913                        &bytesAfter,
   1914                        value);
   1915 
   1916     return itemCount;
   1917 }
   1918 
   1919 GLFWbool _glfwIsVisualTransparentX11(Visual* visual)
   1920 {
   1921     if (!_glfw.x11.xrender.available)
   1922         return GLFW_FALSE;
   1923 
   1924     XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
   1925     return pf && pf->direct.alphaMask;
   1926 }
   1927 
   1928 // Push contents of our selection to clipboard manager
   1929 //
   1930 void _glfwPushSelectionToManagerX11(void)
   1931 {
   1932     XConvertSelection(_glfw.x11.display,
   1933                       _glfw.x11.CLIPBOARD_MANAGER,
   1934                       _glfw.x11.SAVE_TARGETS,
   1935                       None,
   1936                       _glfw.x11.helperWindowHandle,
   1937                       CurrentTime);
   1938 
   1939     for (;;)
   1940     {
   1941         XEvent event;
   1942 
   1943         while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
   1944         {
   1945             switch (event.type)
   1946             {
   1947                 case SelectionRequest:
   1948                     handleSelectionRequest(&event);
   1949                     break;
   1950 
   1951                 case SelectionClear:
   1952                     handleSelectionClear(&event);
   1953                     break;
   1954 
   1955                 case SelectionNotify:
   1956                 {
   1957                     if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
   1958                     {
   1959                         // This means one of two things; either the selection
   1960                         // was not owned, which means there is no clipboard
   1961                         // manager, or the transfer to the clipboard manager has
   1962                         // completed
   1963                         // In either case, it means we are done here
   1964                         return;
   1965                     }
   1966 
   1967                     break;
   1968                 }
   1969             }
   1970         }
   1971 
   1972         waitForEvent(NULL);
   1973     }
   1974 }
   1975 
   1976 
   1977 //////////////////////////////////////////////////////////////////////////
   1978 //////                       GLFW platform API                      //////
   1979 //////////////////////////////////////////////////////////////////////////
   1980 
   1981 int _glfwPlatformCreateWindow(_GLFWwindow* window,
   1982                               const _GLFWwndconfig* wndconfig,
   1983                               const _GLFWctxconfig* ctxconfig,
   1984                               const _GLFWfbconfig* fbconfig)
   1985 {
   1986     Visual* visual = NULL;
   1987     int depth;
   1988 
   1989     if (ctxconfig->client != GLFW_NO_API)
   1990     {
   1991         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
   1992         {
   1993             if (!_glfwInitGLX())
   1994                 return GLFW_FALSE;
   1995             if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
   1996                 return GLFW_FALSE;
   1997         }
   1998         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
   1999         {
   2000             if (!_glfwInitEGL())
   2001                 return GLFW_FALSE;
   2002             if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
   2003                 return GLFW_FALSE;
   2004         }
   2005         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   2006         {
   2007             if (!_glfwInitOSMesa())
   2008                 return GLFW_FALSE;
   2009         }
   2010     }
   2011 
   2012     if (!visual)
   2013     {
   2014         visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
   2015         depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
   2016     }
   2017 
   2018     if (!createNativeWindow(window, wndconfig, visual, depth))
   2019         return GLFW_FALSE;
   2020 
   2021     if (ctxconfig->client != GLFW_NO_API)
   2022     {
   2023         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
   2024         {
   2025             if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
   2026                 return GLFW_FALSE;
   2027         }
   2028         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
   2029         {
   2030             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
   2031                 return GLFW_FALSE;
   2032         }
   2033         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   2034         {
   2035             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
   2036                 return GLFW_FALSE;
   2037         }
   2038     }
   2039 
   2040     if (window->monitor)
   2041     {
   2042         _glfwPlatformShowWindow(window);
   2043         updateWindowMode(window);
   2044         acquireMonitor(window);
   2045     }
   2046 
   2047     XFlush(_glfw.x11.display);
   2048     return GLFW_TRUE;
   2049 }
   2050 
   2051 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
   2052 {
   2053     if (_glfw.x11.disabledCursorWindow == window)
   2054         _glfw.x11.disabledCursorWindow = NULL;
   2055 
   2056     if (window->monitor)
   2057         releaseMonitor(window);
   2058 
   2059     if (window->x11.ic)
   2060     {
   2061         XDestroyIC(window->x11.ic);
   2062         window->x11.ic = NULL;
   2063     }
   2064 
   2065     if (window->context.destroy)
   2066         window->context.destroy(window);
   2067 
   2068     if (window->x11.handle)
   2069     {
   2070         XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
   2071         XUnmapWindow(_glfw.x11.display, window->x11.handle);
   2072         XDestroyWindow(_glfw.x11.display, window->x11.handle);
   2073         window->x11.handle = (Window) 0;
   2074     }
   2075 
   2076     if (window->x11.colormap)
   2077     {
   2078         XFreeColormap(_glfw.x11.display, window->x11.colormap);
   2079         window->x11.colormap = (Colormap) 0;
   2080     }
   2081 
   2082     XFlush(_glfw.x11.display);
   2083 }
   2084 
   2085 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
   2086 {
   2087 #if defined(X_HAVE_UTF8_STRING)
   2088     Xutf8SetWMProperties(_glfw.x11.display,
   2089                          window->x11.handle,
   2090                          title, title,
   2091                          NULL, 0,
   2092                          NULL, NULL, NULL);
   2093 #else
   2094     // This may be a slightly better fallback than using XStoreName and
   2095     // XSetIconName, which always store their arguments using STRING
   2096     XmbSetWMProperties(_glfw.x11.display,
   2097                        window->x11.handle,
   2098                        title, title,
   2099                        NULL, 0,
   2100                        NULL, NULL, NULL);
   2101 #endif
   2102 
   2103     XChangeProperty(_glfw.x11.display,  window->x11.handle,
   2104                     _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
   2105                     PropModeReplace,
   2106                     (unsigned char*) title, strlen(title));
   2107 
   2108     XChangeProperty(_glfw.x11.display,  window->x11.handle,
   2109                     _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
   2110                     PropModeReplace,
   2111                     (unsigned char*) title, strlen(title));
   2112 
   2113     XFlush(_glfw.x11.display);
   2114 }
   2115 
   2116 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
   2117                                 int count, const GLFWimage* images)
   2118 {
   2119     if (count)
   2120     {
   2121         int i, j, longCount = 0;
   2122 
   2123         for (i = 0;  i < count;  i++)
   2124             longCount += 2 + images[i].width * images[i].height;
   2125 
   2126         long* icon = calloc(longCount, sizeof(long));
   2127         long* target = icon;
   2128 
   2129         for (i = 0;  i < count;  i++)
   2130         {
   2131             *target++ = images[i].width;
   2132             *target++ = images[i].height;
   2133 
   2134             for (j = 0;  j < images[i].width * images[i].height;  j++)
   2135             {
   2136                 *target++ = (images[i].pixels[j * 4 + 0] << 16) |
   2137                             (images[i].pixels[j * 4 + 1] <<  8) |
   2138                             (images[i].pixels[j * 4 + 2] <<  0) |
   2139                             (images[i].pixels[j * 4 + 3] << 24);
   2140             }
   2141         }
   2142 
   2143         XChangeProperty(_glfw.x11.display, window->x11.handle,
   2144                         _glfw.x11.NET_WM_ICON,
   2145                         XA_CARDINAL, 32,
   2146                         PropModeReplace,
   2147                         (unsigned char*) icon,
   2148                         longCount);
   2149 
   2150         free(icon);
   2151     }
   2152     else
   2153     {
   2154         XDeleteProperty(_glfw.x11.display, window->x11.handle,
   2155                         _glfw.x11.NET_WM_ICON);
   2156     }
   2157 
   2158     XFlush(_glfw.x11.display);
   2159 }
   2160 
   2161 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
   2162 {
   2163     Window dummy;
   2164     int x, y;
   2165 
   2166     XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
   2167                           0, 0, &x, &y, &dummy);
   2168 
   2169     if (xpos)
   2170         *xpos = x;
   2171     if (ypos)
   2172         *ypos = y;
   2173 }
   2174 
   2175 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
   2176 {
   2177     // HACK: Explicitly setting PPosition to any value causes some WMs, notably
   2178     //       Compiz and Metacity, to honor the position of unmapped windows
   2179     if (!_glfwPlatformWindowVisible(window))
   2180     {
   2181         long supplied;
   2182         XSizeHints* hints = XAllocSizeHints();
   2183 
   2184         if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
   2185         {
   2186             hints->flags |= PPosition;
   2187             hints->x = hints->y = 0;
   2188 
   2189             XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
   2190         }
   2191 
   2192         XFree(hints);
   2193     }
   2194 
   2195     XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
   2196     XFlush(_glfw.x11.display);
   2197 }
   2198 
   2199 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
   2200 {
   2201     XWindowAttributes attribs;
   2202     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
   2203 
   2204     if (width)
   2205         *width = attribs.width;
   2206     if (height)
   2207         *height = attribs.height;
   2208 }
   2209 
   2210 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
   2211 {
   2212     if (window->monitor)
   2213     {
   2214         if (window->monitor->window == window)
   2215             acquireMonitor(window);
   2216     }
   2217     else
   2218     {
   2219         if (!window->resizable)
   2220             updateNormalHints(window, width, height);
   2221 
   2222         XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
   2223     }
   2224 
   2225     XFlush(_glfw.x11.display);
   2226 }
   2227 
   2228 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
   2229                                       int minwidth, int minheight,
   2230                                       int maxwidth, int maxheight)
   2231 {
   2232     int width, height;
   2233     _glfwPlatformGetWindowSize(window, &width, &height);
   2234     updateNormalHints(window, width, height);
   2235     XFlush(_glfw.x11.display);
   2236 }
   2237 
   2238 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
   2239 {
   2240     int width, height;
   2241     _glfwPlatformGetWindowSize(window, &width, &height);
   2242     updateNormalHints(window, width, height);
   2243     XFlush(_glfw.x11.display);
   2244 }
   2245 
   2246 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
   2247 {
   2248     _glfwPlatformGetWindowSize(window, width, height);
   2249 }
   2250 
   2251 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
   2252                                      int* left, int* top,
   2253                                      int* right, int* bottom)
   2254 {
   2255     long* extents = NULL;
   2256 
   2257     if (window->monitor || !window->decorated)
   2258         return;
   2259 
   2260     if (_glfw.x11.NET_FRAME_EXTENTS == None)
   2261         return;
   2262 
   2263     if (!_glfwPlatformWindowVisible(window) &&
   2264         _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
   2265     {
   2266         XEvent event;
   2267         double timeout = 0.5;
   2268 
   2269         // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
   2270         // function before the window is mapped
   2271         sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
   2272                       0, 0, 0, 0, 0);
   2273 
   2274         // HACK: Use a timeout because earlier versions of some window managers
   2275         //       (at least Unity, Fluxbox and Xfwm) failed to send the reply
   2276         //       They have been fixed but broken versions are still in the wild
   2277         //       If you are affected by this and your window manager is NOT
   2278         //       listed above, PLEASE report it to their and our issue trackers
   2279         while (!XCheckIfEvent(_glfw.x11.display,
   2280                               &event,
   2281                               isFrameExtentsEvent,
   2282                               (XPointer) window))
   2283         {
   2284             if (!waitForEvent(&timeout))
   2285             {
   2286                 _glfwInputError(GLFW_PLATFORM_ERROR,
   2287                                 "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
   2288                 return;
   2289             }
   2290         }
   2291     }
   2292 
   2293     if (_glfwGetWindowPropertyX11(window->x11.handle,
   2294                                   _glfw.x11.NET_FRAME_EXTENTS,
   2295                                   XA_CARDINAL,
   2296                                   (unsigned char**) &extents) == 4)
   2297     {
   2298         if (left)
   2299             *left = extents[0];
   2300         if (top)
   2301             *top = extents[2];
   2302         if (right)
   2303             *right = extents[1];
   2304         if (bottom)
   2305             *bottom = extents[3];
   2306     }
   2307 
   2308     if (extents)
   2309         XFree(extents);
   2310 }
   2311 
   2312 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
   2313                                         float* xscale, float* yscale)
   2314 {
   2315     if (xscale)
   2316         *xscale = _glfw.x11.contentScaleX;
   2317     if (yscale)
   2318         *yscale = _glfw.x11.contentScaleY;
   2319 }
   2320 
   2321 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
   2322 {
   2323     if (window->x11.overrideRedirect)
   2324     {
   2325         // Override-redirect windows cannot be iconified or restored, as those
   2326         // tasks are performed by the window manager
   2327         _glfwInputError(GLFW_PLATFORM_ERROR,
   2328                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
   2329         return;
   2330     }
   2331 
   2332     XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
   2333     XFlush(_glfw.x11.display);
   2334 }
   2335 
   2336 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
   2337 {
   2338     if (window->x11.overrideRedirect)
   2339     {
   2340         // Override-redirect windows cannot be iconified or restored, as those
   2341         // tasks are performed by the window manager
   2342         _glfwInputError(GLFW_PLATFORM_ERROR,
   2343                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
   2344         return;
   2345     }
   2346 
   2347     if (_glfwPlatformWindowIconified(window))
   2348     {
   2349         XMapWindow(_glfw.x11.display, window->x11.handle);
   2350         waitForVisibilityNotify(window);
   2351     }
   2352     else if (_glfwPlatformWindowVisible(window))
   2353     {
   2354         if (_glfw.x11.NET_WM_STATE &&
   2355             _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
   2356             _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2357         {
   2358             sendEventToWM(window,
   2359                           _glfw.x11.NET_WM_STATE,
   2360                           _NET_WM_STATE_REMOVE,
   2361                           _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2362                           _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
   2363                           1, 0);
   2364         }
   2365     }
   2366 
   2367     XFlush(_glfw.x11.display);
   2368 }
   2369 
   2370 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
   2371 {
   2372     if (!_glfw.x11.NET_WM_STATE ||
   2373         !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2374         !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2375     {
   2376         return;
   2377     }
   2378 
   2379     if (_glfwPlatformWindowVisible(window))
   2380     {
   2381         sendEventToWM(window,
   2382                     _glfw.x11.NET_WM_STATE,
   2383                     _NET_WM_STATE_ADD,
   2384                     _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2385                     _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
   2386                     1, 0);
   2387     }
   2388     else
   2389     {
   2390         Atom* states = NULL;
   2391         unsigned long count =
   2392             _glfwGetWindowPropertyX11(window->x11.handle,
   2393                                       _glfw.x11.NET_WM_STATE,
   2394                                       XA_ATOM,
   2395                                       (unsigned char**) &states);
   2396 
   2397         // NOTE: We don't check for failure as this property may not exist yet
   2398         //       and that's fine (and we'll create it implicitly with append)
   2399 
   2400         Atom missing[2] =
   2401         {
   2402             _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2403             _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
   2404         };
   2405         unsigned long missingCount = 2;
   2406 
   2407         for (unsigned long i = 0;  i < count;  i++)
   2408         {
   2409             for (unsigned long j = 0;  j < missingCount;  j++)
   2410             {
   2411                 if (states[i] == missing[j])
   2412                 {
   2413                     missing[j] = missing[missingCount - 1];
   2414                     missingCount--;
   2415                 }
   2416             }
   2417         }
   2418 
   2419         if (states)
   2420             XFree(states);
   2421 
   2422         if (!missingCount)
   2423             return;
   2424 
   2425         XChangeProperty(_glfw.x11.display, window->x11.handle,
   2426                         _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2427                         PropModeAppend,
   2428                         (unsigned char*) missing,
   2429                         missingCount);
   2430     }
   2431 
   2432     XFlush(_glfw.x11.display);
   2433 }
   2434 
   2435 void _glfwPlatformShowWindow(_GLFWwindow* window)
   2436 {
   2437     if (_glfwPlatformWindowVisible(window))
   2438         return;
   2439 
   2440     XMapWindow(_glfw.x11.display, window->x11.handle);
   2441     waitForVisibilityNotify(window);
   2442 }
   2443 
   2444 void _glfwPlatformHideWindow(_GLFWwindow* window)
   2445 {
   2446     XUnmapWindow(_glfw.x11.display, window->x11.handle);
   2447     XFlush(_glfw.x11.display);
   2448 }
   2449 
   2450 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
   2451 {
   2452     if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
   2453         return;
   2454 
   2455     sendEventToWM(window,
   2456                   _glfw.x11.NET_WM_STATE,
   2457                   _NET_WM_STATE_ADD,
   2458                   _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
   2459                   0, 1, 0);
   2460 }
   2461 
   2462 void _glfwPlatformFocusWindow(_GLFWwindow* window)
   2463 {
   2464     if (_glfw.x11.NET_ACTIVE_WINDOW)
   2465         sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
   2466     else if (_glfwPlatformWindowVisible(window))
   2467     {
   2468         XRaiseWindow(_glfw.x11.display, window->x11.handle);
   2469         XSetInputFocus(_glfw.x11.display, window->x11.handle,
   2470                        RevertToParent, CurrentTime);
   2471     }
   2472 
   2473     XFlush(_glfw.x11.display);
   2474 }
   2475 
   2476 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
   2477                                    _GLFWmonitor* monitor,
   2478                                    int xpos, int ypos,
   2479                                    int width, int height,
   2480                                    int refreshRate)
   2481 {
   2482     if (window->monitor == monitor)
   2483     {
   2484         if (monitor)
   2485         {
   2486             if (monitor->window == window)
   2487                 acquireMonitor(window);
   2488         }
   2489         else
   2490         {
   2491             if (!window->resizable)
   2492                 updateNormalHints(window, width, height);
   2493 
   2494             XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   2495                               xpos, ypos, width, height);
   2496         }
   2497 
   2498         XFlush(_glfw.x11.display);
   2499         return;
   2500     }
   2501 
   2502     if (window->monitor)
   2503     {
   2504         _glfwPlatformSetWindowDecorated(window, window->decorated);
   2505         _glfwPlatformSetWindowFloating(window, window->floating);
   2506         releaseMonitor(window);
   2507     }
   2508 
   2509     _glfwInputWindowMonitor(window, monitor);
   2510     updateNormalHints(window, width, height);
   2511 
   2512     if (window->monitor)
   2513     {
   2514         if (!_glfwPlatformWindowVisible(window))
   2515         {
   2516             XMapRaised(_glfw.x11.display, window->x11.handle);
   2517             waitForVisibilityNotify(window);
   2518         }
   2519 
   2520         updateWindowMode(window);
   2521         acquireMonitor(window);
   2522     }
   2523     else
   2524     {
   2525         updateWindowMode(window);
   2526         XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   2527                           xpos, ypos, width, height);
   2528     }
   2529 
   2530     XFlush(_glfw.x11.display);
   2531 }
   2532 
   2533 int _glfwPlatformWindowFocused(_GLFWwindow* window)
   2534 {
   2535     Window focused;
   2536     int state;
   2537 
   2538     XGetInputFocus(_glfw.x11.display, &focused, &state);
   2539     return window->x11.handle == focused;
   2540 }
   2541 
   2542 int _glfwPlatformWindowIconified(_GLFWwindow* window)
   2543 {
   2544     return getWindowState(window) == IconicState;
   2545 }
   2546 
   2547 int _glfwPlatformWindowVisible(_GLFWwindow* window)
   2548 {
   2549     XWindowAttributes wa;
   2550     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
   2551     return wa.map_state == IsViewable;
   2552 }
   2553 
   2554 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
   2555 {
   2556     Atom* states;
   2557     unsigned long i;
   2558     GLFWbool maximized = GLFW_FALSE;
   2559 
   2560     if (!_glfw.x11.NET_WM_STATE ||
   2561         !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2562         !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2563     {
   2564         return maximized;
   2565     }
   2566 
   2567     const unsigned long count =
   2568         _glfwGetWindowPropertyX11(window->x11.handle,
   2569                                   _glfw.x11.NET_WM_STATE,
   2570                                   XA_ATOM,
   2571                                   (unsigned char**) &states);
   2572 
   2573     for (i = 0;  i < count;  i++)
   2574     {
   2575         if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2576             states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2577         {
   2578             maximized = GLFW_TRUE;
   2579             break;
   2580         }
   2581     }
   2582 
   2583     if (states)
   2584         XFree(states);
   2585 
   2586     return maximized;
   2587 }
   2588 
   2589 int _glfwPlatformWindowHovered(_GLFWwindow* window)
   2590 {
   2591     Window w = _glfw.x11.root;
   2592     while (w)
   2593     {
   2594         Window root;
   2595         int rootX, rootY, childX, childY;
   2596         unsigned int mask;
   2597 
   2598         _glfwGrabErrorHandlerX11();
   2599 
   2600         const Bool result = XQueryPointer(_glfw.x11.display, w,
   2601                                           &root, &w, &rootX, &rootY,
   2602                                           &childX, &childY, &mask);
   2603 
   2604         _glfwReleaseErrorHandlerX11();
   2605 
   2606         if (_glfw.x11.errorCode == BadWindow)
   2607             w = _glfw.x11.root;
   2608         else if (!result)
   2609             return GLFW_FALSE;
   2610         else if (w == window->x11.handle)
   2611             return GLFW_TRUE;
   2612     }
   2613 
   2614     return GLFW_FALSE;
   2615 }
   2616 
   2617 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
   2618 {
   2619     if (!window->x11.transparent)
   2620         return GLFW_FALSE;
   2621 
   2622     return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
   2623 }
   2624 
   2625 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
   2626 {
   2627     int width, height;
   2628     _glfwPlatformGetWindowSize(window, &width, &height);
   2629     updateNormalHints(window, width, height);
   2630 }
   2631 
   2632 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
   2633 {
   2634     struct
   2635     {
   2636         unsigned long flags;
   2637         unsigned long functions;
   2638         unsigned long decorations;
   2639         long input_mode;
   2640         unsigned long status;
   2641     } hints = {0};
   2642 
   2643     hints.flags = MWM_HINTS_DECORATIONS;
   2644     hints.decorations = enabled ? MWM_DECOR_ALL : 0;
   2645 
   2646     XChangeProperty(_glfw.x11.display, window->x11.handle,
   2647                     _glfw.x11.MOTIF_WM_HINTS,
   2648                     _glfw.x11.MOTIF_WM_HINTS, 32,
   2649                     PropModeReplace,
   2650                     (unsigned char*) &hints,
   2651                     sizeof(hints) / sizeof(long));
   2652 }
   2653 
   2654 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
   2655 {
   2656     if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
   2657         return;
   2658 
   2659     if (_glfwPlatformWindowVisible(window))
   2660     {
   2661         const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
   2662         sendEventToWM(window,
   2663                       _glfw.x11.NET_WM_STATE,
   2664                       action,
   2665                       _glfw.x11.NET_WM_STATE_ABOVE,
   2666                       0, 1, 0);
   2667     }
   2668     else
   2669     {
   2670         Atom* states = NULL;
   2671         unsigned long i, count;
   2672 
   2673         count = _glfwGetWindowPropertyX11(window->x11.handle,
   2674                                           _glfw.x11.NET_WM_STATE,
   2675                                           XA_ATOM,
   2676                                           (unsigned char**) &states);
   2677 
   2678         // NOTE: We don't check for failure as this property may not exist yet
   2679         //       and that's fine (and we'll create it implicitly with append)
   2680 
   2681         if (enabled)
   2682         {
   2683             for (i = 0;  i < count;  i++)
   2684             {
   2685                 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
   2686                     break;
   2687             }
   2688 
   2689             if (i < count)
   2690                 return;
   2691 
   2692             XChangeProperty(_glfw.x11.display, window->x11.handle,
   2693                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2694                             PropModeAppend,
   2695                             (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
   2696                             1);
   2697         }
   2698         else if (states)
   2699         {
   2700             for (i = 0;  i < count;  i++)
   2701             {
   2702                 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
   2703                     break;
   2704             }
   2705 
   2706             if (i == count)
   2707                 return;
   2708 
   2709             states[i] = states[count - 1];
   2710             count--;
   2711 
   2712             XChangeProperty(_glfw.x11.display, window->x11.handle,
   2713                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2714                             PropModeReplace, (unsigned char*) states, count);
   2715         }
   2716 
   2717         if (states)
   2718             XFree(states);
   2719     }
   2720 
   2721     XFlush(_glfw.x11.display);
   2722 }
   2723 
   2724 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
   2725 {
   2726     float opacity = 1.f;
   2727 
   2728     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
   2729     {
   2730         CARD32* value = NULL;
   2731 
   2732         if (_glfwGetWindowPropertyX11(window->x11.handle,
   2733                                       _glfw.x11.NET_WM_WINDOW_OPACITY,
   2734                                       XA_CARDINAL,
   2735                                       (unsigned char**) &value))
   2736         {
   2737             opacity = (float) (*value / (double) 0xffffffffu);
   2738         }
   2739 
   2740         if (value)
   2741             XFree(value);
   2742     }
   2743 
   2744     return opacity;
   2745 }
   2746 
   2747 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
   2748 {
   2749     const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
   2750     XChangeProperty(_glfw.x11.display, window->x11.handle,
   2751                     _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
   2752                     PropModeReplace, (unsigned char*) &value, 1);
   2753 }
   2754 
   2755 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
   2756 {
   2757     if (!_glfw.x11.xi.available)
   2758         return;
   2759 
   2760     if (_glfw.x11.disabledCursorWindow != window)
   2761         return;
   2762 
   2763     if (enabled)
   2764         enableRawMouseMotion(window);
   2765     else
   2766         disableRawMouseMotion(window);
   2767 }
   2768 
   2769 GLFWbool _glfwPlatformRawMouseMotionSupported(void)
   2770 {
   2771     return _glfw.x11.xi.available;
   2772 }
   2773 
   2774 void _glfwPlatformPollEvents(void)
   2775 {
   2776     _GLFWwindow* window;
   2777 
   2778 #if defined(__linux__)
   2779     _glfwDetectJoystickConnectionLinux();
   2780 #endif
   2781     XPending(_glfw.x11.display);
   2782 
   2783     while (XQLength(_glfw.x11.display))
   2784     {
   2785         XEvent event;
   2786         XNextEvent(_glfw.x11.display, &event);
   2787         processEvent(&event);
   2788     }
   2789 
   2790     window = _glfw.x11.disabledCursorWindow;
   2791     if (window)
   2792     {
   2793         int width, height;
   2794         _glfwPlatformGetWindowSize(window, &width, &height);
   2795 
   2796         // NOTE: Re-center the cursor only if it has moved since the last call,
   2797         //       to avoid breaking glfwWaitEvents with MotionNotify
   2798         if (window->x11.lastCursorPosX != width / 2 ||
   2799             window->x11.lastCursorPosY != height / 2)
   2800         {
   2801             _glfwPlatformSetCursorPos(window, width / 2, height / 2);
   2802         }
   2803     }
   2804 
   2805     XFlush(_glfw.x11.display);
   2806 }
   2807 
   2808 void _glfwPlatformWaitEvents(void)
   2809 {
   2810     while (!XPending(_glfw.x11.display))
   2811         waitForEvent(NULL);
   2812 
   2813     _glfwPlatformPollEvents();
   2814 }
   2815 
   2816 void _glfwPlatformWaitEventsTimeout(double timeout)
   2817 {
   2818     while (!XPending(_glfw.x11.display))
   2819     {
   2820         if (!waitForEvent(&timeout))
   2821             break;
   2822     }
   2823 
   2824     _glfwPlatformPollEvents();
   2825 }
   2826 
   2827 void _glfwPlatformPostEmptyEvent(void)
   2828 {
   2829     XEvent event = { ClientMessage };
   2830     event.xclient.window = _glfw.x11.helperWindowHandle;
   2831     event.xclient.format = 32; // Data is 32-bit longs
   2832     event.xclient.message_type = _glfw.x11.NULL_;
   2833 
   2834     XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event);
   2835     XFlush(_glfw.x11.display);
   2836 }
   2837 
   2838 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
   2839 {
   2840     Window root, child;
   2841     int rootX, rootY, childX, childY;
   2842     unsigned int mask;
   2843 
   2844     XQueryPointer(_glfw.x11.display, window->x11.handle,
   2845                   &root, &child,
   2846                   &rootX, &rootY, &childX, &childY,
   2847                   &mask);
   2848 
   2849     if (xpos)
   2850         *xpos = childX;
   2851     if (ypos)
   2852         *ypos = childY;
   2853 }
   2854 
   2855 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
   2856 {
   2857     // Store the new position so it can be recognized later
   2858     window->x11.warpCursorPosX = (int) x;
   2859     window->x11.warpCursorPosY = (int) y;
   2860 
   2861     XWarpPointer(_glfw.x11.display, None, window->x11.handle,
   2862                  0,0,0,0, (int) x, (int) y);
   2863     XFlush(_glfw.x11.display);
   2864 }
   2865 
   2866 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
   2867 {
   2868     if (mode == GLFW_CURSOR_DISABLED)
   2869     {
   2870         if (_glfwPlatformWindowFocused(window))
   2871             disableCursor(window);
   2872     }
   2873     else if (_glfw.x11.disabledCursorWindow == window)
   2874         enableCursor(window);
   2875     else
   2876         updateCursorImage(window);
   2877 
   2878     XFlush(_glfw.x11.display);
   2879 }
   2880 
   2881 const char* _glfwPlatformGetScancodeName(int scancode)
   2882 {
   2883     if (!_glfw.x11.xkb.available)
   2884         return NULL;
   2885 
   2886     if (scancode < 0 || scancode > 0xff ||
   2887         _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN)
   2888     {
   2889         _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
   2890         return NULL;
   2891     }
   2892 
   2893     const int key = _glfw.x11.keycodes[scancode];
   2894     const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display,
   2895                                              scancode, _glfw.x11.xkb.group, 0);
   2896     if (keysym == NoSymbol)
   2897         return NULL;
   2898 
   2899     const long ch = _glfwKeySym2Unicode(keysym);
   2900     if (ch == -1)
   2901         return NULL;
   2902 
   2903     const size_t count = encodeUTF8(_glfw.x11.keynames[key], (unsigned int) ch);
   2904     if (count == 0)
   2905         return NULL;
   2906 
   2907     _glfw.x11.keynames[key][count] = '\0';
   2908     return _glfw.x11.keynames[key];
   2909 }
   2910 
   2911 int _glfwPlatformGetKeyScancode(int key)
   2912 {
   2913     return _glfw.x11.scancodes[key];
   2914 }
   2915 
   2916 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
   2917                               const GLFWimage* image,
   2918                               int xhot, int yhot)
   2919 {
   2920     cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
   2921     if (!cursor->x11.handle)
   2922         return GLFW_FALSE;
   2923 
   2924     return GLFW_TRUE;
   2925 }
   2926 
   2927 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
   2928 {
   2929     int native = 0;
   2930 
   2931     if (shape == GLFW_ARROW_CURSOR)
   2932         native = XC_left_ptr;
   2933     else if (shape == GLFW_IBEAM_CURSOR)
   2934         native = XC_xterm;
   2935     else if (shape == GLFW_CROSSHAIR_CURSOR)
   2936         native = XC_crosshair;
   2937     else if (shape == GLFW_HAND_CURSOR)
   2938         native = XC_hand2;
   2939     else if (shape == GLFW_HRESIZE_CURSOR)
   2940         native = XC_sb_h_double_arrow;
   2941     else if (shape == GLFW_VRESIZE_CURSOR)
   2942         native = XC_sb_v_double_arrow;
   2943     else
   2944         return GLFW_FALSE;
   2945 
   2946     cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
   2947     if (!cursor->x11.handle)
   2948     {
   2949         _glfwInputError(GLFW_PLATFORM_ERROR,
   2950                         "X11: Failed to create standard cursor");
   2951         return GLFW_FALSE;
   2952     }
   2953 
   2954     return GLFW_TRUE;
   2955 }
   2956 
   2957 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
   2958 {
   2959     if (cursor->x11.handle)
   2960         XFreeCursor(_glfw.x11.display, cursor->x11.handle);
   2961 }
   2962 
   2963 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
   2964 {
   2965     if (window->cursorMode == GLFW_CURSOR_NORMAL)
   2966     {
   2967         updateCursorImage(window);
   2968         XFlush(_glfw.x11.display);
   2969     }
   2970 }
   2971 
   2972 void _glfwPlatformSetClipboardString(const char* string)
   2973 {
   2974     char* copy = _glfw_strdup(string);
   2975     free(_glfw.x11.clipboardString);
   2976     _glfw.x11.clipboardString = copy;
   2977 
   2978     XSetSelectionOwner(_glfw.x11.display,
   2979                        _glfw.x11.CLIPBOARD,
   2980                        _glfw.x11.helperWindowHandle,
   2981                        CurrentTime);
   2982 
   2983     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
   2984         _glfw.x11.helperWindowHandle)
   2985     {
   2986         _glfwInputError(GLFW_PLATFORM_ERROR,
   2987                         "X11: Failed to become owner of clipboard selection");
   2988     }
   2989 }
   2990 
   2991 const char* _glfwPlatformGetClipboardString(void)
   2992 {
   2993     return getSelectionString(_glfw.x11.CLIPBOARD);
   2994 }
   2995 
   2996 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
   2997 {
   2998     if (!_glfw.vk.KHR_surface)
   2999         return;
   3000 
   3001     if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
   3002     {
   3003         if (!_glfw.vk.KHR_xlib_surface)
   3004             return;
   3005     }
   3006 
   3007     extensions[0] = "VK_KHR_surface";
   3008 
   3009     // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
   3010     //       not correctly implementing VK_KHR_xlib_surface
   3011     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   3012         extensions[1] = "VK_KHR_xcb_surface";
   3013     else
   3014         extensions[1] = "VK_KHR_xlib_surface";
   3015 }
   3016 
   3017 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
   3018                                                       VkPhysicalDevice device,
   3019                                                       uint32_t queuefamily)
   3020 {
   3021     VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
   3022                                                           _glfw.x11.screen));
   3023 
   3024     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   3025     {
   3026         PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
   3027             vkGetPhysicalDeviceXcbPresentationSupportKHR =
   3028             (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
   3029             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
   3030         if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
   3031         {
   3032             _glfwInputError(GLFW_API_UNAVAILABLE,
   3033                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
   3034             return GLFW_FALSE;
   3035         }
   3036 
   3037         xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
   3038         if (!connection)
   3039         {
   3040             _glfwInputError(GLFW_PLATFORM_ERROR,
   3041                             "X11: Failed to retrieve XCB connection");
   3042             return GLFW_FALSE;
   3043         }
   3044 
   3045         return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
   3046                                                             queuefamily,
   3047                                                             connection,
   3048                                                             visualID);
   3049     }
   3050     else
   3051     {
   3052         PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
   3053             vkGetPhysicalDeviceXlibPresentationSupportKHR =
   3054             (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
   3055             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
   3056         if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
   3057         {
   3058             _glfwInputError(GLFW_API_UNAVAILABLE,
   3059                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
   3060             return GLFW_FALSE;
   3061         }
   3062 
   3063         return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
   3064                                                              queuefamily,
   3065                                                              _glfw.x11.display,
   3066                                                              visualID);
   3067     }
   3068 }
   3069 
   3070 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
   3071                                           _GLFWwindow* window,
   3072                                           const VkAllocationCallbacks* allocator,
   3073                                           VkSurfaceKHR* surface)
   3074 {
   3075     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   3076     {
   3077         VkResult err;
   3078         VkXcbSurfaceCreateInfoKHR sci;
   3079         PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
   3080 
   3081         xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
   3082         if (!connection)
   3083         {
   3084             _glfwInputError(GLFW_PLATFORM_ERROR,
   3085                             "X11: Failed to retrieve XCB connection");
   3086             return VK_ERROR_EXTENSION_NOT_PRESENT;
   3087         }
   3088 
   3089         vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
   3090             vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
   3091         if (!vkCreateXcbSurfaceKHR)
   3092         {
   3093             _glfwInputError(GLFW_API_UNAVAILABLE,
   3094                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
   3095             return VK_ERROR_EXTENSION_NOT_PRESENT;
   3096         }
   3097 
   3098         memset(&sci, 0, sizeof(sci));
   3099         sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
   3100         sci.connection = connection;
   3101         sci.window = window->x11.handle;
   3102 
   3103         err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
   3104         if (err)
   3105         {
   3106             _glfwInputError(GLFW_PLATFORM_ERROR,
   3107                             "X11: Failed to create Vulkan XCB surface: %s",
   3108                             _glfwGetVulkanResultString(err));
   3109         }
   3110 
   3111         return err;
   3112     }
   3113     else
   3114     {
   3115         VkResult err;
   3116         VkXlibSurfaceCreateInfoKHR sci;
   3117         PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
   3118 
   3119         vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
   3120             vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
   3121         if (!vkCreateXlibSurfaceKHR)
   3122         {
   3123             _glfwInputError(GLFW_API_UNAVAILABLE,
   3124                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
   3125             return VK_ERROR_EXTENSION_NOT_PRESENT;
   3126         }
   3127 
   3128         memset(&sci, 0, sizeof(sci));
   3129         sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
   3130         sci.dpy = _glfw.x11.display;
   3131         sci.window = window->x11.handle;
   3132 
   3133         err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
   3134         if (err)
   3135         {
   3136             _glfwInputError(GLFW_PLATFORM_ERROR,
   3137                             "X11: Failed to create Vulkan X11 surface: %s",
   3138                             _glfwGetVulkanResultString(err));
   3139         }
   3140 
   3141         return err;
   3142     }
   3143 }
   3144 
   3145 
   3146 //////////////////////////////////////////////////////////////////////////
   3147 //////                        GLFW native API                       //////
   3148 //////////////////////////////////////////////////////////////////////////
   3149 
   3150 GLFWAPI Display* glfwGetX11Display(void)
   3151 {
   3152     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   3153     return _glfw.x11.display;
   3154 }
   3155 
   3156 GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
   3157 {
   3158     _GLFWwindow* window = (_GLFWwindow*) handle;
   3159     _GLFW_REQUIRE_INIT_OR_RETURN(None);
   3160     return window->x11.handle;
   3161 }
   3162 
   3163 GLFWAPI void glfwSetX11SelectionString(const char* string)
   3164 {
   3165     _GLFW_REQUIRE_INIT();
   3166 
   3167     free(_glfw.x11.primarySelectionString);
   3168     _glfw.x11.primarySelectionString = _glfw_strdup(string);
   3169 
   3170     XSetSelectionOwner(_glfw.x11.display,
   3171                        _glfw.x11.PRIMARY,
   3172                        _glfw.x11.helperWindowHandle,
   3173                        CurrentTime);
   3174 
   3175     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
   3176         _glfw.x11.helperWindowHandle)
   3177     {
   3178         _glfwInputError(GLFW_PLATFORM_ERROR,
   3179                         "X11: Failed to become owner of primary selection");
   3180     }
   3181 }
   3182 
   3183 GLFWAPI const char* glfwGetX11SelectionString(void)
   3184 {
   3185     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   3186     return getSelectionString(_glfw.x11.PRIMARY);
   3187 }
   3188