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