• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //========================================================================
2 // GLFW 3.2 X11 - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2002-2006 Marcus Geelnard
5 // Copyright (c) 2006-2016 Camilla Berglund <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 
52 // Wait for data to arrive using select
53 // This avoids blocking other threads via the per-display Xlib lock that also
54 // covers GLX functions
55 //
waitForEvent(double * timeout)56 static GLFWbool waitForEvent(double* timeout)
57 {
58     fd_set fds;
59     const int fd = ConnectionNumber(_glfw.x11.display);
60     int count = fd + 1;
61 
62     FD_ZERO(&fds);
63     FD_SET(fd, &fds);
64 #if defined(__linux__)
65     FD_SET(_glfw.linux_js.inotify, &fds);
66 
67     if (fd < _glfw.linux_js.inotify)
68         count = _glfw.linux_js.inotify + 1;
69 #endif
70     for (;;)
71     {
72         if (timeout)
73         {
74             const long seconds = (long) *timeout;
75             const long microseconds = (long) ((*timeout - seconds) * 1e6);
76             struct timeval tv = { seconds, microseconds };
77             const uint64_t base = _glfwPlatformGetTimerValue();
78 
79             const int result = select(count, &fds, NULL, NULL, &tv);
80             const int error = errno;
81 
82             *timeout -= (_glfwPlatformGetTimerValue() - base) /
83                 (double) _glfwPlatformGetTimerFrequency();
84 
85             if (result > 0)
86                 return GLFW_TRUE;
87             if ((result == -1 && error == EINTR) || *timeout <= 0.0)
88                 return GLFW_FALSE;
89         }
90         else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR)
91             return GLFW_TRUE;
92     }
93 }
94 
95 // Waits until a VisibilityNotify event arrives for the specified window or the
96 // timeout period elapses (ICCCM section 4.2.2)
97 //
waitForVisibilityNotify(_GLFWwindow * window)98 static GLFWbool waitForVisibilityNotify(_GLFWwindow* window)
99 {
100     XEvent dummy;
101     double timeout = 0.1;
102 
103     while (!XCheckTypedWindowEvent(_glfw.x11.display,
104                                    window->x11.handle,
105                                    VisibilityNotify,
106                                    &dummy))
107     {
108         if (!waitForEvent(&timeout))
109             return GLFW_FALSE;
110     }
111 
112     return GLFW_TRUE;
113 }
114 
115 // Returns whether the window is iconified
116 //
getWindowState(_GLFWwindow * window)117 static int getWindowState(_GLFWwindow* window)
118 {
119     int result = WithdrawnState;
120     struct {
121         CARD32 state;
122         Window icon;
123     } *state = NULL;
124 
125     if (_glfwGetWindowPropertyX11(window->x11.handle,
126                                   _glfw.x11.WM_STATE,
127                                   _glfw.x11.WM_STATE,
128                                   (unsigned char**) &state) >= 2)
129     {
130         result = state->state;
131     }
132 
133     XFree(state);
134     return result;
135 }
136 
137 // Returns whether the event is a selection event
138 //
isSelectionEvent(Display * display,XEvent * event,XPointer pointer)139 static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer)
140 {
141     return event->type == SelectionRequest ||
142            event->type == SelectionNotify ||
143            event->type == SelectionClear;
144 }
145 
146 // Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
147 //
isFrameExtentsEvent(Display * display,XEvent * event,XPointer pointer)148 static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer)
149 {
150     _GLFWwindow* window = (_GLFWwindow*) pointer;
151     return event->type == PropertyNotify &&
152            event->xproperty.state == PropertyNewValue &&
153            event->xproperty.window == window->x11.handle &&
154            event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
155 }
156 
157 // Translates a GLFW standard cursor to a font cursor shape
158 //
translateCursorShape(int shape)159 static int translateCursorShape(int shape)
160 {
161     switch (shape)
162     {
163         case GLFW_ARROW_CURSOR:
164             return XC_left_ptr;
165         case GLFW_IBEAM_CURSOR:
166             return XC_xterm;
167         case GLFW_CROSSHAIR_CURSOR:
168             return XC_crosshair;
169         case GLFW_HAND_CURSOR:
170             return XC_hand1;
171         case GLFW_HRESIZE_CURSOR:
172             return XC_sb_h_double_arrow;
173         case GLFW_VRESIZE_CURSOR:
174             return XC_sb_v_double_arrow;
175     }
176 
177     return 0;
178 }
179 
180 // Translates an X event modifier state mask
181 //
translateState(int state)182 static int translateState(int state)
183 {
184     int mods = 0;
185 
186     if (state & ShiftMask)
187         mods |= GLFW_MOD_SHIFT;
188     if (state & ControlMask)
189         mods |= GLFW_MOD_CONTROL;
190     if (state & Mod1Mask)
191         mods |= GLFW_MOD_ALT;
192     if (state & Mod4Mask)
193         mods |= GLFW_MOD_SUPER;
194 
195     return mods;
196 }
197 
198 // Translates an X11 key code to a GLFW key token
199 //
translateKey(int scancode)200 static int translateKey(int scancode)
201 {
202     // Use the pre-filled LUT (see createKeyTables() in x11_init.c)
203     if (scancode < 0 || scancode > 255)
204         return GLFW_KEY_UNKNOWN;
205 
206     return _glfw.x11.publicKeys[scancode];
207 }
208 
209 // Return the GLFW window corresponding to the specified X11 window
210 //
findWindowByHandle(Window handle)211 static _GLFWwindow* findWindowByHandle(Window handle)
212 {
213     _GLFWwindow* window;
214 
215     if (XFindContext(_glfw.x11.display,
216                      handle,
217                      _glfw.x11.context,
218                      (XPointer*) &window) != 0)
219     {
220         return NULL;
221     }
222 
223     return window;
224 }
225 
226 // Sends an EWMH or ICCCM event to the window manager
227 //
sendEventToWM(_GLFWwindow * window,Atom type,long a,long b,long c,long d,long e)228 static void sendEventToWM(_GLFWwindow* window, Atom type,
229                           long a, long b, long c, long d, long e)
230 {
231     XEvent event;
232     memset(&event, 0, sizeof(event));
233 
234     event.type = ClientMessage;
235     event.xclient.window = window->x11.handle;
236     event.xclient.format = 32; // Data is 32-bit longs
237     event.xclient.message_type = type;
238     event.xclient.data.l[0] = a;
239     event.xclient.data.l[1] = b;
240     event.xclient.data.l[2] = c;
241     event.xclient.data.l[3] = d;
242     event.xclient.data.l[4] = e;
243 
244     XSendEvent(_glfw.x11.display, _glfw.x11.root,
245                False,
246                SubstructureNotifyMask | SubstructureRedirectMask,
247                &event);
248 }
249 
250 // Updates the normal hints according to the window settings
251 //
updateNormalHints(_GLFWwindow * window,int width,int height)252 static void updateNormalHints(_GLFWwindow* window, int width, int height)
253 {
254     XSizeHints* hints = XAllocSizeHints();
255 
256     if (!window->monitor)
257     {
258         if (window->resizable)
259         {
260             if (window->minwidth != GLFW_DONT_CARE &&
261                 window->minheight != GLFW_DONT_CARE)
262             {
263                 hints->flags |= PMinSize;
264                 hints->min_width = window->minwidth;
265                 hints->min_height = window->minheight;
266             }
267 
268             if (window->maxwidth != GLFW_DONT_CARE &&
269                 window->maxheight != GLFW_DONT_CARE)
270             {
271                 hints->flags |= PMaxSize;
272                 hints->max_width = window->maxwidth;
273                 hints->max_height = window->maxheight;
274             }
275 
276             if (window->numer != GLFW_DONT_CARE &&
277                 window->denom != GLFW_DONT_CARE)
278             {
279                 hints->flags |= PAspect;
280                 hints->min_aspect.x = hints->max_aspect.x = window->numer;
281                 hints->min_aspect.y = hints->max_aspect.y = window->denom;
282             }
283         }
284         else
285         {
286             hints->flags |= (PMinSize | PMaxSize);
287             hints->min_width  = hints->max_width  = width;
288             hints->min_height = hints->max_height = height;
289         }
290     }
291 
292     hints->flags |= PWinGravity;
293     hints->win_gravity = StaticGravity;
294 
295     XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
296     XFree(hints);
297 }
298 
299 // Updates the full screen status of the window
300 //
updateWindowMode(_GLFWwindow * window)301 static void updateWindowMode(_GLFWwindow* window)
302 {
303     if (window->monitor)
304     {
305         if (_glfw.x11.xinerama.available &&
306             _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
307         {
308             sendEventToWM(window,
309                           _glfw.x11.NET_WM_FULLSCREEN_MONITORS,
310                           window->monitor->x11.index,
311                           window->monitor->x11.index,
312                           window->monitor->x11.index,
313                           window->monitor->x11.index,
314                           0);
315         }
316 
317         if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
318         {
319             sendEventToWM(window,
320                           _glfw.x11.NET_WM_STATE,
321                           _NET_WM_STATE_ADD,
322                           _glfw.x11.NET_WM_STATE_FULLSCREEN,
323                           0, 1, 0);
324         }
325         else
326         {
327             // This is the butcher's way of removing window decorations
328             // Setting the override-redirect attribute on a window makes the
329             // window manager ignore the window completely (ICCCM, section 4)
330             // The good thing is that this makes undecorated full screen windows
331             // easy to do; the bad thing is that we have to do everything
332             // manually and some things (like iconify/restore) won't work at
333             // all, as those are tasks usually performed by the window manager
334 
335             XSetWindowAttributes attributes;
336             attributes.override_redirect = True;
337             XChangeWindowAttributes(_glfw.x11.display,
338                                     window->x11.handle,
339                                     CWOverrideRedirect,
340                                     &attributes);
341 
342             window->x11.overrideRedirect = GLFW_TRUE;
343         }
344 
345         // Enable compositor bypass
346         {
347             const unsigned long value = 1;
348 
349             XChangeProperty(_glfw.x11.display,  window->x11.handle,
350                             _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
351                             PropModeReplace, (unsigned char*) &value, 1);
352         }
353     }
354     else
355     {
356         if (_glfw.x11.xinerama.available &&
357             _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
358         {
359             XDeleteProperty(_glfw.x11.display, window->x11.handle,
360                             _glfw.x11.NET_WM_FULLSCREEN_MONITORS);
361         }
362 
363         if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
364         {
365             sendEventToWM(window,
366                           _glfw.x11.NET_WM_STATE,
367                           _NET_WM_STATE_REMOVE,
368                           _glfw.x11.NET_WM_STATE_FULLSCREEN,
369                           0, 1, 0);
370         }
371         else
372         {
373             XSetWindowAttributes attributes;
374             attributes.override_redirect = False;
375             XChangeWindowAttributes(_glfw.x11.display,
376                                     window->x11.handle,
377                                     CWOverrideRedirect,
378                                     &attributes);
379 
380             window->x11.overrideRedirect = GLFW_FALSE;
381         }
382 
383         // Disable compositor bypass
384         {
385             XDeleteProperty(_glfw.x11.display, window->x11.handle,
386                             _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
387         }
388     }
389 }
390 
391 // Splits and translates a text/uri-list into separate file paths
392 // NOTE: This function destroys the provided string
393 //
parseUriList(char * text,int * count)394 static char** parseUriList(char* text, int* count)
395 {
396     const char* prefix = "file://";
397     char** paths = NULL;
398     char* line;
399 
400     *count = 0;
401 
402     while ((line = strtok(text, "\r\n")))
403     {
404         text = NULL;
405 
406         if (line[0] == '#')
407             continue;
408 
409         if (strncmp(line, prefix, strlen(prefix)) == 0)
410             line += strlen(prefix);
411 
412         (*count)++;
413 
414         char* path = calloc(strlen(line) + 1, 1);
415         paths = realloc(paths, *count * sizeof(char*));
416         paths[*count - 1] = path;
417 
418         while (*line)
419         {
420             if (line[0] == '%' && line[1] && line[2])
421             {
422                 const char digits[3] = { line[1], line[2], '\0' };
423                 *path = strtol(digits, NULL, 16);
424                 line += 2;
425             }
426             else
427                 *path = *line;
428 
429             path++;
430             line++;
431         }
432     }
433 
434     return paths;
435 }
436 
437 // Centers the cursor over the window client area
438 //
centerCursor(_GLFWwindow * window)439 static void centerCursor(_GLFWwindow* window)
440 {
441     int width, height;
442     _glfwPlatformGetWindowSize(window, &width, &height);
443     _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
444 }
445 
446 // Updates the cursor image according to its cursor mode
447 //
updateCursorImage(_GLFWwindow * window)448 static void updateCursorImage(_GLFWwindow* window)
449 {
450     if (window->cursorMode == GLFW_CURSOR_NORMAL)
451     {
452         if (window->cursor)
453         {
454             XDefineCursor(_glfw.x11.display, window->x11.handle,
455                           window->cursor->x11.handle);
456         }
457         else
458             XUndefineCursor(_glfw.x11.display, window->x11.handle);
459     }
460     else
461         XDefineCursor(_glfw.x11.display, window->x11.handle, _glfw.x11.cursor);
462 }
463 
464 // Create the X11 window (and its colormap)
465 //
createNativeWindow(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,Visual * visual,int depth)466 static GLFWbool createNativeWindow(_GLFWwindow* window,
467                                    const _GLFWwndconfig* wndconfig,
468                                    Visual* visual, int depth)
469 {
470     // Create a colormap based on the visual used by the current context
471     window->x11.colormap = XCreateColormap(_glfw.x11.display,
472                                            _glfw.x11.root,
473                                            visual,
474                                            AllocNone);
475 
476     // Create the actual window
477     {
478         XSetWindowAttributes wa;
479         const unsigned long wamask = CWBorderPixel | CWColormap | CWEventMask;
480 
481         wa.colormap = window->x11.colormap;
482         wa.border_pixel = 0;
483         wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
484                         PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
485                         ExposureMask | FocusChangeMask | VisibilityChangeMask |
486                         EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
487 
488         _glfwGrabErrorHandlerX11();
489 
490         window->x11.handle = XCreateWindow(_glfw.x11.display,
491                                            _glfw.x11.root,
492                                            0, 0,
493                                            wndconfig->width, wndconfig->height,
494                                            0,      // Border width
495                                            depth,  // Color depth
496                                            InputOutput,
497                                            visual,
498                                            wamask,
499                                            &wa);
500 
501         _glfwReleaseErrorHandlerX11();
502 
503         if (!window->x11.handle)
504         {
505             _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
506                                "X11: Failed to create window");
507             return GLFW_FALSE;
508         }
509 
510         XSaveContext(_glfw.x11.display,
511                      window->x11.handle,
512                      _glfw.x11.context,
513                      (XPointer) window);
514     }
515 
516     if (!wndconfig->decorated)
517     {
518         struct
519         {
520             unsigned long flags;
521             unsigned long functions;
522             unsigned long decorations;
523             long input_mode;
524             unsigned long status;
525         } hints;
526 
527         hints.flags = 2;       // Set decorations
528         hints.decorations = 0; // No decorations
529 
530         XChangeProperty(_glfw.x11.display, window->x11.handle,
531                         _glfw.x11.MOTIF_WM_HINTS,
532                         _glfw.x11.MOTIF_WM_HINTS, 32,
533                         PropModeReplace,
534                         (unsigned char*) &hints,
535                         sizeof(hints) / sizeof(long));
536     }
537 
538     if (_glfw.x11.NET_WM_STATE && !window->monitor)
539     {
540         Atom states[3];
541         int count = 0;
542 
543         if (wndconfig->floating)
544         {
545             if (_glfw.x11.NET_WM_STATE_ABOVE)
546                 states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
547         }
548 
549         if (wndconfig->maximized)
550         {
551             if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
552                 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
553             {
554                 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
555                 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
556             }
557         }
558 
559         if (count)
560         {
561             XChangeProperty(_glfw.x11.display, window->x11.handle,
562                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
563                             PropModeReplace, (unsigned char*) &states, count);
564         }
565     }
566 
567     // Declare the WM protocols supported by GLFW
568     {
569         Atom protocols[] =
570         {
571             _glfw.x11.WM_DELETE_WINDOW,
572             _glfw.x11.NET_WM_PING
573         };
574 
575         XSetWMProtocols(_glfw.x11.display, window->x11.handle,
576                         protocols, sizeof(protocols) / sizeof(Atom));
577     }
578 
579     // Declare our PID
580     {
581         const pid_t pid = getpid();
582 
583         XChangeProperty(_glfw.x11.display,  window->x11.handle,
584                         _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
585                         PropModeReplace,
586                         (unsigned char*) &pid, 1);
587     }
588 
589     if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
590     {
591         Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
592         XChangeProperty(_glfw.x11.display,  window->x11.handle,
593                         _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
594                         PropModeReplace, (unsigned char*) &type, 1);
595     }
596 
597     // Set ICCCM WM_HINTS property
598     {
599         XWMHints* hints = XAllocWMHints();
600         if (!hints)
601         {
602             _glfwInputError(GLFW_OUT_OF_MEMORY,
603                             "X11: Failed to allocate WM hints");
604             return GLFW_FALSE;
605         }
606 
607         hints->flags = StateHint;
608         hints->initial_state = NormalState;
609 
610         XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
611         XFree(hints);
612     }
613 
614     updateNormalHints(window, wndconfig->width, wndconfig->height);
615 
616     // Set ICCCM WM_CLASS property
617     // HACK: Until a mechanism for specifying the application name is added, the
618     //       initial window title is used as the window class name
619     if (strlen(wndconfig->title))
620     {
621         XClassHint* hint = XAllocClassHint();
622         hint->res_name = (char*) wndconfig->title;
623         hint->res_class = (char*) wndconfig->title;
624 
625         XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
626         XFree(hint);
627     }
628 
629     if (_glfw.x11.XdndAware)
630     {
631         // Announce support for Xdnd (drag and drop)
632         const Atom version = 5;
633         XChangeProperty(_glfw.x11.display, window->x11.handle,
634                         _glfw.x11.XdndAware, XA_ATOM, 32,
635                         PropModeReplace, (unsigned char*) &version, 1);
636     }
637 
638     _glfwPlatformSetWindowTitle(window, wndconfig->title);
639 
640     if (_glfw.x11.im)
641     {
642         window->x11.ic = XCreateIC(_glfw.x11.im,
643                                    XNInputStyle,
644                                    XIMPreeditNothing | XIMStatusNothing,
645                                    XNClientWindow,
646                                    window->x11.handle,
647                                    XNFocusWindow,
648                                    window->x11.handle,
649                                    NULL);
650     }
651 
652     _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
653     _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
654 
655     return GLFW_TRUE;
656 }
657 
658 // Set the specified property to the selection converted to the requested target
659 //
writeTargetToProperty(const XSelectionRequestEvent * request)660 static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
661 {
662     int i;
663     const Atom formats[] = { _glfw.x11.UTF8_STRING,
664                              _glfw.x11.COMPOUND_STRING,
665                              XA_STRING };
666     const int formatCount = sizeof(formats) / sizeof(formats[0]);
667 
668     if (request->property == None)
669     {
670         // The requester is a legacy client (ICCCM section 2.2)
671         // We don't support legacy clients, so fail here
672         return None;
673     }
674 
675     if (request->target == _glfw.x11.TARGETS)
676     {
677         // The list of supported targets was requested
678 
679         const Atom targets[] = { _glfw.x11.TARGETS,
680                                  _glfw.x11.MULTIPLE,
681                                  _glfw.x11.UTF8_STRING,
682                                  _glfw.x11.COMPOUND_STRING,
683                                  XA_STRING };
684 
685         XChangeProperty(_glfw.x11.display,
686                         request->requestor,
687                         request->property,
688                         XA_ATOM,
689                         32,
690                         PropModeReplace,
691                         (unsigned char*) targets,
692                         sizeof(targets) / sizeof(targets[0]));
693 
694         return request->property;
695     }
696 
697     if (request->target == _glfw.x11.MULTIPLE)
698     {
699         // Multiple conversions were requested
700 
701         Atom* targets;
702         unsigned long i, count;
703 
704         count = _glfwGetWindowPropertyX11(request->requestor,
705                                           request->property,
706                                           _glfw.x11.ATOM_PAIR,
707                                           (unsigned char**) &targets);
708 
709         for (i = 0;  i < count;  i += 2)
710         {
711             int j;
712 
713             for (j = 0;  j < formatCount;  j++)
714             {
715                 if (targets[i] == formats[j])
716                     break;
717             }
718 
719             if (j < formatCount)
720             {
721                 XChangeProperty(_glfw.x11.display,
722                                 request->requestor,
723                                 targets[i + 1],
724                                 targets[i],
725                                 8,
726                                 PropModeReplace,
727                                 (unsigned char*) _glfw.x11.clipboardString,
728                                 strlen(_glfw.x11.clipboardString));
729             }
730             else
731                 targets[i + 1] = None;
732         }
733 
734         XChangeProperty(_glfw.x11.display,
735                         request->requestor,
736                         request->property,
737                         _glfw.x11.ATOM_PAIR,
738                         32,
739                         PropModeReplace,
740                         (unsigned char*) targets,
741                         count);
742 
743         XFree(targets);
744 
745         return request->property;
746     }
747 
748     if (request->target == _glfw.x11.SAVE_TARGETS)
749     {
750         // The request is a check whether we support SAVE_TARGETS
751         // It should be handled as a no-op side effect target
752 
753         XChangeProperty(_glfw.x11.display,
754                         request->requestor,
755                         request->property,
756                         _glfw.x11.NULL_,
757                         32,
758                         PropModeReplace,
759                         NULL,
760                         0);
761 
762         return request->property;
763     }
764 
765     // Conversion to a data target was requested
766 
767     for (i = 0;  i < formatCount;  i++)
768     {
769         if (request->target == formats[i])
770         {
771             // The requested target is one we support
772 
773             XChangeProperty(_glfw.x11.display,
774                             request->requestor,
775                             request->property,
776                             request->target,
777                             8,
778                             PropModeReplace,
779                             (unsigned char*) _glfw.x11.clipboardString,
780                             strlen(_glfw.x11.clipboardString));
781 
782             return request->property;
783         }
784     }
785 
786     // The requested target is not supported
787 
788     return None;
789 }
790 
handleSelectionClear(XEvent * event)791 static void handleSelectionClear(XEvent* event)
792 {
793     free(_glfw.x11.clipboardString);
794     _glfw.x11.clipboardString = NULL;
795 }
796 
handleSelectionRequest(XEvent * event)797 static void handleSelectionRequest(XEvent* event)
798 {
799     const XSelectionRequestEvent* request = &event->xselectionrequest;
800 
801     XEvent reply;
802     memset(&reply, 0, sizeof(reply));
803 
804     reply.xselection.property = writeTargetToProperty(request);
805     reply.xselection.type = SelectionNotify;
806     reply.xselection.display = request->display;
807     reply.xselection.requestor = request->requestor;
808     reply.xselection.selection = request->selection;
809     reply.xselection.target = request->target;
810     reply.xselection.time = request->time;
811 
812     XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
813 }
814 
pushSelectionToManager(_GLFWwindow * window)815 static void pushSelectionToManager(_GLFWwindow* window)
816 {
817     XConvertSelection(_glfw.x11.display,
818                       _glfw.x11.CLIPBOARD_MANAGER,
819                       _glfw.x11.SAVE_TARGETS,
820                       None,
821                       window->x11.handle,
822                       CurrentTime);
823 
824     for (;;)
825     {
826         XEvent event;
827 
828         while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
829         {
830             switch (event.type)
831             {
832                 case SelectionRequest:
833                     handleSelectionRequest(&event);
834                     break;
835 
836                 case SelectionClear:
837                     handleSelectionClear(&event);
838                     break;
839 
840                 case SelectionNotify:
841                 {
842                     if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
843                     {
844                         // This means one of two things; either the selection was
845                         // not owned, which means there is no clipboard manager, or
846                         // the transfer to the clipboard manager has completed
847                         // In either case, it means we are done here
848                         return;
849                     }
850 
851                     break;
852                 }
853             }
854         }
855 
856         waitForEvent(NULL);
857     }
858 }
859 
860 // Make the specified window and its video mode active on its monitor
861 //
acquireMonitor(_GLFWwindow * window)862 static GLFWbool acquireMonitor(_GLFWwindow* window)
863 {
864     GLFWbool status;
865 
866     if (_glfw.x11.saver.count == 0)
867     {
868         // Remember old screen saver settings
869         XGetScreenSaver(_glfw.x11.display,
870                         &_glfw.x11.saver.timeout,
871                         &_glfw.x11.saver.interval,
872                         &_glfw.x11.saver.blanking,
873                         &_glfw.x11.saver.exposure);
874 
875         // Disable screen saver
876         XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
877                         DefaultExposures);
878     }
879 
880     if (!window->monitor->window)
881         _glfw.x11.saver.count++;
882 
883     status = _glfwSetVideoModeX11(window->monitor, &window->videoMode);
884 
885     if (window->x11.overrideRedirect)
886     {
887         int xpos, ypos;
888         GLFWvidmode mode;
889 
890         // Manually position the window over its monitor
891         _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
892         _glfwPlatformGetVideoMode(window->monitor, &mode);
893 
894         XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
895                           xpos, ypos, mode.width, mode.height);
896     }
897 
898     _glfwInputMonitorWindowChange(window->monitor, window);
899     return status;
900 }
901 
902 // Remove the window and restore the original video mode
903 //
releaseMonitor(_GLFWwindow * window)904 static void releaseMonitor(_GLFWwindow* window)
905 {
906     if (window->monitor->window != window)
907         return;
908 
909     _glfwInputMonitorWindowChange(window->monitor, NULL);
910     _glfwRestoreVideoModeX11(window->monitor);
911 
912     _glfw.x11.saver.count--;
913 
914     if (_glfw.x11.saver.count == 0)
915     {
916         // Restore old screen saver settings
917         XSetScreenSaver(_glfw.x11.display,
918                         _glfw.x11.saver.timeout,
919                         _glfw.x11.saver.interval,
920                         _glfw.x11.saver.blanking,
921                         _glfw.x11.saver.exposure);
922     }
923 }
924 
925 // Decode a Unicode code point from a UTF-8 stream
926 // Based on cutef8 by Jeff Bezanson (Public Domain)
927 //
928 #if defined(X_HAVE_UTF8_STRING)
decodeUTF8(const char ** s)929 static unsigned int decodeUTF8(const char** s)
930 {
931     unsigned int ch = 0, count = 0;
932     static const unsigned int offsets[] =
933     {
934         0x00000000u, 0x00003080u, 0x000e2080u,
935         0x03c82080u, 0xfa082080u, 0x82082080u
936     };
937 
938     do
939     {
940         ch = (ch << 6) + (unsigned char) **s;
941         (*s)++;
942         count++;
943     } while ((**s & 0xc0) == 0x80);
944 
945     assert(count <= 6);
946     return ch - offsets[count - 1];
947 }
948 #endif /*X_HAVE_UTF8_STRING*/
949 
950 // Process the specified X event
951 //
processEvent(XEvent * event)952 static void processEvent(XEvent *event)
953 {
954     _GLFWwindow* window = NULL;
955     int keycode = 0;
956     Bool filtered = False;
957 
958     // HACK: Save scancode as some IMs clear the field in XFilterEvent
959     if (event->type == KeyPress || event->type == KeyRelease)
960         keycode = event->xkey.keycode;
961 
962     if (_glfw.x11.im)
963         filtered = XFilterEvent(event, None);
964 
965     if (_glfw.x11.randr.available)
966     {
967         if (event->type == _glfw.x11.randr.eventBase + RRNotify)
968         {
969             XRRUpdateConfiguration(event);
970             _glfwInputMonitorChange();
971             return;
972         }
973     }
974 
975     if (event->type != GenericEvent)
976     {
977         window = findWindowByHandle(event->xany.window);
978         if (window == NULL)
979         {
980             // This is an event for a window that has already been destroyed
981             return;
982         }
983     }
984 
985     switch (event->type)
986     {
987         case KeyPress:
988         {
989             const int key = translateKey(keycode);
990             const int mods = translateState(event->xkey.state);
991             const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
992 
993             if (window->x11.ic)
994             {
995                 // HACK: Ignore duplicate key press events generated by ibus
996                 //       Corresponding release events are filtered out by the
997                 //       GLFW key repeat logic
998                 if (window->x11.lastKeyCode != keycode ||
999                     window->x11.lastKeyTime != event->xkey.time)
1000                 {
1001                     if (keycode)
1002                         _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
1003                 }
1004 
1005                 window->x11.lastKeyCode = keycode;
1006                 window->x11.lastKeyTime = event->xkey.time;
1007 
1008                 if (!filtered)
1009                 {
1010                     int count;
1011                     Status status;
1012 #if defined(X_HAVE_UTF8_STRING)
1013                     char buffer[100];
1014                     char* chars = buffer;
1015 
1016                     count = Xutf8LookupString(window->x11.ic,
1017                                               &event->xkey,
1018                                               buffer, sizeof(buffer) - 1,
1019                                               NULL, &status);
1020 
1021                     if (status == XBufferOverflow)
1022                     {
1023                         chars = calloc(count + 1, 1);
1024                         count = Xutf8LookupString(window->x11.ic,
1025                                                   &event->xkey,
1026                                                   chars, count,
1027                                                   NULL, &status);
1028                     }
1029 
1030                     if (status == XLookupChars || status == XLookupBoth)
1031                     {
1032                         const char* c = chars;
1033                         chars[count] = '\0';
1034                         while (c - chars < count)
1035                             _glfwInputChar(window, decodeUTF8(&c), mods, plain);
1036                     }
1037 #else /*X_HAVE_UTF8_STRING*/
1038                     wchar_t buffer[16];
1039                     wchar_t* chars = buffer;
1040 
1041                     count = XwcLookupString(window->x11.ic,
1042                                             &event->xkey,
1043                                             buffer, sizeof(buffer) / sizeof(wchar_t),
1044                                             NULL, &status);
1045 
1046                     if (status == XBufferOverflow)
1047                     {
1048                         chars = calloc(count, sizeof(wchar_t));
1049                         count = XwcLookupString(window->x11.ic,
1050                                                 &event->xkey,
1051                                                 chars, count,
1052                                                 NULL, &status);
1053                     }
1054 
1055                     if (status == XLookupChars || status == XLookupBoth)
1056                     {
1057                         int i;
1058                         for (i = 0;  i < count;  i++)
1059                             _glfwInputChar(window, chars[i], mods, plain);
1060                     }
1061 #endif /*X_HAVE_UTF8_STRING*/
1062 
1063                     if (chars != buffer)
1064                         free(chars);
1065                 }
1066             }
1067             else
1068             {
1069                 KeySym keysym;
1070                 XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
1071 
1072                 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
1073 
1074                 const long character = _glfwKeySym2Unicode(keysym);
1075                 if (character != -1)
1076                     _glfwInputChar(window, character, mods, plain);
1077             }
1078 
1079             return;
1080         }
1081 
1082         case KeyRelease:
1083         {
1084             const int key = translateKey(keycode);
1085             const int mods = translateState(event->xkey.state);
1086 
1087             if (!_glfw.x11.xkb.detectable)
1088             {
1089                 // HACK: Key repeat events will arrive as KeyRelease/KeyPress
1090                 //       pairs with similar or identical time stamps
1091                 //       The key repeat logic in _glfwInputKey expects only key
1092                 //       presses to repeat, so detect and discard release events
1093                 if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
1094                 {
1095                     XEvent next;
1096                     XPeekEvent(_glfw.x11.display, &next);
1097 
1098                     if (next.type == KeyPress &&
1099                         next.xkey.window == event->xkey.window &&
1100                         next.xkey.keycode == keycode)
1101                     {
1102                         // HACK: The time of repeat events sometimes doesn't
1103                         //       match that of the press event, so add an
1104                         //       epsilon
1105                         //       Toshiyuki Takahashi can press a button
1106                         //       16 times per second so it's fairly safe to
1107                         //       assume that no human is pressing the key 50
1108                         //       times per second (value is ms)
1109                         if ((next.xkey.time - event->xkey.time) < 20)
1110                         {
1111                             // This is very likely a server-generated key repeat
1112                             // event, so ignore it
1113                             return;
1114                         }
1115                     }
1116                 }
1117             }
1118 
1119             _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
1120             return;
1121         }
1122 
1123         case ButtonPress:
1124         {
1125             const int mods = translateState(event->xbutton.state);
1126 
1127             if (event->xbutton.button == Button1)
1128                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
1129             else if (event->xbutton.button == Button2)
1130                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
1131             else if (event->xbutton.button == Button3)
1132                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
1133 
1134             // Modern X provides scroll events as mouse button presses
1135             else if (event->xbutton.button == Button4)
1136                 _glfwInputScroll(window, 0.0, 1.0);
1137             else if (event->xbutton.button == Button5)
1138                 _glfwInputScroll(window, 0.0, -1.0);
1139             else if (event->xbutton.button == Button6)
1140                 _glfwInputScroll(window, 1.0, 0.0);
1141             else if (event->xbutton.button == Button7)
1142                 _glfwInputScroll(window, -1.0, 0.0);
1143 
1144             else
1145             {
1146                 // Additional buttons after 7 are treated as regular buttons
1147                 // We subtract 4 to fill the gap left by scroll input above
1148                 _glfwInputMouseClick(window,
1149                                      event->xbutton.button - Button1 - 4,
1150                                      GLFW_PRESS,
1151                                      mods);
1152             }
1153 
1154             return;
1155         }
1156 
1157         case ButtonRelease:
1158         {
1159             const int mods = translateState(event->xbutton.state);
1160 
1161             if (event->xbutton.button == Button1)
1162             {
1163                 _glfwInputMouseClick(window,
1164                                      GLFW_MOUSE_BUTTON_LEFT,
1165                                      GLFW_RELEASE,
1166                                      mods);
1167             }
1168             else if (event->xbutton.button == Button2)
1169             {
1170                 _glfwInputMouseClick(window,
1171                                      GLFW_MOUSE_BUTTON_MIDDLE,
1172                                      GLFW_RELEASE,
1173                                      mods);
1174             }
1175             else if (event->xbutton.button == Button3)
1176             {
1177                 _glfwInputMouseClick(window,
1178                                      GLFW_MOUSE_BUTTON_RIGHT,
1179                                      GLFW_RELEASE,
1180                                      mods);
1181             }
1182             else if (event->xbutton.button > Button7)
1183             {
1184                 // Additional buttons after 7 are treated as regular buttons
1185                 // We subtract 4 to fill the gap left by scroll input above
1186                 _glfwInputMouseClick(window,
1187                                      event->xbutton.button - Button1 - 4,
1188                                      GLFW_RELEASE,
1189                                      mods);
1190             }
1191 
1192             return;
1193         }
1194 
1195         case EnterNotify:
1196         {
1197             // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
1198             //       ignore the defined cursor for hidden cursor mode
1199             if (window->cursorMode == GLFW_CURSOR_HIDDEN)
1200                 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_HIDDEN);
1201 
1202             _glfwInputCursorEnter(window, GLFW_TRUE);
1203             return;
1204         }
1205 
1206         case LeaveNotify:
1207         {
1208             _glfwInputCursorEnter(window, GLFW_FALSE);
1209             return;
1210         }
1211 
1212         case MotionNotify:
1213         {
1214             const int x = event->xmotion.x;
1215             const int y = event->xmotion.y;
1216 
1217             if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY)
1218             {
1219                 // The cursor was moved by something other than GLFW
1220 
1221                 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1222                 {
1223                     if (_glfw.x11.disabledCursorWindow != window)
1224                         return;
1225 
1226                     const int dx = x - window->x11.lastCursorPosX;
1227                     const int dy = y - window->x11.lastCursorPosY;
1228 
1229                     _glfwInputCursorPos(window,
1230                                         window->virtualCursorPosX + dx,
1231                                         window->virtualCursorPosY + dy);
1232                 }
1233                 else
1234                     _glfwInputCursorPos(window, x, y);
1235             }
1236 
1237             window->x11.lastCursorPosX = x;
1238             window->x11.lastCursorPosY = y;
1239             return;
1240         }
1241 
1242         case ConfigureNotify:
1243         {
1244             if (event->xconfigure.width != window->x11.width ||
1245                 event->xconfigure.height != window->x11.height)
1246             {
1247                 _glfwInputFramebufferSize(window,
1248                                           event->xconfigure.width,
1249                                           event->xconfigure.height);
1250 
1251                 _glfwInputWindowSize(window,
1252                                      event->xconfigure.width,
1253                                      event->xconfigure.height);
1254 
1255                 window->x11.width = event->xconfigure.width;
1256                 window->x11.height = event->xconfigure.height;
1257             }
1258 
1259             if (event->xconfigure.x != window->x11.xpos ||
1260                 event->xconfigure.y != window->x11.ypos)
1261             {
1262                 if (window->x11.overrideRedirect || event->xany.send_event)
1263                 {
1264                     _glfwInputWindowPos(window,
1265                                         event->xconfigure.x,
1266                                         event->xconfigure.y);
1267 
1268                     window->x11.xpos = event->xconfigure.x;
1269                     window->x11.ypos = event->xconfigure.y;
1270                 }
1271             }
1272 
1273             return;
1274         }
1275 
1276         case ClientMessage:
1277         {
1278             // Custom client message, probably from the window manager
1279 
1280             if (filtered)
1281                 return;
1282 
1283             if (event->xclient.message_type == None)
1284                 return;
1285 
1286             if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
1287             {
1288                 const Atom protocol = event->xclient.data.l[0];
1289                 if (protocol == None)
1290                     return;
1291 
1292                 if (protocol == _glfw.x11.WM_DELETE_WINDOW)
1293                 {
1294                     // The window manager was asked to close the window, for example by
1295                     // the user pressing a 'close' window decoration button
1296                     _glfwInputWindowCloseRequest(window);
1297                 }
1298                 else if (protocol == _glfw.x11.NET_WM_PING)
1299                 {
1300                     // The window manager is pinging the application to ensure it's
1301                     // still responding to events
1302 
1303                     XEvent reply = *event;
1304                     reply.xclient.window = _glfw.x11.root;
1305 
1306                     XSendEvent(_glfw.x11.display, _glfw.x11.root,
1307                                False,
1308                                SubstructureNotifyMask | SubstructureRedirectMask,
1309                                &reply);
1310                 }
1311             }
1312             else if (event->xclient.message_type == _glfw.x11.XdndEnter)
1313             {
1314                 // A drag operation has entered the window
1315                 // TODO: Check if UTF-8 string is supported by the source
1316             }
1317             else if (event->xclient.message_type == _glfw.x11.XdndDrop)
1318             {
1319                 // The drag operation has finished dropping on
1320                 // the window, ask to convert it to a UTF-8 string
1321                 _glfw.x11.xdnd.source = event->xclient.data.l[0];
1322                 XConvertSelection(_glfw.x11.display,
1323                                   _glfw.x11.XdndSelection,
1324                                   _glfw.x11.UTF8_STRING,
1325                                   _glfw.x11.XdndSelection,
1326                                   window->x11.handle, CurrentTime);
1327             }
1328             else if (event->xclient.message_type == _glfw.x11.XdndPosition)
1329             {
1330                 // The drag operation has moved over the window
1331                 const int absX = (event->xclient.data.l[2] >> 16) & 0xFFFF;
1332                 const int absY = (event->xclient.data.l[2]) & 0xFFFF;
1333                 int x, y;
1334 
1335                 _glfwPlatformGetWindowPos(window, &x, &y);
1336                 _glfwInputCursorPos(window, absX - x, absY - y);
1337 
1338                 // Reply that we are ready to copy the dragged data
1339                 XEvent reply;
1340                 memset(&reply, 0, sizeof(reply));
1341 
1342                 reply.type = ClientMessage;
1343                 reply.xclient.window = event->xclient.data.l[0];
1344                 reply.xclient.message_type = _glfw.x11.XdndStatus;
1345                 reply.xclient.format = 32;
1346                 reply.xclient.data.l[0] = window->x11.handle;
1347                 reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle
1348                 reply.xclient.data.l[2] = 0; // Specify an empty rectangle
1349                 reply.xclient.data.l[3] = 0;
1350                 reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
1351 
1352                 XSendEvent(_glfw.x11.display, event->xclient.data.l[0],
1353                            False, NoEventMask, &reply);
1354                 XFlush(_glfw.x11.display);
1355             }
1356 
1357             return;
1358         }
1359 
1360         case SelectionNotify:
1361         {
1362             if (event->xselection.property)
1363             {
1364                 // The converted data from the drag operation has arrived
1365                 char* data;
1366                 const int result =
1367                     _glfwGetWindowPropertyX11(event->xselection.requestor,
1368                                               event->xselection.property,
1369                                               event->xselection.target,
1370                                               (unsigned char**) &data);
1371 
1372                 if (result)
1373                 {
1374                     int i, count;
1375                     char** paths = parseUriList(data, &count);
1376 
1377                     _glfwInputDrop(window, count, (const char**) paths);
1378 
1379                     for (i = 0;  i < count;  i++)
1380                         free(paths[i]);
1381                     free(paths);
1382                 }
1383 
1384                 XFree(data);
1385 
1386                 XEvent reply;
1387                 memset(&reply, 0, sizeof(reply));
1388 
1389                 reply.type = ClientMessage;
1390                 reply.xclient.window = _glfw.x11.xdnd.source;
1391                 reply.xclient.message_type = _glfw.x11.XdndFinished;
1392                 reply.xclient.format = 32;
1393                 reply.xclient.data.l[0] = window->x11.handle;
1394                 reply.xclient.data.l[1] = result;
1395                 reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
1396 
1397                 // Reply that all is well
1398                 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
1399                            False, NoEventMask, &reply);
1400                 XFlush(_glfw.x11.display);
1401             }
1402 
1403             return;
1404         }
1405 
1406         case FocusIn:
1407         {
1408             if (window->cursorMode == GLFW_CURSOR_DISABLED)
1409                 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
1410 
1411             if (event->xfocus.mode == NotifyGrab ||
1412                 event->xfocus.mode == NotifyUngrab)
1413             {
1414                 // Ignore focus events from popup indicator windows, window menu
1415                 // key chords and window dragging
1416                 return;
1417             }
1418 
1419             if (window->x11.ic)
1420                 XSetICFocus(window->x11.ic);
1421 
1422             _glfwInputWindowFocus(window, GLFW_TRUE);
1423             return;
1424         }
1425 
1426         case FocusOut:
1427         {
1428             if (window->cursorMode == GLFW_CURSOR_DISABLED)
1429                 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
1430 
1431             if (event->xfocus.mode == NotifyGrab ||
1432                 event->xfocus.mode == NotifyUngrab)
1433             {
1434                 // Ignore focus events from popup indicator windows, window menu
1435                 // key chords and window dragging
1436                 return;
1437             }
1438 
1439             if (window->x11.ic)
1440                 XUnsetICFocus(window->x11.ic);
1441 
1442             if (window->monitor && window->autoIconify)
1443                 _glfwPlatformIconifyWindow(window);
1444 
1445             _glfwInputWindowFocus(window, GLFW_FALSE);
1446             return;
1447         }
1448 
1449         case Expose:
1450         {
1451             _glfwInputWindowDamage(window);
1452             return;
1453         }
1454 
1455         case PropertyNotify:
1456         {
1457             if (event->xproperty.atom == _glfw.x11.WM_STATE &&
1458                 event->xproperty.state == PropertyNewValue)
1459             {
1460                 const int state = getWindowState(window);
1461                 if (state == IconicState)
1462                 {
1463                     if (window->monitor)
1464                         releaseMonitor(window);
1465 
1466                     _glfwInputWindowIconify(window, GLFW_TRUE);
1467                 }
1468                 else if (state == NormalState)
1469                 {
1470                     if (window->monitor)
1471                         acquireMonitor(window);
1472 
1473                     _glfwInputWindowIconify(window, GLFW_FALSE);
1474                 }
1475             }
1476 
1477             return;
1478         }
1479 
1480         case SelectionClear:
1481         {
1482             handleSelectionClear(event);
1483             return;
1484         }
1485 
1486         case SelectionRequest:
1487         {
1488             handleSelectionRequest(event);
1489             return;
1490         }
1491 
1492         case DestroyNotify:
1493             return;
1494     }
1495 }
1496 
1497 
1498 //////////////////////////////////////////////////////////////////////////
1499 //////                       GLFW internal API                      //////
1500 //////////////////////////////////////////////////////////////////////////
1501 
1502 // Retrieve a single window property of the specified type
1503 // Inspired by fghGetWindowProperty from freeglut
1504 //
_glfwGetWindowPropertyX11(Window window,Atom property,Atom type,unsigned char ** value)1505 unsigned long _glfwGetWindowPropertyX11(Window window,
1506                                         Atom property,
1507                                         Atom type,
1508                                         unsigned char** value)
1509 {
1510     Atom actualType;
1511     int actualFormat;
1512     unsigned long itemCount, bytesAfter;
1513 
1514     XGetWindowProperty(_glfw.x11.display,
1515                        window,
1516                        property,
1517                        0,
1518                        LONG_MAX,
1519                        False,
1520                        type,
1521                        &actualType,
1522                        &actualFormat,
1523                        &itemCount,
1524                        &bytesAfter,
1525                        value);
1526 
1527     if (type != AnyPropertyType && actualType != type)
1528         return 0;
1529 
1530     return itemCount;
1531 }
1532 
1533 
1534 //////////////////////////////////////////////////////////////////////////
1535 //////                       GLFW platform API                      //////
1536 //////////////////////////////////////////////////////////////////////////
1537 
_glfwPlatformCreateWindow(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWctxconfig * ctxconfig,const _GLFWfbconfig * fbconfig)1538 int _glfwPlatformCreateWindow(_GLFWwindow* window,
1539                               const _GLFWwndconfig* wndconfig,
1540                               const _GLFWctxconfig* ctxconfig,
1541                               const _GLFWfbconfig* fbconfig)
1542 {
1543     Visual* visual;
1544     int depth;
1545 
1546     if (ctxconfig->client == GLFW_NO_API)
1547     {
1548         visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
1549         depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
1550     }
1551     else
1552     {
1553         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
1554         {
1555             if (!_glfwInitGLX())
1556                 return GLFW_FALSE;
1557             if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth))
1558                 return GLFW_FALSE;
1559         }
1560         else
1561         {
1562             if (!_glfwInitEGL())
1563                 return GLFW_FALSE;
1564             if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth))
1565                 return GLFW_FALSE;
1566         }
1567     }
1568 
1569     if (!createNativeWindow(window, wndconfig, visual, depth))
1570         return GLFW_FALSE;
1571 
1572     if (ctxconfig->client != GLFW_NO_API)
1573     {
1574         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
1575         {
1576             if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
1577                 return GLFW_FALSE;
1578         }
1579         else
1580         {
1581             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
1582                 return GLFW_FALSE;
1583         }
1584     }
1585 
1586     if (window->monitor)
1587     {
1588         _glfwPlatformShowWindow(window);
1589         updateWindowMode(window);
1590         if (!acquireMonitor(window))
1591             return GLFW_FALSE;
1592 
1593         centerCursor(window);
1594     }
1595 
1596     XFlush(_glfw.x11.display);
1597     return GLFW_TRUE;
1598 }
1599 
_glfwPlatformDestroyWindow(_GLFWwindow * window)1600 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
1601 {
1602     if (_glfw.x11.disabledCursorWindow == window)
1603         _glfw.x11.disabledCursorWindow = NULL;
1604 
1605     if (window->monitor)
1606         releaseMonitor(window);
1607 
1608     if (window->x11.ic)
1609     {
1610         XDestroyIC(window->x11.ic);
1611         window->x11.ic = NULL;
1612     }
1613 
1614     if (window->context.destroy)
1615         window->context.destroy(window);
1616 
1617     if (window->x11.handle)
1618     {
1619         if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) ==
1620             window->x11.handle)
1621         {
1622             pushSelectionToManager(window);
1623         }
1624 
1625         XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
1626         XUnmapWindow(_glfw.x11.display, window->x11.handle);
1627         XDestroyWindow(_glfw.x11.display, window->x11.handle);
1628         window->x11.handle = (Window) 0;
1629     }
1630 
1631     if (window->x11.colormap)
1632     {
1633         XFreeColormap(_glfw.x11.display, window->x11.colormap);
1634         window->x11.colormap = (Colormap) 0;
1635     }
1636 
1637     XFlush(_glfw.x11.display);
1638 }
1639 
_glfwPlatformSetWindowTitle(_GLFWwindow * window,const char * title)1640 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
1641 {
1642 #if defined(X_HAVE_UTF8_STRING)
1643     Xutf8SetWMProperties(_glfw.x11.display,
1644                          window->x11.handle,
1645                          title, title,
1646                          NULL, 0,
1647                          NULL, NULL, NULL);
1648 #else
1649     // This may be a slightly better fallback than using XStoreName and
1650     // XSetIconName, which always store their arguments using STRING
1651     XmbSetWMProperties(_glfw.x11.display,
1652                        window->x11.handle,
1653                        title, title,
1654                        NULL, 0,
1655                        NULL, NULL, NULL);
1656 #endif
1657 
1658     XChangeProperty(_glfw.x11.display,  window->x11.handle,
1659                     _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
1660                     PropModeReplace,
1661                     (unsigned char*) title, strlen(title));
1662 
1663     XChangeProperty(_glfw.x11.display,  window->x11.handle,
1664                     _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
1665                     PropModeReplace,
1666                     (unsigned char*) title, strlen(title));
1667 
1668     XFlush(_glfw.x11.display);
1669 }
1670 
_glfwPlatformSetWindowIcon(_GLFWwindow * window,int count,const GLFWimage * images)1671 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
1672                                 int count, const GLFWimage* images)
1673 {
1674     if (count)
1675     {
1676         int i, j, longCount = 0;
1677 
1678         for (i = 0;  i < count;  i++)
1679             longCount += 2 + images[i].width * images[i].height;
1680 
1681         long* icon = calloc(longCount, sizeof(long));
1682         long* target = icon;
1683 
1684         for (i = 0;  i < count;  i++)
1685         {
1686             *target++ = images[i].width;
1687             *target++ = images[i].height;
1688 
1689             for (j = 0;  j < images[i].width * images[i].height;  j++)
1690             {
1691                 *target++ = (images[i].pixels[j * 4 + 0] << 16) |
1692                             (images[i].pixels[j * 4 + 1] <<  8) |
1693                             (images[i].pixels[j * 4 + 2] <<  0) |
1694                             (images[i].pixels[j * 4 + 3] << 24);
1695             }
1696         }
1697 
1698         XChangeProperty(_glfw.x11.display, window->x11.handle,
1699                         _glfw.x11.NET_WM_ICON,
1700                         XA_CARDINAL, 32,
1701                         PropModeReplace,
1702                         (unsigned char*) icon,
1703                         longCount);
1704 
1705         free(icon);
1706     }
1707     else
1708     {
1709         XDeleteProperty(_glfw.x11.display, window->x11.handle,
1710                         _glfw.x11.NET_WM_ICON);
1711     }
1712 
1713     XFlush(_glfw.x11.display);
1714 }
1715 
_glfwPlatformGetWindowPos(_GLFWwindow * window,int * xpos,int * ypos)1716 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
1717 {
1718     Window dummy;
1719     int x, y;
1720 
1721     XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
1722                           0, 0, &x, &y, &dummy);
1723 
1724     if (xpos)
1725         *xpos = x;
1726     if (ypos)
1727         *ypos = y;
1728 }
1729 
_glfwPlatformSetWindowPos(_GLFWwindow * window,int xpos,int ypos)1730 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
1731 {
1732     // HACK: Explicitly setting PPosition to any value causes some WMs, notably
1733     //       Compiz and Metacity, to honor the position of unmapped windows
1734     if (!_glfwPlatformWindowVisible(window))
1735     {
1736         long supplied;
1737         XSizeHints* hints = XAllocSizeHints();
1738 
1739         if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
1740         {
1741             hints->flags |= PPosition;
1742             hints->x = hints->y = 0;
1743 
1744             XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
1745         }
1746 
1747         XFree(hints);
1748     }
1749 
1750     XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
1751     XFlush(_glfw.x11.display);
1752 }
1753 
_glfwPlatformGetWindowSize(_GLFWwindow * window,int * width,int * height)1754 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
1755 {
1756     XWindowAttributes attribs;
1757     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
1758 
1759     if (width)
1760         *width = attribs.width;
1761     if (height)
1762         *height = attribs.height;
1763 }
1764 
_glfwPlatformSetWindowSize(_GLFWwindow * window,int width,int height)1765 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
1766 {
1767     if (window->monitor)
1768     {
1769         if (window->monitor->window == window)
1770             acquireMonitor(window);
1771     }
1772     else
1773     {
1774         if (!window->resizable)
1775             updateNormalHints(window, width, height);
1776 
1777         XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
1778     }
1779 
1780     XFlush(_glfw.x11.display);
1781 }
1782 
_glfwPlatformSetWindowSizeLimits(_GLFWwindow * window,int minwidth,int minheight,int maxwidth,int maxheight)1783 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
1784                                       int minwidth, int minheight,
1785                                       int maxwidth, int maxheight)
1786 {
1787     int width, height;
1788     _glfwPlatformGetWindowSize(window, &width, &height);
1789     updateNormalHints(window, width, height);
1790     XFlush(_glfw.x11.display);
1791 }
1792 
_glfwPlatformSetWindowAspectRatio(_GLFWwindow * window,int numer,int denom)1793 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
1794 {
1795     int width, height;
1796     _glfwPlatformGetWindowSize(window, &width, &height);
1797     updateNormalHints(window, width, height);
1798     XFlush(_glfw.x11.display);
1799 }
1800 
_glfwPlatformGetFramebufferSize(_GLFWwindow * window,int * width,int * height)1801 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
1802 {
1803     _glfwPlatformGetWindowSize(window, width, height);
1804 }
1805 
_glfwPlatformGetWindowFrameSize(_GLFWwindow * window,int * left,int * top,int * right,int * bottom)1806 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
1807                                      int* left, int* top,
1808                                      int* right, int* bottom)
1809 {
1810     long* extents = NULL;
1811 
1812     if (window->monitor || !window->decorated)
1813         return;
1814 
1815     if (_glfw.x11.NET_FRAME_EXTENTS == None)
1816         return;
1817 
1818     if (!_glfwPlatformWindowVisible(window) &&
1819         _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
1820     {
1821         XEvent event;
1822         double timeout = 0.5;
1823 
1824         // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
1825         // function before the window is mapped
1826         sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
1827                       0, 0, 0, 0, 0);
1828 
1829         // HACK: Use a timeout because earlier versions of some window managers
1830         //       (at least Unity, Fluxbox and Xfwm) failed to send the reply
1831         //       They have been fixed but broken versions are still in the wild
1832         //       If you are affected by this and your window manager is NOT
1833         //       listed above, PLEASE report it to their and our issue trackers
1834         while (!XCheckIfEvent(_glfw.x11.display,
1835                               &event,
1836                               isFrameExtentsEvent,
1837                               (XPointer) window))
1838         {
1839             if (!waitForEvent(&timeout))
1840             {
1841                 _glfwInputError(GLFW_PLATFORM_ERROR,
1842                                 "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
1843                 return;
1844             }
1845         }
1846     }
1847 
1848     if (_glfwGetWindowPropertyX11(window->x11.handle,
1849                                   _glfw.x11.NET_FRAME_EXTENTS,
1850                                   XA_CARDINAL,
1851                                   (unsigned char**) &extents) == 4)
1852     {
1853         if (left)
1854             *left = extents[0];
1855         if (top)
1856             *top = extents[2];
1857         if (right)
1858             *right = extents[1];
1859         if (bottom)
1860             *bottom = extents[3];
1861     }
1862 
1863     if (extents)
1864         XFree(extents);
1865 }
1866 
_glfwPlatformIconifyWindow(_GLFWwindow * window)1867 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
1868 {
1869     if (window->x11.overrideRedirect)
1870     {
1871         // Override-redirect windows cannot be iconified or restored, as those
1872         // tasks are performed by the window manager
1873         _glfwInputError(GLFW_PLATFORM_ERROR,
1874                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
1875         return;
1876     }
1877 
1878     XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
1879     XFlush(_glfw.x11.display);
1880 }
1881 
_glfwPlatformRestoreWindow(_GLFWwindow * window)1882 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
1883 {
1884     if (window->x11.overrideRedirect)
1885     {
1886         // Override-redirect windows cannot be iconified or restored, as those
1887         // tasks are performed by the window manager
1888         _glfwInputError(GLFW_PLATFORM_ERROR,
1889                         "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
1890         return;
1891     }
1892 
1893     if (_glfwPlatformWindowIconified(window))
1894     {
1895         XMapWindow(_glfw.x11.display, window->x11.handle);
1896         waitForVisibilityNotify(window);
1897     }
1898     else if (_glfwPlatformWindowVisible(window))
1899     {
1900         if (_glfw.x11.NET_WM_STATE &&
1901             _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
1902             _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
1903         {
1904             sendEventToWM(window,
1905                           _glfw.x11.NET_WM_STATE,
1906                           _NET_WM_STATE_REMOVE,
1907                           _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
1908                           _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
1909                           1, 0);
1910         }
1911     }
1912 
1913     XFlush(_glfw.x11.display);
1914 }
1915 
_glfwPlatformMaximizeWindow(_GLFWwindow * window)1916 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
1917 {
1918     if (_glfw.x11.NET_WM_STATE &&
1919         _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
1920         _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
1921     {
1922         sendEventToWM(window,
1923                       _glfw.x11.NET_WM_STATE,
1924                       _NET_WM_STATE_ADD,
1925                       _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
1926                       _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
1927                       1, 0);
1928         XFlush(_glfw.x11.display);
1929     }
1930 }
1931 
_glfwPlatformShowWindow(_GLFWwindow * window)1932 void _glfwPlatformShowWindow(_GLFWwindow* window)
1933 {
1934     if (_glfwPlatformWindowVisible(window))
1935         return;
1936 
1937     XMapWindow(_glfw.x11.display, window->x11.handle);
1938     waitForVisibilityNotify(window);
1939 }
1940 
_glfwPlatformHideWindow(_GLFWwindow * window)1941 void _glfwPlatformHideWindow(_GLFWwindow* window)
1942 {
1943     XUnmapWindow(_glfw.x11.display, window->x11.handle);
1944     XFlush(_glfw.x11.display);
1945 }
1946 
_glfwPlatformFocusWindow(_GLFWwindow * window)1947 void _glfwPlatformFocusWindow(_GLFWwindow* window)
1948 {
1949     if (_glfw.x11.NET_ACTIVE_WINDOW)
1950         sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
1951     else
1952     {
1953         XRaiseWindow(_glfw.x11.display, window->x11.handle);
1954         XSetInputFocus(_glfw.x11.display, window->x11.handle,
1955                        RevertToParent, CurrentTime);
1956     }
1957 
1958     XFlush(_glfw.x11.display);
1959 }
1960 
_glfwPlatformSetWindowMonitor(_GLFWwindow * window,_GLFWmonitor * monitor,int xpos,int ypos,int width,int height,int refreshRate)1961 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
1962                                    _GLFWmonitor* monitor,
1963                                    int xpos, int ypos,
1964                                    int width, int height,
1965                                    int refreshRate)
1966 {
1967     if (window->monitor == monitor)
1968     {
1969         if (monitor)
1970         {
1971             if (monitor->window == window)
1972                 acquireMonitor(window);
1973         }
1974         else
1975         {
1976             XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
1977                               xpos, ypos, width, height);
1978         }
1979 
1980         return;
1981     }
1982 
1983     if (window->monitor)
1984         releaseMonitor(window);
1985 
1986     _glfwInputWindowMonitorChange(window, monitor);
1987     updateNormalHints(window, width, height);
1988     updateWindowMode(window);
1989 
1990     if (window->monitor)
1991     {
1992         XMapRaised(_glfw.x11.display, window->x11.handle);
1993         if (waitForVisibilityNotify(window))
1994             acquireMonitor(window);
1995     }
1996     else
1997     {
1998         XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
1999                           xpos, ypos, width, height);
2000     }
2001 
2002     XFlush(_glfw.x11.display);
2003 }
2004 
_glfwPlatformWindowFocused(_GLFWwindow * window)2005 int _glfwPlatformWindowFocused(_GLFWwindow* window)
2006 {
2007     Window focused;
2008     int state;
2009 
2010     XGetInputFocus(_glfw.x11.display, &focused, &state);
2011     return window->x11.handle == focused;
2012 }
2013 
_glfwPlatformWindowIconified(_GLFWwindow * window)2014 int _glfwPlatformWindowIconified(_GLFWwindow* window)
2015 {
2016     return getWindowState(window) == IconicState;
2017 }
2018 
_glfwPlatformWindowVisible(_GLFWwindow * window)2019 int _glfwPlatformWindowVisible(_GLFWwindow* window)
2020 {
2021     XWindowAttributes wa;
2022     XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
2023     return wa.map_state == IsViewable;
2024 }
2025 
_glfwPlatformWindowMaximized(_GLFWwindow * window)2026 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
2027 {
2028     Atom* states;
2029     unsigned long i;
2030     GLFWbool maximized = GLFW_FALSE;
2031     const unsigned long count =
2032         _glfwGetWindowPropertyX11(window->x11.handle,
2033                                   _glfw.x11.NET_WM_STATE,
2034                                   XA_ATOM,
2035                                   (unsigned char**) &states);
2036 
2037     for (i = 0;  i < count;  i++)
2038     {
2039         if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
2040             states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
2041         {
2042             maximized = GLFW_TRUE;
2043             break;
2044         }
2045     }
2046 
2047     XFree(states);
2048     return maximized;
2049 }
2050 
_glfwPlatformPollEvents(void)2051 void _glfwPlatformPollEvents(void)
2052 {
2053     _glfwPollJoystickEvents();
2054 
2055     int count = XPending(_glfw.x11.display);
2056     while (count--)
2057     {
2058         XEvent event;
2059         XNextEvent(_glfw.x11.display, &event);
2060         processEvent(&event);
2061     }
2062 
2063     if (_glfw.x11.disabledCursorWindow)
2064         centerCursor(_glfw.x11.disabledCursorWindow);
2065 
2066     XFlush(_glfw.x11.display);
2067 }
2068 
_glfwPlatformWaitEvents(void)2069 void _glfwPlatformWaitEvents(void)
2070 {
2071     while (!XPending(_glfw.x11.display))
2072         waitForEvent(NULL);
2073 
2074     _glfwPlatformPollEvents();
2075 }
2076 
_glfwPlatformWaitEventsTimeout(double timeout)2077 void _glfwPlatformWaitEventsTimeout(double timeout)
2078 {
2079     while (!XPending(_glfw.x11.display))
2080     {
2081         if (!waitForEvent(&timeout))
2082             break;
2083     }
2084 
2085     _glfwPlatformPollEvents();
2086 }
2087 
_glfwPlatformPostEmptyEvent(void)2088 void _glfwPlatformPostEmptyEvent(void)
2089 {
2090     XEvent event;
2091     _GLFWwindow* window = _glfw.windowListHead;
2092 
2093     memset(&event, 0, sizeof(event));
2094     event.type = ClientMessage;
2095     event.xclient.window = window->x11.handle;
2096     event.xclient.format = 32; // Data is 32-bit longs
2097     event.xclient.message_type = _glfw.x11.NULL_;
2098 
2099     XSendEvent(_glfw.x11.display, window->x11.handle, False, 0, &event);
2100     XFlush(_glfw.x11.display);
2101 }
2102 
_glfwPlatformGetCursorPos(_GLFWwindow * window,double * xpos,double * ypos)2103 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
2104 {
2105     Window root, child;
2106     int rootX, rootY, childX, childY;
2107     unsigned int mask;
2108 
2109     XQueryPointer(_glfw.x11.display, window->x11.handle,
2110                   &root, &child,
2111                   &rootX, &rootY, &childX, &childY,
2112                   &mask);
2113 
2114     if (xpos)
2115         *xpos = childX;
2116     if (ypos)
2117         *ypos = childY;
2118 }
2119 
_glfwPlatformSetCursorPos(_GLFWwindow * window,double x,double y)2120 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
2121 {
2122     // Store the new position so it can be recognized later
2123     window->x11.warpCursorPosX = (int) x;
2124     window->x11.warpCursorPosY = (int) y;
2125 
2126     XWarpPointer(_glfw.x11.display, None, window->x11.handle,
2127                  0,0,0,0, (int) x, (int) y);
2128     XFlush(_glfw.x11.display);
2129 }
2130 
_glfwPlatformSetCursorMode(_GLFWwindow * window,int mode)2131 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
2132 {
2133     if (mode == GLFW_CURSOR_DISABLED)
2134     {
2135         _glfw.x11.disabledCursorWindow = window;
2136         _glfwPlatformGetCursorPos(window,
2137                                   &_glfw.x11.restoreCursorPosX,
2138                                   &_glfw.x11.restoreCursorPosY);
2139         centerCursor(window);
2140         XGrabPointer(_glfw.x11.display, window->x11.handle, True,
2141                      ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
2142                      GrabModeAsync, GrabModeAsync,
2143                      window->x11.handle, _glfw.x11.cursor, CurrentTime);
2144     }
2145     else if (_glfw.x11.disabledCursorWindow == window)
2146     {
2147         _glfw.x11.disabledCursorWindow = NULL;
2148         XUngrabPointer(_glfw.x11.display, CurrentTime);
2149         _glfwPlatformSetCursorPos(window,
2150                                   _glfw.x11.restoreCursorPosX,
2151                                   _glfw.x11.restoreCursorPosY);
2152     }
2153 
2154     updateCursorImage(window);
2155     XFlush(_glfw.x11.display);
2156 }
2157 
_glfwPlatformGetKeyName(int key,int scancode)2158 const char* _glfwPlatformGetKeyName(int key, int scancode)
2159 {
2160     KeySym keysym;
2161     int extra;
2162 
2163     if (!_glfw.x11.xkb.available)
2164         return NULL;
2165 
2166     if (key != GLFW_KEY_UNKNOWN)
2167         scancode = _glfw.x11.nativeKeys[key];
2168 
2169     if (!_glfwIsPrintable(_glfw.x11.publicKeys[scancode]))
2170         return NULL;
2171 
2172     keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
2173     if (keysym == NoSymbol)
2174       return NULL;
2175 
2176     XkbTranslateKeySym(_glfw.x11.display, &keysym, 0,
2177                        _glfw.x11.keyName, sizeof(_glfw.x11.keyName),
2178                        &extra);
2179 
2180     if (!strlen(_glfw.x11.keyName))
2181         return NULL;
2182 
2183     return _glfw.x11.keyName;
2184 }
2185 
_glfwPlatformCreateCursor(_GLFWcursor * cursor,const GLFWimage * image,int xhot,int yhot)2186 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
2187                               const GLFWimage* image,
2188                               int xhot, int yhot)
2189 {
2190     cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
2191     if (!cursor->x11.handle)
2192         return GLFW_FALSE;
2193 
2194     return GLFW_TRUE;
2195 }
2196 
_glfwPlatformCreateStandardCursor(_GLFWcursor * cursor,int shape)2197 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
2198 {
2199     cursor->x11.handle = XCreateFontCursor(_glfw.x11.display,
2200                                            translateCursorShape(shape));
2201     if (!cursor->x11.handle)
2202     {
2203         _glfwInputError(GLFW_PLATFORM_ERROR,
2204                         "X11: Failed to create standard cursor");
2205         return GLFW_FALSE;
2206     }
2207 
2208     return GLFW_TRUE;
2209 }
2210 
_glfwPlatformDestroyCursor(_GLFWcursor * cursor)2211 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
2212 {
2213     if (cursor->x11.handle)
2214         XFreeCursor(_glfw.x11.display, cursor->x11.handle);
2215 }
2216 
_glfwPlatformSetCursor(_GLFWwindow * window,_GLFWcursor * cursor)2217 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
2218 {
2219     if (window->cursorMode == GLFW_CURSOR_NORMAL)
2220     {
2221         updateCursorImage(window);
2222         XFlush(_glfw.x11.display);
2223     }
2224 }
2225 
_glfwPlatformSetClipboardString(_GLFWwindow * window,const char * string)2226 void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
2227 {
2228     free(_glfw.x11.clipboardString);
2229     _glfw.x11.clipboardString = strdup(string);
2230 
2231     XSetSelectionOwner(_glfw.x11.display,
2232                        _glfw.x11.CLIPBOARD,
2233                        window->x11.handle, CurrentTime);
2234 
2235     if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
2236         window->x11.handle)
2237     {
2238         _glfwInputError(GLFW_PLATFORM_ERROR,
2239                         "X11: Failed to become owner of clipboard selection");
2240     }
2241 }
2242 
_glfwPlatformGetClipboardString(_GLFWwindow * window)2243 const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
2244 {
2245     size_t i;
2246     const Atom formats[] = { _glfw.x11.UTF8_STRING,
2247                              _glfw.x11.COMPOUND_STRING,
2248                              XA_STRING };
2249     const size_t formatCount = sizeof(formats) / sizeof(formats[0]);
2250 
2251     if (findWindowByHandle(XGetSelectionOwner(_glfw.x11.display,
2252                                               _glfw.x11.CLIPBOARD)))
2253     {
2254         // Instead of doing a large number of X round-trips just to put this
2255         // string into a window property and then read it back, just return it
2256         return _glfw.x11.clipboardString;
2257     }
2258 
2259     free(_glfw.x11.clipboardString);
2260     _glfw.x11.clipboardString = NULL;
2261 
2262     for (i = 0;  i < formatCount;  i++)
2263     {
2264         char* data;
2265         XEvent event;
2266 
2267         XConvertSelection(_glfw.x11.display,
2268                           _glfw.x11.CLIPBOARD,
2269                           formats[i],
2270                           _glfw.x11.GLFW_SELECTION,
2271                           window->x11.handle, CurrentTime);
2272 
2273         while (!XCheckTypedEvent(_glfw.x11.display, SelectionNotify, &event))
2274             waitForEvent(NULL);
2275 
2276         if (event.xselection.property == None)
2277             continue;
2278 
2279         if (_glfwGetWindowPropertyX11(event.xselection.requestor,
2280                                       event.xselection.property,
2281                                       event.xselection.target,
2282                                       (unsigned char**) &data))
2283         {
2284             _glfw.x11.clipboardString = strdup(data);
2285         }
2286 
2287         XFree(data);
2288 
2289         XDeleteProperty(_glfw.x11.display,
2290                         event.xselection.requestor,
2291                         event.xselection.property);
2292 
2293         if (_glfw.x11.clipboardString)
2294             break;
2295     }
2296 
2297     if (_glfw.x11.clipboardString == NULL)
2298     {
2299         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
2300                         "X11: Failed to convert clipboard to string");
2301     }
2302 
2303     return _glfw.x11.clipboardString;
2304 }
2305 
_glfwPlatformGetRequiredInstanceExtensions(uint32_t * count)2306 char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count)
2307 {
2308     char** extensions;
2309 
2310     *count = 0;
2311 
2312     if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
2313     {
2314         if (!_glfw.vk.KHR_xlib_surface)
2315             return NULL;
2316     }
2317 
2318     extensions = calloc(2, sizeof(char*));
2319     extensions[0] = strdup("VK_KHR_surface");
2320 
2321     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
2322         extensions[1] = strdup("VK_KHR_xcb_surface");
2323     else
2324         extensions[1] = strdup("VK_KHR_xlib_surface");
2325 
2326     *count = 2;
2327     return extensions;
2328 }
2329 
_glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,VkPhysicalDevice device,uint32_t queuefamily)2330 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
2331                                                       VkPhysicalDevice device,
2332                                                       uint32_t queuefamily)
2333 {
2334     VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
2335                                                           _glfw.x11.screen));
2336 
2337     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
2338     {
2339         PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR =
2340             (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
2341             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
2342         if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
2343         {
2344             _glfwInputError(GLFW_API_UNAVAILABLE,
2345                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
2346             return GLFW_FALSE;
2347         }
2348 
2349         xcb_connection_t* connection =
2350             _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display);
2351         if (!connection)
2352         {
2353             _glfwInputError(GLFW_PLATFORM_ERROR,
2354                             "X11: Failed to retrieve XCB connection");
2355             return GLFW_FALSE;
2356         }
2357 
2358         return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
2359                                                             queuefamily,
2360                                                             connection,
2361                                                             visualID);
2362     }
2363     else
2364     {
2365         PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR =
2366             (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
2367             vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
2368         if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
2369         {
2370             _glfwInputError(GLFW_API_UNAVAILABLE,
2371                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
2372             return GLFW_FALSE;
2373         }
2374 
2375         return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
2376                                                              queuefamily,
2377                                                              _glfw.x11.display,
2378                                                              visualID);
2379     }
2380 }
2381 
_glfwPlatformCreateWindowSurface(VkInstance instance,_GLFWwindow * window,const VkAllocationCallbacks * allocator,VkSurfaceKHR * surface)2382 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
2383                                           _GLFWwindow* window,
2384                                           const VkAllocationCallbacks* allocator,
2385                                           VkSurfaceKHR* surface)
2386 {
2387     if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
2388     {
2389         VkResult err;
2390         VkXcbSurfaceCreateInfoKHR sci;
2391         PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
2392 
2393         xcb_connection_t* connection =
2394             _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display);
2395         if (!connection)
2396         {
2397             _glfwInputError(GLFW_PLATFORM_ERROR,
2398                             "X11: Failed to retrieve XCB connection");
2399             return VK_ERROR_EXTENSION_NOT_PRESENT;
2400         }
2401 
2402         vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
2403             vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
2404         if (!vkCreateXcbSurfaceKHR)
2405         {
2406             _glfwInputError(GLFW_API_UNAVAILABLE,
2407                             "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
2408             return VK_ERROR_EXTENSION_NOT_PRESENT;
2409         }
2410 
2411         memset(&sci, 0, sizeof(sci));
2412         sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
2413         sci.connection = connection;
2414         sci.window = window->x11.handle;
2415 
2416         err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
2417         if (err)
2418         {
2419             _glfwInputError(GLFW_PLATFORM_ERROR,
2420                             "X11: Failed to create Vulkan XCB surface: %s",
2421                             _glfwGetVulkanResultString(err));
2422         }
2423 
2424         return err;
2425     }
2426     else
2427     {
2428         VkResult err;
2429         VkXlibSurfaceCreateInfoKHR sci;
2430         PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
2431 
2432         vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
2433             vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
2434         if (!vkCreateXlibSurfaceKHR)
2435         {
2436             _glfwInputError(GLFW_API_UNAVAILABLE,
2437                             "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
2438             return VK_ERROR_EXTENSION_NOT_PRESENT;
2439         }
2440 
2441         memset(&sci, 0, sizeof(sci));
2442         sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
2443         sci.dpy = _glfw.x11.display;
2444         sci.window = window->x11.handle;
2445 
2446         err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
2447         if (err)
2448         {
2449             _glfwInputError(GLFW_PLATFORM_ERROR,
2450                             "X11: Failed to create Vulkan X11 surface: %s",
2451                             _glfwGetVulkanResultString(err));
2452         }
2453 
2454         return err;
2455     }
2456 }
2457 
2458 
2459 //////////////////////////////////////////////////////////////////////////
2460 //////                        GLFW native API                       //////
2461 //////////////////////////////////////////////////////////////////////////
2462 
glfwGetX11Display(void)2463 GLFWAPI Display* glfwGetX11Display(void)
2464 {
2465     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
2466     return _glfw.x11.display;
2467 }
2468 
glfwGetX11Window(GLFWwindow * handle)2469 GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
2470 {
2471     _GLFWwindow* window = (_GLFWwindow*) handle;
2472     _GLFW_REQUIRE_INIT_OR_RETURN(None);
2473     return window->x11.handle;
2474 }
2475 
2476