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