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