| //======================================================================== |
| // GLFW 3.3 - www.glfw.org |
| //------------------------------------------------------------------------ |
| // Copyright (c) 2002-2006 Marcus Geelnard |
| // Copyright (c) 2006-2016 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. |
| // |
| //======================================================================== |
| |
| #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 |
| |
| // 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 + 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_HAND_CURSOR && |
| shape != GLFW_HRESIZE_CURSOR && |
| shape != GLFW_VRESIZE_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; |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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); |
| _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; |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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; |
| 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 if (e->type == _GLFW_JOYSTICK_BUTTON) |
| state->axes[i] = (float) js->buttons[e->index]; |
| } |
| |
| 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(); |
| } |