]> git.sesse.net Git - pistorm/blobdiff - raylib_pi4_test/external/glfw/src/input.c
[MEGA-WIP] Raylib-based RTG output
[pistorm] / raylib_pi4_test / external / glfw / src / input.c
diff --git a/raylib_pi4_test/external/glfw/src/input.c b/raylib_pi4_test/external/glfw/src/input.c
new file mode 100644 (file)
index 0000000..226f835
--- /dev/null
@@ -0,0 +1,1407 @@
+//========================================================================
+// GLFW 3.4 - www.glfw.org
+//------------------------------------------------------------------------
+// Copyright (c) 2002-2006 Marcus Geelnard
+// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//
+//========================================================================
+// Please use C89 style variable declarations in this file because VS 2010
+//========================================================================
+
+#include "internal.h"
+
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Internal key state used for sticky keys
+#define _GLFW_STICK 3
+
+// Internal constants for gamepad mapping source types
+#define _GLFW_JOYSTICK_AXIS     1
+#define _GLFW_JOYSTICK_BUTTON   2
+#define _GLFW_JOYSTICK_HATBIT   3
+
+// Initializes the platform joystick API if it has not been already
+//
+static GLFWbool initJoysticks(void)
+{
+    if (!_glfw.joysticksInitialized)
+    {
+        if (!_glfwPlatformInitJoysticks())
+        {
+            _glfwPlatformTerminateJoysticks();
+            return GLFW_FALSE;
+        }
+    }
+
+    return _glfw.joysticksInitialized = GLFW_TRUE;
+}
+
+// Finds a mapping based on joystick GUID
+//
+static _GLFWmapping* findMapping(const char* guid)
+{
+    int i;
+
+    for (i = 0;  i < _glfw.mappingCount;  i++)
+    {
+        if (strcmp(_glfw.mappings[i].guid, guid) == 0)
+            return _glfw.mappings + i;
+    }
+
+    return NULL;
+}
+
+// Checks whether a gamepad mapping element is present in the hardware
+//
+static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e,
+                                          const _GLFWjoystick* js)
+{
+    if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount)
+        return GLFW_FALSE;
+    else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount)
+        return GLFW_FALSE;
+    else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount)
+        return GLFW_FALSE;
+
+    return GLFW_TRUE;
+}
+
+// Finds a mapping based on joystick GUID and verifies element indices
+//
+static _GLFWmapping* findValidMapping(const _GLFWjoystick* js)
+{
+    _GLFWmapping* mapping = findMapping(js->guid);
+    if (mapping)
+    {
+        int i;
+
+        for (i = 0;  i <= GLFW_GAMEPAD_BUTTON_LAST;  i++)
+        {
+            if (!isValidElementForJoystick(mapping->buttons + i, js))
+            {
+                _glfwInputError(GLFW_INVALID_VALUE,
+                                "Invalid button in gamepad mapping %s (%s)",
+                                mapping->guid,
+                                mapping->name);
+                return NULL;
+            }
+        }
+
+        for (i = 0;  i <= GLFW_GAMEPAD_AXIS_LAST;  i++)
+        {
+            if (!isValidElementForJoystick(mapping->axes + i, js))
+            {
+                _glfwInputError(GLFW_INVALID_VALUE,
+                                "Invalid axis in gamepad mapping %s (%s)",
+                                mapping->guid,
+                                mapping->name);
+                return NULL;
+            }
+        }
+    }
+
+    return mapping;
+}
+
+// Parses an SDL_GameControllerDB line and adds it to the mapping list
+//
+static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
+{
+    const char* c = string;
+    size_t i, length;
+    struct
+    {
+        const char* name;
+        _GLFWmapelement* element;
+    } fields[] =
+    {
+        { "platform",      NULL },
+        { "a",             mapping->buttons + GLFW_GAMEPAD_BUTTON_A },
+        { "b",             mapping->buttons + GLFW_GAMEPAD_BUTTON_B },
+        { "x",             mapping->buttons + GLFW_GAMEPAD_BUTTON_X },
+        { "y",             mapping->buttons + GLFW_GAMEPAD_BUTTON_Y },
+        { "back",          mapping->buttons + GLFW_GAMEPAD_BUTTON_BACK },
+        { "start",         mapping->buttons + GLFW_GAMEPAD_BUTTON_START },
+        { "guide",         mapping->buttons + GLFW_GAMEPAD_BUTTON_GUIDE },
+        { "leftshoulder",  mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER },
+        { "rightshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER },
+        { "leftstick",     mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_THUMB },
+        { "rightstick",    mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB },
+        { "dpup",          mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_UP },
+        { "dpright",       mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT },
+        { "dpdown",        mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_DOWN },
+        { "dpleft",        mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_LEFT },
+        { "lefttrigger",   mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER },
+        { "righttrigger",  mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER },
+        { "leftx",         mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_X },
+        { "lefty",         mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_Y },
+        { "rightx",        mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_X },
+        { "righty",        mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_Y }
+    };
+
+    length = strcspn(c, ",");
+    if (length != 32 || c[length] != ',')
+    {
+        _glfwInputError(GLFW_INVALID_VALUE, NULL);
+        return GLFW_FALSE;
+    }
+
+    memcpy(mapping->guid, c, length);
+    c += length + 1;
+
+    length = strcspn(c, ",");
+    if (length >= sizeof(mapping->name) || c[length] != ',')
+    {
+        _glfwInputError(GLFW_INVALID_VALUE, NULL);
+        return GLFW_FALSE;
+    }
+
+    memcpy(mapping->name, c, length);
+    c += length + 1;
+
+    while (*c)
+    {
+        // TODO: Implement output modifiers
+        if (*c == '+' || *c == '-')
+            return GLFW_FALSE;
+
+        for (i = 0;  i < sizeof(fields) / sizeof(fields[0]);  i++)
+        {
+            length = strlen(fields[i].name);
+            if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':')
+                continue;
+
+            c += length + 1;
+
+            if (fields[i].element)
+            {
+                _GLFWmapelement* e = fields[i].element;
+                int8_t minimum = -1;
+                int8_t maximum = 1;
+
+                if (*c == '+')
+                {
+                    minimum = 0;
+                    c += 1;
+                }
+                else if (*c == '-')
+                {
+                    maximum = 0;
+                    c += 1;
+                }
+
+                if (*c == 'a')
+                    e->type = _GLFW_JOYSTICK_AXIS;
+                else if (*c == 'b')
+                    e->type = _GLFW_JOYSTICK_BUTTON;
+                else if (*c == 'h')
+                    e->type = _GLFW_JOYSTICK_HATBIT;
+                else
+                    break;
+
+                if (e->type == _GLFW_JOYSTICK_HATBIT)
+                {
+                    const unsigned long hat = strtoul(c + 1, (char**) &c, 10);
+                    const unsigned long bit = strtoul(c + 1, (char**) &c, 10);
+                    e->index = (uint8_t) ((hat << 4) | bit);
+                }
+                else
+                    e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10);
+
+                if (e->type == _GLFW_JOYSTICK_AXIS)
+                {
+                    e->axisScale = 2 / (maximum - minimum);
+                    e->axisOffset = -(maximum + minimum);
+
+                    if (*c == '~')
+                    {
+                        e->axisScale = -e->axisScale;
+                        e->axisOffset = -e->axisOffset;
+                    }
+                }
+            }
+            else
+            {
+                length = strlen(_GLFW_PLATFORM_MAPPING_NAME);
+                if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0)
+                    return GLFW_FALSE;
+            }
+
+            break;
+        }
+
+        c += strcspn(c, ",");
+        c += strspn(c, ",");
+    }
+
+    for (i = 0;  i < 32;  i++)
+    {
+        if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F')
+            mapping->guid[i] += 'a' - 'A';
+    }
+
+    _glfwPlatformUpdateGamepadGUID(mapping->guid);
+    return GLFW_TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//////                         GLFW event API                       //////
+//////////////////////////////////////////////////////////////////////////
+
+// Notifies shared code of a physical key event
+//
+void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+    if (key >= 0 && key <= GLFW_KEY_LAST)
+    {
+        GLFWbool repeated = GLFW_FALSE;
+
+        if (action == GLFW_RELEASE && window->keys[key] == GLFW_RELEASE)
+            return;
+
+        if (action == GLFW_PRESS && window->keys[key] == GLFW_PRESS)
+            repeated = GLFW_TRUE;
+
+        if (action == GLFW_RELEASE && window->stickyKeys)
+            window->keys[key] = _GLFW_STICK;
+        else
+            window->keys[key] = (char) action;
+
+        if (repeated)
+            action = GLFW_REPEAT;
+    }
+
+    if (!window->lockKeyMods)
+        mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
+
+    if (window->callbacks.key)
+        window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods);
+}
+
+// Notifies shared code of a Unicode codepoint input event
+// The 'plain' parameter determines whether to emit a regular character event
+//
+void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain)
+{
+    if (codepoint < 32 || (codepoint > 126 && codepoint < 160))
+        return;
+
+    if (!window->lockKeyMods)
+        mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
+
+    if (window->callbacks.charmods)
+        window->callbacks.charmods((GLFWwindow*) window, codepoint, mods);
+
+    if (plain)
+    {
+        if (window->callbacks.character)
+            window->callbacks.character((GLFWwindow*) window, codepoint);
+    }
+}
+
+// Notifies shared code of a scroll event
+//
+void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
+{
+    if (window->callbacks.scroll)
+        window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset);
+}
+
+// Notifies shared code of a mouse button click event
+//
+void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods)
+{
+    if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST)
+        return;
+
+    if (!window->lockKeyMods)
+        mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
+
+    if (action == GLFW_RELEASE && window->stickyMouseButtons)
+        window->mouseButtons[button] = _GLFW_STICK;
+    else
+        window->mouseButtons[button] = (char) action;
+
+    if (window->callbacks.mouseButton)
+        window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods);
+}
+
+// Notifies shared code of a cursor motion event
+// The position is specified in content area relative screen coordinates
+//
+void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos)
+{
+    if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos)
+        return;
+
+    window->virtualCursorPosX = xpos;
+    window->virtualCursorPosY = ypos;
+
+    if (window->callbacks.cursorPos)
+        window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos);
+}
+
+// Notifies shared code of a cursor enter/leave event
+//
+void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered)
+{
+    if (window->callbacks.cursorEnter)
+        window->callbacks.cursorEnter((GLFWwindow*) window, entered);
+}
+
+// Notifies shared code of files or directories dropped on a window
+//
+void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
+{
+    if (window->callbacks.drop)
+        window->callbacks.drop((GLFWwindow*) window, count, paths);
+}
+
+// Notifies shared code of a joystick connection or disconnection
+//
+void _glfwInputJoystick(_GLFWjoystick* js, int event)
+{
+    const int jid = (int) (js - _glfw.joysticks);
+
+    if (_glfw.callbacks.joystick)
+        _glfw.callbacks.joystick(jid, event);
+}
+
+// Notifies shared code of the new value of a joystick axis
+//
+void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value)
+{
+    js->axes[axis] = value;
+}
+
+// Notifies shared code of the new value of a joystick button
+//
+void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value)
+{
+    js->buttons[button] = value;
+}
+
+// Notifies shared code of the new value of a joystick hat
+//
+void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value)
+{
+    const int base = js->buttonCount + hat * 4;
+
+    js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE;
+    js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE;
+    js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE;
+    js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE;
+
+    js->hats[hat] = value;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW internal API                      //////
+//////////////////////////////////////////////////////////////////////////
+
+// Returns an available joystick object with arrays and name allocated
+//
+_GLFWjoystick* _glfwAllocJoystick(const char* name,
+                                  const char* guid,
+                                  int axisCount,
+                                  int buttonCount,
+                                  int hatCount)
+{
+    int jid;
+    _GLFWjoystick* js;
+
+    for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
+    {
+        if (!_glfw.joysticks[jid].present)
+            break;
+    }
+
+    if (jid > GLFW_JOYSTICK_LAST)
+        return NULL;
+
+    js = _glfw.joysticks + jid;
+    js->present     = GLFW_TRUE;
+    js->name        = _glfw_strdup(name);
+    js->axes        = calloc(axisCount, sizeof(float));
+    js->buttons     = calloc(buttonCount + (size_t) hatCount * 4, 1);
+    js->hats        = calloc(hatCount, 1);
+    js->axisCount   = axisCount;
+    js->buttonCount = buttonCount;
+    js->hatCount    = hatCount;
+
+    strncpy(js->guid, guid, sizeof(js->guid) - 1);
+    js->mapping = findValidMapping(js);
+
+    return js;
+}
+
+// Frees arrays and name and flags the joystick object as unused
+//
+void _glfwFreeJoystick(_GLFWjoystick* js)
+{
+    free(js->name);
+    free(js->axes);
+    free(js->buttons);
+    free(js->hats);
+    memset(js, 0, sizeof(_GLFWjoystick));
+}
+
+// Center the cursor in the content area of the specified window
+//
+void _glfwCenterCursorInContentArea(_GLFWwindow* window)
+{
+    int width, height;
+
+    _glfwPlatformGetWindowSize(window, &width, &height);
+    _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//////                        GLFW public API                       //////
+//////////////////////////////////////////////////////////////////////////
+
+GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(0);
+
+    switch (mode)
+    {
+        case GLFW_CURSOR:
+            return window->cursorMode;
+        case GLFW_STICKY_KEYS:
+            return window->stickyKeys;
+        case GLFW_STICKY_MOUSE_BUTTONS:
+            return window->stickyMouseButtons;
+        case GLFW_LOCK_KEY_MODS:
+            return window->lockKeyMods;
+        case GLFW_RAW_MOUSE_MOTION:
+            return window->rawMouseMotion;
+    }
+
+    _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
+    return 0;
+}
+
+GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT();
+
+    if (mode == GLFW_CURSOR)
+    {
+        if (value != GLFW_CURSOR_NORMAL &&
+            value != GLFW_CURSOR_HIDDEN &&
+            value != GLFW_CURSOR_DISABLED)
+        {
+            _glfwInputError(GLFW_INVALID_ENUM,
+                            "Invalid cursor mode 0x%08X",
+                            value);
+            return;
+        }
+
+        if (window->cursorMode == value)
+            return;
+
+        window->cursorMode = value;
+
+        _glfwPlatformGetCursorPos(window,
+                                  &window->virtualCursorPosX,
+                                  &window->virtualCursorPosY);
+        _glfwPlatformSetCursorMode(window, value);
+    }
+    else if (mode == GLFW_STICKY_KEYS)
+    {
+        value = value ? GLFW_TRUE : GLFW_FALSE;
+        if (window->stickyKeys == value)
+            return;
+
+        if (!value)
+        {
+            int i;
+
+            // Release all sticky keys
+            for (i = 0;  i <= GLFW_KEY_LAST;  i++)
+            {
+                if (window->keys[i] == _GLFW_STICK)
+                    window->keys[i] = GLFW_RELEASE;
+            }
+        }
+
+        window->stickyKeys = value;
+    }
+    else if (mode == GLFW_STICKY_MOUSE_BUTTONS)
+    {
+        value = value ? GLFW_TRUE : GLFW_FALSE;
+        if (window->stickyMouseButtons == value)
+            return;
+
+        if (!value)
+        {
+            int i;
+
+            // Release all sticky mouse buttons
+            for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
+            {
+                if (window->mouseButtons[i] == _GLFW_STICK)
+                    window->mouseButtons[i] = GLFW_RELEASE;
+            }
+        }
+
+        window->stickyMouseButtons = value;
+    }
+    else if (mode == GLFW_LOCK_KEY_MODS)
+    {
+        window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE;
+    }
+    else if (mode == GLFW_RAW_MOUSE_MOTION)
+    {
+        if (!_glfwPlatformRawMouseMotionSupported())
+        {
+            _glfwInputError(GLFW_PLATFORM_ERROR,
+                            "Raw mouse motion is not supported on this system");
+            return;
+        }
+
+        value = value ? GLFW_TRUE : GLFW_FALSE;
+        if (window->rawMouseMotion == value)
+            return;
+
+        window->rawMouseMotion = value;
+        _glfwPlatformSetRawMouseMotion(window, value);
+    }
+    else
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
+}
+
+GLFWAPI int glfwRawMouseMotionSupported(void)
+{
+    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
+    return _glfwPlatformRawMouseMotionSupported();
+}
+
+GLFWAPI const char* glfwGetKeyName(int key, int scancode)
+{
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (key != GLFW_KEY_UNKNOWN)
+    {
+        if (key != GLFW_KEY_KP_EQUAL &&
+            (key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) &&
+            (key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2))
+        {
+            return NULL;
+        }
+
+        scancode = _glfwPlatformGetKeyScancode(key);
+    }
+
+    return _glfwPlatformGetScancodeName(scancode);
+}
+
+GLFWAPI int glfwGetKeyScancode(int key)
+{
+    _GLFW_REQUIRE_INIT_OR_RETURN(-1);
+
+    if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
+        return GLFW_RELEASE;
+    }
+
+    return _glfwPlatformGetKeyScancode(key);
+}
+
+GLFWAPI int glfwGetKey(GLFWwindow* handle, int key)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE);
+
+    if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
+        return GLFW_RELEASE;
+    }
+
+    if (window->keys[key] == _GLFW_STICK)
+    {
+        // Sticky mode: release key now
+        window->keys[key] = GLFW_RELEASE;
+        return GLFW_PRESS;
+    }
+
+    return (int) window->keys[key];
+}
+
+GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE);
+
+    if (button < GLFW_MOUSE_BUTTON_1 || button > GLFW_MOUSE_BUTTON_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid mouse button %i", button);
+        return GLFW_RELEASE;
+    }
+
+    if (window->mouseButtons[button] == _GLFW_STICK)
+    {
+        // Sticky mode: release mouse button now
+        window->mouseButtons[button] = GLFW_RELEASE;
+        return GLFW_PRESS;
+    }
+
+    return (int) window->mouseButtons[button];
+}
+
+GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    if (xpos)
+        *xpos = 0;
+    if (ypos)
+        *ypos = 0;
+
+    _GLFW_REQUIRE_INIT();
+
+    if (window->cursorMode == GLFW_CURSOR_DISABLED)
+    {
+        if (xpos)
+            *xpos = window->virtualCursorPosX;
+        if (ypos)
+            *ypos = window->virtualCursorPosY;
+    }
+    else
+        _glfwPlatformGetCursorPos(window, xpos, ypos);
+}
+
+GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT();
+
+    if (xpos != xpos || xpos < -DBL_MAX || xpos > DBL_MAX ||
+        ypos != ypos || ypos < -DBL_MAX || ypos > DBL_MAX)
+    {
+        _glfwInputError(GLFW_INVALID_VALUE,
+                        "Invalid cursor position %f %f",
+                        xpos, ypos);
+        return;
+    }
+
+    if (!_glfwPlatformWindowFocused(window))
+        return;
+
+    if (window->cursorMode == GLFW_CURSOR_DISABLED)
+    {
+        // Only update the accumulated position if the cursor is disabled
+        window->virtualCursorPosX = xpos;
+        window->virtualCursorPosY = ypos;
+    }
+    else
+    {
+        // Update system cursor position
+        _glfwPlatformSetCursorPos(window, xpos, ypos);
+    }
+}
+
+GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot)
+{
+    _GLFWcursor* cursor;
+
+    assert(image != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    cursor = calloc(1, sizeof(_GLFWcursor));
+    cursor->next = _glfw.cursorListHead;
+    _glfw.cursorListHead = cursor;
+
+    if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot))
+    {
+        glfwDestroyCursor((GLFWcursor*) cursor);
+        return NULL;
+    }
+
+    return (GLFWcursor*) cursor;
+}
+
+GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape)
+{
+    _GLFWcursor* cursor;
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (shape != GLFW_ARROW_CURSOR &&
+        shape != GLFW_IBEAM_CURSOR &&
+        shape != GLFW_CROSSHAIR_CURSOR &&
+        shape != GLFW_POINTING_HAND_CURSOR &&
+        shape != GLFW_RESIZE_EW_CURSOR &&
+        shape != GLFW_RESIZE_NS_CURSOR &&
+        shape != GLFW_RESIZE_NWSE_CURSOR &&
+        shape != GLFW_RESIZE_NESW_CURSOR &&
+        shape != GLFW_RESIZE_ALL_CURSOR &&
+        shape != GLFW_NOT_ALLOWED_CURSOR)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape);
+        return NULL;
+    }
+
+    cursor = calloc(1, sizeof(_GLFWcursor));
+    cursor->next = _glfw.cursorListHead;
+    _glfw.cursorListHead = cursor;
+
+    if (!_glfwPlatformCreateStandardCursor(cursor, shape))
+    {
+        glfwDestroyCursor((GLFWcursor*) cursor);
+        return NULL;
+    }
+
+    return (GLFWcursor*) cursor;
+}
+
+GLFWAPI void glfwDestroyCursor(GLFWcursor* handle)
+{
+    _GLFWcursor* cursor = (_GLFWcursor*) handle;
+
+    _GLFW_REQUIRE_INIT();
+
+    if (cursor == NULL)
+        return;
+
+    // Make sure the cursor is not being used by any window
+    {
+        _GLFWwindow* window;
+
+        for (window = _glfw.windowListHead;  window;  window = window->next)
+        {
+            if (window->cursor == cursor)
+                glfwSetCursor((GLFWwindow*) window, NULL);
+        }
+    }
+
+    _glfwPlatformDestroyCursor(cursor);
+
+    // Unlink cursor from global linked list
+    {
+        _GLFWcursor** prev = &_glfw.cursorListHead;
+
+        while (*prev != cursor)
+            prev = &((*prev)->next);
+
+        *prev = cursor->next;
+    }
+
+    free(cursor);
+}
+
+GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
+{
+    _GLFWwindow* window = (_GLFWwindow*) windowHandle;
+    _GLFWcursor* cursor = (_GLFWcursor*) cursorHandle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT();
+
+    window->cursor = cursor;
+
+    _glfwPlatformSetCursor(window, cursor);
+}
+
+GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    _GLFW_SWAP_POINTERS(window->callbacks.key, cbfun);
+    return cbfun;
+}
+
+GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    _GLFW_SWAP_POINTERS(window->callbacks.character, cbfun);
+    return cbfun;
+}
+
+GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    _GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun);
+    return cbfun;
+}
+
+GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle,
+                                                      GLFWmousebuttonfun cbfun)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    _GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun);
+    return cbfun;
+}
+
+GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle,
+                                                  GLFWcursorposfun cbfun)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    _GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun);
+    return cbfun;
+}
+
+GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle,
+                                                      GLFWcursorenterfun cbfun)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    _GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun);
+    return cbfun;
+}
+
+GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle,
+                                            GLFWscrollfun cbfun)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    _GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun);
+    return cbfun;
+}
+
+GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    _GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun);
+    return cbfun;
+}
+
+GLFWAPI int glfwJoystickPresent(int jid)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
+
+    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
+        return GLFW_FALSE;
+    }
+
+    if (!initJoysticks())
+        return GLFW_FALSE;
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return GLFW_FALSE;
+
+    return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE);
+}
+
+GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+    assert(count != NULL);
+
+    *count = 0;
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
+        return NULL;
+    }
+
+    if (!initJoysticks())
+        return NULL;
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return NULL;
+
+    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES))
+        return NULL;
+
+    *count = js->axisCount;
+    return js->axes;
+}
+
+GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+    assert(count != NULL);
+
+    *count = 0;
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
+        return NULL;
+    }
+
+    if (!initJoysticks())
+        return NULL;
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return NULL;
+
+    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS))
+        return NULL;
+
+    if (_glfw.hints.init.hatButtons)
+        *count = js->buttonCount + js->hatCount * 4;
+    else
+        *count = js->buttonCount;
+
+    return js->buttons;
+}
+
+GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+    assert(count != NULL);
+
+    *count = 0;
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
+        return NULL;
+    }
+
+    if (!initJoysticks())
+        return NULL;
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return NULL;
+
+    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS))
+        return NULL;
+
+    *count = js->hatCount;
+    return js->hats;
+}
+
+GLFWAPI const char* glfwGetJoystickName(int jid)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
+        return NULL;
+    }
+
+    if (!initJoysticks())
+        return NULL;
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return NULL;
+
+    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
+        return NULL;
+
+    return js->name;
+}
+
+GLFWAPI const char* glfwGetJoystickGUID(int jid)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
+        return NULL;
+    }
+
+    if (!initJoysticks())
+        return NULL;
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return NULL;
+
+    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
+        return NULL;
+
+    return js->guid;
+}
+
+GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+
+    _GLFW_REQUIRE_INIT();
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return;
+
+    js->userPointer = pointer;
+}
+
+GLFWAPI void* glfwGetJoystickUserPointer(int jid)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return NULL;
+
+    return js->userPointer;
+}
+
+GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
+{
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (!initJoysticks())
+        return NULL;
+
+    _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun);
+    return cbfun;
+}
+
+GLFWAPI int glfwUpdateGamepadMappings(const char* string)
+{
+    int jid;
+    const char* c = string;
+
+    assert(string != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
+
+    while (*c)
+    {
+        if ((*c >= '0' && *c <= '9') ||
+            (*c >= 'a' && *c <= 'f') ||
+            (*c >= 'A' && *c <= 'F'))
+        {
+            char line[1024];
+
+            const size_t length = strcspn(c, "\r\n");
+            if (length < sizeof(line))
+            {
+                _GLFWmapping mapping = {{0}};
+
+                memcpy(line, c, length);
+                line[length] = '\0';
+
+                if (parseMapping(&mapping, line))
+                {
+                    _GLFWmapping* previous = findMapping(mapping.guid);
+                    if (previous)
+                        *previous = mapping;
+                    else
+                    {
+                        _glfw.mappingCount++;
+                        _glfw.mappings =
+                            realloc(_glfw.mappings,
+                                    sizeof(_GLFWmapping) * _glfw.mappingCount);
+                        _glfw.mappings[_glfw.mappingCount - 1] = mapping;
+                    }
+                }
+            }
+
+            c += length;
+        }
+        else
+        {
+            c += strcspn(c, "\r\n");
+            c += strspn(c, "\r\n");
+        }
+    }
+
+    for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
+    {
+        _GLFWjoystick* js = _glfw.joysticks + jid;
+        if (js->present)
+            js->mapping = findValidMapping(js);
+    }
+
+    return GLFW_TRUE;
+}
+
+GLFWAPI int glfwJoystickIsGamepad(int jid)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
+
+    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
+        return GLFW_FALSE;
+    }
+
+    if (!initJoysticks())
+        return GLFW_FALSE;
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return GLFW_FALSE;
+
+    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
+        return GLFW_FALSE;
+
+    return js->mapping != NULL;
+}
+
+GLFWAPI const char* glfwGetGamepadName(int jid)
+{
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
+        return NULL;
+    }
+
+    if (!initJoysticks())
+        return NULL;
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return NULL;
+
+    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
+        return NULL;
+
+    if (!js->mapping)
+        return NULL;
+
+    return js->mapping->name;
+}
+
+GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
+{
+    int i;
+    _GLFWjoystick* js;
+
+    assert(jid >= GLFW_JOYSTICK_1);
+    assert(jid <= GLFW_JOYSTICK_LAST);
+    assert(state != NULL);
+
+    memset(state, 0, sizeof(GLFWgamepadstate));
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
+
+    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
+    {
+        _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
+        return GLFW_FALSE;
+    }
+
+    if (!initJoysticks())
+        return GLFW_FALSE;
+
+    js = _glfw.joysticks + jid;
+    if (!js->present)
+        return GLFW_FALSE;
+
+    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL))
+        return GLFW_FALSE;
+
+    if (!js->mapping)
+        return GLFW_FALSE;
+
+    for (i = 0;  i <= GLFW_GAMEPAD_BUTTON_LAST;  i++)
+    {
+        const _GLFWmapelement* e = js->mapping->buttons + i;
+        if (e->type == _GLFW_JOYSTICK_AXIS)
+        {
+            const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
+            // HACK: This should be baked into the value transform
+            // TODO: Bake into transform when implementing output modifiers
+            if (e->axisOffset < 0 || (e->axisOffset == 0 && e->axisScale > 0))
+            {
+                if (value >= 0.f)
+                    state->buttons[i] = GLFW_PRESS;
+            }
+            else
+            {
+                if (value <= 0.f)
+                    state->buttons[i] = GLFW_PRESS;
+            }
+        }
+        else if (e->type == _GLFW_JOYSTICK_HATBIT)
+        {
+            const unsigned int hat = e->index >> 4;
+            const unsigned int bit = e->index & 0xf;
+            if (js->hats[hat] & bit)
+                state->buttons[i] = GLFW_PRESS;
+        }
+        else if (e->type == _GLFW_JOYSTICK_BUTTON)
+            state->buttons[i] = js->buttons[e->index];
+    }
+
+    for (i = 0;  i <= GLFW_GAMEPAD_AXIS_LAST;  i++)
+    {
+        const _GLFWmapelement* e = js->mapping->axes + i;
+        if (e->type == _GLFW_JOYSTICK_AXIS)
+        {
+            const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
+            state->axes[i] = _glfw_fminf(_glfw_fmaxf(value, -1.f), 1.f);
+        }
+        else if (e->type == _GLFW_JOYSTICK_HATBIT)
+        {
+            const unsigned int hat = e->index >> 4;
+            const unsigned int bit = e->index & 0xf;
+            if (js->hats[hat] & bit)
+                state->axes[i] = 1.f;
+            else
+                state->axes[i] = -1.f;
+        }
+        else if (e->type == _GLFW_JOYSTICK_BUTTON)
+            state->axes[i] = js->buttons[e->index] * 2.f - 1.f;
+    }
+
+    return GLFW_TRUE;
+}
+
+GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string)
+{
+    assert(string != NULL);
+
+    _GLFW_REQUIRE_INIT();
+    _glfwPlatformSetClipboardString(string);
+}
+
+GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle)
+{
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    return _glfwPlatformGetClipboardString();
+}
+
+GLFWAPI double glfwGetTime(void)
+{
+    _GLFW_REQUIRE_INIT_OR_RETURN(0.0);
+    return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) /
+        _glfwPlatformGetTimerFrequency();
+}
+
+GLFWAPI void glfwSetTime(double time)
+{
+    _GLFW_REQUIRE_INIT();
+
+    if (time != time || time < 0.0 || time > 18446744073.0)
+    {
+        _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time);
+        return;
+    }
+
+    _glfw.timer.offset = _glfwPlatformGetTimerValue() -
+        (uint64_t) (time * _glfwPlatformGetTimerFrequency());
+}
+
+GLFWAPI uint64_t glfwGetTimerValue(void)
+{
+    _GLFW_REQUIRE_INIT_OR_RETURN(0);
+    return _glfwPlatformGetTimerValue();
+}
+
+GLFWAPI uint64_t glfwGetTimerFrequency(void)
+{
+    _GLFW_REQUIRE_INIT_OR_RETURN(0);
+    return _glfwPlatformGetTimerFrequency();
+}