1 /// Translated from C to D
2 module glfw3.input;
3 
4 extern(C): @nogc: nothrow: __gshared:
5 import core.stdc.config: c_long, c_ulong;
6 
7 //========================================================================
8 // GLFW 3.3 - www.glfw.org
9 //------------------------------------------------------------------------
10 // Copyright (c) 2002-2006 Marcus Geelnard
11 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
12 //
13 // This software is provided 'as-is', without any express or implied
14 // warranty. In no event will the authors be held liable for any damages
15 // arising from the use of this software.
16 //
17 // Permission is granted to anyone to use this software for any purpose,
18 // including commercial applications, and to alter it and redistribute it
19 // freely, subject to the following restrictions:
20 //
21 // 1. The origin of this software must not be misrepresented; you must not
22 //    claim that you wrote the original software. If you use this software
23 //    in a product, an acknowledgment in the product documentation would
24 //    be appreciated but is not required.
25 //
26 // 2. Altered source versions must be plainly marked as such, and must not
27 //    be misrepresented as being the original software.
28 //
29 // 3. This notice may not be removed or altered from any source
30 //    distribution.
31 //
32 //========================================================================
33 // Please use C89 style variable declarations in this file because VS 2010
34 //========================================================================
35 
36 import glfw3.internal;
37 
38 import core.stdc.assert_;
39 import core.stdc.math;
40 import core.stdc.stdlib;
41 import core.stdc.string;
42 
43 // Internal key state used for sticky keys
44 enum _GLFW_STICK = 3;
45 
46 // Internal constants for gamepad mapping source types
47 enum _GLFW_JOYSTICK_AXIS =     1;
48 enum _GLFW_JOYSTICK_BUTTON =   2;
49 enum _GLFW_JOYSTICK_HATBIT =   3;
50 
51 // Finds a mapping based on joystick GUID
52 //
53 private _GLFWmapping* findMapping(const(char)* guid) {
54     int i;
55 
56     for (i = 0;  i < _glfw.mappingCount;  i++)
57     {
58         if (strcmp(_glfw.mappings[i].guid.ptr, guid) == 0)
59             return _glfw.mappings + i;
60     }
61 
62     return null;
63 }
64 
65 // Checks whether a gamepad mapping element is present in the hardware
66 //
67 private GLFWbool isValidElementForJoystick(const(_GLFWmapelement)* e, const(_GLFWjoystick)* js) {
68     if (e.type == _GLFW_JOYSTICK_HATBIT && (e.index >> 4) >= js.hatCount)
69         return GLFW_FALSE;
70     else if (e.type == _GLFW_JOYSTICK_BUTTON && e.index >= js.buttonCount)
71         return GLFW_FALSE;
72     else if (e.type == _GLFW_JOYSTICK_AXIS && e.index >= js.axisCount)
73         return GLFW_FALSE;
74 
75     return GLFW_TRUE;
76 }
77 
78 // Finds a mapping based on joystick GUID and verifies element indices
79 //
80 private _GLFWmapping* findValidMapping(const(_GLFWjoystick)* js) {
81     _GLFWmapping* mapping = findMapping(js.guid.ptr);
82     if (mapping)
83     {
84         int i;
85 
86         for (i = 0;  i <= GLFW_GAMEPAD_BUTTON_LAST;  i++)
87         {
88             if (!isValidElementForJoystick(mapping.buttons.ptr + i, js))
89             {
90                 _glfwInputError(GLFW_INVALID_VALUE,
91                                 "Invalid button in gamepad mapping %s (%s)",
92                                 mapping.guid.ptr,
93                                 mapping.name.ptr);
94                 return null;
95             }
96         }
97 
98         for (i = 0;  i <= GLFW_GAMEPAD_AXIS_LAST;  i++)
99         {
100             if (!isValidElementForJoystick(mapping.axes.ptr + i, js))
101             {
102                 _glfwInputError(GLFW_INVALID_VALUE,
103                                 "Invalid axis in gamepad mapping %s (%s)",
104                                 mapping.guid.ptr,
105                                 mapping.name.ptr);
106                 return null;
107             }
108         }
109     }
110 
111     return mapping;
112 }
113 
114 // Parses an SDL_GameControllerDB line and adds it to the mapping list
115 //
116 private GLFWbool parseMapping(_GLFWmapping* mapping, const(char)* string) {
117     const(char)* c = string;
118     size_t i;size_t length;
119     static struct Field {
120         const(char)* name;
121         _GLFWmapelement* element;
122     }
123     Field[22] fields = [
124         Field("platform",      null),
125         Field("a",             mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_A),
126         Field("b",             mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_B),
127         Field("x",             mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_X),
128         Field("y",             mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_Y),
129         Field("back",          mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_BACK),
130         Field("start",         mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_START),
131         Field("guide",         mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_GUIDE),
132         Field("leftshoulder",  mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER),
133         Field("rightshoulder", mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER),
134         Field("leftstick",     mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_LEFT_THUMB),
135         Field("rightstick",    mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB),
136         Field("dpup",          mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_DPAD_UP),
137         Field("dpright",       mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT),
138         Field("dpdown",        mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_DPAD_DOWN),
139         Field("dpleft",        mapping.buttons.ptr + GLFW_GAMEPAD_BUTTON_DPAD_LEFT),
140         Field("lefttrigger",   mapping.axes.ptr + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER),
141         Field("righttrigger",  mapping.axes.ptr + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER),
142         Field("leftx",         mapping.axes.ptr + GLFW_GAMEPAD_AXIS_LEFT_X),
143         Field("lefty",         mapping.axes.ptr + GLFW_GAMEPAD_AXIS_LEFT_Y),
144         Field("rightx",        mapping.axes.ptr + GLFW_GAMEPAD_AXIS_RIGHT_X),
145         Field("righty",        mapping.axes.ptr + GLFW_GAMEPAD_AXIS_RIGHT_Y),
146     ];
147 
148     length = strcspn(c, ",");
149     if (length != 32 || c[length] != ',')
150     {
151         _glfwInputError(GLFW_INVALID_VALUE, null);
152         return GLFW_FALSE;
153     }
154 
155     memcpy(mapping.guid.ptr, c, length);
156     c += length + 1;
157 
158     length = strcspn(c, ",");
159     if (length >= typeof(mapping.name).sizeof || c[length] != ',')
160     {
161         _glfwInputError(GLFW_INVALID_VALUE, null);
162         return GLFW_FALSE;
163     }
164 
165     memcpy(mapping.name.ptr, c, length);
166     c += length + 1;
167 
168     while (*c)
169     {
170         // TODO: Implement output modifiers
171         if (*c == '+' || *c == '-')
172             return GLFW_FALSE;
173 
174         for (i = 0;  i < fields.sizeof / typeof(fields[0]).sizeof;  i++)
175         {
176             length = strlen(fields[i].name);
177             if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':')
178                 continue;
179 
180             c += length + 1;
181 
182             if (fields[i].element)
183             {
184                 _GLFWmapelement* e = fields[i].element;
185                 byte minimum = -1;
186                 byte maximum = 1;
187 
188                 if (*c == '+')
189                 {
190                     minimum = 0;
191                     c += 1;
192                 }
193                 else if (*c == '-')
194                 {
195                     maximum = 0;
196                     c += 1;
197                 }
198 
199                 if (*c == 'a')
200                     e.type = _GLFW_JOYSTICK_AXIS;
201                 else if (*c == 'b')
202                     e.type = _GLFW_JOYSTICK_BUTTON;
203                 else if (*c == 'h')
204                     e.type = _GLFW_JOYSTICK_HATBIT;
205                 else
206                     break;
207 
208                 if (e.type == _GLFW_JOYSTICK_HATBIT)
209                 {
210                     const c_ulong hat = strtoul(c + 1, &c, 10);
211                     const c_ulong bit = strtoul(c + 1, &c, 10);
212                     e.index = cast(ubyte) ((hat << 4) | bit);
213                 }
214                 else
215                     e.index = cast(ubyte) strtoul(c + 1, &c, 10);
216 
217                 if (e.type == _GLFW_JOYSTICK_AXIS)
218                 {
219                     e.axisScale = 2 / (maximum - minimum);
220                     e.axisOffset = cast(byte) -(maximum + minimum);
221 
222                     if (*c == '~')
223                     {
224                         e.axisScale = cast(byte) -cast(int)e.axisScale;
225                         e.axisOffset = cast(byte) -cast(int)e.axisOffset;
226                     }
227                 }
228             }
229             else
230             {
231                 length = strlen(_GLFW_PLATFORM_MAPPING_NAME);
232                 if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0)
233                     return GLFW_FALSE;
234             }
235 
236             break;
237         }
238 
239         c += strcspn(c, ",");
240         c += strspn(c, ",");
241     }
242 
243     for (i = 0;  i < 32;  i++)
244     {
245         if (mapping.guid[i] >= 'A' && mapping.guid[i] <= 'F')
246             mapping.guid[i] += 'a' - 'A';
247     }
248 
249     _glfwPlatformUpdateGamepadGUID(mapping.guid.ptr);
250     return GLFW_TRUE;
251 }
252 
253 
254 //////////////////////////////////////////////////////////////////////////
255 //////                         GLFW event API                       //////
256 //////////////////////////////////////////////////////////////////////////
257 
258 // Notifies shared code of a physical key event
259 //
260 void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods) {
261     if (key >= 0 && key <= GLFW_KEY_LAST)
262     {
263         GLFWbool repeated = GLFW_FALSE;
264 
265         if (action == GLFW_RELEASE && window.keys[key] == GLFW_RELEASE)
266             return;
267 
268         if (action == GLFW_PRESS && window.keys[key] == GLFW_PRESS)
269             repeated = GLFW_TRUE;
270 
271         if (action == GLFW_RELEASE && window.stickyKeys)
272             window.keys[key] = _GLFW_STICK;
273         else
274             window.keys[key] = cast(char) action;
275 
276         if (repeated)
277             action = GLFW_REPEAT;
278     }
279 
280     if (!window.lockKeyMods)
281         mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
282 
283     if (window.callbacks.key)
284         window.callbacks.key(cast(GLFWwindow*) window, key, scancode, action, mods);
285 }
286 
287 // Notifies shared code of a Unicode codepoint input event
288 // The 'plain' parameter determines whether to emit a regular character event
289 //
290 void _glfwInputChar(_GLFWwindow* window, uint codepoint, int mods, GLFWbool plain) {
291     if (codepoint < 32 || (codepoint > 126 && codepoint < 160))
292         return;
293 
294     if (!window.lockKeyMods)
295         mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
296 
297     if (window.callbacks.charmods)
298         window.callbacks.charmods(cast(GLFWwindow*) window, codepoint, mods);
299 
300     if (plain)
301     {
302         if (window.callbacks.character)
303             window.callbacks.character(cast(GLFWwindow*) window, codepoint);
304     }
305 }
306 
307 // Notifies shared code of a scroll event
308 //
309 void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) {
310     if (window.callbacks.scroll)
311         window.callbacks.scroll(cast(GLFWwindow*) window, xoffset, yoffset);
312 }
313 
314 // Notifies shared code of a mouse button click event
315 //
316 void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) {
317     if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST)
318         return;
319 
320     if (!window.lockKeyMods)
321         mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
322 
323     if (action == GLFW_RELEASE && window.stickyMouseButtons)
324         window.mouseButtons[button] = _GLFW_STICK;
325     else
326         window.mouseButtons[button] = cast(char) action;
327 
328     if (window.callbacks.mouseButton)
329         window.callbacks.mouseButton(cast(GLFWwindow*) window, button, action, mods);
330 }
331 
332 // Notifies shared code of a cursor motion event
333 // The position is specified in content area relative screen coordinates
334 //
335 void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) {
336     if (window.virtualCursorPosX == xpos && window.virtualCursorPosY == ypos)
337         return;
338 
339     window.virtualCursorPosX = xpos;
340     window.virtualCursorPosY = ypos;
341 
342     if (window.callbacks.cursorPos)
343         window.callbacks.cursorPos(cast(GLFWwindow*) window, xpos, ypos);
344 }
345 
346 // Notifies shared code of a cursor enter/leave event
347 //
348 void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) {
349     if (window.callbacks.cursorEnter)
350         window.callbacks.cursorEnter(cast(GLFWwindow*) window, entered);
351 }
352 
353 // Notifies shared code of files or directories dropped on a window
354 //
355 void _glfwInputDrop(_GLFWwindow* window, int count, const(char)** paths) {
356     if (window.callbacks.drop)
357         window.callbacks.drop(cast(GLFWwindow*) window, count, paths);
358 }
359 
360 // Notifies shared code of a joystick connection or disconnection
361 //
362 void _glfwInputJoystick(_GLFWjoystick* js, int event) {
363     const(int) jid = cast(int) (js - _glfw.joysticks.ptr);
364 
365     if (_glfw.callbacks.joystick)
366         _glfw.callbacks.joystick(jid, event);
367 }
368 
369 // Notifies shared code of the new value of a joystick axis
370 //
371 void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value) {
372     js.axes[axis] = value;
373 }
374 
375 // Notifies shared code of the new value of a joystick button
376 //
377 void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value) {
378     js.buttons[button] = value;
379 }
380 
381 // Notifies shared code of the new value of a joystick hat
382 //
383 void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) {
384     const(int) base = js.buttonCount + hat * 4;
385 
386     js.buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE;
387     js.buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE;
388     js.buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE;
389     js.buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE;
390 
391     js.hats[hat] = value;
392 }
393 
394 
395 //////////////////////////////////////////////////////////////////////////
396 //////                       GLFW internal API                      //////
397 //////////////////////////////////////////////////////////////////////////
398 
399 // Returns an available joystick object with arrays and name allocated
400 //
401 _GLFWjoystick* _glfwAllocJoystick(const(char)* name, const(char)* guid, int axisCount, int buttonCount, int hatCount) {
402     int jid;
403     _GLFWjoystick* js;
404 
405     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
406     {
407         if (!_glfw.joysticks[jid].present)
408             break;
409     }
410 
411     if (jid > GLFW_JOYSTICK_LAST)
412         return null;
413 
414     js = _glfw.joysticks.ptr + jid;
415     js.present     = GLFW_TRUE;
416     js.name        = _glfw_strdup(name);
417     js.axes        = cast(float*) calloc(axisCount, float.sizeof);
418     js.buttons     = cast(ubyte*) calloc(buttonCount + cast(size_t) hatCount * 4, 1);
419     js.hats        = cast(ubyte*) calloc(hatCount, 1);
420     js.axisCount   = axisCount;
421     js.buttonCount = buttonCount;
422     js.hatCount    = hatCount;
423 
424     strncpy(js.guid.ptr, guid, js.guid.length - 1);
425     js.mapping = findValidMapping(js);
426 
427     return js;
428 }
429 
430 // Frees arrays and name and flags the joystick object as unused
431 //
432 void _glfwFreeJoystick(_GLFWjoystick* js) {
433     free(js.name);
434     free(js.axes);
435     free(js.buttons);
436     free(js.hats);
437     memset(js, 0, _GLFWjoystick.sizeof);
438 }
439 
440 // Center the cursor in the content area of the specified window
441 //
442 void _glfwCenterCursorInContentArea(_GLFWwindow* window) {
443     int width;int height;
444 
445     _glfwPlatformGetWindowSize(window, &width, &height);
446     _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
447 }
448 
449 
450 //////////////////////////////////////////////////////////////////////////
451 //////                        GLFW public API                       //////
452 //////////////////////////////////////////////////////////////////////////
453 
454 int glfwGetInputMode(GLFWwindow* handle, int mode) {
455     _GLFWwindow* window = cast(_GLFWwindow*) handle;
456     assert(window != null);
457 
458     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"0");
459 
460     switch (mode)
461     {
462         case GLFW_CURSOR:
463             return window.cursorMode;
464         case GLFW_STICKY_KEYS:
465             return window.stickyKeys;
466         case GLFW_STICKY_MOUSE_BUTTONS:
467             return window.stickyMouseButtons;
468         case GLFW_LOCK_KEY_MODS:
469             return window.lockKeyMods;
470         case GLFW_RAW_MOUSE_MOTION:
471             return window.rawMouseMotion;
472         default: break;
473     }
474 
475     _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
476     return 0;
477 }
478 
479 void glfwSetInputMode(GLFWwindow* handle, int mode, int value) {
480     _GLFWwindow* window = cast(_GLFWwindow*) handle;
481     assert(window != null);
482 
483     mixin(_GLFW_REQUIRE_INIT);
484 
485     if (mode == GLFW_CURSOR)
486     {
487         if (value != GLFW_CURSOR_NORMAL &&
488             value != GLFW_CURSOR_HIDDEN &&
489             value != GLFW_CURSOR_DISABLED)
490         {
491             _glfwInputError(GLFW_INVALID_ENUM,
492                             "Invalid cursor mode 0x%08X",
493                             value);
494             return;
495         }
496 
497         if (window.cursorMode == value)
498             return;
499 
500         window.cursorMode = value;
501 
502         _glfwPlatformGetCursorPos(window,
503                                   &window.virtualCursorPosX,
504                                   &window.virtualCursorPosY);
505         _glfwPlatformSetCursorMode(window, value);
506     }
507     else if (mode == GLFW_STICKY_KEYS)
508     {
509         value = value ? GLFW_TRUE : GLFW_FALSE;
510         if (window.stickyKeys == value)
511             return;
512 
513         if (!value)
514         {
515             int i;
516 
517             // Release all sticky keys
518             for (i = 0;  i <= GLFW_KEY_LAST;  i++)
519             {
520                 if (window.keys[i] == _GLFW_STICK)
521                     window.keys[i] = GLFW_RELEASE;
522             }
523         }
524 
525         window.stickyKeys = value;
526     }
527     else if (mode == GLFW_STICKY_MOUSE_BUTTONS)
528     {
529         value = value ? GLFW_TRUE : GLFW_FALSE;
530         if (window.stickyMouseButtons == value)
531             return;
532 
533         if (!value)
534         {
535             int i;
536 
537             // Release all sticky mouse buttons
538             for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
539             {
540                 if (window.mouseButtons[i] == _GLFW_STICK)
541                     window.mouseButtons[i] = GLFW_RELEASE;
542             }
543         }
544 
545         window.stickyMouseButtons = value;
546     }
547     else if (mode == GLFW_LOCK_KEY_MODS)
548     {
549         window.lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE;
550     }
551     else if (mode == GLFW_RAW_MOUSE_MOTION)
552     {
553         if (!_glfwPlatformRawMouseMotionSupported())
554         {
555             _glfwInputError(GLFW_PLATFORM_ERROR,
556                             "Raw mouse motion is not supported on this system");
557             return;
558         }
559 
560         value = value ? GLFW_TRUE : GLFW_FALSE;
561         if (window.rawMouseMotion == value)
562             return;
563 
564         window.rawMouseMotion = value;
565         _glfwPlatformSetRawMouseMotion(window, value);
566     }
567     else
568         _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
569 }
570 
571 int glfwRawMouseMotionSupported() {
572     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"GLFW_FALSE");
573     return _glfwPlatformRawMouseMotionSupported();
574 }
575 
576 const(char)* glfwGetKeyName(int key, int scancode) {
577     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
578 
579     if (key != GLFW_KEY_UNKNOWN)
580     {
581         if (key != GLFW_KEY_KP_EQUAL &&
582             (key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) &&
583             (key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2))
584         {
585             return null;
586         }
587 
588         scancode = _glfwPlatformGetKeyScancode(key);
589     }
590 
591     return _glfwPlatformGetScancodeName(scancode);
592 }
593 
594 int glfwGetKeyScancode(int key) {
595     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"-1");
596 
597     if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
598     {
599         _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
600         return GLFW_RELEASE;
601     }
602 
603     return _glfwPlatformGetKeyScancode(key);
604 }
605 
606 int glfwGetKey(GLFWwindow* handle, int key) {
607     _GLFWwindow* window = cast(_GLFWwindow*) handle;
608     assert(window != null);
609 
610     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"GLFW_RELEASE");
611 
612     if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
613     {
614         _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
615         return GLFW_RELEASE;
616     }
617 
618     if (window.keys[key] == _GLFW_STICK)
619     {
620         // Sticky mode: release key now
621         window.keys[key] = GLFW_RELEASE;
622         return GLFW_PRESS;
623     }
624 
625     return cast(int) window.keys[key];
626 }
627 
628 int glfwGetMouseButton(GLFWwindow* handle, int button) {
629     _GLFWwindow* window = cast(_GLFWwindow*) handle;
630     assert(window != null);
631 
632     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"GLFW_RELEASE");
633 
634     if (button < GLFW_MOUSE_BUTTON_1 || button > GLFW_MOUSE_BUTTON_LAST)
635     {
636         _glfwInputError(GLFW_INVALID_ENUM, "Invalid mouse button %i", button);
637         return GLFW_RELEASE;
638     }
639 
640     if (window.mouseButtons[button] == _GLFW_STICK)
641     {
642         // Sticky mode: release mouse button now
643         window.mouseButtons[button] = GLFW_RELEASE;
644         return GLFW_PRESS;
645     }
646 
647     return cast(int) window.mouseButtons[button];
648 }
649 
650 void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) {
651     _GLFWwindow* window = cast(_GLFWwindow*) handle;
652     assert(window != null);
653 
654     if (xpos)
655         *xpos = 0;
656     if (ypos)
657         *ypos = 0;
658 
659     mixin(_GLFW_REQUIRE_INIT);
660 
661     if (window.cursorMode == GLFW_CURSOR_DISABLED)
662     {
663         if (xpos)
664             *xpos = window.virtualCursorPosX;
665         if (ypos)
666             *ypos = window.virtualCursorPosY;
667     }
668     else
669         _glfwPlatformGetCursorPos(window, xpos, ypos);
670 }
671 
672 void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) {
673     _GLFWwindow* window = cast(_GLFWwindow*) handle;
674     assert(window != null);
675 
676     mixin(_GLFW_REQUIRE_INIT);
677 
678     if (xpos != xpos || xpos < -double.max || xpos > double.max ||
679         ypos != ypos || ypos < -double.max || ypos > double.max)
680     {
681         _glfwInputError(GLFW_INVALID_VALUE,
682                         "Invalid cursor position %f %f",
683                         xpos, ypos);
684         return;
685     }
686 
687     if (!_glfwPlatformWindowFocused(window))
688         return;
689 
690     if (window.cursorMode == GLFW_CURSOR_DISABLED)
691     {
692         // Only update the accumulated position if the cursor is disabled
693         window.virtualCursorPosX = xpos;
694         window.virtualCursorPosY = ypos;
695     }
696     else
697     {
698         // Update system cursor position
699         _glfwPlatformSetCursorPos(window, xpos, ypos);
700     }
701 }
702 
703 GLFWcursor* glfwCreateCursor(const(GLFWimage)* image, int xhot, int yhot) {
704     _GLFWcursor* cursor;
705 
706     assert(image != null);
707 
708     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
709 
710     cursor = cast(_GLFWcursor*) calloc(1, _GLFWcursor.sizeof);
711     cursor.next = _glfw.cursorListHead;
712     _glfw.cursorListHead = cursor;
713 
714     if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot))
715     {
716         glfwDestroyCursor(cast(GLFWcursor*) cursor);
717         return null;
718     }
719 
720     return cast(GLFWcursor*) cursor;
721 }
722 
723 GLFWcursor* glfwCreateStandardCursor(int shape) {
724     _GLFWcursor* cursor;
725 
726     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
727 
728     if (shape != GLFW_ARROW_CURSOR &&
729         shape != GLFW_IBEAM_CURSOR &&
730         shape != GLFW_CROSSHAIR_CURSOR &&
731         shape != GLFW_HAND_CURSOR &&
732         shape != GLFW_HRESIZE_CURSOR &&
733         shape != GLFW_VRESIZE_CURSOR)
734     {
735         _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape);
736         return null;
737     }
738 
739     cursor = cast(_GLFWcursor*) calloc(1, _GLFWcursor.sizeof);
740     cursor.next = _glfw.cursorListHead;
741     _glfw.cursorListHead = cursor;
742 
743     if (!_glfwPlatformCreateStandardCursor(cursor, shape))
744     {
745         glfwDestroyCursor(cast(GLFWcursor*) cursor);
746         return null;
747     }
748 
749     return cast(GLFWcursor*) cursor;
750 }
751 
752 void glfwDestroyCursor(GLFWcursor* handle) {
753     _GLFWcursor* cursor = cast(_GLFWcursor*) handle;
754 
755     mixin(_GLFW_REQUIRE_INIT);
756 
757     if (cursor == null)
758         return;
759 
760     // Make sure the cursor is not being used by any window
761     {
762         _GLFWwindow* window;
763 
764         for (window = _glfw.windowListHead;  window;  window = window.next)
765         {
766             if (window.cursor == cursor)
767                 glfwSetCursor(cast(GLFWwindow*) window, null);
768         }
769     }
770 
771     _glfwPlatformDestroyCursor(cursor);
772 
773     // Unlink cursor from global linked list
774     {
775         _GLFWcursor** prev = &_glfw.cursorListHead;
776 
777         while (*prev != cursor)
778             prev = &((*prev).next);
779 
780         *prev = cursor.next;
781     }
782 
783     free(cursor);
784 }
785 
786 void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) {
787     _GLFWwindow* window = cast(_GLFWwindow*) windowHandle;
788     _GLFWcursor* cursor = cast(_GLFWcursor*) cursorHandle;
789     assert(window != null);
790 
791     mixin(_GLFW_REQUIRE_INIT);
792 
793     window.cursor = cursor;
794 
795     _glfwPlatformSetCursor(window, cursor);
796 }
797 
798 GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) {
799     _GLFWwindow* window = cast(_GLFWwindow*) handle;
800     assert(window != null);
801 
802     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
803     _GLFW_SWAP_POINTERS(window.callbacks.key, cbfun);
804     return cbfun;
805 }
806 
807 GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) {
808     _GLFWwindow* window = cast(_GLFWwindow*) handle;
809     assert(window != null);
810 
811     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
812     _GLFW_SWAP_POINTERS(window.callbacks.character, cbfun);
813     return cbfun;
814 }
815 
816 GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun) {
817     _GLFWwindow* window = cast(_GLFWwindow*) handle;
818     assert(window != null);
819 
820     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
821     _GLFW_SWAP_POINTERS(window.callbacks.charmods, cbfun);
822     return cbfun;
823 }
824 
825 GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle, GLFWmousebuttonfun cbfun) {
826     _GLFWwindow* window = cast(_GLFWwindow*) handle;
827     assert(window != null);
828 
829     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
830     _GLFW_SWAP_POINTERS(window.callbacks.mouseButton, cbfun);
831     return cbfun;
832 }
833 
834 GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle, GLFWcursorposfun cbfun) {
835     _GLFWwindow* window = cast(_GLFWwindow*) handle;
836     assert(window != null);
837 
838     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
839     _GLFW_SWAP_POINTERS(window.callbacks.cursorPos, cbfun);
840     return cbfun;
841 }
842 
843 GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle, GLFWcursorenterfun cbfun) {
844     _GLFWwindow* window = cast(_GLFWwindow*) handle;
845     assert(window != null);
846 
847     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
848     _GLFW_SWAP_POINTERS(window.callbacks.cursorEnter, cbfun);
849     return cbfun;
850 }
851 
852 GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, GLFWscrollfun cbfun) {
853     _GLFWwindow* window = cast(_GLFWwindow*) handle;
854     assert(window != null);
855 
856     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
857     _GLFW_SWAP_POINTERS(window.callbacks.scroll, cbfun);
858     return cbfun;
859 }
860 
861 GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) {
862     _GLFWwindow* window = cast(_GLFWwindow*) handle;
863     assert(window != null);
864 
865     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
866     _GLFW_SWAP_POINTERS(window.callbacks.drop, cbfun);
867     return cbfun;
868 }
869 
870 int glfwJoystickPresent(int jid) {
871     _GLFWjoystick* js;
872 
873     assert(jid >= GLFW_JOYSTICK_1);
874     assert(jid <= GLFW_JOYSTICK_LAST);
875 
876     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"GLFW_FALSE");
877 
878     if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
879     {
880         _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
881         return GLFW_FALSE;
882     }
883 
884     js = _glfw.joysticks.ptr + jid;
885     if (!js.present)
886         return GLFW_FALSE;
887 
888     return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE);
889 }
890 
891 const(float)* glfwGetJoystickAxes(int jid, int* count) {
892     _GLFWjoystick* js;
893 
894     assert(jid >= GLFW_JOYSTICK_1);
895     assert(jid <= GLFW_JOYSTICK_LAST);
896     assert(count != null);
897 
898     *count = 0;
899 
900     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
901 
902     if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
903     {
904         _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
905         return null;
906     }
907 
908     js = _glfw.joysticks.ptr + jid;
909     if (!js.present)
910         return null;
911 
912     if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES))
913         return null;
914 
915     *count = js.axisCount;
916     return js.axes;
917 }
918 
919 const(ubyte)* glfwGetJoystickButtons(int jid, int* count) {
920     _GLFWjoystick* js;
921 
922     assert(jid >= GLFW_JOYSTICK_1);
923     assert(jid <= GLFW_JOYSTICK_LAST);
924     assert(count != null);
925 
926     *count = 0;
927 
928     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
929 
930     if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
931     {
932         _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
933         return null;
934     }
935 
936     js = _glfw.joysticks.ptr + jid;
937     if (!js.present)
938         return null;
939 
940     if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS))
941         return null;
942 
943     if (_glfw.hints.init.hatButtons)
944         *count = js.buttonCount + js.hatCount * 4;
945     else
946         *count = js.buttonCount;
947 
948     return js.buttons;
949 }
950 
951 const(ubyte)* glfwGetJoystickHats(int jid, int* count) {
952     _GLFWjoystick* js;
953 
954     assert(jid >= GLFW_JOYSTICK_1);
955     assert(jid <= GLFW_JOYSTICK_LAST);
956     assert(count != null);
957 
958     *count = 0;
959 
960     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
961 
962     if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
963     {
964         _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
965         return null;
966     }
967 
968     js = _glfw.joysticks.ptr + jid;
969     if (!js.present)
970         return null;
971 
972     if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS))
973         return null;
974 
975     *count = js.hatCount;
976     return js.hats;
977 }
978 
979 const(char)* glfwGetJoystickName(int jid) {
980     _GLFWjoystick* js;
981 
982     assert(jid >= GLFW_JOYSTICK_1);
983     assert(jid <= GLFW_JOYSTICK_LAST);
984 
985     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
986 
987     if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
988     {
989         _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
990         return null;
991     }
992 
993     js = _glfw.joysticks.ptr + jid;
994     if (!js.present)
995         return null;
996 
997     if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
998         return null;
999 
1000     return js.name;
1001 }
1002 
1003 const(char)* glfwGetJoystickGUID(int jid) {
1004     _GLFWjoystick* js;
1005 
1006     assert(jid >= GLFW_JOYSTICK_1);
1007     assert(jid <= GLFW_JOYSTICK_LAST);
1008 
1009     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
1010 
1011     if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
1012     {
1013         _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
1014         return null;
1015     }
1016 
1017     js = _glfw.joysticks.ptr + jid;
1018     if (!js.present)
1019         return null;
1020 
1021     if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
1022         return null;
1023 
1024     return js.guid.ptr;
1025 }
1026 
1027 void glfwSetJoystickUserPointer(int jid, void* pointer) {
1028     _GLFWjoystick* js;
1029 
1030     assert(jid >= GLFW_JOYSTICK_1);
1031     assert(jid <= GLFW_JOYSTICK_LAST);
1032 
1033     mixin(_GLFW_REQUIRE_INIT);
1034 
1035     js = _glfw.joysticks.ptr + jid;
1036     if (!js.present)
1037         return;
1038 
1039     js.userPointer = pointer;
1040 }
1041 
1042 void* glfwGetJoystickUserPointer(int jid) {
1043     _GLFWjoystick* js;
1044 
1045     assert(jid >= GLFW_JOYSTICK_1);
1046     assert(jid <= GLFW_JOYSTICK_LAST);
1047 
1048     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
1049 
1050     js = _glfw.joysticks.ptr + jid;
1051     if (!js.present)
1052         return null;
1053 
1054     return js.userPointer;
1055 }
1056 
1057 GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) {
1058     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
1059     _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun);
1060     return cbfun;
1061 }
1062 
1063 int glfwUpdateGamepadMappings(const(char)* string) {
1064     int jid;
1065     const(char)* c = string;
1066 
1067     assert(string != null);
1068 
1069     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"GLFW_FALSE");
1070 
1071     while (*c)
1072     {
1073         if ((*c >= '0' && *c <= '9') ||
1074             (*c >= 'a' && *c <= 'f') ||
1075             (*c >= 'A' && *c <= 'F'))
1076         {
1077             char[1024] line;
1078 
1079             const(size_t) length = strcspn(c, "\r\n");
1080             if (length < typeof(line).sizeof)
1081             {
1082                 _GLFWmapping mapping = _GLFWmapping.init;
1083 
1084                 memcpy(line.ptr, c, length);
1085                 line[length] = '\0';
1086 
1087                 if (parseMapping(&mapping, line.ptr))
1088                 {
1089                     _GLFWmapping* previous = findMapping(mapping.guid.ptr);
1090                     if (previous)
1091                         *previous = mapping;
1092                     else
1093                     {
1094                         _glfw.mappingCount++;
1095                         _glfw.mappings =
1096                             cast(_GLFWmapping*) realloc(_glfw.mappings,
1097                                     _GLFWmapping.sizeof * _glfw.mappingCount);
1098                         _glfw.mappings[_glfw.mappingCount - 1] = mapping;
1099                     }
1100                 }
1101             }
1102 
1103             c += length;
1104         }
1105         else
1106         {
1107             c += strcspn(c, "\r\n");
1108             c += strspn(c, "\r\n");
1109         }
1110     }
1111 
1112     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
1113     {
1114         _GLFWjoystick* js = _glfw.joysticks.ptr + jid;
1115         if (js.present)
1116             js.mapping = findValidMapping(js);
1117     }
1118 
1119     return GLFW_TRUE;
1120 }
1121 
1122 int glfwJoystickIsGamepad(int jid) {
1123     _GLFWjoystick* js;
1124 
1125     assert(jid >= GLFW_JOYSTICK_1);
1126     assert(jid <= GLFW_JOYSTICK_LAST);
1127 
1128     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"GLFW_FALSE");
1129 
1130     if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
1131     {
1132         _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
1133         return GLFW_FALSE;
1134     }
1135 
1136     js = _glfw.joysticks.ptr + jid;
1137     if (!js.present)
1138         return GLFW_FALSE;
1139 
1140     if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
1141         return GLFW_FALSE;
1142 
1143     return js.mapping != null;
1144 }
1145 
1146 const(char)* glfwGetGamepadName(int jid) {
1147     _GLFWjoystick* js;
1148 
1149     assert(jid >= GLFW_JOYSTICK_1);
1150     assert(jid <= GLFW_JOYSTICK_LAST);
1151 
1152     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
1153 
1154     if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
1155     {
1156         _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
1157         return null;
1158     }
1159 
1160     js = _glfw.joysticks.ptr + jid;
1161     if (!js.present)
1162         return null;
1163 
1164     if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
1165         return null;
1166 
1167     if (!js.mapping)
1168         return null;
1169 
1170     return js.mapping.name.ptr;
1171 }
1172 
1173 int glfwGetGamepadState(int jid, GLFWgamepadstate* state) {
1174     int i;
1175     _GLFWjoystick* js;
1176 
1177     assert(jid >= GLFW_JOYSTICK_1);
1178     assert(jid <= GLFW_JOYSTICK_LAST);
1179     assert(state != null);
1180 
1181     memset(state, 0, GLFWgamepadstate.sizeof);
1182 
1183     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"GLFW_FALSE");
1184 
1185     if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
1186     {
1187         _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
1188         return GLFW_FALSE;
1189     }
1190 
1191     js = _glfw.joysticks.ptr + jid;
1192     if (!js.present)
1193         return GLFW_FALSE;
1194 
1195     if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL))
1196         return GLFW_FALSE;
1197 
1198     if (!js.mapping)
1199         return GLFW_FALSE;
1200 
1201     for (i = 0;  i <= GLFW_GAMEPAD_BUTTON_LAST;  i++)
1202     {
1203         const(_GLFWmapelement)* e = js.mapping.buttons.ptr + i;
1204         if (e.type == _GLFW_JOYSTICK_AXIS)
1205         {
1206             const(float) value = js.axes[e.index] * e.axisScale + e.axisOffset;
1207             // HACK: This should be baked into the value transform
1208             // TODO: Bake into transform when implementing output modifiers
1209             if (e.axisOffset < 0 || (e.axisOffset == 0 && e.axisScale > 0))
1210             {
1211                 if (value >= 0.0f)
1212                     state.buttons[i] = GLFW_PRESS;
1213             }
1214             else
1215             {
1216                 if (value <= 0.0f)
1217                     state.buttons[i] = GLFW_PRESS;
1218             }
1219         }
1220         else if (e.type == _GLFW_JOYSTICK_HATBIT)
1221         {
1222             const(uint) hat = e.index >> 4;
1223             const(uint) bit = e.index & 0xf;
1224             if (js.hats[hat] & bit)
1225                 state.buttons[i] = GLFW_PRESS;
1226         }
1227         else if (e.type == _GLFW_JOYSTICK_BUTTON)
1228             state.buttons[i] = js.buttons[e.index];
1229     }
1230 
1231     for (i = 0;  i <= GLFW_GAMEPAD_AXIS_LAST;  i++)
1232     {
1233         const(_GLFWmapelement)* e = js.mapping.axes.ptr + i;
1234         if (e.type == _GLFW_JOYSTICK_AXIS)
1235         {
1236             const(float) value = js.axes[e.index] * e.axisScale + e.axisOffset;
1237             state.axes[i] = _glfw_fminf(_glfw_fmaxf(value, -1.0f), 1.0f);
1238         }
1239         else if (e.type == _GLFW_JOYSTICK_HATBIT)
1240         {
1241             const(uint) hat = e.index >> 4;
1242             const(uint) bit = e.index & 0xf;
1243             if (js.hats[hat] & bit)
1244                 state.axes[i] = 1.0f;
1245             else
1246                 state.axes[i] = -1.0f;
1247         }
1248         else if (e.type == _GLFW_JOYSTICK_BUTTON)
1249             state.axes[i] = js.buttons[e.index] * 2.0f - 1.0f;
1250     }
1251 
1252     return GLFW_TRUE;
1253 }
1254 
1255 void glfwSetClipboardString(GLFWwindow* handle, const(char)* string) {
1256     assert(string != null);
1257 
1258     mixin(_GLFW_REQUIRE_INIT);
1259     _glfwPlatformSetClipboardString(string);
1260 }
1261 
1262 const(char)* glfwGetClipboardString(GLFWwindow* handle) {
1263     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
1264     return _glfwPlatformGetClipboardString();
1265 }
1266 
1267 double glfwGetTime() {
1268     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"0.0");
1269     return cast(double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) /
1270         _glfwPlatformGetTimerFrequency();
1271 }
1272 
1273 void glfwSetTime(double time) {
1274     mixin(_GLFW_REQUIRE_INIT);
1275 
1276     if (time != time || time < 0.0 || time > 18446744073.0)
1277     {
1278         _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time);
1279         return;
1280     }
1281 
1282     _glfw.timer.offset = _glfwPlatformGetTimerValue() -
1283         cast(ulong) (time * _glfwPlatformGetTimerFrequency());
1284 }
1285 
1286 ulong glfwGetTimerValue() {
1287     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"0");
1288     return _glfwPlatformGetTimerValue();
1289 }
1290 
1291 ulong glfwGetTimerFrequency() {
1292     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"0");
1293     return _glfwPlatformGetTimerFrequency();
1294 }