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