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