hs

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | LICENSE

input.c (37912B)


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