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