From a6811623260e3c2f9ecba82dfac7c499f6aeb339 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Jan 2021 11:14:37 +0530 Subject: [PATCH] Start work on supporting arbitrary unicode keys --- gen-key-constants.py | 129 +++++++++++++++++++++++++++++++++++++++++ glfw/cocoa_window.m | 13 +++-- glfw/glfw3.h | 133 ++++++++++++++++++++++++++++++++++--------- glfw/input.c | 84 ++++++++++++++++++++------- glfw/internal.h | 7 ++- glfw/window.c | 11 ++-- 6 files changed, 319 insertions(+), 58 deletions(-) create mode 100644 gen-key-constants.py diff --git a/gen-key-constants.py b/gen-key-constants.py new file mode 100644 index 000000000..4b524b4cf --- /dev/null +++ b/gen-key-constants.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPLv3 Copyright: 2021, Kovid Goyal + +functional_key_names = ( # {{{ + 'escape', + 'enter', + 'tab', + 'backspace', + 'insert', + 'delete', + 'right', + 'left', + 'down', + 'up', + 'page_up', + 'page_down', + 'home', + 'end', + 'caps_lock', + 'scroll_lock', + 'num_lock', + 'print_screen', + 'pause', + 'f1', + 'f2', + 'f3', + 'f4', + 'f5', + 'f6', + 'f7', + 'f8', + 'f9', + 'f10', + 'f11', + 'f12', + 'f13', + 'f14', + 'f15', + 'f16', + 'f17', + 'f18', + 'f19', + 'f20', + 'f21', + 'f22', + 'f23', + 'f24', + 'f25', + 'f26', + 'f27', + 'f28', + 'f29', + 'f30', + 'f31', + 'f32', + 'f33', + 'f34', + 'f35', + 'kp_0', + 'kp_1', + 'kp_2', + 'kp_3', + 'kp_4', + 'kp_5', + 'kp_6', + 'kp_7', + 'kp_8', + 'kp_9', + 'kp_decimal', + 'kp_divide', + 'kp_multiply', + 'kp_subtract', + 'kp_add', + 'kp_enter', + 'kp_equal', + 'left_shift', + 'left_control', + 'left_alt', + 'left_super', + 'right_shift', + 'right_control', + 'right_alt', + 'right_super', + 'media_play', + 'media_pause', + 'media_play_pause', + 'media_reverse', + 'media_stop', + 'media_fast_forward', + 'media_rewind', + 'media_track_next', + 'media_track_previous', + 'media_record', + 'menu', +) # }}} +start_code = 0xe000 +last_code = start_code + len(functional_key_names) - 1 +name_to_code = {n: start_code + i for i, n in enumerate(functional_key_names)} + + +def generate_glfw_header() -> None: + lines = [ + '/* start functional key names */', + 'typedef enum {', + f' GLFW_FKEY_FIRST = 0x{start_code:x},', + ] + for name, code in name_to_code.items(): + lines.append(f' GLFW_FKEY_{name.upper()} = 0x{code:x},') + lines.append(f' GLFW_FKEY_LAST = 0x{last_code:x}') + lines.append('} GLFWFunctionKey;') + end_marker = '/* end functional key names */' + + with open('glfw/glfw3.h', 'r+') as f: + text = f.read() + start = text.index(lines[0]) + end = text.index(end_marker) + ntext = text[:start] + '\n'.join(lines) + '\n' + text[end:] + f.seek(0) + f.truncate() + f.write(ntext) + + +def main() -> None: + generate_glfw_header() + + +if __name__ == '__main__': + main() diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 2deece35b..59d4488bf 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1070,7 +1070,7 @@ is_ascii_control_char(char x) { - (void)flagsChanged:(NSEvent *)event { - int action; + int action = GLFW_RELEASE; const unsigned int modifierFlags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; const int key = translateKey([event keyCode], false); @@ -1079,13 +1079,18 @@ is_ascii_control_char(char x) { if (keyFlag & modifierFlags) { - if (window->keys[key] == GLFW_PRESS) + int current_action == GLFW_RELEASE; + for (unsigned i = 0; i < arraysz(window->activated_keys); i++) { + if (window->activated_keys[i] == key) { + current_action = window->activated_keys[i].action; + break; + } + } + if (current_action == GLFW_PRESS) action = GLFW_RELEASE; else action = GLFW_PRESS; } - else - action = GLFW_RELEASE; GLFWkeyevent glfw_keyevent; _glfwInitializeKeyEvent(&glfw_keyevent, key, [event keyCode], action, mods); diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 880e3045b..2fc76f1dd 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -300,31 +300,6 @@ extern "C" { #define GLFW_VERSION_REVISION 0 /*! @} */ -/*! @name Key and button actions - * @{ */ -/*! @brief The key or mouse button was released. - * - * The key or mouse button was released. - * - * @ingroup input - */ -#define GLFW_RELEASE 0 -/*! @brief The key or mouse button was pressed. - * - * The key or mouse button was pressed. - * - * @ingroup input - */ -#define GLFW_PRESS 1 -/*! @brief The key was held down until it repeated. - * - * The key was held down until it repeated. - * - * @ingroup input - */ -#define GLFW_REPEAT 2 -/*! @} */ - /*! @defgroup hat_state Joystick hat states * @brief Joystick hat states. * @@ -367,6 +342,103 @@ extern "C" { * @{ */ +/* start functional key names */ +typedef enum { + GLFW_FKEY_FIRST = 0xe000, + GLFW_FKEY_ESCAPE = 0xe000, + GLFW_FKEY_ENTER = 0xe001, + GLFW_FKEY_TAB = 0xe002, + GLFW_FKEY_BACKSPACE = 0xe003, + GLFW_FKEY_INSERT = 0xe004, + GLFW_FKEY_DELETE = 0xe005, + GLFW_FKEY_RIGHT = 0xe006, + GLFW_FKEY_LEFT = 0xe007, + GLFW_FKEY_DOWN = 0xe008, + GLFW_FKEY_UP = 0xe009, + GLFW_FKEY_PAGE_UP = 0xe00a, + GLFW_FKEY_PAGE_DOWN = 0xe00b, + GLFW_FKEY_HOME = 0xe00c, + GLFW_FKEY_END = 0xe00d, + GLFW_FKEY_CAPS_LOCK = 0xe00e, + GLFW_FKEY_SCROLL_LOCK = 0xe00f, + GLFW_FKEY_NUM_LOCK = 0xe010, + GLFW_FKEY_PRINT_SCREEN = 0xe011, + GLFW_FKEY_PAUSE = 0xe012, + GLFW_FKEY_F1 = 0xe013, + GLFW_FKEY_F2 = 0xe014, + GLFW_FKEY_F3 = 0xe015, + GLFW_FKEY_F4 = 0xe016, + GLFW_FKEY_F5 = 0xe017, + GLFW_FKEY_F6 = 0xe018, + GLFW_FKEY_F7 = 0xe019, + GLFW_FKEY_F8 = 0xe01a, + GLFW_FKEY_F9 = 0xe01b, + GLFW_FKEY_F10 = 0xe01c, + GLFW_FKEY_F11 = 0xe01d, + GLFW_FKEY_F12 = 0xe01e, + GLFW_FKEY_F13 = 0xe01f, + GLFW_FKEY_F14 = 0xe020, + GLFW_FKEY_F15 = 0xe021, + GLFW_FKEY_F16 = 0xe022, + GLFW_FKEY_F17 = 0xe023, + GLFW_FKEY_F18 = 0xe024, + GLFW_FKEY_F19 = 0xe025, + GLFW_FKEY_F20 = 0xe026, + GLFW_FKEY_F21 = 0xe027, + GLFW_FKEY_F22 = 0xe028, + GLFW_FKEY_F23 = 0xe029, + GLFW_FKEY_F24 = 0xe02a, + GLFW_FKEY_F25 = 0xe02b, + GLFW_FKEY_F26 = 0xe02c, + GLFW_FKEY_F27 = 0xe02d, + GLFW_FKEY_F28 = 0xe02e, + GLFW_FKEY_F29 = 0xe02f, + GLFW_FKEY_F30 = 0xe030, + GLFW_FKEY_F31 = 0xe031, + GLFW_FKEY_F32 = 0xe032, + GLFW_FKEY_F33 = 0xe033, + GLFW_FKEY_F34 = 0xe034, + GLFW_FKEY_F35 = 0xe035, + GLFW_FKEY_KP_0 = 0xe036, + GLFW_FKEY_KP_1 = 0xe037, + GLFW_FKEY_KP_2 = 0xe038, + GLFW_FKEY_KP_3 = 0xe039, + GLFW_FKEY_KP_4 = 0xe03a, + GLFW_FKEY_KP_5 = 0xe03b, + GLFW_FKEY_KP_6 = 0xe03c, + GLFW_FKEY_KP_7 = 0xe03d, + GLFW_FKEY_KP_8 = 0xe03e, + GLFW_FKEY_KP_9 = 0xe03f, + GLFW_FKEY_KP_DECIMAL = 0xe040, + GLFW_FKEY_KP_DIVIDE = 0xe041, + GLFW_FKEY_KP_MULTIPLY = 0xe042, + GLFW_FKEY_KP_SUBTRACT = 0xe043, + GLFW_FKEY_KP_ADD = 0xe044, + GLFW_FKEY_KP_ENTER = 0xe045, + GLFW_FKEY_KP_EQUAL = 0xe046, + GLFW_FKEY_LEFT_SHIFT = 0xe047, + GLFW_FKEY_LEFT_CONTROL = 0xe048, + GLFW_FKEY_LEFT_ALT = 0xe049, + GLFW_FKEY_LEFT_SUPER = 0xe04a, + GLFW_FKEY_RIGHT_SHIFT = 0xe04b, + GLFW_FKEY_RIGHT_CONTROL = 0xe04c, + GLFW_FKEY_RIGHT_ALT = 0xe04d, + GLFW_FKEY_RIGHT_SUPER = 0xe04e, + GLFW_FKEY_MEDIA_PLAY = 0xe04f, + GLFW_FKEY_MEDIA_PAUSE = 0xe050, + GLFW_FKEY_MEDIA_PLAY_PAUSE = 0xe051, + GLFW_FKEY_MEDIA_REVERSE = 0xe052, + GLFW_FKEY_MEDIA_STOP = 0xe053, + GLFW_FKEY_MEDIA_FAST_FORWARD = 0xe054, + GLFW_FKEY_MEDIA_REWIND = 0xe055, + GLFW_FKEY_MEDIA_TRACK_NEXT = 0xe056, + GLFW_FKEY_MEDIA_TRACK_PREVIOUS = 0xe057, + GLFW_FKEY_MEDIA_RECORD = 0xe058, + GLFW_FKEY_MENU = 0xe059, + GLFW_FKEY_LAST = 0xe059 +} GLFWFunctionKey; +/* end functional key names */ + /* The unknown key */ #define GLFW_KEY_UNKNOWN -1 @@ -1284,17 +1356,22 @@ typedef struct GLFWwindow GLFWwindow; * @ingroup input */ typedef struct GLFWcursor GLFWcursor; +typedef enum { + GLFW_RELEASE = 0, + GLFW_PRESS = 1, + GLFW_REPEAT = 2 +} GLFWKeyAction; typedef struct GLFWkeyevent { // The [keyboard key](@ref keys) that was pressed or released. - int key; + uint32_t key; // The platform-specific identifier of the key. int native_key; // The event action. Either `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. - int action; + GLFWKeyAction action; // Bit field describing which [modifier keys](@ref mods) were held down. int mods; @@ -4486,7 +4563,7 @@ GLFWAPI int glfwGetNativeKeyForKey(int key); * * @ingroup input */ -GLFWAPI int glfwGetKey(GLFWwindow* window, int key); +GLFWAPI GLFWKeyAction glfwGetKey(GLFWwindow* window, uint32_t key); /*! @brief Returns the last reported state of a mouse button for the specified * window. diff --git a/glfw/input.c b/glfw/input.c index bc0a66d8f..37ed460f9 100644 --- a/glfw/input.c +++ b/glfw/input.c @@ -283,24 +283,61 @@ void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int acti ev->ime_state = 0; } +static void +set_key_action(_GLFWwindow *window, uint32_t key, int val, int idx) { + const unsigned sz = arraysz(window->activated_keys); + if (idx < 0) { + for (unsigned i = 0; i < sz; i++) { + if (window->activated_keys[i].key == 0) { + idx = i; + break; + } + } + if (idx < 0) { + idx = sz - 1; + memmove(window->activated_keys, window->activated_keys + 1, sizeof(window->activated_keys[0]) * (sz - 1)); + window->activated_keys[sz - 1].key = key; + } + } + if (val == GLFW_RELEASE) { + memset(window->activated_keys + idx, 0, sizeof(window->activated_keys[0])); + if (idx < (int)sz - 1) { + memmove(window->activated_keys + idx, window->activated_keys + idx + 1, sizeof(window->activated_keys[0]) * (sz - 1 - idx)); + memset(window->activated_keys + sz - 1, 0, sizeof(window->activated_keys[0])); + } + } else { + window->activated_keys[idx].action = val; + } +} + // Notifies shared code of a physical key event // void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev) { - if (ev->key >= 0 && ev->key <= GLFW_KEY_LAST) + if (ev->key > 0) { bool repeated = false; + int idx = -1; + int current_action = GLFW_RELEASE; + const unsigned sz = arraysz(window->activated_keys); + for (unsigned i = 0; i < sz; i++) { + if (window->activated_keys[i].key == ev->key) { + idx = i; + current_action = window->activated_keys[i].action; + break; + } + } - if (ev->action == GLFW_RELEASE && window->keys[ev->key] == GLFW_RELEASE) + if (ev->action == GLFW_RELEASE && current_action == GLFW_RELEASE) return; - if (ev->action == GLFW_PRESS && window->keys[ev->key] == GLFW_PRESS) + if (ev->action == GLFW_PRESS && current_action == GLFW_PRESS) repeated = true; if (ev->action == GLFW_RELEASE && window->stickyKeys) - window->keys[ev->key] = _GLFW_STICK; + set_key_action(window, ev->key, _GLFW_STICK, idx); else - window->keys[ev->key] = (char) ev->action; + set_key_action(window, ev->key, ev->action, idx); if (repeated) ev->action = GLFW_REPEAT; @@ -736,13 +773,15 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) if (!value) { - int i; - // Release all sticky keys - for (i = 0; i <= GLFW_KEY_LAST; i++) + for (unsigned i = arraysz(window->activated_keys) - 1; i-- > 0;) { - if (window->keys[i] == _GLFW_STICK) - window->keys[i] = GLFW_RELEASE; + if (window->activated_keys[i].action == _GLFW_STICK) { + if (i < arraysz(window->activated_keys) - 1) { + memmove(window->activated_keys + i, window->activated_keys + i + 1, sizeof(window->activated_keys[0]) * (arraysz(window->activated_keys) - 1 - i)); + } + memset(window->activated_keys + arraysz(window->activated_keys) - 1, 0, sizeof(window->activated_keys[0])); + } } } @@ -830,27 +869,34 @@ GLFWAPI int glfwGetNativeKeyForKey(int key) return _glfwPlatformGetNativeKeyForKey(key); } -GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) +GLFWAPI GLFWKeyAction glfwGetKey(GLFWwindow* handle, uint32_t key) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); + if (!key) return GLFW_RELEASE; - if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) - { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); - return GLFW_RELEASE; + int current_action = GLFW_RELEASE; + const unsigned sz = arraysz(window->activated_keys); + int idx = -1; + for (unsigned i = 0; i < sz; i++) { + if (window->activated_keys[i].key == key) { + idx = i; + current_action = window->activated_keys[i].action; + break; + } } - if (window->keys[key] == _GLFW_STICK) + + if (current_action == _GLFW_STICK) { // Sticky mode: release key now - window->keys[key] = GLFW_RELEASE; - return GLFW_PRESS; + set_key_action(window, key, GLFW_RELEASE, idx); + current_action = GLFW_PRESS; } - return (int) window->keys[key]; + return current_action; } GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button) diff --git a/glfw/internal.h b/glfw/internal.h index e020f6557..6d8a15f50 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -401,6 +401,11 @@ struct _GLFWcontext _GLFWcontextOSMesa osmesa; }; +typedef struct GLFWKeyState { + uint32_t key; + char action; +} GLFWKeyState; + // Window and context structure // struct _GLFWwindow @@ -431,7 +436,7 @@ struct _GLFWwindow bool lockKeyMods; int cursorMode; char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; - char keys[GLFW_KEY_LAST + 1]; + GLFWKeyState activated_keys[16]; // Virtual cursor position when cursor is disabled double virtualCursorPosX, virtualCursorPosY; bool rawMouseMotion; diff --git a/glfw/window.c b/glfw/window.c index 7e8f74468..aee43e996 100644 --- a/glfw/window.c +++ b/glfw/window.c @@ -50,21 +50,20 @@ void _glfwInputWindowFocus(_GLFWwindow* window, bool focused) if (!focused) { - int key, button; _glfw.focusedWindowId = 0; - for (key = 0; key <= GLFW_KEY_LAST; key++) + for (unsigned i = 0; i < arraysz(window->activated_keys); i++) { - if (window->keys[key] == GLFW_PRESS) + if (window->activated_keys[i].key > 0 && window->activated_keys[i].action == GLFW_PRESS) { - const int native_key = _glfwPlatformGetNativeKeyForKey(key); + const int native_key = _glfwPlatformGetNativeKeyForKey(window->activated_keys[i].key); GLFWkeyevent ev; - _glfwInitializeKeyEvent(&ev, key, native_key, GLFW_RELEASE, 0); + _glfwInitializeKeyEvent(&ev, window->activated_keys[i].key, native_key, GLFW_RELEASE, 0); _glfwInputKeyboard(window, &ev); } } - for (button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++) + for (int button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++) { if (window->mouseButtons[button] == GLFW_PRESS) _glfwInputMouseClick(window, button, GLFW_RELEASE, 0);