1 /// Translated from C to D
2 module glfw3.wl_init;
3 
4 extern(C): @nogc: nothrow: __gshared:
5 
6 private template HasVersion(string versionId) {
7 	mixin("version("~versionId~") {enum HasVersion = true;} else {enum HasVersion = false;}");
8 }
9 import core.stdc.config: c_long, c_ulong;
10 //========================================================================
11 // GLFW 3.3 Wayland - www.glfw.org
12 //------------------------------------------------------------------------
13 // Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
14 //
15 // This software is provided 'as-is', without any express or implied
16 // warranty. In no event will the authors be held liable for any damages
17 // arising from the use of this software.
18 //
19 // Permission is granted to anyone to use this software for any purpose,
20 // including commercial applications, and to alter it and redistribute it
21 // freely, subject to the following restrictions:
22 //
23 // 1. The origin of this software must not be misrepresented; you must not
24 //    claim that you wrote the original software. If you use this software
25 //    in a product, an acknowledgment in the product documentation would
26 //    be appreciated but is not required.
27 //
28 // 2. Altered source versions must be plainly marked as such, and must not
29 //    be misrepresented as being the original software.
30 //
31 // 3. This notice may not be removed or altered from any source
32 //    distribution.
33 //
34 //========================================================================
35 // It is fine to use C99 in this file because it will not be built with VS
36 //========================================================================
37 
38 public import glfw3.internal;
39 
40 import core.stdc.assert_;
41 import core.stdc.errno;
42 import core.stdc.limits;
43 import glfw3.linuxinput;
44 import core.stdc.stdio;
45 import core.stdc.stdlib;
46 import core.stdc.string;
47 import core.sys.posix.sys.mman;
48 import core.sys.linux.timerfd;
49 import core.sys.posix.unistd;
50 import xkbcommon.xkbcommon;
51 
52 //public import wayland-client;
53 //import wayland.native.util;
54 
55 pragma(inline, true) extern(D) static int min(int n1, int n2) {
56     return n1 < n2 ? n1 : n2;
57 }
58 
59 static _GLFWwindow* findWindowFromDecorationSurface(wl_surface* surface, int* which) {
60     int focus;
61     _GLFWwindow* window = _glfw.windowListHead;
62     if (!which)
63         which = &focus;
64     while (window)
65     {
66         if (surface == window.wl.decorations.top.surface)
67         {
68             *which = topDecoration;
69             break;
70         }
71         if (surface == window.wl.decorations.left.surface)
72         {
73             *which = leftDecoration;
74             break;
75         }
76         if (surface == window.wl.decorations.right.surface)
77         {
78             *which = rightDecoration;
79             break;
80         }
81         if (surface == window.wl.decorations.bottom.surface)
82         {
83             *which = bottomDecoration;
84             break;
85         }
86         window = window.next;
87     }
88     return window;
89 }
90 
91 static void pointerHandleEnter(void* data, wl_pointer* pointer, uint serial, wl_surface* surface, wl_fixed_t sx, wl_fixed_t sy) {
92     // Happens in the case we just destroyed the surface.
93     if (!surface)
94         return;
95 
96     int focus = 0;
97     _GLFWwindow* window = cast(_GLFWwindow*) wl_surface_get_user_data(surface);
98     if (!window)
99     {
100         window = findWindowFromDecorationSurface(surface, &focus);
101         if (!window)
102             return;
103     }
104 
105     window.wl.decorations.focus = focus;
106     _glfw.wl.serial = serial;
107     _glfw.wl.pointerFocus = window;
108 
109     window.wl.hovered = GLFW_TRUE;
110 
111     _glfwPlatformSetCursor(window, window.wl.currentCursor);
112     _glfwInputCursorEnter(window, GLFW_TRUE);
113 }
114 
115 static void pointerHandleLeave(void* data, wl_pointer* pointer, uint serial, wl_surface* surface) {
116     _GLFWwindow* window = _glfw.wl.pointerFocus;
117 
118     if (!window)
119         return;
120 
121     window.wl.hovered = GLFW_FALSE;
122 
123     _glfw.wl.serial = serial;
124     _glfw.wl.pointerFocus = null;
125     _glfwInputCursorEnter(window, GLFW_FALSE);
126     _glfw.wl.cursorPreviousName = null;
127 }
128 
129 static void setCursor(_GLFWwindow* window, const(char)* name) {
130     wl_buffer* buffer;
131     wl_cursor* cursor;
132     wl_cursor_image* image;
133     wl_surface* surface = _glfw.wl.cursorSurface;
134     wl_cursor_theme* theme = _glfw.wl.cursorTheme;
135     int scale = 1;
136 
137     if (window.wl.scale > 1 && _glfw.wl.cursorThemeHiDPI)
138     {
139         // We only support up to scale=2 for now, since libwayland-cursor
140         // requires us to load a different theme for each size.
141         scale = 2;
142         theme = _glfw.wl.cursorThemeHiDPI;
143     }
144 
145     cursor = _glfw.wl.cursor.theme_get_cursor(theme, name);
146     if (!cursor)
147     {
148         _glfwInputError(GLFW_PLATFORM_ERROR,
149                         "Wayland: Standard cursor not found");
150         return;
151     }
152     // TODO: handle animated cursors too.
153     image = cursor.images[0];
154 
155     if (!image)
156         return;
157 
158     buffer = _glfw.wl.cursor.image_get_buffer(image);
159     if (!buffer)
160         return;
161     wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
162                           surface,
163                           image.hotspot_x / scale,
164                           image.hotspot_y / scale);
165     wl_surface_set_buffer_scale(surface, scale);
166     wl_surface_attach(surface, buffer, 0, 0);
167     wl_surface_damage(surface, 0, 0,
168                       image.width, image.height);
169     wl_surface_commit(surface);
170     _glfw.wl.cursorPreviousName = name;
171 }
172 
173 static void pointerHandleMotion(void* data, wl_pointer* pointer, uint time, wl_fixed_t sx, wl_fixed_t sy) {
174     _GLFWwindow* window = _glfw.wl.pointerFocus;
175     const(char)* cursorName = null;
176     double x;double y;
177 
178     if (!window)
179         return;
180 
181     if (window.cursorMode == GLFW_CURSOR_DISABLED)
182         return;
183     x = wl_fixed_to_double(sx);
184     y = wl_fixed_to_double(sy);
185 
186     switch (window.wl.decorations.focus)
187     {
188         case mainWindow:
189             window.wl.cursorPosX = x;
190             window.wl.cursorPosY = y;
191             _glfwInputCursorPos(window, x, y);
192             _glfw.wl.cursorPreviousName = null;
193             return;
194         case topDecoration:
195             if (y < _GLFW_DECORATION_WIDTH)
196                 cursorName = "n-resize";
197             else
198                 cursorName = "left_ptr";
199             break;
200         case leftDecoration:
201             if (y < _GLFW_DECORATION_WIDTH)
202                 cursorName = "nw-resize";
203             else
204                 cursorName = "w-resize";
205             break;
206         case rightDecoration:
207             if (y < _GLFW_DECORATION_WIDTH)
208                 cursorName = "ne-resize";
209             else
210                 cursorName = "e-resize";
211             break;
212         case bottomDecoration:
213             if (x < _GLFW_DECORATION_WIDTH)
214                 cursorName = "sw-resize";
215             else if (x > window.wl.width + _GLFW_DECORATION_WIDTH)
216                 cursorName = "se-resize";
217             else
218                 cursorName = "s-resize";
219             break;
220         default:
221             assert(0);
222     }
223     if (_glfw.wl.cursorPreviousName != cursorName)
224         setCursor(window, cursorName);
225 }
226 
227 static void pointerHandleButton(void* data, wl_pointer* pointer, uint serial, uint time, uint button, uint state) {
228     _GLFWwindow* window = _glfw.wl.pointerFocus;
229     int glfwButton;
230 
231     // Both xdg-shell and wl_shell use the same values.
232     uint edges = WL_SHELL_SURFACE_RESIZE_NONE;
233 
234     if (!window)
235         return;
236     if (button == BTN_LEFT)
237     {
238         switch (window.wl.decorations.focus)
239         {
240             case mainWindow:
241                 break;
242             case topDecoration:
243                 if (window.wl.cursorPosY < _GLFW_DECORATION_WIDTH)
244                     edges = WL_SHELL_SURFACE_RESIZE_TOP;
245                 else
246                 {
247                     if (window.wl.xdg.toplevel)
248                         xdg_toplevel_move(window.wl.xdg.toplevel, _glfw.wl.seat, serial);
249                     else
250                         wl_shell_surface_move(window.wl.shellSurface, _glfw.wl.seat, serial);
251                 }
252                 break;
253             case leftDecoration:
254                 if (window.wl.cursorPosY < _GLFW_DECORATION_WIDTH)
255                     edges = WL_SHELL_SURFACE_RESIZE_TOP_LEFT;
256                 else
257                     edges = WL_SHELL_SURFACE_RESIZE_LEFT;
258                 break;
259             case rightDecoration:
260                 if (window.wl.cursorPosY < _GLFW_DECORATION_WIDTH)
261                     edges = WL_SHELL_SURFACE_RESIZE_TOP_RIGHT;
262                 else
263                     edges = WL_SHELL_SURFACE_RESIZE_RIGHT;
264                 break;
265             case bottomDecoration:
266                 if (window.wl.cursorPosX < _GLFW_DECORATION_WIDTH)
267                     edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT;
268                 else if (window.wl.cursorPosX > window.wl.width + _GLFW_DECORATION_WIDTH)
269                     edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT;
270                 else
271                     edges = WL_SHELL_SURFACE_RESIZE_BOTTOM;
272                 break;
273             default:
274                 assert(0);
275         }
276         if (edges != WL_SHELL_SURFACE_RESIZE_NONE)
277         {
278             if (window.wl.xdg.toplevel)
279                 xdg_toplevel_resize(window.wl.xdg.toplevel, _glfw.wl.seat,
280                                     serial, edges);
281             else
282                 wl_shell_surface_resize(window.wl.shellSurface, _glfw.wl.seat,
283                                         serial, edges);
284         }
285     }
286     else if (button == BTN_RIGHT)
287     {
288         if (window.wl.decorations.focus != mainWindow && window.wl.xdg.toplevel)
289         {
290             xdg_toplevel_show_window_menu(window.wl.xdg.toplevel,
291                                           _glfw.wl.seat, serial,
292                                           window.wl.cursorPosX,
293                                           window.wl.cursorPosY);
294             return;
295         }
296     }
297 
298     // Don’t pass the button to the user if it was related to a decoration.
299     if (window.wl.decorations.focus != mainWindow)
300         return;
301 
302     _glfw.wl.serial = serial;
303 
304     /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev
305      * codes. */
306     glfwButton = button - BTN_LEFT;
307 
308     _glfwInputMouseClick(window,
309                          glfwButton,
310                          state == WL_POINTER_BUTTON_STATE_PRESSED
311                                 ? GLFW_PRESS
312                                 : GLFW_RELEASE,
313                          _glfw.wl.xkb.modifiers);
314 }
315 
316 static void pointerHandleAxis(void* data, wl_pointer* pointer, uint time, uint axis, wl_fixed_t value) {
317     _GLFWwindow* window = _glfw.wl.pointerFocus;
318     double x = 0.0;double y = 0.0;
319     // Wayland scroll events are in pointer motion coordinate space (think two
320     // finger scroll).  The factor 10 is commonly used to convert to "scroll
321     // step means 1.0.
322     const(double) scrollFactor = 1.0 / 10.0;
323 
324     if (!window)
325         return;
326 
327     assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL ||
328            axis == WL_POINTER_AXIS_VERTICAL_SCROLL);
329 
330     if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
331         x = wl_fixed_to_double(value) * scrollFactor;
332     else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
333         y = wl_fixed_to_double(value) * scrollFactor;
334 
335     _glfwInputScroll(window, x, y);
336 }
337 
338 static const(wl_pointer_listener) pointerListener = wl_pointer_listener(
339     &pointerHandleEnter,
340     &pointerHandleLeave,
341     &pointerHandleMotion,
342     &pointerHandleButton,
343     &pointerHandleAxis,
344 );
345 
346 static void keyboardHandleKeymap(void* data, wl_keyboard* keyboard, uint format, int fd, uint size) {
347     xkb_keymap* keymap;
348     xkb_state* state;
349 
350 version (HAVE_XKBCOMMON_COMPOSE_H) {
351     xkb_compose_table* composeTable;
352     xkb_compose_state* composeState;
353 }
354 
355     char* mapStr;
356     const(char)* locale;
357 
358     if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
359     {
360         close(fd);
361         return;
362     }
363 
364     mapStr = cast(char*) mmap(null, size, PROT_READ, MAP_SHARED, fd, 0);
365     if (mapStr == MAP_FAILED) {
366         close(fd);
367         return;
368     }
369 
370     keymap = _glfw.wl.xkb.keymap_new_from_string(_glfw.wl.xkb.context,
371                                         mapStr,
372                                         XKB_KEYMAP_FORMAT_TEXT_V1,
373                                         0);
374     munmap(mapStr, size);
375     close(fd);
376 
377     if (!keymap)
378     {
379         _glfwInputError(GLFW_PLATFORM_ERROR,
380                         "Wayland: Failed to compile keymap");
381         return;
382     }
383 
384     state = _glfw.wl.xkb.state_new(keymap);
385     if (!state)
386     {
387         _glfwInputError(GLFW_PLATFORM_ERROR,
388                         "Wayland: Failed to create XKB state");
389         _glfw.wl.xkb.keymap_unref(keymap);
390         return;
391     }
392 
393     // Look up the preferred locale, falling back to "C" as default.
394     locale = getenv("LC_ALL");
395     if (!locale)
396         locale = getenv("LC_CTYPE");
397     if (!locale)
398         locale = getenv("LANG");
399     if (!locale)
400         locale = "C";
401 
402 version (HAVE_XKBCOMMON_COMPOSE_H) {
403     composeTable =
404         xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
405                                           XKB_COMPOSE_COMPILE_NO_FLAGS);
406     if (composeTable)
407     {
408         composeState =
409             xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
410         xkb_compose_table_unref(composeTable);
411         if (composeState)
412             _glfw.wl.xkb.composeState = composeState;
413         else
414             _glfwInputError(GLFW_PLATFORM_ERROR,
415                             "Wayland: Failed to create XKB compose state");
416     }
417     else
418     {
419         _glfwInputError(GLFW_PLATFORM_ERROR,
420                         "Wayland: Failed to create XKB compose table");
421     }
422 }
423 
424     _glfw.wl.xkb.keymap_unref(_glfw.wl.xkb.keymap);
425     _glfw.wl.xkb.state_unref(_glfw.wl.xkb.state);
426     _glfw.wl.xkb.keymap = keymap;
427     _glfw.wl.xkb.state = state;
428 
429     _glfw.wl.xkb.controlMask =
430         1 << _glfw.wl.xkb.keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control");
431     _glfw.wl.xkb.altMask =
432         1 << _glfw.wl.xkb.keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
433     _glfw.wl.xkb.shiftMask =
434         1 << _glfw.wl.xkb.keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
435     _glfw.wl.xkb.superMask =
436         1 << _glfw.wl.xkb.keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
437     _glfw.wl.xkb.capsLockMask =
438         1 << _glfw.wl.xkb.keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
439     _glfw.wl.xkb.numLockMask =
440         1 << _glfw.wl.xkb.keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
441 }
442 
443 static void keyboardHandleEnter(void* data, wl_keyboard* keyboard, uint serial, wl_surface* surface, wl_array* keys) {
444     // Happens in the case we just destroyed the surface.
445     if (!surface)
446         return;
447 
448     _GLFWwindow* window = wl_surface_get_user_data(surface);
449     if (!window)
450     {
451         window = findWindowFromDecorationSurface(surface, null);
452         if (!window)
453             return;
454     }
455 
456     _glfw.wl.serial = serial;
457     _glfw.wl.keyboardFocus = window;
458     _glfwInputWindowFocus(window, GLFW_TRUE);
459 }
460 
461 static void keyboardHandleLeave(void* data, wl_keyboard* keyboard, uint serial, wl_surface* surface) {
462     _GLFWwindow* window = _glfw.wl.keyboardFocus;
463 
464     if (!window)
465         return;
466 
467     _glfw.wl.serial = serial;
468     _glfw.wl.keyboardFocus = null;
469     _glfwInputWindowFocus(window, GLFW_FALSE);
470 }
471 
472 static int toGLFWKeyCode(uint key) {
473     if (key < _glfw.wl.keycodes.length)
474         return _glfw.wl.keycodes[key];
475 
476     return GLFW_KEY_UNKNOWN;
477 }
478 
479 version (HAVE_XKBCOMMON_COMPOSE_H) {
480 static xkb_keysym_t composeSymbol(xkb_keysym_t sym) {
481     if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState)
482         return sym;
483     if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym)
484             != XKB_COMPOSE_FEED_ACCEPTED)
485         return sym;
486     switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState))
487     {
488         case XKB_COMPOSE_COMPOSED:
489             return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState);
490         case XKB_COMPOSE_COMPOSING:
491         case XKB_COMPOSE_CANCELLED:
492             return XKB_KEY_NoSymbol;
493         case XKB_COMPOSE_NOTHING:
494         default:
495             return sym;
496     }
497 }
498 }
499 
500 static GLFWbool inputChar(_GLFWwindow* window, uint key) {
501     uint code;uint numSyms;
502     c_long cp;
503     const(xkb_keysym_t)* syms;
504     xkb_keysym_t sym;
505 
506     code = key + 8;
507     numSyms = _glfw.wl.xkb.state_key_get_syms(_glfw.wl.xkb.state, code, &syms);
508 
509     if (numSyms == 1)
510     {
511 version (HAVE_XKBCOMMON_COMPOSE_H) {
512         sym = composeSymbol(syms[0]);
513 } else {
514         sym = syms[0];
515 }
516         cp = _glfwKeySym2Unicode(sym);
517         if (cp != -1)
518         {
519             const(int) mods = _glfw.wl.xkb.modifiers;
520             const(int) plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
521             _glfwInputChar(window, cast(uint) cp, mods, plain);
522         }
523     }
524 
525     return _glfw.wl.xkb.keymap_key_repeats(_glfw.wl.xkb.keymap, syms[0]);
526 }
527 
528 static void keyboardHandleKey(void* data, wl_keyboard* keyboard, uint serial, uint time, uint key, uint state) {
529     int keyCode;
530     int action;
531     _GLFWwindow* window = _glfw.wl.keyboardFocus;
532     GLFWbool shouldRepeat;
533     itimerspec timer = {};
534 
535     if (!window)
536         return;
537 
538     keyCode = toGLFWKeyCode(key);
539     action = state == WL_KEYBOARD_KEY_STATE_PRESSED
540             ? GLFW_PRESS : GLFW_RELEASE;
541 
542     _glfw.wl.serial = serial;
543     _glfwInputKey(window, keyCode, key, action,
544                   _glfw.wl.xkb.modifiers);
545 
546     if (action == GLFW_PRESS)
547     {
548         shouldRepeat = inputChar(window, key);
549 
550         if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0)
551         {
552             _glfw.wl.keyboardLastKey = keyCode;
553             _glfw.wl.keyboardLastScancode = key;
554             if (_glfw.wl.keyboardRepeatRate > 1)
555                 timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyboardRepeatRate;
556             else
557                 timer.it_interval.tv_sec = 1;
558             timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000;
559             timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000;
560         }
561     }
562     timerfd_settime(_glfw.wl.timerfd, 0, &timer, null);
563 }
564 
565 static void keyboardHandleModifiers(void* data, wl_keyboard* keyboard, uint serial, uint modsDepressed, uint modsLatched, uint modsLocked, uint group) {
566     xkb_mod_mask_t mask;
567     uint modifiers = 0;
568 
569     _glfw.wl.serial = serial;
570 
571     if (!_glfw.wl.xkb.keymap)
572         return;
573 
574     _glfw.wl.xkb.state_update_mask(_glfw.wl.xkb.state,
575                           modsDepressed,
576                           modsLatched,
577                           modsLocked,
578                           0,
579                           0,
580                           group);
581 
582     mask = _glfw.wl.xkb.state_serialize_mods(_glfw.wl.xkb.state,
583                                     XKB_STATE_MODS_DEPRESSED |
584                                     XKB_STATE_LAYOUT_DEPRESSED |
585                                     XKB_STATE_MODS_LATCHED |
586                                     XKB_STATE_LAYOUT_LATCHED);
587     if (mask & _glfw.wl.xkb.controlMask)
588         modifiers |= GLFW_MOD_CONTROL;
589     if (mask & _glfw.wl.xkb.altMask)
590         modifiers |= GLFW_MOD_ALT;
591     if (mask & _glfw.wl.xkb.shiftMask)
592         modifiers |= GLFW_MOD_SHIFT;
593     if (mask & _glfw.wl.xkb.superMask)
594         modifiers |= GLFW_MOD_SUPER;
595     if (mask & _glfw.wl.xkb.capsLockMask)
596         modifiers |= GLFW_MOD_CAPS_LOCK;
597     if (mask & _glfw.wl.xkb.numLockMask)
598         modifiers |= GLFW_MOD_NUM_LOCK;
599     _glfw.wl.xkb.modifiers = modifiers;
600 }
601 
602 version (WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) {
603     static void keyboardHandleRepeatInfo(void* data, wl_keyboard* keyboard, int rate, int delay) {
604         if (keyboard != _glfw.wl.keyboard)
605             return;
606 
607         _glfw.wl.keyboardRepeatRate = rate;
608         _glfw.wl.keyboardRepeatDelay = delay;
609     }
610 
611     static const(wl_keyboard_listener) keyboardListener = wl_keyboard_listener(
612         &keyboardHandleKeymap,
613         &keyboardHandleEnter,
614         &keyboardHandleLeave,
615         &keyboardHandleKey,
616         &keyboardHandleModifiers,
617         &keyboardHandleRepeatInfo,
618     );
619 } else {
620     static const(wl_keyboard_listener) keyboardListener = wl_keyboard_listener(
621         &keyboardHandleKeymap,
622         &keyboardHandleEnter,
623         &keyboardHandleLeave,
624         &keyboardHandleKey,
625         &keyboardHandleModifiers,
626         // no keyboardHandleRepeatInfo,
627     );
628 }
629 
630 static void seatHandleCapabilities(void* data, wl_seat* seat, /*enum wl_seat_capability*/ uint caps) {
631     if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer)
632     {
633         _glfw.wl.pointer = wl_seat_get_pointer(seat);
634         wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, null);
635     }
636     else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
637     {
638         wl_pointer_destroy(_glfw.wl.pointer);
639         _glfw.wl.pointer = null;
640     }
641 
642     if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)
643     {
644         _glfw.wl.keyboard = wl_seat_get_keyboard(seat);
645         wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, null);
646     }
647     else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard)
648     {
649         wl_keyboard_destroy(_glfw.wl.keyboard);
650         _glfw.wl.keyboard = null;
651     }
652 }
653 
654 static void seatHandleName(void* data, wl_seat* seat, const(char)* name) {
655 }
656 
657 static const(wl_seat_listener) seatListener = wl_seat_listener(
658     &seatHandleCapabilities,
659     &seatHandleName,
660 );
661 
662 static void dataOfferHandleOffer(void* data, wl_data_offer* dataOffer, const(char)* mimeType) {
663 }
664 
665 static const(wl_data_offer_listener) dataOfferListener = wl_data_offer_listener(
666     &dataOfferHandleOffer,
667 );
668 
669 static void dataDeviceHandleDataOffer(void* data, wl_data_device* dataDevice, wl_data_offer* id) {
670     if (_glfw.wl.dataOffer)
671         wl_data_offer_destroy(_glfw.wl.dataOffer);
672 
673     _glfw.wl.dataOffer = id;
674     wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, null);
675 }
676 
677 static void dataDeviceHandleEnter(void* data, wl_data_device* dataDevice, uint serial, wl_surface* surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer* id) {
678 }
679 
680 static void dataDeviceHandleLeave(void* data, wl_data_device* dataDevice) {
681 }
682 
683 static void dataDeviceHandleMotion(void* data, wl_data_device* dataDevice, uint time, wl_fixed_t x, wl_fixed_t y) {
684 }
685 
686 static void dataDeviceHandleDrop(void* data, wl_data_device* dataDevice) {
687 }
688 
689 static void dataDeviceHandleSelection(void* data, wl_data_device* dataDevice, wl_data_offer* id) {
690 }
691 
692 static const(wl_data_device_listener) dataDeviceListener = wl_data_device_listener(
693     &dataDeviceHandleDataOffer,
694     &dataDeviceHandleEnter,
695     &dataDeviceHandleLeave,
696     &dataDeviceHandleMotion,
697     &dataDeviceHandleDrop,
698     &dataDeviceHandleSelection,
699 );
700 
701 static void wmBaseHandlePing(void* data, xdg_wm_base* wmBase, uint serial) {
702     xdg_wm_base_pong(wmBase, serial);
703 }
704 
705 static const(xdg_wm_base_listener) wmBaseListener = xdg_wm_base_listener(
706     &wmBaseHandlePing
707 );
708 
709 static void registryHandleGlobal(void* data, wl_registry* registry, uint name, const(char)* interface_, uint version_) {
710     if (strcmp(interface_, "wl_compositor") == 0)
711     {
712         _glfw.wl.compositorVersion = min(3, version_);
713         _glfw.wl.compositor = cast(wl_compositor*)
714             wl_registry_bind(registry, name, &wl_compositor_interface,
715                              _glfw.wl.compositorVersion);
716     }
717     else if (strcmp(interface_, "wl_subcompositor") == 0)
718     {
719         _glfw.wl.subcompositor = cast(wl_subcompositor*)
720             wl_registry_bind(registry, name, &wl_subcompositor_interface, 1);
721     }
722     else if (strcmp(interface_, "wl_shm") == 0)
723     {
724         _glfw.wl.shm = cast(wl_shm*)
725             wl_registry_bind(registry, name, &wl_shm_interface, 1);
726     }
727     else if (strcmp(interface_, "wl_shell") == 0)
728     {
729         _glfw.wl.shell = cast(wl_shell*)
730             wl_registry_bind(registry, name, &wl_shell_interface, 1);
731     }
732     else if (strcmp(interface_, "wl_output") == 0)
733     {
734         _glfwAddOutputWayland(name, version_);
735     }
736     else if (strcmp(interface_, "wl_seat") == 0)
737     {
738         if (!_glfw.wl.seat)
739         {
740             _glfw.wl.seatVersion = min(4, version_);
741             _glfw.wl.seat = cast(wl_seat*)
742                 wl_registry_bind(registry, name, &wl_seat_interface,
743                                  _glfw.wl.seatVersion);
744             wl_seat_add_listener(_glfw.wl.seat, &seatListener, null);
745         }
746     }
747     else if (strcmp(interface_, "wl_data_device_manager") == 0)
748     {
749         if (!_glfw.wl.dataDeviceManager)
750         {
751             _glfw.wl.dataDeviceManager = cast(wl_data_device_manager*)
752                 wl_registry_bind(registry, name,
753                                  &wl_data_device_manager_interface, 1);
754         }
755     }
756     else if (strcmp(interface_, "xdg_wm_base") == 0)
757     {
758         _glfw.wl.wmBase =
759             wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
760         xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, null);
761     }
762     else if (strcmp(interface_, "zxdg_decoration_manager_v1") == 0)
763     {
764         _glfw.wl.decorationManager =
765             wl_registry_bind(registry, name,
766                              &zxdg_decoration_manager_v1_interface,
767                              1);
768     }
769     else if (strcmp(interface_, "wp_viewporter") == 0)
770     {
771         _glfw.wl.viewporter =
772             wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
773     }
774     else if (strcmp(interface_, "zwp_relative_pointer_manager_v1") == 0)
775     {
776         _glfw.wl.relativePointerManager =
777             wl_registry_bind(registry, name,
778                              &zwp_relative_pointer_manager_v1_interface,
779                              1);
780     }
781     else if (strcmp(interface_, "zwp_pointer_constraints_v1") == 0)
782     {
783         _glfw.wl.pointerConstraints =
784             wl_registry_bind(registry, name,
785                              &zwp_pointer_constraints_v1_interface,
786                              1);
787     }
788     else if (strcmp(interface_, "zwp_idle_inhibit_manager_v1") == 0)
789     {
790         _glfw.wl.idleInhibitManager =
791             wl_registry_bind(registry, name,
792                              &zwp_idle_inhibit_manager_v1_interface,
793                              1);
794     }
795 }
796 
797 static void registryHandleGlobalRemove(void* data, wl_registry* registry, uint name) {
798     int i;
799     _GLFWmonitor* monitor;
800 
801     for (i = 0; i < _glfw.monitorCount; ++i)
802     {
803         monitor = _glfw.monitors[i];
804         if (monitor.wl.name == name)
805         {
806             _glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0);
807             return;
808         }
809     }
810 }
811 
812 
813 static const(wl_registry_listener) registryListener = wl_registry_listener(
814     &registryHandleGlobal,
815     &registryHandleGlobalRemove
816 );
817 
818 // Create key code translation tables
819 //
820 static void createKeyTables() {
821     int scancode;
822 
823     memset(_glfw.wl.keycodes.ptr, -1, typeof(_glfw.wl.keycodes).sizeof);
824     memset(_glfw.wl.scancodes.ptr, -1, typeof(_glfw.wl.scancodes).sizeof);
825 
826     _glfw.wl.keycodes[KEY_GRAVE]      = GLFW_KEY_GRAVE_ACCENT;
827     _glfw.wl.keycodes[KEY_1]          = GLFW_KEY_1;
828     _glfw.wl.keycodes[KEY_2]          = GLFW_KEY_2;
829     _glfw.wl.keycodes[KEY_3]          = GLFW_KEY_3;
830     _glfw.wl.keycodes[KEY_4]          = GLFW_KEY_4;
831     _glfw.wl.keycodes[KEY_5]          = GLFW_KEY_5;
832     _glfw.wl.keycodes[KEY_6]          = GLFW_KEY_6;
833     _glfw.wl.keycodes[KEY_7]          = GLFW_KEY_7;
834     _glfw.wl.keycodes[KEY_8]          = GLFW_KEY_8;
835     _glfw.wl.keycodes[KEY_9]          = GLFW_KEY_9;
836     _glfw.wl.keycodes[KEY_0]          = GLFW_KEY_0;
837     _glfw.wl.keycodes[KEY_SPACE]      = GLFW_KEY_SPACE;
838     _glfw.wl.keycodes[KEY_MINUS]      = GLFW_KEY_MINUS;
839     _glfw.wl.keycodes[KEY_EQUAL]      = GLFW_KEY_EQUAL;
840     _glfw.wl.keycodes[KEY_Q]          = GLFW_KEY_Q;
841     _glfw.wl.keycodes[KEY_W]          = GLFW_KEY_W;
842     _glfw.wl.keycodes[KEY_E]          = GLFW_KEY_E;
843     _glfw.wl.keycodes[KEY_R]          = GLFW_KEY_R;
844     _glfw.wl.keycodes[KEY_T]          = GLFW_KEY_T;
845     _glfw.wl.keycodes[KEY_Y]          = GLFW_KEY_Y;
846     _glfw.wl.keycodes[KEY_U]          = GLFW_KEY_U;
847     _glfw.wl.keycodes[KEY_I]          = GLFW_KEY_I;
848     _glfw.wl.keycodes[KEY_O]          = GLFW_KEY_O;
849     _glfw.wl.keycodes[KEY_P]          = GLFW_KEY_P;
850     _glfw.wl.keycodes[KEY_LEFTBRACE]  = GLFW_KEY_LEFT_BRACKET;
851     _glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET;
852     _glfw.wl.keycodes[KEY_A]          = GLFW_KEY_A;
853     _glfw.wl.keycodes[KEY_S]          = GLFW_KEY_S;
854     _glfw.wl.keycodes[KEY_D]          = GLFW_KEY_D;
855     _glfw.wl.keycodes[KEY_F]          = GLFW_KEY_F;
856     _glfw.wl.keycodes[KEY_G]          = GLFW_KEY_G;
857     _glfw.wl.keycodes[KEY_H]          = GLFW_KEY_H;
858     _glfw.wl.keycodes[KEY_J]          = GLFW_KEY_J;
859     _glfw.wl.keycodes[KEY_K]          = GLFW_KEY_K;
860     _glfw.wl.keycodes[KEY_L]          = GLFW_KEY_L;
861     _glfw.wl.keycodes[KEY_SEMICOLON]  = GLFW_KEY_SEMICOLON;
862     _glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE;
863     _glfw.wl.keycodes[KEY_Z]          = GLFW_KEY_Z;
864     _glfw.wl.keycodes[KEY_X]          = GLFW_KEY_X;
865     _glfw.wl.keycodes[KEY_C]          = GLFW_KEY_C;
866     _glfw.wl.keycodes[KEY_V]          = GLFW_KEY_V;
867     _glfw.wl.keycodes[KEY_B]          = GLFW_KEY_B;
868     _glfw.wl.keycodes[KEY_N]          = GLFW_KEY_N;
869     _glfw.wl.keycodes[KEY_M]          = GLFW_KEY_M;
870     _glfw.wl.keycodes[KEY_COMMA]      = GLFW_KEY_COMMA;
871     _glfw.wl.keycodes[KEY_DOT]        = GLFW_KEY_PERIOD;
872     _glfw.wl.keycodes[KEY_SLASH]      = GLFW_KEY_SLASH;
873     _glfw.wl.keycodes[KEY_BACKSLASH]  = GLFW_KEY_BACKSLASH;
874     _glfw.wl.keycodes[KEY_ESC]        = GLFW_KEY_ESCAPE;
875     _glfw.wl.keycodes[KEY_TAB]        = GLFW_KEY_TAB;
876     _glfw.wl.keycodes[KEY_LEFTSHIFT]  = GLFW_KEY_LEFT_SHIFT;
877     _glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT;
878     _glfw.wl.keycodes[KEY_LEFTCTRL]   = GLFW_KEY_LEFT_CONTROL;
879     _glfw.wl.keycodes[KEY_RIGHTCTRL]  = GLFW_KEY_RIGHT_CONTROL;
880     _glfw.wl.keycodes[KEY_LEFTALT]    = GLFW_KEY_LEFT_ALT;
881     _glfw.wl.keycodes[KEY_RIGHTALT]   = GLFW_KEY_RIGHT_ALT;
882     _glfw.wl.keycodes[KEY_LEFTMETA]   = GLFW_KEY_LEFT_SUPER;
883     _glfw.wl.keycodes[KEY_RIGHTMETA]  = GLFW_KEY_RIGHT_SUPER;
884     _glfw.wl.keycodes[KEY_MENU]       = GLFW_KEY_MENU;
885     _glfw.wl.keycodes[KEY_NUMLOCK]    = GLFW_KEY_NUM_LOCK;
886     _glfw.wl.keycodes[KEY_CAPSLOCK]   = GLFW_KEY_CAPS_LOCK;
887     _glfw.wl.keycodes[KEY_PRINT]      = GLFW_KEY_PRINT_SCREEN;
888     _glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK;
889     _glfw.wl.keycodes[KEY_PAUSE]      = GLFW_KEY_PAUSE;
890     _glfw.wl.keycodes[KEY_DELETE]     = GLFW_KEY_DELETE;
891     _glfw.wl.keycodes[KEY_BACKSPACE]  = GLFW_KEY_BACKSPACE;
892     _glfw.wl.keycodes[KEY_ENTER]      = GLFW_KEY_ENTER;
893     _glfw.wl.keycodes[KEY_HOME]       = GLFW_KEY_HOME;
894     _glfw.wl.keycodes[KEY_END]        = GLFW_KEY_END;
895     _glfw.wl.keycodes[KEY_PAGEUP]     = GLFW_KEY_PAGE_UP;
896     _glfw.wl.keycodes[KEY_PAGEDOWN]   = GLFW_KEY_PAGE_DOWN;
897     _glfw.wl.keycodes[KEY_INSERT]     = GLFW_KEY_INSERT;
898     _glfw.wl.keycodes[KEY_LEFT]       = GLFW_KEY_LEFT;
899     _glfw.wl.keycodes[KEY_RIGHT]      = GLFW_KEY_RIGHT;
900     _glfw.wl.keycodes[KEY_DOWN]       = GLFW_KEY_DOWN;
901     _glfw.wl.keycodes[KEY_UP]         = GLFW_KEY_UP;
902     _glfw.wl.keycodes[KEY_F1]         = GLFW_KEY_F1;
903     _glfw.wl.keycodes[KEY_F2]         = GLFW_KEY_F2;
904     _glfw.wl.keycodes[KEY_F3]         = GLFW_KEY_F3;
905     _glfw.wl.keycodes[KEY_F4]         = GLFW_KEY_F4;
906     _glfw.wl.keycodes[KEY_F5]         = GLFW_KEY_F5;
907     _glfw.wl.keycodes[KEY_F6]         = GLFW_KEY_F6;
908     _glfw.wl.keycodes[KEY_F7]         = GLFW_KEY_F7;
909     _glfw.wl.keycodes[KEY_F8]         = GLFW_KEY_F8;
910     _glfw.wl.keycodes[KEY_F9]         = GLFW_KEY_F9;
911     _glfw.wl.keycodes[KEY_F10]        = GLFW_KEY_F10;
912     _glfw.wl.keycodes[KEY_F11]        = GLFW_KEY_F11;
913     _glfw.wl.keycodes[KEY_F12]        = GLFW_KEY_F12;
914     _glfw.wl.keycodes[KEY_F13]        = GLFW_KEY_F13;
915     _glfw.wl.keycodes[KEY_F14]        = GLFW_KEY_F14;
916     _glfw.wl.keycodes[KEY_F15]        = GLFW_KEY_F15;
917     _glfw.wl.keycodes[KEY_F16]        = GLFW_KEY_F16;
918     _glfw.wl.keycodes[KEY_F17]        = GLFW_KEY_F17;
919     _glfw.wl.keycodes[KEY_F18]        = GLFW_KEY_F18;
920     _glfw.wl.keycodes[KEY_F19]        = GLFW_KEY_F19;
921     _glfw.wl.keycodes[KEY_F20]        = GLFW_KEY_F20;
922     _glfw.wl.keycodes[KEY_F21]        = GLFW_KEY_F21;
923     _glfw.wl.keycodes[KEY_F22]        = GLFW_KEY_F22;
924     _glfw.wl.keycodes[KEY_F23]        = GLFW_KEY_F23;
925     _glfw.wl.keycodes[KEY_F24]        = GLFW_KEY_F24;
926     _glfw.wl.keycodes[KEY_KPSLASH]    = GLFW_KEY_KP_DIVIDE;
927     _glfw.wl.keycodes[KEY_KPDOT]      = GLFW_KEY_KP_MULTIPLY;
928     _glfw.wl.keycodes[KEY_KPMINUS]    = GLFW_KEY_KP_SUBTRACT;
929     _glfw.wl.keycodes[KEY_KPPLUS]     = GLFW_KEY_KP_ADD;
930     _glfw.wl.keycodes[KEY_KP0]        = GLFW_KEY_KP_0;
931     _glfw.wl.keycodes[KEY_KP1]        = GLFW_KEY_KP_1;
932     _glfw.wl.keycodes[KEY_KP2]        = GLFW_KEY_KP_2;
933     _glfw.wl.keycodes[KEY_KP3]        = GLFW_KEY_KP_3;
934     _glfw.wl.keycodes[KEY_KP4]        = GLFW_KEY_KP_4;
935     _glfw.wl.keycodes[KEY_KP5]        = GLFW_KEY_KP_5;
936     _glfw.wl.keycodes[KEY_KP6]        = GLFW_KEY_KP_6;
937     _glfw.wl.keycodes[KEY_KP7]        = GLFW_KEY_KP_7;
938     _glfw.wl.keycodes[KEY_KP8]        = GLFW_KEY_KP_8;
939     _glfw.wl.keycodes[KEY_KP9]        = GLFW_KEY_KP_9;
940     _glfw.wl.keycodes[KEY_KPCOMMA]    = GLFW_KEY_KP_DECIMAL;
941     _glfw.wl.keycodes[KEY_KPEQUAL]    = GLFW_KEY_KP_EQUAL;
942     _glfw.wl.keycodes[KEY_KPENTER]    = GLFW_KEY_KP_ENTER;
943 
944     for (scancode = 0;  scancode < 256;  scancode++)
945     {
946         if (_glfw.wl.keycodes[scancode] > 0)
947             _glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode;
948     }
949 }
950 
951 
952 //////////////////////////////////////////////////////////////////////////
953 //////                       GLFW platform API                      //////
954 //////////////////////////////////////////////////////////////////////////
955 
956 int _glfwPlatformInit() {
957     const(char)* cursorTheme;
958     const(char)* cursorSizeStr;
959     const(char)* cursorSizeEnd;
960     c_long cursorSizeLong;
961     int cursorSize;
962 
963     _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0");
964     if (!_glfw.wl.cursor.handle)
965     {
966         _glfwInputError(GLFW_PLATFORM_ERROR,
967                         "Wayland: Failed to open libwayland-cursor");
968         return GLFW_FALSE;
969     }
970 
971     _glfw.wl.cursor.theme_load = cast(PFN_wl_cursor_theme_load)
972         _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_load");
973     _glfw.wl.cursor.theme_destroy = cast(PFN_wl_cursor_theme_destroy)
974         _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy");
975     _glfw.wl.cursor.theme_get_cursor = cast(PFN_wl_cursor_theme_get_cursor)
976         _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor");
977     _glfw.wl.cursor.image_get_buffer = cast(PFN_wl_cursor_image_get_buffer)
978         _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer");
979 
980     _glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1");
981     if (!_glfw.wl.egl.handle)
982     {
983         _glfwInputError(GLFW_PLATFORM_ERROR,
984                         "Wayland: Failed to open libwayland-egl");
985         return GLFW_FALSE;
986     }
987 
988     _glfw.wl.egl.window_create = cast(PFN_wl_egl_window_create)
989         _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_create");
990     _glfw.wl.egl.window_destroy = cast(PFN_wl_egl_window_destroy)
991         _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_destroy");
992     _glfw.wl.egl.window_resize = cast(PFN_wl_egl_window_resize)
993         _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize");
994 
995     _glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0");
996     if (!_glfw.wl.xkb.handle)
997     {
998         _glfwInputError(GLFW_PLATFORM_ERROR,
999                         "Wayland: Failed to open libxkbcommon");
1000         return GLFW_FALSE;
1001     }
1002 
1003     _glfw.wl.xkb.context_new = cast(PFN_xkb_context_new)
1004         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new");
1005     _glfw.wl.xkb.context_unref = cast(PFN_xkb_context_unref)
1006         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref");
1007     _glfw.wl.xkb.keymap_new_from_string = cast(PFN_xkb_keymap_new_from_string)
1008         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string");
1009     _glfw.wl.xkb.keymap_unref = cast(PFN_xkb_keymap_unref)
1010         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref");
1011     _glfw.wl.xkb.keymap_mod_get_index = cast(PFN_xkb_keymap_mod_get_index)
1012         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
1013     _glfw.wl.xkb.keymap_key_repeats = cast(PFN_xkb_keymap_key_repeats)
1014         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats");
1015     _glfw.wl.xkb.state_new = cast(PFN_xkb_state_new)
1016         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new");
1017     _glfw.wl.xkb.state_unref = cast(PFN_xkb_state_unref)
1018         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref");
1019     _glfw.wl.xkb.state_key_get_syms = cast(PFN_xkb_state_key_get_syms)
1020         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms");
1021     _glfw.wl.xkb.state_update_mask = cast(PFN_xkb_state_update_mask)
1022         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask");
1023     _glfw.wl.xkb.state_serialize_mods = cast(PFN_xkb_state_serialize_mods)
1024         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods");
1025 
1026 version (HAVE_XKBCOMMON_COMPOSE_H) {
1027     _glfw.wl.xkb.compose_table_new_from_locale = cast(PFN_xkb_compose_table_new_from_locale)
1028         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale");
1029     _glfw.wl.xkb.compose_table_unref = cast(PFN_xkb_compose_table_unref)
1030         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref");
1031     _glfw.wl.xkb.compose_state_new = cast(PFN_xkb_compose_state_new)
1032         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new");
1033     _glfw.wl.xkb.compose_state_unref = cast(PFN_xkb_compose_state_unref)
1034         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref");
1035     _glfw.wl.xkb.compose_state_feed = cast(PFN_xkb_compose_state_feed)
1036         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed");
1037     _glfw.wl.xkb.compose_state_get_status = cast(PFN_xkb_compose_state_get_status)
1038         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status");
1039     _glfw.wl.xkb.compose_state_get_one_sym = cast(PFN_xkb_compose_state_get_one_sym)
1040         _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym");
1041 }
1042 
1043     _glfw.wl.display = wl_display_connect(null);
1044     if (!_glfw.wl.display)
1045     {
1046         _glfwInputError(GLFW_PLATFORM_ERROR,
1047                         "Wayland: Failed to connect to display");
1048         return GLFW_FALSE;
1049     }
1050 
1051     _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
1052     wl_registry_add_listener(_glfw.wl.registry, &registryListener, null);
1053 
1054     createKeyTables();
1055 
1056     _glfw.wl.xkb.context = _glfw.wl.xkb.context_new(0);
1057     if (!_glfw.wl.xkb.context)
1058     {
1059         _glfwInputError(GLFW_PLATFORM_ERROR,
1060                         "Wayland: Failed to initialize xkb context");
1061         return GLFW_FALSE;
1062     }
1063 
1064     // Sync so we got all registry objects
1065     wl_display_roundtrip(_glfw.wl.display);
1066 
1067     // Sync so we got all initial output events
1068     wl_display_roundtrip(_glfw.wl.display);
1069 
1070 version (linux) {
1071     if (!_glfwInitJoysticksLinux())
1072         return GLFW_FALSE;
1073 }
1074 
1075     _glfwInitTimerPOSIX();
1076 
1077     _glfw.wl.timerfd = -1;
1078     if (_glfw.wl.seatVersion >= 4)
1079         _glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
1080 
1081     if (_glfw.wl.pointer && _glfw.wl.shm)
1082     {
1083         cursorTheme = getenv("XCURSOR_THEME");
1084         cursorSizeStr = getenv("XCURSOR_SIZE");
1085         cursorSize = 32;
1086         if (cursorSizeStr)
1087         {
1088             errno = 0;
1089             cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10);
1090             if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX)
1091                 cursorSize = cast(int)cursorSizeLong;
1092         }
1093         _glfw.wl.cursorTheme =
1094             _glfw.wl.cursor.theme_load(cursorTheme, cursorSize, _glfw.wl.shm);
1095         if (!_glfw.wl.cursorTheme)
1096         {
1097             _glfwInputError(GLFW_PLATFORM_ERROR,
1098                             "Wayland: Unable to load default cursor theme");
1099             return GLFW_FALSE;
1100         }
1101         // If this happens to be NULL, we just fallback to the scale=1 version.
1102         _glfw.wl.cursorThemeHiDPI =
1103             _glfw.wl.cursor.theme_load(cursorTheme, 2 * cursorSize, _glfw.wl.shm);
1104         _glfw.wl.cursorSurface =
1105             wl_compositor_create_surface(_glfw.wl.compositor);
1106         _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
1107     }
1108 
1109     if (_glfw.wl.seat && _glfw.wl.dataDeviceManager)
1110     {
1111         _glfw.wl.dataDevice =
1112             wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager,
1113                                                    _glfw.wl.seat);
1114         wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, null);
1115         _glfw.wl.clipboardString = cast(char*) malloc(4096);
1116         if (!_glfw.wl.clipboardString)
1117         {
1118             _glfwInputError(GLFW_PLATFORM_ERROR,
1119                             "Wayland: Unable to allocate clipboard memory");
1120             return GLFW_FALSE;
1121         }
1122         _glfw.wl.clipboardSize = 4096;
1123     }
1124 
1125     return GLFW_TRUE;
1126 }
1127 
1128 void _glfwPlatformTerminate() {
1129 version (linux) {
1130     _glfwTerminateJoysticksLinux();
1131 }
1132     _glfwTerminateEGL();
1133     if (_glfw.wl.egl.handle)
1134     {
1135         _glfw_dlclose(_glfw.wl.egl.handle);
1136         _glfw.wl.egl.handle = null;
1137     }
1138 
1139 version (HAVE_XKBCOMMON_COMPOSE_H) {
1140     if (_glfw.wl.xkb.composeState)
1141         xkb_compose_state_unref(_glfw.wl.xkb.composeState);
1142 }
1143     if (_glfw.wl.xkb.keymap)
1144         _glfw.wl.xkb.keymap_unref(_glfw.wl.xkb.keymap);
1145     if (_glfw.wl.xkb.state)
1146         _glfw.wl.xkb.state_unref(_glfw.wl.xkb.state);
1147     if (_glfw.wl.xkb.context)
1148         _glfw.wl.xkb.context_unref(_glfw.wl.xkb.context);
1149     if (_glfw.wl.xkb.handle)
1150     {
1151         _glfw_dlclose(_glfw.wl.xkb.handle);
1152         _glfw.wl.xkb.handle = null;
1153     }
1154 
1155     if (_glfw.wl.cursorTheme)
1156         _glfw.wl.cursor.theme_destroy(_glfw.wl.cursorTheme);
1157     if (_glfw.wl.cursorThemeHiDPI)
1158         _glfw.wl.cursor.theme_destroy(_glfw.wl.cursorThemeHiDPI);
1159     if (_glfw.wl.cursor.handle)
1160     {
1161         _glfw_dlclose(_glfw.wl.cursor.handle);
1162         _glfw.wl.cursor.handle = null;
1163     }
1164 
1165     if (_glfw.wl.cursorSurface)
1166         wl_surface_destroy(_glfw.wl.cursorSurface);
1167     if (_glfw.wl.subcompositor)
1168         wl_subcompositor_destroy(_glfw.wl.subcompositor);
1169     if (_glfw.wl.compositor)
1170         wl_compositor_destroy(_glfw.wl.compositor);
1171     if (_glfw.wl.shm)
1172         wl_shm_destroy(_glfw.wl.shm);
1173     if (_glfw.wl.shell)
1174         wl_shell_destroy(_glfw.wl.shell);
1175     if (_glfw.wl.viewporter)
1176         wp_viewporter_destroy(_glfw.wl.viewporter);
1177     if (_glfw.wl.decorationManager)
1178         zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager);
1179     if (_glfw.wl.wmBase)
1180         xdg_wm_base_destroy(_glfw.wl.wmBase);
1181     if (_glfw.wl.dataSource)
1182         wl_data_source_destroy(_glfw.wl.dataSource);
1183     if (_glfw.wl.dataDevice)
1184         wl_data_device_destroy(_glfw.wl.dataDevice);
1185     if (_glfw.wl.dataOffer)
1186         wl_data_offer_destroy(_glfw.wl.dataOffer);
1187     if (_glfw.wl.dataDeviceManager)
1188         wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager);
1189     if (_glfw.wl.pointer)
1190         wl_pointer_destroy(_glfw.wl.pointer);
1191     if (_glfw.wl.keyboard)
1192         wl_keyboard_destroy(_glfw.wl.keyboard);
1193     if (_glfw.wl.seat)
1194         wl_seat_destroy(_glfw.wl.seat);
1195     if (_glfw.wl.relativePointerManager)
1196         zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager);
1197     if (_glfw.wl.pointerConstraints)
1198         zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);
1199     if (_glfw.wl.idleInhibitManager)
1200         zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
1201     if (_glfw.wl.registry)
1202         wl_registry_destroy(_glfw.wl.registry);
1203     if (_glfw.wl.display)
1204     {
1205         wl_display_flush(_glfw.wl.display);
1206         wl_display_disconnect(_glfw.wl.display);
1207     }
1208 
1209     if (_glfw.wl.timerfd >= 0)
1210         close(_glfw.wl.timerfd);
1211     if (_glfw.wl.cursorTimerfd >= 0)
1212         close(_glfw.wl.cursorTimerfd);
1213 
1214     if (_glfw.wl.clipboardString)
1215         free(_glfw.wl.clipboardString);
1216     if (_glfw.wl.clipboardSendString)
1217         free(_glfw.wl.clipboardSendString);
1218 }
1219 
1220 const(char)* _glfwPlatformGetVersionString() {
1221     version (_POSIX_TIMERS) {
1222         enum timeStr = " clock_gettime";
1223     } else version (_POSIX_MONOTONIC_CLOCK) {
1224         enum timeStr = " gettimeofday";
1225     } else {
1226         enum timeStr = " gettimeofday";
1227     }
1228     version (_GLFW_BUILD_DLL) {
1229         enum dllStr = " shared";
1230     } else {
1231         enum dllStr = "";
1232     }
1233     return _GLFW_VERSION_NUMBER ~ " Wayland EGL OSMesa" ~ timeStr ~ " evdev" ~ dllStr;
1234 }