twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

x11_window.c (100293B)


      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     }
   1200 
   1201     if (event->type == GenericEvent)
   1202     {
   1203         if (_glfw.x11.xi.available)
   1204         {
   1205             _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
   1206 
   1207             if (window &&
   1208                 window->rawMouseMotion &&
   1209                 event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
   1210                 XGetEventData(_glfw.x11.display, &event->xcookie) &&
   1211                 event->xcookie.evtype == XI_RawMotion)
   1212             {
   1213                 XIRawEvent* re = event->xcookie.data;
   1214                 if (re->valuators.mask_len)
   1215                 {
   1216                     const double* values = re->raw_values;
   1217                     double xpos = window->virtualCursorPosX;
   1218                     double ypos = window->virtualCursorPosY;
   1219 
   1220                     if (XIMaskIsSet(re->valuators.mask, 0))
   1221                     {
   1222                         xpos += *values;
   1223                         values++;
   1224                     }
   1225 
   1226                     if (XIMaskIsSet(re->valuators.mask, 1))
   1227                         ypos += *values;
   1228 
   1229                     _glfwInputCursorPos(window, xpos, ypos);
   1230                 }
   1231             }
   1232 
   1233             XFreeEventData(_glfw.x11.display, &event->xcookie);
   1234         }
   1235 
   1236         return;
   1237     }
   1238 
   1239     if (event->type == SelectionClear)
   1240     {
   1241         handleSelectionClear(event);
   1242         return;
   1243     }
   1244     else if (event->type == SelectionRequest)
   1245     {
   1246         handleSelectionRequest(event);
   1247         return;
   1248     }
   1249 
   1250     _GLFWwindow* window = NULL;
   1251     if (XFindContext(_glfw.x11.display,
   1252                      event->xany.window,
   1253                      _glfw.x11.context,
   1254                      (XPointer*) &window) != 0)
   1255     {
   1256         // This is an event for a window that has already been destroyed
   1257         return;
   1258     }
   1259 
   1260     switch (event->type)
   1261     {
   1262         case ReparentNotify:
   1263         {
   1264             window->x11.parent = event->xreparent.parent;
   1265             return;
   1266         }
   1267 
   1268         case KeyPress:
   1269         {
   1270             const int key = translateKey(keycode);
   1271             const int mods = translateState(event->xkey.state);
   1272             const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
   1273 
   1274             if (window->x11.ic)
   1275             {
   1276                 // HACK: Ignore duplicate key press events generated by ibus
   1277                 //       These have the same timestamp as the original event
   1278                 //       Corresponding release events are filtered out
   1279                 //       implicitly by the GLFW key repeat logic
   1280                 if (window->x11.lastKeyTime < event->xkey.time)
   1281                 {
   1282                     if (keycode)
   1283                         _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
   1284 
   1285                     window->x11.lastKeyTime = event->xkey.time;
   1286                 }
   1287 
   1288                 if (!filtered)
   1289                 {
   1290                     int count;
   1291                     Status status;
   1292 #if defined(X_HAVE_UTF8_STRING)
   1293                     char buffer[100];
   1294                     char* chars = buffer;
   1295 
   1296                     count = Xutf8LookupString(window->x11.ic,
   1297                                               &event->xkey,
   1298                                               buffer, sizeof(buffer) - 1,
   1299                                               NULL, &status);
   1300 
   1301                     if (status == XBufferOverflow)
   1302                     {
   1303                         chars = calloc(count + 1, 1);
   1304                         count = Xutf8LookupString(window->x11.ic,
   1305                                                   &event->xkey,
   1306                                                   chars, count,
   1307                                                   NULL, &status);
   1308                     }
   1309 
   1310                     if (status == XLookupChars || status == XLookupBoth)
   1311                     {
   1312                         const char* c = chars;
   1313                         chars[count] = '\0';
   1314                         while (c - chars < count)
   1315                             _glfwInputChar(window, decodeUTF8(&c), mods, plain);
   1316                     }
   1317 #else /*X_HAVE_UTF8_STRING*/
   1318                     wchar_t buffer[16];
   1319                     wchar_t* chars = buffer;
   1320 
   1321                     count = XwcLookupString(window->x11.ic,
   1322                                             &event->xkey,
   1323                                             buffer,
   1324                                             sizeof(buffer) / sizeof(wchar_t),
   1325                                             NULL,
   1326                                             &status);
   1327 
   1328                     if (status == XBufferOverflow)
   1329                     {
   1330                         chars = calloc(count, sizeof(wchar_t));
   1331                         count = XwcLookupString(window->x11.ic,
   1332                                                 &event->xkey,
   1333                                                 chars, count,
   1334                                                 NULL, &status);
   1335                     }
   1336 
   1337                     if (status == XLookupChars || status == XLookupBoth)
   1338                     {
   1339                         int i;
   1340                         for (i = 0;  i < count;  i++)
   1341                             _glfwInputChar(window, chars[i], mods, plain);
   1342                     }
   1343 #endif /*X_HAVE_UTF8_STRING*/
   1344 
   1345                     if (chars != buffer)
   1346                         free(chars);
   1347                 }
   1348             }
   1349             else
   1350             {
   1351                 KeySym keysym;
   1352                 XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
   1353 
   1354                 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
   1355 
   1356                 const long character = _glfwKeySym2Unicode(keysym);
   1357                 if (character != -1)
   1358                     _glfwInputChar(window, character, mods, plain);
   1359             }
   1360 
   1361             return;
   1362         }
   1363 
   1364         case KeyRelease:
   1365         {
   1366             const int key = translateKey(keycode);
   1367             const int mods = translateState(event->xkey.state);
   1368 
   1369             if (!_glfw.x11.xkb.detectable)
   1370             {
   1371                 // HACK: Key repeat events will arrive as KeyRelease/KeyPress
   1372                 //       pairs with similar or identical time stamps
   1373                 //       The key repeat logic in _glfwInputKey expects only key
   1374                 //       presses to repeat, so detect and discard release events
   1375                 if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
   1376                 {
   1377                     XEvent next;
   1378                     XPeekEvent(_glfw.x11.display, &next);
   1379 
   1380                     if (next.type == KeyPress &&
   1381                         next.xkey.window == event->xkey.window &&
   1382                         next.xkey.keycode == keycode)
   1383                     {
   1384                         // HACK: The time of repeat events sometimes doesn't
   1385                         //       match that of the press event, so add an
   1386                         //       epsilon
   1387                         //       Toshiyuki Takahashi can press a button
   1388                         //       16 times per second so it's fairly safe to
   1389                         //       assume that no human is pressing the key 50
   1390                         //       times per second (value is ms)
   1391                         if ((next.xkey.time - event->xkey.time) < 20)
   1392                         {
   1393                             // This is very likely a server-generated key repeat
   1394                             // event, so ignore it
   1395                             return;
   1396                         }
   1397                     }
   1398                 }
   1399             }
   1400 
   1401             _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
   1402             return;
   1403         }
   1404 
   1405         case ButtonPress:
   1406         {
   1407             const int mods = translateState(event->xbutton.state);
   1408 
   1409             if (event->xbutton.button == Button1)
   1410                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
   1411             else if (event->xbutton.button == Button2)
   1412                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
   1413             else if (event->xbutton.button == Button3)
   1414                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
   1415 
   1416             // Modern X provides scroll events as mouse button presses
   1417             else if (event->xbutton.button == Button4)
   1418                 _glfwInputScroll(window, 0.0, 1.0);
   1419             else if (event->xbutton.button == Button5)
   1420                 _glfwInputScroll(window, 0.0, -1.0);
   1421             else if (event->xbutton.button == Button6)
   1422                 _glfwInputScroll(window, 1.0, 0.0);
   1423             else if (event->xbutton.button == Button7)
   1424                 _glfwInputScroll(window, -1.0, 0.0);
   1425 
   1426             else
   1427             {
   1428                 // Additional buttons after 7 are treated as regular buttons
   1429                 // We subtract 4 to fill the gap left by scroll input above
   1430                 _glfwInputMouseClick(window,
   1431                                      event->xbutton.button - Button1 - 4,
   1432                                      GLFW_PRESS,
   1433                                      mods);
   1434             }
   1435 
   1436             return;
   1437         }
   1438 
   1439         case ButtonRelease:
   1440         {
   1441             const int mods = translateState(event->xbutton.state);
   1442 
   1443             if (event->xbutton.button == Button1)
   1444             {
   1445                 _glfwInputMouseClick(window,
   1446                                      GLFW_MOUSE_BUTTON_LEFT,
   1447                                      GLFW_RELEASE,
   1448                                      mods);
   1449             }
   1450             else if (event->xbutton.button == Button2)
   1451             {
   1452                 _glfwInputMouseClick(window,
   1453                                      GLFW_MOUSE_BUTTON_MIDDLE,
   1454                                      GLFW_RELEASE,
   1455                                      mods);
   1456             }
   1457             else if (event->xbutton.button == Button3)
   1458             {
   1459                 _glfwInputMouseClick(window,
   1460                                      GLFW_MOUSE_BUTTON_RIGHT,
   1461                                      GLFW_RELEASE,
   1462                                      mods);
   1463             }
   1464             else if (event->xbutton.button > Button7)
   1465             {
   1466                 // Additional buttons after 7 are treated as regular buttons
   1467                 // We subtract 4 to fill the gap left by scroll input above
   1468                 _glfwInputMouseClick(window,
   1469                                      event->xbutton.button - Button1 - 4,
   1470                                      GLFW_RELEASE,
   1471                                      mods);
   1472             }
   1473 
   1474             return;
   1475         }
   1476 
   1477         case EnterNotify:
   1478         {
   1479             // XEnterWindowEvent is XCrossingEvent
   1480             const int x = event->xcrossing.x;
   1481             const int y = event->xcrossing.y;
   1482 
   1483             // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
   1484             //       ignore the defined cursor for hidden cursor mode
   1485             if (window->cursorMode == GLFW_CURSOR_HIDDEN)
   1486                 updateCursorImage(window);
   1487 
   1488             _glfwInputCursorEnter(window, GLFW_TRUE);
   1489             _glfwInputCursorPos(window, x, y);
   1490 
   1491             window->x11.lastCursorPosX = x;
   1492             window->x11.lastCursorPosY = y;
   1493             return;
   1494         }
   1495 
   1496         case LeaveNotify:
   1497         {
   1498             _glfwInputCursorEnter(window, GLFW_FALSE);
   1499             return;
   1500         }
   1501 
   1502         case MotionNotify:
   1503         {
   1504             const int x = event->xmotion.x;
   1505             const int y = event->xmotion.y;
   1506 
   1507             if (x != window->x11.warpCursorPosX ||
   1508                 y != window->x11.warpCursorPosY)
   1509             {
   1510                 // The cursor was moved by something other than GLFW
   1511 
   1512                 if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1513                 {
   1514                     if (_glfw.x11.disabledCursorWindow != window)
   1515                         return;
   1516                     if (window->rawMouseMotion)
   1517                         return;
   1518 
   1519                     const int dx = x - window->x11.lastCursorPosX;
   1520                     const int dy = y - window->x11.lastCursorPosY;
   1521 
   1522                     _glfwInputCursorPos(window,
   1523                                         window->virtualCursorPosX + dx,
   1524                                         window->virtualCursorPosY + dy);
   1525                 }
   1526                 else
   1527                     _glfwInputCursorPos(window, x, y);
   1528             }
   1529 
   1530             window->x11.lastCursorPosX = x;
   1531             window->x11.lastCursorPosY = y;
   1532             return;
   1533         }
   1534 
   1535         case ConfigureNotify:
   1536         {
   1537             if (event->xconfigure.width != window->x11.width ||
   1538                 event->xconfigure.height != window->x11.height)
   1539             {
   1540                 _glfwInputFramebufferSize(window,
   1541                                           event->xconfigure.width,
   1542                                           event->xconfigure.height);
   1543 
   1544                 _glfwInputWindowSize(window,
   1545                                      event->xconfigure.width,
   1546                                      event->xconfigure.height);
   1547 
   1548                 window->x11.width = event->xconfigure.width;
   1549                 window->x11.height = event->xconfigure.height;
   1550             }
   1551 
   1552             int xpos = event->xconfigure.x;
   1553             int ypos = event->xconfigure.y;
   1554 
   1555             // NOTE: ConfigureNotify events from the server are in local
   1556             //       coordinates, so if we are reparented we need to translate
   1557             //       the position into root (screen) coordinates
   1558             if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
   1559             {
   1560                 Window dummy;
   1561                 XTranslateCoordinates(_glfw.x11.display,
   1562                                       window->x11.parent,
   1563                                       _glfw.x11.root,
   1564                                       xpos, ypos,
   1565                                       &xpos, &ypos,
   1566                                       &dummy);
   1567             }
   1568 
   1569             if (xpos != window->x11.xpos || ypos != window->x11.ypos)
   1570             {
   1571                 _glfwInputWindowPos(window, xpos, ypos);
   1572                 window->x11.xpos = xpos;
   1573                 window->x11.ypos = ypos;
   1574             }
   1575 
   1576             return;
   1577         }
   1578 
   1579         case ClientMessage:
   1580         {
   1581             // Custom client message, probably from the window manager
   1582 
   1583             if (filtered)
   1584                 return;
   1585 
   1586             if (event->xclient.message_type == None)
   1587                 return;
   1588 
   1589             if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
   1590             {
   1591                 const Atom protocol = event->xclient.data.l[0];
   1592                 if (protocol == None)
   1593                     return;
   1594 
   1595                 if (protocol == _glfw.x11.WM_DELETE_WINDOW)
   1596                 {
   1597                     // The window manager was asked to close the window, for
   1598                     // example by the user pressing a 'close' window decoration
   1599                     // button
   1600                     _glfwInputWindowCloseRequest(window);
   1601                 }
   1602                 else if (protocol == _glfw.x11.NET_WM_PING)
   1603                 {
   1604                     // The window manager is pinging the application to ensure
   1605                     // it's still responding to events
   1606 
   1607                     XEvent reply = *event;
   1608                     reply.xclient.window = _glfw.x11.root;
   1609 
   1610                     XSendEvent(_glfw.x11.display, _glfw.x11.root,
   1611                                False,
   1612                                SubstructureNotifyMask | SubstructureRedirectMask,
   1613                                &reply);
   1614                 }
   1615             }
   1616             else if (event->xclient.message_type == _glfw.x11.XdndEnter)
   1617             {
   1618                 // A drag operation has entered the window
   1619                 unsigned long i, count;
   1620                 Atom* formats = NULL;
   1621                 const GLFWbool list = event->xclient.data.l[1] & 1;
   1622 
   1623                 _glfw.x11.xdnd.source  = event->xclient.data.l[0];
   1624                 _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
   1625                 _glfw.x11.xdnd.format  = None;
   1626 
   1627                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1628                     return;
   1629 
   1630                 if (list)
   1631                 {
   1632                     count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
   1633                                                       _glfw.x11.XdndTypeList,
   1634                                                       XA_ATOM,
   1635                                                       (unsigned char**) &formats);
   1636                 }
   1637                 else
   1638                 {
   1639                     count = 3;
   1640                     formats = (Atom*) event->xclient.data.l + 2;
   1641                 }
   1642 
   1643                 for (i = 0;  i < count;  i++)
   1644                 {
   1645                     if (formats[i] == _glfw.x11.text_uri_list)
   1646                     {
   1647                         _glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
   1648                         break;
   1649                     }
   1650                 }
   1651 
   1652                 if (list && formats)
   1653                     XFree(formats);
   1654             }
   1655             else if (event->xclient.message_type == _glfw.x11.XdndDrop)
   1656             {
   1657                 // The drag operation has finished by dropping on the window
   1658                 Time time = CurrentTime;
   1659 
   1660                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1661                     return;
   1662 
   1663                 if (_glfw.x11.xdnd.format)
   1664                 {
   1665                     if (_glfw.x11.xdnd.version >= 1)
   1666                         time = event->xclient.data.l[2];
   1667 
   1668                     // Request the chosen format from the source window
   1669                     XConvertSelection(_glfw.x11.display,
   1670                                       _glfw.x11.XdndSelection,
   1671                                       _glfw.x11.xdnd.format,
   1672                                       _glfw.x11.XdndSelection,
   1673                                       window->x11.handle,
   1674                                       time);
   1675                 }
   1676                 else if (_glfw.x11.xdnd.version >= 2)
   1677                 {
   1678                     XEvent reply = { ClientMessage };
   1679                     reply.xclient.window = _glfw.x11.xdnd.source;
   1680                     reply.xclient.message_type = _glfw.x11.XdndFinished;
   1681                     reply.xclient.format = 32;
   1682                     reply.xclient.data.l[0] = window->x11.handle;
   1683                     reply.xclient.data.l[1] = 0; // The drag was rejected
   1684                     reply.xclient.data.l[2] = None;
   1685 
   1686                     XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1687                                False, NoEventMask, &reply);
   1688                     XFlush(_glfw.x11.display);
   1689                 }
   1690             }
   1691             else if (event->xclient.message_type == _glfw.x11.XdndPosition)
   1692             {
   1693                 // The drag operation has moved over the window
   1694                 const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
   1695                 const int yabs = (event->xclient.data.l[2]) & 0xffff;
   1696                 Window dummy;
   1697                 int xpos, ypos;
   1698 
   1699                 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
   1700                     return;
   1701 
   1702                 XTranslateCoordinates(_glfw.x11.display,
   1703                                       _glfw.x11.root,
   1704                                       window->x11.handle,
   1705                                       xabs, yabs,
   1706                                       &xpos, &ypos,
   1707                                       &dummy);
   1708 
   1709                 _glfwInputCursorPos(window, xpos, ypos);
   1710 
   1711                 XEvent reply = { ClientMessage };
   1712                 reply.xclient.window = _glfw.x11.xdnd.source;
   1713                 reply.xclient.message_type = _glfw.x11.XdndStatus;
   1714                 reply.xclient.format = 32;
   1715                 reply.xclient.data.l[0] = window->x11.handle;
   1716                 reply.xclient.data.l[2] = 0; // Specify an empty rectangle
   1717                 reply.xclient.data.l[3] = 0;
   1718 
   1719                 if (_glfw.x11.xdnd.format)
   1720                 {
   1721                     // Reply that we are ready to copy the dragged data
   1722                     reply.xclient.data.l[1] = 1; // Accept with no rectangle
   1723                     if (_glfw.x11.xdnd.version >= 2)
   1724                         reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
   1725                 }
   1726 
   1727                 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1728                            False, NoEventMask, &reply);
   1729                 XFlush(_glfw.x11.display);
   1730             }
   1731 
   1732             return;
   1733         }
   1734 
   1735         case SelectionNotify:
   1736         {
   1737             if (event->xselection.property == _glfw.x11.XdndSelection)
   1738             {
   1739                 // The converted data from the drag operation has arrived
   1740                 char* data;
   1741                 const unsigned long result =
   1742                     _glfwGetWindowPropertyX11(event->xselection.requestor,
   1743                                               event->xselection.property,
   1744                                               event->xselection.target,
   1745                                               (unsigned char**) &data);
   1746 
   1747                 if (result)
   1748                 {
   1749                     int i, count;
   1750                     char** paths = parseUriList(data, &count);
   1751 
   1752                     _glfwInputDrop(window, count, (const char**) paths);
   1753 
   1754                     for (i = 0;  i < count;  i++)
   1755                         free(paths[i]);
   1756                     free(paths);
   1757                 }
   1758 
   1759                 if (data)
   1760                     XFree(data);
   1761 
   1762                 if (_glfw.x11.xdnd.version >= 2)
   1763                 {
   1764                     XEvent reply = { ClientMessage };
   1765                     reply.xclient.window = _glfw.x11.xdnd.source;
   1766                     reply.xclient.message_type = _glfw.x11.XdndFinished;
   1767                     reply.xclient.format = 32;
   1768                     reply.xclient.data.l[0] = window->x11.handle;
   1769                     reply.xclient.data.l[1] = result;
   1770                     reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
   1771 
   1772                     XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
   1773                                False, NoEventMask, &reply);
   1774                     XFlush(_glfw.x11.display);
   1775                 }
   1776             }
   1777 
   1778             return;
   1779         }
   1780 
   1781         case FocusIn:
   1782         {
   1783             if (event->xfocus.mode == NotifyGrab ||
   1784                 event->xfocus.mode == NotifyUngrab)
   1785             {
   1786                 // Ignore focus events from popup indicator windows, window menu
   1787                 // key chords and window dragging
   1788                 return;
   1789             }
   1790 
   1791             if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1792                 disableCursor(window);
   1793 
   1794             if (window->x11.ic)
   1795                 XSetICFocus(window->x11.ic);
   1796 
   1797             _glfwInputWindowFocus(window, GLFW_TRUE);
   1798             return;
   1799         }
   1800 
   1801         case FocusOut:
   1802         {
   1803             if (event->xfocus.mode == NotifyGrab ||
   1804                 event->xfocus.mode == NotifyUngrab)
   1805             {
   1806                 // Ignore focus events from popup indicator windows, window menu
   1807                 // key chords and window dragging
   1808                 return;
   1809             }
   1810 
   1811             if (window->cursorMode == GLFW_CURSOR_DISABLED)
   1812                 enableCursor(window);
   1813 
   1814             if (window->x11.ic)
   1815                 XUnsetICFocus(window->x11.ic);
   1816 
   1817             if (window->monitor && window->autoIconify)
   1818                 _glfwPlatformIconifyWindow(window);
   1819 
   1820             _glfwInputWindowFocus(window, GLFW_FALSE);
   1821             return;
   1822         }
   1823 
   1824         case Expose:
   1825         {
   1826             _glfwInputWindowDamage(window);
   1827             return;
   1828         }
   1829 
   1830         case PropertyNotify:
   1831         {
   1832             if (event->xproperty.state != PropertyNewValue)
   1833                 return;
   1834 
   1835             if (event->xproperty.atom == _glfw.x11.WM_STATE)
   1836             {
   1837                 const int state = getWindowState(window);
   1838                 if (state != IconicState && state != NormalState)
   1839                     return;
   1840 
   1841                 const GLFWbool iconified = (state == IconicState);
   1842                 if (window->x11.iconified != iconified)
   1843                 {
   1844                     if (window->monitor)
   1845                     {
   1846                         if (iconified)
   1847                             releaseMonitor(window);
   1848                         else
   1849                             acquireMonitor(window);
   1850                     }
   1851 
   1852                     window->x11.iconified = iconified;
   1853                     _glfwInputWindowIconify(window, iconified);
   1854                 }
   1855             }
   1856             else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
   1857             {
   1858                 const GLFWbool maximized = _glfwPlatformWindowMaximized(window);
   1859                 if (window->x11.maximized != maximized)
   1860                 {
   1861                     window->x11.maximized = maximized;
   1862                     _glfwInputWindowMaximize(window, maximized);
   1863                 }
   1864             }
   1865 
   1866             return;
   1867         }
   1868 
   1869         case DestroyNotify:
   1870             return;
   1871     }
   1872 }
   1873 
   1874 
   1875 //////////////////////////////////////////////////////////////////////////
   1876 //////                       GLFW internal API                      //////
   1877 //////////////////////////////////////////////////////////////////////////
   1878 
   1879 // Retrieve a single window property of the specified type
   1880 // Inspired by fghGetWindowProperty from freeglut
   1881 //
   1882 unsigned long _glfwGetWindowPropertyX11(Window window,
   1883                                         Atom property,
   1884                                         Atom type,
   1885                                         unsigned char** value)
   1886 {
   1887     Atom actualType;
   1888     int actualFormat;
   1889     unsigned long itemCount, bytesAfter;
   1890 
   1891     XGetWindowProperty(_glfw.x11.display,
   1892                        window,
   1893                        property,
   1894                        0,
   1895                        LONG_MAX,
   1896                        False,
   1897                        type,
   1898                        &actualType,
   1899                        &actualFormat,
   1900                        &itemCount,
   1901                        &bytesAfter,
   1902                        value);
   1903 
   1904     return itemCount;
   1905 }
   1906 
   1907 GLFWbool _glfwIsVisualTransparentX11(Visual* visual)
   1908 {
   1909     if (!_glfw.x11.xrender.available)
   1910         return GLFW_FALSE;
   1911 
   1912     XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
   1913     return pf && pf->direct.alphaMask;
   1914 }
   1915 
   1916 // Push contents of our selection to clipboard manager
   1917 //
   1918 void _glfwPushSelectionToManagerX11(void)
   1919 {
   1920     XConvertSelection(_glfw.x11.display,
   1921                       _glfw.x11.CLIPBOARD_MANAGER,
   1922                       _glfw.x11.SAVE_TARGETS,
   1923                       None,
   1924                       _glfw.x11.helperWindowHandle,
   1925                       CurrentTime);
   1926 
   1927     for (;;)
   1928     {
   1929         XEvent event;
   1930 
   1931         while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
   1932         {
   1933             switch (event.type)
   1934             {
   1935                 case SelectionRequest:
   1936                     handleSelectionRequest(&event);
   1937                     break;
   1938 
   1939                 case SelectionClear:
   1940                     handleSelectionClear(&event);
   1941                     break;
   1942 
   1943                 case SelectionNotify:
   1944                 {
   1945                     if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
   1946                     {
   1947                         // This means one of two things; either the selection
   1948                         // was not owned, which means there is no clipboard
   1949                         // manager, or the transfer to the clipboard manager has
   1950                         // completed
   1951                         // In either case, it means we are done here
   1952                         return;
   1953                     }
   1954 
   1955                     break;
   1956                 }
   1957             }
   1958         }
   1959 
   1960         waitForEvent(NULL);
   1961     }
   1962 }
   1963 
   1964 
   1965 //////////////////////////////////////////////////////////////////////////
   1966 //////                       GLFW platform API                      //////
   1967 //////////////////////////////////////////////////////////////////////////
   1968 
   1969 int _glfwPlatformCreateWindow(_GLFWwindow* window,
   1970                               const _GLFWwndconfig* wndconfig,
   1971                               const _GLFWctxconfig* ctxconfig,
   1972                               const _GLFWfbconfig* fbconfig)
   1973 {
   1974     Visual* visual;
   1975     int depth;
   1976 
   1977     if (ctxconfig->client != GLFW_NO_API)
   1978     {
   1979         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
   1980         {
   1981             if (!_glfwInitGLX())
   1982                 return GLFW_FALSE;
   1983             if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
   1984                 return GLFW_FALSE;
   1985         }
   1986         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
   1987         {
   1988             if (!_glfwInitEGL())
   1989                 return GLFW_FALSE;
   1990             if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
   1991                 return GLFW_FALSE;
   1992         }
   1993         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   1994         {
   1995             if (!_glfwInitOSMesa())
   1996                 return GLFW_FALSE;
   1997         }
   1998     }
   1999 
   2000     if (ctxconfig->client == GLFW_NO_API ||
   2001         ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   2002     {
   2003         visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
   2004         depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
   2005     }
   2006 
   2007     if (!createNativeWindow(window, wndconfig, visual, depth))
   2008         return GLFW_FALSE;
   2009 
   2010     if (ctxconfig->client != GLFW_NO_API)
   2011     {
   2012         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
   2013         {
   2014             if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
   2015                 return GLFW_FALSE;
   2016         }
   2017         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
   2018         {
   2019             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
   2020                 return GLFW_FALSE;
   2021         }
   2022         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   2023         {
   2024             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
   2025                 return GLFW_FALSE;
   2026         }
   2027     }
   2028 
   2029     if (window->monitor)
   2030     {
   2031         _glfwPlatformShowWindow(window);
   2032         updateWindowMode(window);
   2033         acquireMonitor(window);
   2034     }
   2035 
   2036     XFlush(_glfw.x11.display);
   2037     return GLFW_TRUE;
   2038 }
   2039 
   2040 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
   2041 {
   2042     if (_glfw.x11.disabledCursorWindow == window)
   2043         _glfw.x11.disabledCursorWindow = NULL;
   2044 
   2045     if (window->monitor)
   2046         releaseMonitor(window);
   2047 
   2048     if (window->x11.ic)
   2049     {
   2050         XDestroyIC(window->x11.ic);
   2051         window->x11.ic = NULL;
   2052     }
   2053 
   2054     if (window->context.destroy)
   2055         window->context.destroy(window);
   2056 
   2057     if (window->x11.handle)
   2058     {
   2059         XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
   2060         XUnmapWindow(_glfw.x11.display, window->x11.handle);
   2061         XDestroyWindow(_glfw.x11.display, window->x11.handle);
   2062         window->x11.handle = (Window) 0;
   2063     }
   2064 
   2065     if (window->x11.colormap)
   2066     {
   2067         XFreeColormap(_glfw.x11.display, window->x11.colormap);
   2068         window->x11.colormap = (Colormap) 0;
   2069     }
   2070 
   2071     XFlush(_glfw.x11.display);
   2072 }
   2073 
   2074 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
   2075 {
   2076 #if defined(X_HAVE_UTF8_STRING)
   2077     Xutf8SetWMProperties(_glfw.x11.display,
   2078                          window->x11.handle,
   2079                          title, title,
   2080                          NULL, 0,
   2081                          NULL, NULL, NULL);
   2082 #else
   2083     // This may be a slightly better fallback than using XStoreName and
   2084     // XSetIconName, which always store their arguments using STRING
   2085     XmbSetWMProperties(_glfw.x11.display,
   2086                        window->x11.handle,
   2087                        title, title,
   2088                        NULL, 0,
   2089                        NULL, NULL, NULL);
   2090 #endif
   2091 
   2092     XChangeProperty(_glfw.x11.display,  window->x11.handle,
   2093                     _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
   2094                     PropModeReplace,
   2095                     (unsigned char*) title, strlen(title));
   2096 
   2097     XChangeProperty(_glfw.x11.display,  window->x11.handle,
   2098                     _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
   2099                     PropModeReplace,
   2100                     (unsigned char*) title, strlen(title));
   2101 
   2102     XFlush(_glfw.x11.display);
   2103 }
   2104 
   2105 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
   2106                                 int count, const GLFWimage* images)
   2107 {
   2108     if (count)
   2109     {
   2110         int i, j, longCount = 0;
   2111 
   2112         for (i = 0;  i < count;  i++)
   2113             longCount += 2 + images[i].width * images[i].height;
   2114 
   2115         long* icon = calloc(longCount, sizeof(long));
   2116         long* target = icon;
   2117 
   2118         for (i = 0;  i < count;  i++)
   2119         {
   2120             *target++ = images[i].width;
   2121             *target++ = images[i].height;
   2122 
   2123             for (j = 0;  j < images[i].width * images[i].height;  j++)
   2124             {
   2125                 *target++ = (images[i].pixels[j * 4 + 0] << 16) |
   2126                             (images[i].pixels[j * 4 + 1] <<  8) |
   2127                             (images[i].pixels[j * 4 + 2] <<  0) |
   2128                             (images[i].pixels[j * 4 + 3] << 24);
   2129             }
   2130         }
   2131 
   2132         XChangeProperty(_glfw.x11.display, window->x11.handle,
   2133                         _glfw.x11.NET_WM_ICON,
   2134                         XA_CARDINAL, 32,
   2135                         PropModeReplace,
   2136                         (unsigned char*) icon,
   2137                         longCount);
   2138 
   2139         free(icon);
   2140     }
   2141     else
   2142     {
   2143         XDeleteProperty(_glfw.x11.display, window->x11.handle,
   2144                         _glfw.x11.NET_WM_ICON);
   2145     }
   2146 
   2147     XFlush(_glfw.x11.display);
   2148 }
   2149 
   2150 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
   2151 {
   2152     Window dummy;
   2153     int x, y;
   2154 
   2155     XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
   2156                           0, 0, &x, &y, &dummy);
   2157 
   2158     if (xpos)
   2159         *xpos = x;
   2160     if (ypos)
   2161         *ypos = y;
   2162 }
   2163 
   2164 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
   2165 {
   2166     // HACK: Explicitly setting PPosition to any value causes some WMs, notably
   2167     //       Compiz and Metacity, to honor the position of unmapped windows
   2168     if (!_glfwPlatformWindowVisible(window))
   2169     {
   2170         long supplied;
   2171         XSizeHints* hints = XAllocSizeHints();
   2172 
   2173         if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
   2174         {
   2175             hints->flags |= PPosition;
   2176             hints->x = hints->y = 0;
   2177 
   2178             XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
   2179         }
   2180 
   2181         XFree(hints);
   2182     }
   2183 
   2184     XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
   2185     XFlush(_glfw.x11.display);
   2186 }
   2187 
   2188 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
   2189 {
   2190     XWindowAttributes attribs;
   2191     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
   2192 
   2193     if (width)
   2194         *width = attribs.width;
   2195     if (height)
   2196         *height = attribs.height;
   2197 }
   2198 
   2199 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
   2200 {
   2201     if (window->monitor)
   2202     {
   2203         if (window->monitor->window == window)
   2204             acquireMonitor(window);
   2205     }
   2206     else
   2207     {
   2208         if (!window->resizable)
   2209             updateNormalHints(window, width, height);
   2210 
   2211         XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
   2212     }
   2213 
   2214     XFlush(_glfw.x11.display);
   2215 }
   2216 
   2217 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
   2218                                       int minwidth, int minheight,
   2219                                       int maxwidth, int maxheight)
   2220 {
   2221     int width, height;
   2222     _glfwPlatformGetWindowSize(window, &width, &height);
   2223     updateNormalHints(window, width, height);
   2224     XFlush(_glfw.x11.display);
   2225 }
   2226 
   2227 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
   2228 {
   2229     int width, height;
   2230     _glfwPlatformGetWindowSize(window, &width, &height);
   2231     updateNormalHints(window, width, height);
   2232     XFlush(_glfw.x11.display);
   2233 }
   2234 
   2235 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
   2236 {
   2237     _glfwPlatformGetWindowSize(window, width, height);
   2238 }
   2239 
   2240 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
   2241                                      int* left, int* top,
   2242                                      int* right, int* bottom)
   2243 {
   2244     long* extents = NULL;
   2245 
   2246     if (window->monitor || !window->decorated)
   2247         return;
   2248 
   2249     if (_glfw.x11.NET_FRAME_EXTENTS == None)
   2250         return;
   2251 
   2252     if (!_glfwPlatformWindowVisible(window) &&
   2253         _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
   2254     {
   2255         XEvent event;
   2256         double timeout = 0.5;
   2257 
   2258         // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
   2259         // function before the window is mapped
   2260         sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
   2261                       0, 0, 0, 0, 0);
   2262 
   2263         // HACK: Use a timeout because earlier versions of some window managers
   2264         //       (at least Unity, Fluxbox and Xfwm) failed to send the reply
   2265         //       They have been fixed but broken versions are still in the wild
   2266         //       If you are affected by this and your window manager is NOT
   2267         //       listed above, PLEASE report it to their and our issue trackers
   2268         while (!XCheckIfEvent(_glfw.x11.display,
   2269                               &event,
   2270                               isFrameExtentsEvent,
   2271                               (XPointer) window))
   2272         {
   2273             if (!waitForEvent(&timeout))
   2274             {
   2275                 _glfwInputError(GLFW_PLATFORM_ERROR,
   2276                                 "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
   2277                 return;
   2278             }
   2279         }
   2280     }
   2281 
   2282     if (_glfwGetWindowPropertyX11(window->x11.handle,
   2283                                   _glfw.x11.NET_FRAME_EXTENTS,
   2284                                   XA_CARDINAL,
   2285                                   (unsigned char**) &extents) == 4)
   2286     {
   2287         if (left)
   2288             *left = extents[0];
   2289         if (top)
   2290             *top = extents[2];
   2291         if (right)
   2292             *right = extents[1];
   2293         if (bottom)
   2294             *bottom = extents[3];
   2295     }
   2296 
   2297     if (extents)
   2298         XFree(extents);
   2299 }
   2300 
   2301 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
   2302                                         float* xscale, float* yscale)
   2303 {
   2304     if (xscale)
   2305         *xscale = _glfw.x11.contentScaleX;
   2306     if (yscale)
   2307         *yscale = _glfw.x11.contentScaleY;
   2308 }
   2309 
   2310 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
   2311 {
   2312     if (window->x11.overrideRedirect)
   2313     {
   2314         // Override-redirect windows cannot be iconified or restored, as those
   2315         // tasks are performed by the window manager
   2316         _glfwInputError(GLFW_PLATFORM_ERROR,
   2317                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
   2318         return;
   2319     }
   2320 
   2321     XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
   2322     XFlush(_glfw.x11.display);
   2323 }
   2324 
   2325 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
   2326 {
   2327     if (window->x11.overrideRedirect)
   2328     {
   2329         // Override-redirect windows cannot be iconified or restored, as those
   2330         // tasks are performed by the window manager
   2331         _glfwInputError(GLFW_PLATFORM_ERROR,
   2332                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
   2333         return;
   2334     }
   2335 
   2336     if (_glfwPlatformWindowIconified(window))
   2337     {
   2338         XMapWindow(_glfw.x11.display, window->x11.handle);
   2339         waitForVisibilityNotify(window);
   2340     }
   2341     else if (_glfwPlatformWindowVisible(window))
   2342     {
   2343         if (_glfw.x11.NET_WM_STATE &&
   2344             _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
   2345             _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2346         {
   2347             sendEventToWM(window,
   2348                           _glfw.x11.NET_WM_STATE,
   2349                           _NET_WM_STATE_REMOVE,
   2350                           _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2351                           _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
   2352                           1, 0);
   2353         }
   2354     }
   2355 
   2356     XFlush(_glfw.x11.display);
   2357 }
   2358 
   2359 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
   2360 {
   2361     if (!_glfw.x11.NET_WM_STATE ||
   2362         !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2363         !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2364     {
   2365         return;
   2366     }
   2367 
   2368     if (_glfwPlatformWindowVisible(window))
   2369     {
   2370         sendEventToWM(window,
   2371                     _glfw.x11.NET_WM_STATE,
   2372                     _NET_WM_STATE_ADD,
   2373                     _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2374                     _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
   2375                     1, 0);
   2376     }
   2377     else
   2378     {
   2379         Atom* states = NULL;
   2380         unsigned long count =
   2381             _glfwGetWindowPropertyX11(window->x11.handle,
   2382                                       _glfw.x11.NET_WM_STATE,
   2383                                       XA_ATOM,
   2384                                       (unsigned char**) &states);
   2385 
   2386         // NOTE: We don't check for failure as this property may not exist yet
   2387         //       and that's fine (and we'll create it implicitly with append)
   2388 
   2389         Atom missing[2] =
   2390         {
   2391             _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
   2392             _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
   2393         };
   2394         unsigned long missingCount = 2;
   2395 
   2396         for (unsigned long i = 0;  i < count;  i++)
   2397         {
   2398             for (unsigned long j = 0;  j < missingCount;  j++)
   2399             {
   2400                 if (states[i] == missing[j])
   2401                 {
   2402                     missing[j] = missing[missingCount - 1];
   2403                     missingCount--;
   2404                 }
   2405             }
   2406         }
   2407 
   2408         if (states)
   2409             XFree(states);
   2410 
   2411         if (!missingCount)
   2412             return;
   2413 
   2414         XChangeProperty(_glfw.x11.display, window->x11.handle,
   2415                         _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2416                         PropModeAppend,
   2417                         (unsigned char*) missing,
   2418                         missingCount);
   2419     }
   2420 
   2421     XFlush(_glfw.x11.display);
   2422 }
   2423 
   2424 void _glfwPlatformShowWindow(_GLFWwindow* window)
   2425 {
   2426     if (_glfwPlatformWindowVisible(window))
   2427         return;
   2428 
   2429     XMapWindow(_glfw.x11.display, window->x11.handle);
   2430     waitForVisibilityNotify(window);
   2431 }
   2432 
   2433 void _glfwPlatformHideWindow(_GLFWwindow* window)
   2434 {
   2435     XUnmapWindow(_glfw.x11.display, window->x11.handle);
   2436     XFlush(_glfw.x11.display);
   2437 }
   2438 
   2439 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
   2440 {
   2441     if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
   2442         return;
   2443 
   2444     sendEventToWM(window,
   2445                   _glfw.x11.NET_WM_STATE,
   2446                   _NET_WM_STATE_ADD,
   2447                   _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
   2448                   0, 1, 0);
   2449 }
   2450 
   2451 void _glfwPlatformFocusWindow(_GLFWwindow* window)
   2452 {
   2453     if (_glfw.x11.NET_ACTIVE_WINDOW)
   2454         sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
   2455     else if (_glfwPlatformWindowVisible(window))
   2456     {
   2457         XRaiseWindow(_glfw.x11.display, window->x11.handle);
   2458         XSetInputFocus(_glfw.x11.display, window->x11.handle,
   2459                        RevertToParent, CurrentTime);
   2460     }
   2461 
   2462     XFlush(_glfw.x11.display);
   2463 }
   2464 
   2465 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
   2466                                    _GLFWmonitor* monitor,
   2467                                    int xpos, int ypos,
   2468                                    int width, int height,
   2469                                    int refreshRate)
   2470 {
   2471     if (window->monitor == monitor)
   2472     {
   2473         if (monitor)
   2474         {
   2475             if (monitor->window == window)
   2476                 acquireMonitor(window);
   2477         }
   2478         else
   2479         {
   2480             if (!window->resizable)
   2481                 updateNormalHints(window, width, height);
   2482 
   2483             XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   2484                               xpos, ypos, width, height);
   2485         }
   2486 
   2487         XFlush(_glfw.x11.display);
   2488         return;
   2489     }
   2490 
   2491     if (window->monitor)
   2492         releaseMonitor(window);
   2493 
   2494     _glfwInputWindowMonitor(window, monitor);
   2495     updateNormalHints(window, width, height);
   2496 
   2497     if (window->monitor)
   2498     {
   2499         if (!_glfwPlatformWindowVisible(window))
   2500         {
   2501             XMapRaised(_glfw.x11.display, window->x11.handle);
   2502             waitForVisibilityNotify(window);
   2503         }
   2504 
   2505         updateWindowMode(window);
   2506         acquireMonitor(window);
   2507     }
   2508     else
   2509     {
   2510         updateWindowMode(window);
   2511         XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
   2512                           xpos, ypos, width, height);
   2513     }
   2514 
   2515     XFlush(_glfw.x11.display);
   2516 }
   2517 
   2518 int _glfwPlatformWindowFocused(_GLFWwindow* window)
   2519 {
   2520     Window focused;
   2521     int state;
   2522 
   2523     XGetInputFocus(_glfw.x11.display, &focused, &state);
   2524     return window->x11.handle == focused;
   2525 }
   2526 
   2527 int _glfwPlatformWindowIconified(_GLFWwindow* window)
   2528 {
   2529     return getWindowState(window) == IconicState;
   2530 }
   2531 
   2532 int _glfwPlatformWindowVisible(_GLFWwindow* window)
   2533 {
   2534     XWindowAttributes wa;
   2535     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
   2536     return wa.map_state == IsViewable;
   2537 }
   2538 
   2539 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
   2540 {
   2541     Atom* states;
   2542     unsigned long i;
   2543     GLFWbool maximized = GLFW_FALSE;
   2544 
   2545     if (!_glfw.x11.NET_WM_STATE ||
   2546         !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2547         !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2548     {
   2549         return maximized;
   2550     }
   2551 
   2552     const unsigned long count =
   2553         _glfwGetWindowPropertyX11(window->x11.handle,
   2554                                   _glfw.x11.NET_WM_STATE,
   2555                                   XA_ATOM,
   2556                                   (unsigned char**) &states);
   2557 
   2558     for (i = 0;  i < count;  i++)
   2559     {
   2560         if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
   2561             states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   2562         {
   2563             maximized = GLFW_TRUE;
   2564             break;
   2565         }
   2566     }
   2567 
   2568     if (states)
   2569         XFree(states);
   2570 
   2571     return maximized;
   2572 }
   2573 
   2574 int _glfwPlatformWindowHovered(_GLFWwindow* window)
   2575 {
   2576     Window w = _glfw.x11.root;
   2577     while (w)
   2578     {
   2579         Window root;
   2580         int rootX, rootY, childX, childY;
   2581         unsigned int mask;
   2582 
   2583         if (!XQueryPointer(_glfw.x11.display, w,
   2584                            &root, &w, &rootX, &rootY, &childX, &childY, &mask))
   2585         {
   2586             return GLFW_FALSE;
   2587         }
   2588 
   2589         if (w == window->x11.handle)
   2590             return GLFW_TRUE;
   2591     }
   2592 
   2593     return GLFW_FALSE;
   2594 }
   2595 
   2596 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
   2597 {
   2598     if (!window->x11.transparent)
   2599         return GLFW_FALSE;
   2600 
   2601     return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
   2602 }
   2603 
   2604 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
   2605 {
   2606     int width, height;
   2607     _glfwPlatformGetWindowSize(window, &width, &height);
   2608     updateNormalHints(window, width, height);
   2609 }
   2610 
   2611 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
   2612 {
   2613     struct
   2614     {
   2615         unsigned long flags;
   2616         unsigned long functions;
   2617         unsigned long decorations;
   2618         long input_mode;
   2619         unsigned long status;
   2620     } hints = {0};
   2621 
   2622     hints.flags = MWM_HINTS_DECORATIONS;
   2623     hints.decorations = enabled ? MWM_DECOR_ALL : 0;
   2624 
   2625     XChangeProperty(_glfw.x11.display, window->x11.handle,
   2626                     _glfw.x11.MOTIF_WM_HINTS,
   2627                     _glfw.x11.MOTIF_WM_HINTS, 32,
   2628                     PropModeReplace,
   2629                     (unsigned char*) &hints,
   2630                     sizeof(hints) / sizeof(long));
   2631 }
   2632 
   2633 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
   2634 {
   2635     if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
   2636         return;
   2637 
   2638     if (_glfwPlatformWindowVisible(window))
   2639     {
   2640         const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
   2641         sendEventToWM(window,
   2642                       _glfw.x11.NET_WM_STATE,
   2643                       action,
   2644                       _glfw.x11.NET_WM_STATE_ABOVE,
   2645                       0, 1, 0);
   2646     }
   2647     else
   2648     {
   2649         Atom* states = NULL;
   2650         unsigned long i, count;
   2651 
   2652         count = _glfwGetWindowPropertyX11(window->x11.handle,
   2653                                           _glfw.x11.NET_WM_STATE,
   2654                                           XA_ATOM,
   2655                                           (unsigned char**) &states);
   2656 
   2657         // NOTE: We don't check for failure as this property may not exist yet
   2658         //       and that's fine (and we'll create it implicitly with append)
   2659 
   2660         if (enabled)
   2661         {
   2662             for (i = 0;  i < count;  i++)
   2663             {
   2664                 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
   2665                     break;
   2666             }
   2667 
   2668             if (i < count)
   2669                 return;
   2670 
   2671             XChangeProperty(_glfw.x11.display, window->x11.handle,
   2672                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2673                             PropModeAppend,
   2674                             (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
   2675                             1);
   2676         }
   2677         else if (states)
   2678         {
   2679             for (i = 0;  i < count;  i++)
   2680             {
   2681                 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
   2682                     break;
   2683             }
   2684 
   2685             if (i == count)
   2686                 return;
   2687 
   2688             states[i] = states[count - 1];
   2689             count--;
   2690 
   2691             XChangeProperty(_glfw.x11.display, window->x11.handle,
   2692                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   2693                             PropModeReplace, (unsigned char*) states, count);
   2694         }
   2695 
   2696         if (states)
   2697             XFree(states);
   2698     }
   2699 
   2700     XFlush(_glfw.x11.display);
   2701 }
   2702 
   2703 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
   2704 {
   2705     float opacity = 1.f;
   2706 
   2707     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
   2708     {
   2709         CARD32* value = NULL;
   2710 
   2711         if (_glfwGetWindowPropertyX11(window->x11.handle,
   2712                                       _glfw.x11.NET_WM_WINDOW_OPACITY,
   2713                                       XA_CARDINAL,
   2714                                       (unsigned char**) &value))
   2715         {
   2716             opacity = (float) (*value / (double) 0xffffffffu);
   2717         }
   2718 
   2719         if (value)
   2720             XFree(value);
   2721     }
   2722 
   2723     return opacity;
   2724 }
   2725 
   2726 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
   2727 {
   2728     const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
   2729     XChangeProperty(_glfw.x11.display, window->x11.handle,
   2730                     _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
   2731                     PropModeReplace, (unsigned char*) &value, 1);
   2732 }
   2733 
   2734 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
   2735 {
   2736     if (!_glfw.x11.xi.available)
   2737         return;
   2738 
   2739     if (_glfw.x11.disabledCursorWindow != window)
   2740         return;
   2741 
   2742     if (enabled)
   2743         enableRawMouseMotion(window);
   2744     else
   2745         disableRawMouseMotion(window);
   2746 }
   2747 
   2748 GLFWbool _glfwPlatformRawMouseMotionSupported(void)
   2749 {
   2750     return _glfw.x11.xi.available;
   2751 }
   2752 
   2753 void _glfwPlatformPollEvents(void)
   2754 {
   2755     _GLFWwindow* window;
   2756 
   2757 #if defined(__linux__)
   2758     _glfwDetectJoystickConnectionLinux();
   2759 #endif
   2760     XPending(_glfw.x11.display);
   2761 
   2762     while (XQLength(_glfw.x11.display))
   2763     {
   2764         XEvent event;
   2765         XNextEvent(_glfw.x11.display, &event);
   2766         processEvent(&event);
   2767     }
   2768 
   2769     window = _glfw.x11.disabledCursorWindow;
   2770     if (window)
   2771     {
   2772         int width, height;
   2773         _glfwPlatformGetWindowSize(window, &width, &height);
   2774 
   2775         // NOTE: Re-center the cursor only if it has moved since the last call,
   2776         //       to avoid breaking glfwWaitEvents with MotionNotify
   2777         if (window->x11.lastCursorPosX != width / 2 ||
   2778             window->x11.lastCursorPosY != height / 2)
   2779         {
   2780             _glfwPlatformSetCursorPos(window, width / 2, height / 2);
   2781         }
   2782     }
   2783 
   2784     XFlush(_glfw.x11.display);
   2785 }
   2786 
   2787 void _glfwPlatformWaitEvents(void)
   2788 {
   2789     while (!XPending(_glfw.x11.display))
   2790         waitForEvent(NULL);
   2791 
   2792     _glfwPlatformPollEvents();
   2793 }
   2794 
   2795 void _glfwPlatformWaitEventsTimeout(double timeout)
   2796 {
   2797     while (!XPending(_glfw.x11.display))
   2798     {
   2799         if (!waitForEvent(&timeout))
   2800             break;
   2801     }
   2802 
   2803     _glfwPlatformPollEvents();
   2804 }
   2805 
   2806 void _glfwPlatformPostEmptyEvent(void)
   2807 {
   2808     XEvent event = { ClientMessage };
   2809     event.xclient.window = _glfw.x11.helperWindowHandle;
   2810     event.xclient.format = 32; // Data is 32-bit longs
   2811     event.xclient.message_type = _glfw.x11.NULL_;
   2812 
   2813     XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event);
   2814     XFlush(_glfw.x11.display);
   2815 }
   2816 
   2817 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
   2818 {
   2819     Window root, child;
   2820     int rootX, rootY, childX, childY;
   2821     unsigned int mask;
   2822 
   2823     XQueryPointer(_glfw.x11.display, window->x11.handle,
   2824                   &root, &child,
   2825                   &rootX, &rootY, &childX, &childY,
   2826                   &mask);
   2827 
   2828     if (xpos)
   2829         *xpos = childX;
   2830     if (ypos)
   2831         *ypos = childY;
   2832 }
   2833 
   2834 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
   2835 {
   2836     // Store the new position so it can be recognized later
   2837     window->x11.warpCursorPosX = (int) x;
   2838     window->x11.warpCursorPosY = (int) y;
   2839 
   2840     XWarpPointer(_glfw.x11.display, None, window->x11.handle,
   2841                  0,0,0,0, (int) x, (int) y);
   2842     XFlush(_glfw.x11.display);
   2843 }
   2844 
   2845 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
   2846 {
   2847     if (mode == GLFW_CURSOR_DISABLED)
   2848     {
   2849         if (_glfwPlatformWindowFocused(window))
   2850             disableCursor(window);
   2851     }
   2852     else if (_glfw.x11.disabledCursorWindow == window)
   2853         enableCursor(window);
   2854     else
   2855         updateCursorImage(window);
   2856 
   2857     XFlush(_glfw.x11.display);
   2858 }
   2859 
   2860 const char* _glfwPlatformGetScancodeName(int scancode)
   2861 {
   2862     if (!_glfw.x11.xkb.available)
   2863         return NULL;
   2864 
   2865     if (scancode < 0 || scancode > 0xff ||
   2866         _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN)
   2867     {
   2868         _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
   2869         return NULL;
   2870     }
   2871 
   2872     const int key = _glfw.x11.keycodes[scancode];
   2873     const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display,
   2874                                              scancode, _glfw.x11.xkb.group, 0);
   2875     if (keysym == NoSymbol)
   2876         return NULL;
   2877 
   2878     const long ch = _glfwKeySym2Unicode(keysym);
   2879     if (ch == -1)
   2880         return NULL;
   2881 
   2882     const size_t count = encodeUTF8(_glfw.x11.keynames[key], (unsigned int) ch);
   2883     if (count == 0)
   2884         return NULL;
   2885 
   2886     _glfw.x11.keynames[key][count] = '\0';
   2887     return _glfw.x11.keynames[key];
   2888 }
   2889 
   2890 int _glfwPlatformGetKeyScancode(int key)
   2891 {
   2892     return _glfw.x11.scancodes[key];
   2893 }
   2894 
   2895 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
   2896                               const GLFWimage* image,
   2897                               int xhot, int yhot)
   2898 {
   2899     cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
   2900     if (!cursor->x11.handle)
   2901         return GLFW_FALSE;
   2902 
   2903     return GLFW_TRUE;
   2904 }
   2905 
   2906 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
   2907 {
   2908     int native = 0;
   2909 
   2910     if (shape == GLFW_ARROW_CURSOR)
   2911         native = XC_left_ptr;
   2912     else if (shape == GLFW_IBEAM_CURSOR)
   2913         native = XC_xterm;
   2914     else if (shape == GLFW_CROSSHAIR_CURSOR)
   2915         native = XC_crosshair;
   2916     else if (shape == GLFW_HAND_CURSOR)
   2917         native = XC_hand2;
   2918     else if (shape == GLFW_HRESIZE_CURSOR)
   2919         native = XC_sb_h_double_arrow;
   2920     else if (shape == GLFW_VRESIZE_CURSOR)
   2921         native = XC_sb_v_double_arrow;
   2922     else
   2923         return GLFW_FALSE;
   2924 
   2925     cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
   2926     if (!cursor->x11.handle)
   2927     {
   2928         _glfwInputError(GLFW_PLATFORM_ERROR,
   2929                         "X11: Failed to create standard cursor");
   2930         return GLFW_FALSE;
   2931     }
   2932 
   2933     return GLFW_TRUE;
   2934 }
   2935 
   2936 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
   2937 {
   2938     if (cursor->x11.handle)
   2939         XFreeCursor(_glfw.x11.display, cursor->x11.handle);
   2940 }
   2941 
   2942 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
   2943 {
   2944     if (window->cursorMode == GLFW_CURSOR_NORMAL)
   2945     {
   2946         updateCursorImage(window);
   2947         XFlush(_glfw.x11.display);
   2948     }
   2949 }
   2950 
   2951 void _glfwPlatformSetClipboardString(const char* string)
   2952 {
   2953     free(_glfw.x11.clipboardString);
   2954     _glfw.x11.clipboardString = _glfw_strdup(string);
   2955 
   2956     XSetSelectionOwner(_glfw.x11.display,
   2957                        _glfw.x11.CLIPBOARD,
   2958                        _glfw.x11.helperWindowHandle,
   2959                        CurrentTime);
   2960 
   2961     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
   2962         _glfw.x11.helperWindowHandle)
   2963     {
   2964         _glfwInputError(GLFW_PLATFORM_ERROR,
   2965                         "X11: Failed to become owner of clipboard selection");
   2966     }
   2967 }
   2968 
   2969 const char* _glfwPlatformGetClipboardString(void)
   2970 {
   2971     return getSelectionString(_glfw.x11.CLIPBOARD);
   2972 }
   2973 
   2974 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
   2975 {
   2976     if (!_glfw.vk.KHR_surface)
   2977         return;
   2978 
   2979     if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
   2980     {
   2981         if (!_glfw.vk.KHR_xlib_surface)
   2982             return;
   2983     }
   2984 
   2985     extensions[0] = "VK_KHR_surface";
   2986 
   2987     // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
   2988     //       not correctly implementing VK_KHR_xlib_surface
   2989     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   2990         extensions[1] = "VK_KHR_xcb_surface";
   2991     else
   2992         extensions[1] = "VK_KHR_xlib_surface";
   2993 }
   2994 
   2995 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
   2996                                                       VkPhysicalDevice device,
   2997                                                       uint32_t queuefamily)
   2998 {
   2999     VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
   3000                                                           _glfw.x11.screen));
   3001 
   3002     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   3003     {
   3004         PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
   3005             vkGetPhysicalDeviceXcbPresentationSupportKHR =
   3006             (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
   3007             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
   3008         if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
   3009         {
   3010             _glfwInputError(GLFW_API_UNAVAILABLE,
   3011                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
   3012             return GLFW_FALSE;
   3013         }
   3014 
   3015         xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
   3016         if (!connection)
   3017         {
   3018             _glfwInputError(GLFW_PLATFORM_ERROR,
   3019                             "X11: Failed to retrieve XCB connection");
   3020             return GLFW_FALSE;
   3021         }
   3022 
   3023         return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
   3024                                                             queuefamily,
   3025                                                             connection,
   3026                                                             visualID);
   3027     }
   3028     else
   3029     {
   3030         PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
   3031             vkGetPhysicalDeviceXlibPresentationSupportKHR =
   3032             (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
   3033             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
   3034         if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
   3035         {
   3036             _glfwInputError(GLFW_API_UNAVAILABLE,
   3037                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
   3038             return GLFW_FALSE;
   3039         }
   3040 
   3041         return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
   3042                                                              queuefamily,
   3043                                                              _glfw.x11.display,
   3044                                                              visualID);
   3045     }
   3046 }
   3047 
   3048 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
   3049                                           _GLFWwindow* window,
   3050                                           const VkAllocationCallbacks* allocator,
   3051                                           VkSurfaceKHR* surface)
   3052 {
   3053     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
   3054     {
   3055         VkResult err;
   3056         VkXcbSurfaceCreateInfoKHR sci;
   3057         PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
   3058 
   3059         xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
   3060         if (!connection)
   3061         {
   3062             _glfwInputError(GLFW_PLATFORM_ERROR,
   3063                             "X11: Failed to retrieve XCB connection");
   3064             return VK_ERROR_EXTENSION_NOT_PRESENT;
   3065         }
   3066 
   3067         vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
   3068             vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
   3069         if (!vkCreateXcbSurfaceKHR)
   3070         {
   3071             _glfwInputError(GLFW_API_UNAVAILABLE,
   3072                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
   3073             return VK_ERROR_EXTENSION_NOT_PRESENT;
   3074         }
   3075 
   3076         memset(&sci, 0, sizeof(sci));
   3077         sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
   3078         sci.connection = connection;
   3079         sci.window = window->x11.handle;
   3080 
   3081         err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
   3082         if (err)
   3083         {
   3084             _glfwInputError(GLFW_PLATFORM_ERROR,
   3085                             "X11: Failed to create Vulkan XCB surface: %s",
   3086                             _glfwGetVulkanResultString(err));
   3087         }
   3088 
   3089         return err;
   3090     }
   3091     else
   3092     {
   3093         VkResult err;
   3094         VkXlibSurfaceCreateInfoKHR sci;
   3095         PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
   3096 
   3097         vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
   3098             vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
   3099         if (!vkCreateXlibSurfaceKHR)
   3100         {
   3101             _glfwInputError(GLFW_API_UNAVAILABLE,
   3102                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
   3103             return VK_ERROR_EXTENSION_NOT_PRESENT;
   3104         }
   3105 
   3106         memset(&sci, 0, sizeof(sci));
   3107         sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
   3108         sci.dpy = _glfw.x11.display;
   3109         sci.window = window->x11.handle;
   3110 
   3111         err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
   3112         if (err)
   3113         {
   3114             _glfwInputError(GLFW_PLATFORM_ERROR,
   3115                             "X11: Failed to create Vulkan X11 surface: %s",
   3116                             _glfwGetVulkanResultString(err));
   3117         }
   3118 
   3119         return err;
   3120     }
   3121 }
   3122 
   3123 
   3124 //////////////////////////////////////////////////////////////////////////
   3125 //////                        GLFW native API                       //////
   3126 //////////////////////////////////////////////////////////////////////////
   3127 
   3128 GLFWAPI Display* glfwGetX11Display(void)
   3129 {
   3130     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   3131     return _glfw.x11.display;
   3132 }
   3133 
   3134 GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
   3135 {
   3136     _GLFWwindow* window = (_GLFWwindow*) handle;
   3137     _GLFW_REQUIRE_INIT_OR_RETURN(None);
   3138     return window->x11.handle;
   3139 }
   3140 
   3141 GLFWAPI void glfwSetX11SelectionString(const char* string)
   3142 {
   3143     _GLFW_REQUIRE_INIT();
   3144 
   3145     free(_glfw.x11.primarySelectionString);
   3146     _glfw.x11.primarySelectionString = _glfw_strdup(string);
   3147 
   3148     XSetSelectionOwner(_glfw.x11.display,
   3149                        _glfw.x11.PRIMARY,
   3150                        _glfw.x11.helperWindowHandle,
   3151                        CurrentTime);
   3152 
   3153     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
   3154         _glfw.x11.helperWindowHandle)
   3155     {
   3156         _glfwInputError(GLFW_PLATFORM_ERROR,
   3157                         "X11: Failed to become owner of primary selection");
   3158     }
   3159 }
   3160 
   3161 GLFWAPI const char* glfwGetX11SelectionString(void)
   3162 {
   3163     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   3164     return getSelectionString(_glfw.x11.PRIMARY);
   3165 }
   3166