From 96b12ee504672824d98509b65bd46b3632416f8d Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 12 Jun 2014 23:04:20 +0200 Subject: [PATCH] Added character with modifiers callback. The undefined behaviour changed with #40 has been reverted, making the character-only callback again behave like a system text field. This behavior has now been documentated. Fixes #203. Fixes #305. --- README.md | 1 + docs/news.dox | 8 ++++++ include/GLFW/glfw3.h | 61 +++++++++++++++++++++++++++++++++++++++++--- src/cocoa_window.m | 4 ++- src/input.c | 20 ++++++++++++--- src/internal.h | 6 ++++- src/win32_window.c | 9 +++++-- src/x11_window.c | 5 +++- tests/events.c | 57 +++++++++++++++++++++-------------------- 9 files changed, 133 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 3f5cc1f1..d3b2729b 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ GLFW bundles a number of dependencies in the `deps/` directory. - Added `glfwPostEmptyEvent` for allowing secondary threads to cause `glfwWaitEvents` to return - Added `empty` test program for verifying posting of empty events + - Added `glfwSetCharModsCallback` for receiving character events with modifiers - Added `glfwGetWindowFrameSize` for retrieving the size of the frame around the client area of a window - Added `GLFW_AUTO_ICONIFY` for controlling whether full screen windows diff --git a/docs/news.dox b/docs/news.dox index 6788faf3..25885bb1 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -47,6 +47,14 @@ GLFW not supports floating windows, also called topmost or always on top, for easier debugging, with the `GLFW_FLOATING` window hint. +@subsection news_31_charmods Character with modifiers callback + +GLFW now provides a callback for character events with modifier key bits. +Unlike the regular character callback, this will report character events that +will not result in a character being input, for example if the Control key is +held down. + + @subsection news_31_egl Stable EGL support The support for EGL is now stable, successfully running on PandaBoards, Mesa, diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 51743c90..13d0a52e 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -788,6 +788,24 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); */ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); +/*! @brief The function signature for Unicode character with modifiers + * callbacks. + * + * This is the function signature for Unicode character with modifiers callback + * functions. It is called for each input character, regardless of what + * modifier keys are held down. + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa glfwSetCharModsCallback + * + * @ingroup input + */ +typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); + /*! @brief The function signature for file drop callbacks. * * This is the function signature for file drop callbacks. @@ -2151,9 +2169,19 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * This function sets the character callback of the specific window, which is * called when a Unicode character is input. * - * The character callback is intended for text input. If you want to know - * whether a specific key was pressed or released, use the - * [key callback](@ref glfwSetKeyCallback) instead. + * The character callback is intended for Unicode text input. As it deals with + * characters, it is keyboard layout dependent, whereas the + * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1 + * to physical keys, as a key may produce zero, one or more characters. If you + * want to know whether a specific physical key was pressed or released, see + * the key callback instead. + * + * The character callback behaves as system text input normally does and will + * not be called if modifier keys are held down that would prevent normal text + * input on that platform, for example a Super (Command) key on OS X or Alt key + * on Windows. There is a + * [character with modifiers callback](@ref glfwSetCharModsCallback) that + * receives these events. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -2167,6 +2195,33 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); */ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); +/*! @brief Sets the Unicode character with modifiers callback. + * + * This function sets the character with modifiers callback of the specific + * window, which is called when a Unicode character is input regardless of what + * modifier keys are used. + * + * The character with modifiers callback is intended for implementing custom + * Unicode character input. For regular Unicode text input, see the + * [character callback](@ref glfwSetCharCallback). Like the character + * callback, the character with modifiers callback deals with characters and is + * keyboard layout dependent. Characters do not map 1:1 to physical keys, as + * a key may produce zero, one or more characters. If you want to know whether + * a specific physical key was pressed or released, see the + * [key callback](@ref glfwSetKeyCallback) instead. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @note This function may only be called from the main thread. + * + * @ingroup input + */ +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun); + /*! @brief Sets the mouse button callback. * * This function sets the mouse button callback of the specified window, which diff --git a/src/cocoa_window.m b/src/cocoa_window.m index b7fbde97..0eff37ac 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -605,13 +605,15 @@ static int translateKey(unsigned int key) { const int key = translateKey([event keyCode]); const int mods = translateFlags([event modifierFlags]); + _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); NSString* characters = [event characters]; NSUInteger i, length = [characters length]; + const int plain = !(mods & GLFW_MOD_SUPER); for (i = 0; i < length; i++) - _glfwInputChar(window, [characters characterAtIndex:i]); + _glfwInputChar(window, [characters characterAtIndex:i], mods, plain); } - (void)flagsChanged:(NSEvent *)event diff --git a/src/input.c b/src/input.c index 4aa6b23d..fd061f85 100644 --- a/src/input.c +++ b/src/input.c @@ -153,13 +153,19 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods); } -void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint) +void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, int plain) { if (codepoint < 32 || (codepoint > 126 && codepoint < 160)) return; - if (window->callbacks.character) - window->callbacks.character((GLFWwindow*) window, codepoint); + if (window->callbacks.charmods) + window->callbacks.charmods((GLFWwindow*) window, codepoint, mods); + + if (plain) + { + if (window->callbacks.character) + window->callbacks.character((GLFWwindow*) window, codepoint); + } } void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) @@ -439,6 +445,14 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) return cbfun; } +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun); + return cbfun; +} + GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle, GLFWmousebuttonfun cbfun) { diff --git a/src/internal.h b/src/internal.h index ddf6f24a..252f20c0 100644 --- a/src/internal.h +++ b/src/internal.h @@ -262,6 +262,7 @@ struct _GLFWwindow GLFWscrollfun scroll; GLFWkeyfun key; GLFWcharfun character; + GLFWcharmodsfun charmods; GLFWdropfun drop; } callbacks; @@ -700,9 +701,12 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m /*! @brief Notifies shared code of a Unicode character input event. * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the input character. + * @param[in] mods Bit field describing which modifier keys were held down. + * @param[in] plain `GL_TRUE` if the character is regular text input, or + * `GL_FALSE` otherwise. * @ingroup event */ -void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint); +void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, int plain); /*! @brief Notifies shared code of a scroll event. * @param[in] window The window that received the event. diff --git a/src/win32_window.c b/src/win32_window.c index 2818ed81..57e17660 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -526,9 +526,14 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, } case WM_CHAR: + { + _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), GL_TRUE); + return 0; + } + case WM_SYSCHAR: { - _glfwInputChar(window, (unsigned int) wParam); + _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), GL_FALSE); return 0; } @@ -543,7 +548,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return TRUE; } - _glfwInputChar(window, (unsigned int) wParam); + _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), GL_TRUE); return FALSE; } diff --git a/src/x11_window.c b/src/x11_window.c index 0c41303f..3d1863d5 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -609,7 +609,10 @@ static void processEvent(XEvent *event) _glfwInputKey(window, key, event->xkey.keycode, GLFW_PRESS, mods); if (character != -1) - _glfwInputChar(window, character); + { + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + _glfwInputChar(window, character, mods, plain); + } break; } diff --git a/tests/events.c b/tests/events.c index 234a0a05..2deb9a6b 100644 --- a/tests/events.c +++ b/tests/events.c @@ -187,9 +187,8 @@ static const char* get_key_name(int key) case GLFW_KEY_LEFT_SUPER: return "LEFT SUPER"; case GLFW_KEY_RIGHT_SUPER: return "RIGHT SUPER"; case GLFW_KEY_MENU: return "MENU"; - case GLFW_KEY_UNKNOWN: return "UNKNOWN"; - default: return NULL; + default: return "UNKNOWN"; } } @@ -218,15 +217,22 @@ static const char* get_button_name(int button) return "right"; case GLFW_MOUSE_BUTTON_MIDDLE: return "middle"; + default: + { + static char name[16]; + sprintf(name, "%i", button); + return name; + } } - - return NULL; } static const char* get_mods_name(int mods) { static char name[512]; + if (mods == 0) + return " no mods"; + name[0] = '\0'; if (mods & GLFW_MOD_SHIFT) @@ -321,18 +327,11 @@ static void window_iconify_callback(GLFWwindow* window, int iconified) static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { Slot* slot = glfwGetWindowUserPointer(window); - const char* name = get_button_name(button); - - printf("%08x to %i at %0.3f: Mouse button %i", - counter++, slot->number, glfwGetTime(), button); - - if (name) - printf(" (%s)", name); - - if (mods) - printf(" (with%s)", get_mods_name(mods)); - - printf(" was %s\n", get_action_name(action)); + printf("%08x to %i at %0.3f: Mouse button %i (%s) (with%s) was %s\n", + counter++, slot->number, glfwGetTime(), button, + get_button_name(button), + get_mods_name(mods), + get_action_name(action)); } static void cursor_position_callback(GLFWwindow* window, double x, double y) @@ -359,19 +358,13 @@ static void scroll_callback(GLFWwindow* window, double x, double y) static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { - const char* name = get_key_name(key); Slot* slot = glfwGetWindowUserPointer(window); - printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x", - counter++, slot->number, glfwGetTime(), key, scancode); - - if (name) - printf(" (%s)", name); - - if (mods) - printf(" (with%s)", get_mods_name(mods)); - - printf(" was %s\n", get_action_name(action)); + printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (with%s) was %s\n", + counter++, slot->number, glfwGetTime(), key, scancode, + get_key_name(key), + get_mods_name(mods), + get_action_name(action)); if (action != GLFW_PRESS) return; @@ -396,6 +389,15 @@ static void char_callback(GLFWwindow* window, unsigned int codepoint) get_character_string(codepoint)); } +static void char_mods_callback(GLFWwindow* window, unsigned int codepoint, int mods) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Character 0x%08x (%s) (with%s) input\n", + counter++, slot->number, glfwGetTime(), codepoint, + get_character_string(codepoint), + get_mods_name(mods)); +} + static void drop_callback(GLFWwindow* window, int count, const char** names) { int i; @@ -546,6 +548,7 @@ int main(int argc, char** argv) glfwSetScrollCallback(slots[i].window, scroll_callback); glfwSetKeyCallback(slots[i].window, key_callback); glfwSetCharCallback(slots[i].window, char_callback); + glfwSetCharModsCallback(slots[i].window, char_mods_callback); glfwSetDropCallback(slots[i].window, drop_callback); glfwMakeContextCurrent(slots[i].window);