diff --git a/CMakeLists.txt b/CMakeLists.txt index e0b643ba..637ee947 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -356,13 +356,15 @@ if (_GLFW_COCOA AND _GLFW_NSGL) find_library(COCOA_FRAMEWORK Cocoa) find_library(IOKIT_FRAMEWORK IOKit) find_library(CORE_FOUNDATION_FRAMEWORK CoreFoundation) + find_library(CARBON_FRAMEWORK Carbon) list(APPEND glfw_LIBRARIES ${COCOA_FRAMEWORK} ${OPENGL_gl_LIBRARY} ${IOKIT_FRAMEWORK} - ${CORE_FOUNDATION_FRAMEWORK}) + ${CORE_FOUNDATION_FRAMEWORK} + ${CARBON_FRAMEWORK}) set(GLFW_PKG_DEPS "") - set(GLFW_PKG_LIBS "-framework Cocoa -framework OpenGL -framework IOKit -framework CoreFoundation") + set(GLFW_PKG_LIBS "-framework Cocoa -framework OpenGL -framework IOKit -framework CoreFoundation -framework Carbon") endif() #-------------------------------------------------------------------- diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index c5593552..4078b24a 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1775,6 +1775,23 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); +/*! @brief Returns the name of the specified key. + * + * This function returns the name, encoded as UTF-8, of the specified key. + * + * @param[in] key The key to query. + * @return The UTF-8 encoded name of the key, or `NULL` if there is an error. + * + * @note The returned string is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned string is valid only until the next call to @ref + * glfwGetKeyName. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetKeyName(int key); + /*! @brief Returns the last reported state of a keyboard key for the specified * window. * diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 0e92c4a2..e4689c97 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -106,6 +106,7 @@ typedef struct _GLFWlibraryNS id cursor; char* clipboardString; + char* keyName; _GLFWjoy joysticks[GLFW_JOYSTICK_LAST + 1]; } _GLFWlibraryNS; diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 0016a701..d41b06d6 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -29,6 +29,8 @@ // Needed for _NSGetProgname #include +// Need for _glfwPlatformGetKeyName TIS* functions +#include // Center the cursor in the view of the window // @@ -1079,6 +1081,271 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) CGAssociateMouseAndMouseCursorPosition(true); } +const char* _glfwPlatformGetKeyName(int key) +{ + // free temp keyname + free(_glfw.ns.keyName); + _glfw.ns.keyName = 0; + + // Try to translate virtual key mac os x style + UInt16 vKey = 0; + switch(key) + { + case GLFW_KEY_A: vKey = 0; break; + case GLFW_KEY_S: vKey = 1; break; + case GLFW_KEY_D: vKey = 2; break; + case GLFW_KEY_F: vKey = 3; break; + case GLFW_KEY_H: vKey = 4; break; + case GLFW_KEY_G: vKey = 5; break; + case GLFW_KEY_Z: vKey = 6; break; + case GLFW_KEY_X: vKey = 7; break; + case GLFW_KEY_C: vKey = 8; break; + case GLFW_KEY_V: vKey = 9; break; + case GLFW_KEY_GRAVE_ACCENT: vKey = 10; break; + case GLFW_KEY_B: vKey = 11; break; + case GLFW_KEY_Q: vKey = 12; break; + case GLFW_KEY_W: vKey = 13; break; + case GLFW_KEY_E: vKey = 14; break; + case GLFW_KEY_R: vKey = 15; break; + case GLFW_KEY_Y: vKey = 16; break; + case GLFW_KEY_T: vKey = 17; break; + case GLFW_KEY_1: vKey = 18; break; + case GLFW_KEY_2: vKey = 19; break; + case GLFW_KEY_3: vKey = 20; break; + case GLFW_KEY_4: vKey = 21; break; + case GLFW_KEY_6: vKey = 22; break; + case GLFW_KEY_5: vKey = 23; break; + case GLFW_KEY_EQUAL: vKey = 24; break; + case GLFW_KEY_9: vKey = 25; break; + case GLFW_KEY_7: vKey = 26; break; + case GLFW_KEY_MINUS: vKey = 27; break; + case GLFW_KEY_8: vKey = 28; break; + case GLFW_KEY_0: vKey = 29; break; + case GLFW_KEY_RIGHT_BRACKET: vKey = 30; break; + case GLFW_KEY_O: vKey = 31; break; + case GLFW_KEY_U: vKey = 32; break; + case GLFW_KEY_LEFT_BRACKET: vKey = 33; break; + case GLFW_KEY_I: vKey = 34; break; + case GLFW_KEY_P: vKey = 35; break; + case GLFW_KEY_ENTER: vKey = 36; break; + case GLFW_KEY_L: vKey = 37; break; + case GLFW_KEY_J: vKey = 38; break; + case GLFW_KEY_APOSTROPHE: vKey = 39; break; + case GLFW_KEY_K: vKey = 40; break; + case GLFW_KEY_SEMICOLON: vKey = 41; break; + case GLFW_KEY_BACKSLASH: vKey = 42; break; + case GLFW_KEY_COMMA: vKey = 43; break; + case GLFW_KEY_SLASH: vKey = 44; break; + case GLFW_KEY_N: vKey = 45; break; + case GLFW_KEY_M: vKey = 46; break; + case GLFW_KEY_PERIOD: vKey = 47; break; + case GLFW_KEY_TAB: vKey = 48; break; + case GLFW_KEY_SPACE: vKey = 49; break; + case GLFW_KEY_WORLD_1: vKey = 50; break; + } + + if (vKey) + { + // get the current keyboard, need to do this every time + // in case keyboard has changed + TISInputSourceRef tisInputSource = TISCopyCurrentKeyboardInputSource(); + CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(tisInputSource, kTISPropertyUnicodeKeyLayoutData); + const UCKeyboardLayout *kbLayoutUC = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr); + + if (kbLayoutUC) + { + UInt32 deadKeyState = 0; + UniCharCount maxStringLength = 255; + UniCharCount actualStringLength = 0; + UniChar unicodeString[maxStringLength]; + + OSStatus status = UCKeyTranslate(kbLayoutUC, + vKey, kUCKeyActionDown, 0, + LMGetKbdType(), 0, + &deadKeyState, + maxStringLength, + &actualStringLength, unicodeString); + + if (actualStringLength == 0 && deadKeyState) + { + status = UCKeyTranslate(kbLayoutUC, + kVK_Space, kUCKeyActionDown, 0, + LMGetKbdType(), 0, + &deadKeyState, + maxStringLength, + &actualStringLength, unicodeString); + } + if (actualStringLength > 0 && status == noErr) + { + NSString* tempNS = [[NSString stringWithCharacters:unicodeString length:(NSUInteger)actualStringLength] uppercaseString]; + _glfw.ns.keyName = strdup([tempNS UTF8String]); + + // need to ensure common chars are interpreted similarily: + int length = strlen(_glfw.ns.keyName); + if(1==length) + { + if(_glfw.ns.keyName[0]>='a' && _glfw.ns.keyName[0]<='z') + { + // Capitalize + _glfw.ns.keyName[0] += 'A'-'a'; + } + + switch(_glfw.ns.keyName[0]) + { + case ' ': return "SPACE"; + case '-': return "MINUS"; + case '=': return "EQUAL"; + case '[': return "LEFT BRACKET"; + case ']': return "RIGHT BRACKET"; + case '\\': return "BACKSLASH"; + case ';': return "SEMICOLON"; + case '\'': return "APOSTROPHE"; + case '`': return "GRAVE ACCENT"; + case ',': return "COMMA"; + case '.': return "PERIOD"; + case '/': return "SLASH"; + default: break; + } + } + + + return _glfw.ns.keyName; + } + } + } + + + // Fallback to hard coded + switch (key) + { + // Printable keys + case GLFW_KEY_A: return "A"; + case GLFW_KEY_B: return "B"; + case GLFW_KEY_C: return "C"; + case GLFW_KEY_D: return "D"; + case GLFW_KEY_E: return "E"; + case GLFW_KEY_F: return "F"; + case GLFW_KEY_G: return "G"; + case GLFW_KEY_H: return "H"; + case GLFW_KEY_I: return "I"; + case GLFW_KEY_J: return "J"; + case GLFW_KEY_K: return "K"; + case GLFW_KEY_L: return "L"; + case GLFW_KEY_M: return "M"; + case GLFW_KEY_N: return "N"; + case GLFW_KEY_O: return "O"; + case GLFW_KEY_P: return "P"; + case GLFW_KEY_Q: return "Q"; + case GLFW_KEY_R: return "R"; + case GLFW_KEY_S: return "S"; + case GLFW_KEY_T: return "T"; + case GLFW_KEY_U: return "U"; + case GLFW_KEY_V: return "V"; + case GLFW_KEY_W: return "W"; + case GLFW_KEY_X: return "X"; + case GLFW_KEY_Y: return "Y"; + case GLFW_KEY_Z: return "Z"; + case GLFW_KEY_1: return "1"; + case GLFW_KEY_2: return "2"; + case GLFW_KEY_3: return "3"; + case GLFW_KEY_4: return "4"; + case GLFW_KEY_5: return "5"; + case GLFW_KEY_6: return "6"; + case GLFW_KEY_7: return "7"; + case GLFW_KEY_8: return "8"; + case GLFW_KEY_9: return "9"; + case GLFW_KEY_0: return "0"; + case GLFW_KEY_SPACE: return "SPACE"; + case GLFW_KEY_MINUS: return "MINUS"; + case GLFW_KEY_EQUAL: return "EQUAL"; + case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET"; + case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET"; + case GLFW_KEY_BACKSLASH: return "BACKSLASH"; + case GLFW_KEY_SEMICOLON: return "SEMICOLON"; + case GLFW_KEY_APOSTROPHE: return "APOSTROPHE"; + case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT"; + case GLFW_KEY_COMMA: return "COMMA"; + case GLFW_KEY_PERIOD: return "PERIOD"; + case GLFW_KEY_SLASH: return "SLASH"; + case GLFW_KEY_WORLD_1: return "WORLD 1"; + case GLFW_KEY_WORLD_2: return "WORLD 2"; + + // Function keys + case GLFW_KEY_ESCAPE: return "ESCAPE"; + case GLFW_KEY_F1: return "F1"; + case GLFW_KEY_F2: return "F2"; + case GLFW_KEY_F3: return "F3"; + case GLFW_KEY_F4: return "F4"; + case GLFW_KEY_F5: return "F5"; + case GLFW_KEY_F6: return "F6"; + case GLFW_KEY_F7: return "F7"; + case GLFW_KEY_F8: return "F8"; + case GLFW_KEY_F9: return "F9"; + case GLFW_KEY_F10: return "F10"; + case GLFW_KEY_F11: return "F11"; + case GLFW_KEY_F12: return "F12"; + case GLFW_KEY_F13: return "F13"; + case GLFW_KEY_F14: return "F14"; + case GLFW_KEY_F15: return "F15"; + case GLFW_KEY_F16: return "F16"; + case GLFW_KEY_F17: return "F17"; + case GLFW_KEY_F18: return "F18"; + case GLFW_KEY_F19: return "F19"; + case GLFW_KEY_F20: return "F20"; + case GLFW_KEY_F21: return "F21"; + case GLFW_KEY_F22: return "F22"; + case GLFW_KEY_F23: return "F23"; + case GLFW_KEY_F24: return "F24"; + case GLFW_KEY_F25: return "F25"; + case GLFW_KEY_UP: return "UP"; + case GLFW_KEY_DOWN: return "DOWN"; + case GLFW_KEY_LEFT: return "LEFT"; + case GLFW_KEY_RIGHT: return "RIGHT"; + case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT"; + case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT"; + case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL"; + case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL"; + case GLFW_KEY_LEFT_ALT: return "LEFT ALT"; + case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT"; + case GLFW_KEY_TAB: return "TAB"; + case GLFW_KEY_ENTER: return "ENTER"; + case GLFW_KEY_BACKSPACE: return "BACKSPACE"; + case GLFW_KEY_INSERT: return "INSERT"; + case GLFW_KEY_DELETE: return "DELETE"; + case GLFW_KEY_PAGE_UP: return "PAGE UP"; + case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN"; + case GLFW_KEY_HOME: return "HOME"; + case GLFW_KEY_END: return "END"; + case GLFW_KEY_KP_0: return "KEYPAD 0"; + case GLFW_KEY_KP_1: return "KEYPAD 1"; + case GLFW_KEY_KP_2: return "KEYPAD 2"; + case GLFW_KEY_KP_3: return "KEYPAD 3"; + case GLFW_KEY_KP_4: return "KEYPAD 4"; + case GLFW_KEY_KP_5: return "KEYPAD 5"; + case GLFW_KEY_KP_6: return "KEYPAD 6"; + case GLFW_KEY_KP_7: return "KEYPAD 7"; + case GLFW_KEY_KP_8: return "KEYPAD 8"; + case GLFW_KEY_KP_9: return "KEYPAD 9"; + case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE"; + case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTPLY"; + case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT"; + case GLFW_KEY_KP_ADD: return "KEYPAD ADD"; + case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL"; + case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL"; + case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER"; + case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN"; + case GLFW_KEY_NUM_LOCK: return "NUM LOCK"; + case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK"; + case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK"; + case GLFW_KEY_PAUSE: return "PAUSE"; + 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; + } +} ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// diff --git a/src/input.c b/src/input.c index 1bd7e6d8..3693167a 100644 --- a/src/input.c +++ b/src/input.c @@ -260,6 +260,20 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) } } +GLFWAPI const char* glfwGetKeyName(int key) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (key < 0 || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "The specified key is invalid"); + return NULL; + } + + return _glfwPlatformGetKeyName(key); +} + + GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/src/internal.h b/src/internal.h index b6e2136b..644d8dc7 100644 --- a/src/internal.h +++ b/src/internal.h @@ -367,6 +367,11 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); */ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); +/*! @copydoc glfwGetKeyName + * @ingroup platform + */ +const char*_glfwPlatformGetKeyName(int key); + /*! @copydoc glfwGetMonitors * @ingroup platform */ diff --git a/src/win32_init.c b/src/win32_init.c index 8e0c19b0..64e56afe 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -235,6 +235,8 @@ void _glfwPlatformTerminate(void) SPIF_SENDCHANGE); free(_glfw.win32.clipboardString); + free(_glfw.win32.keyName); + _glfwTerminateJoysticks(); _glfwTerminateContextAPI(); diff --git a/src/win32_platform.h b/src/win32_platform.h index 1652ab8c..3e97e046 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -169,6 +169,7 @@ typedef struct _GLFWlibraryWin32 ATOM classAtom; DWORD foregroundLockTimeout; char* clipboardString; + char* keyName; // Timer data struct { diff --git a/src/win32_window.c b/src/win32_window.c index 139e661d..409ccdb4 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1124,6 +1124,249 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) } } +const char*_glfwPlatformGetKeyName(int key) +{ + UINT charVK; + WCHAR keyName[2]; + int vkKey = 0; + int length; + + free(_glfw.win32.keyName); + _glfw.win32.keyName = NULL; + + switch( key ) + { + // Printable keys are handled via being turned into a win32 virtual key + // and then translated by MapVirtualKey. + // This correctly translates certain keys where hardcoding would not + // (A-Z are usually fine, but others may not be depending on locale). + case GLFW_KEY_SPACE: vkKey = VK_SPACE; break; + case GLFW_KEY_0: vkKey = 0x30; break; + case GLFW_KEY_1: vkKey = 0x31; break; + case GLFW_KEY_2: vkKey = 0x32; break; + case GLFW_KEY_3: vkKey = 0x33; break; + case GLFW_KEY_4: vkKey = 0x34; break; + case GLFW_KEY_5: vkKey = 0x35; break; + case GLFW_KEY_6: vkKey = 0x36; break; + case GLFW_KEY_7: vkKey = 0x37; break; + case GLFW_KEY_8: vkKey = 0x38; break; + case GLFW_KEY_9: vkKey = 0x39; break; + case GLFW_KEY_A: vkKey = 0x41; break; + case GLFW_KEY_B: vkKey = 0x42; break; + case GLFW_KEY_C: vkKey = 0x43; break; + case GLFW_KEY_D: vkKey = 0x44; break; + case GLFW_KEY_E: vkKey = 0x45; break; + case GLFW_KEY_F: vkKey = 0x46; break; + case GLFW_KEY_G: vkKey = 0x47; break; + case GLFW_KEY_H: vkKey = 0x48; break; + case GLFW_KEY_I: vkKey = 0x49; break; + case GLFW_KEY_J: vkKey = 0x4A; break; + case GLFW_KEY_K: vkKey = 0x4B; break; + case GLFW_KEY_L: vkKey = 0x4C; break; + case GLFW_KEY_M: vkKey = 0x4D; break; + case GLFW_KEY_N: vkKey = 0x4E; break; + case GLFW_KEY_O: vkKey = 0x4F; break; + case GLFW_KEY_P: vkKey = 0x50; break; + case GLFW_KEY_Q: vkKey = 0x51; break; + case GLFW_KEY_R: vkKey = 0x52; break; + case GLFW_KEY_S: vkKey = 0x53; break; + case GLFW_KEY_T: vkKey = 0x54; break; + case GLFW_KEY_U: vkKey = 0x55; break; + case GLFW_KEY_V: vkKey = 0x56; break; + case GLFW_KEY_W: vkKey = 0x57; break; + case GLFW_KEY_X: vkKey = 0x58; break; + case GLFW_KEY_Y: vkKey = 0x59; break; + case GLFW_KEY_Z: vkKey = 0x5A; break; + case GLFW_KEY_MINUS: vkKey = 0xBD; break; + case GLFW_KEY_EQUAL: vkKey = 0xBB; break; + case GLFW_KEY_LEFT_BRACKET: vkKey = 0xDB; break; + case GLFW_KEY_RIGHT_BRACKET: vkKey = 0xDD; break; + case GLFW_KEY_BACKSLASH: vkKey = 0xDC; break; + case GLFW_KEY_SEMICOLON: vkKey = 0xBA; break; + case GLFW_KEY_APOSTROPHE: vkKey = 0xDE; break; + case GLFW_KEY_GRAVE_ACCENT: vkKey = 0xC0; break; + case GLFW_KEY_COMMA: vkKey = 0xBC; break; + case GLFW_KEY_PERIOD: vkKey = 0xBE; break; + case GLFW_KEY_SLASH: vkKey = 0xBF; break; + case GLFW_KEY_WORLD_1: vkKey = 0xDF; break; + case GLFW_KEY_WORLD_2: vkKey = 0xE2; break; + default: break; + } + + if( vkKey ) + { + charVK = MapVirtualKey(vkKey, MAPVK_VK_TO_CHAR ); + if (charVK) + { + // construct null terminated wchar string from charachter for translation + keyName[0] = charVK; + keyName[1] = 0; + _glfw.win32.keyName = _glfwCreateUTF8FromWideString(keyName); + + // need to ensure common chars are interpreted similarily: + length = strlen(_glfw.win32.keyName); + if(1==length) + { + if(_glfw.win32.keyName[0]>='a' && _glfw.win32.keyName[0]<='z') + { + // Capitalize + _glfw.win32.keyName[0] += 'A'-'a'; + } + + switch(_glfw.win32.keyName[0]) + { + case ' ': return "SPACE"; + case '-': return "MINUS"; + case '=': return "EQUAL"; + case '[': return "LEFT BRACKET"; + case ']': return "RIGHT BRACKET"; + case '\\': return "BACKSLASH"; + case ';': return "SEMICOLON"; + case '\'': return "APOSTROPHE"; + case '`': return "GRAVE ACCENT"; + case ',': return "COMMA"; + case '.': return "PERIOD"; + case '/': return "SLASH"; + default: break; + } + } + + return _glfw.win32.keyName; + } + } + + // key could not be mapped or was not a printable key + // fall back to hard-coded values but do not set _glfw.win32.keyName + switch (key) + { + // Printable keys + case GLFW_KEY_A: return "A"; + case GLFW_KEY_B: return "B"; + case GLFW_KEY_C: return "C"; + case GLFW_KEY_D: return "D"; + case GLFW_KEY_E: return "E"; + case GLFW_KEY_F: return "F"; + case GLFW_KEY_G: return "G"; + case GLFW_KEY_H: return "H"; + case GLFW_KEY_I: return "I"; + case GLFW_KEY_J: return "J"; + case GLFW_KEY_K: return "K"; + case GLFW_KEY_L: return "L"; + case GLFW_KEY_M: return "M"; + case GLFW_KEY_N: return "N"; + case GLFW_KEY_O: return "O"; + case GLFW_KEY_P: return "P"; + case GLFW_KEY_Q: return "Q"; + case GLFW_KEY_R: return "R"; + case GLFW_KEY_S: return "S"; + case GLFW_KEY_T: return "T"; + case GLFW_KEY_U: return "U"; + case GLFW_KEY_V: return "V"; + case GLFW_KEY_W: return "W"; + case GLFW_KEY_X: return "X"; + case GLFW_KEY_Y: return "Y"; + case GLFW_KEY_Z: return "Z"; + case GLFW_KEY_1: return "1"; + case GLFW_KEY_2: return "2"; + case GLFW_KEY_3: return "3"; + case GLFW_KEY_4: return "4"; + case GLFW_KEY_5: return "5"; + case GLFW_KEY_6: return "6"; + case GLFW_KEY_7: return "7"; + case GLFW_KEY_8: return "8"; + case GLFW_KEY_9: return "9"; + case GLFW_KEY_0: return "0"; + case GLFW_KEY_SPACE: return "SPACE"; + case GLFW_KEY_MINUS: return "MINUS"; + case GLFW_KEY_EQUAL: return "EQUAL"; + case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET"; + case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET"; + case GLFW_KEY_BACKSLASH: return "BACKSLASH"; + case GLFW_KEY_SEMICOLON: return "SEMICOLON"; + case GLFW_KEY_APOSTROPHE: return "APOSTROPHE"; + case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT"; + case GLFW_KEY_COMMA: return "COMMA"; + case GLFW_KEY_PERIOD: return "PERIOD"; + case GLFW_KEY_SLASH: return "SLASH"; + case GLFW_KEY_WORLD_1: return "WORLD 1"; + case GLFW_KEY_WORLD_2: return "WORLD 2"; + + // Function keys + case GLFW_KEY_ESCAPE: return "ESCAPE"; + case GLFW_KEY_F1: return "F1"; + case GLFW_KEY_F2: return "F2"; + case GLFW_KEY_F3: return "F3"; + case GLFW_KEY_F4: return "F4"; + case GLFW_KEY_F5: return "F5"; + case GLFW_KEY_F6: return "F6"; + case GLFW_KEY_F7: return "F7"; + case GLFW_KEY_F8: return "F8"; + case GLFW_KEY_F9: return "F9"; + case GLFW_KEY_F10: return "F10"; + case GLFW_KEY_F11: return "F11"; + case GLFW_KEY_F12: return "F12"; + case GLFW_KEY_F13: return "F13"; + case GLFW_KEY_F14: return "F14"; + case GLFW_KEY_F15: return "F15"; + case GLFW_KEY_F16: return "F16"; + case GLFW_KEY_F17: return "F17"; + case GLFW_KEY_F18: return "F18"; + case GLFW_KEY_F19: return "F19"; + case GLFW_KEY_F20: return "F20"; + case GLFW_KEY_F21: return "F21"; + case GLFW_KEY_F22: return "F22"; + case GLFW_KEY_F23: return "F23"; + case GLFW_KEY_F24: return "F24"; + case GLFW_KEY_F25: return "F25"; + case GLFW_KEY_UP: return "UP"; + case GLFW_KEY_DOWN: return "DOWN"; + case GLFW_KEY_LEFT: return "LEFT"; + case GLFW_KEY_RIGHT: return "RIGHT"; + case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT"; + case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT"; + case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL"; + case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL"; + case GLFW_KEY_LEFT_ALT: return "LEFT ALT"; + case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT"; + case GLFW_KEY_TAB: return "TAB"; + case GLFW_KEY_ENTER: return "ENTER"; + case GLFW_KEY_BACKSPACE: return "BACKSPACE"; + case GLFW_KEY_INSERT: return "INSERT"; + case GLFW_KEY_DELETE: return "DELETE"; + case GLFW_KEY_PAGE_UP: return "PAGE UP"; + case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN"; + case GLFW_KEY_HOME: return "HOME"; + case GLFW_KEY_END: return "END"; + case GLFW_KEY_KP_0: return "KEYPAD 0"; + case GLFW_KEY_KP_1: return "KEYPAD 1"; + case GLFW_KEY_KP_2: return "KEYPAD 2"; + case GLFW_KEY_KP_3: return "KEYPAD 3"; + case GLFW_KEY_KP_4: return "KEYPAD 4"; + case GLFW_KEY_KP_5: return "KEYPAD 5"; + case GLFW_KEY_KP_6: return "KEYPAD 6"; + case GLFW_KEY_KP_7: return "KEYPAD 7"; + case GLFW_KEY_KP_8: return "KEYPAD 8"; + case GLFW_KEY_KP_9: return "KEYPAD 9"; + case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE"; + case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTPLY"; + case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT"; + case GLFW_KEY_KP_ADD: return "KEYPAD ADD"; + case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL"; + case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL"; + case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER"; + case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN"; + case GLFW_KEY_NUM_LOCK: return "NUM LOCK"; + case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK"; + case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK"; + case GLFW_KEY_PAUSE: return "PAUSE"; + 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"; + } + + return NULL; +} ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// diff --git a/src/x11_init.c b/src/x11_init.c index 3d94ff5e..f040c06a 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -68,6 +68,7 @@ static int translateKey(int keyCode) default: break; } + // Now try pimary keysym for function keys (non-printable keys). These // should not be layout dependent (i.e. US layout and international // layouts should give the same result). @@ -672,6 +673,7 @@ void _glfwPlatformTerminate(void) } free(_glfw.x11.selection.string); + free(_glfw.x11.keyName); _glfwTerminateJoysticks(); _glfwTerminateContextAPI(); diff --git a/src/x11_platform.h b/src/x11_platform.h index 868429fa..cd1dc054 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -190,6 +190,8 @@ typedef struct _GLFWlibraryX11 char* string; } selection; + char* keyName; + struct { int present; int fd; diff --git a/src/x11_window.c b/src/x11_window.c index f0e872e4..a3916e41 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include // Action for EWMH client messages #define _NET_WM_STATE_REMOVE 0 @@ -1179,6 +1181,274 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) } } +const char*_glfwPlatformGetKeyName(int key) +{ + int KeyCodeFound = -1; + int printable = 0; + int keySym, ucsChar, keyCode, length; + char* locale; + + switch(key) + { + case GLFW_KEY_A: printable=1; break; + case GLFW_KEY_B: printable=1; break; + case GLFW_KEY_C: printable=1; break; + case GLFW_KEY_D: printable=1; break; + case GLFW_KEY_E: printable=1; break; + case GLFW_KEY_F: printable=1; break; + case GLFW_KEY_G: printable=1; break; + case GLFW_KEY_H: printable=1; break; + case GLFW_KEY_I: printable=1; break; + case GLFW_KEY_J: printable=1; break; + case GLFW_KEY_K: printable=1; break; + case GLFW_KEY_L: printable=1; break; + case GLFW_KEY_M: printable=1; break; + case GLFW_KEY_N: printable=1; break; + case GLFW_KEY_O: printable=1; break; + case GLFW_KEY_P: printable=1; break; + case GLFW_KEY_Q: printable=1; break; + case GLFW_KEY_R: printable=1; break; + case GLFW_KEY_S: printable=1; break; + case GLFW_KEY_T: printable=1; break; + case GLFW_KEY_U: printable=1; break; + case GLFW_KEY_V: printable=1; break; + case GLFW_KEY_W: printable=1; break; + case GLFW_KEY_X: printable=1; break; + case GLFW_KEY_Y: printable=1; break; + case GLFW_KEY_Z: printable=1; break; + case GLFW_KEY_1: printable=1; break; + case GLFW_KEY_2: printable=1; break; + case GLFW_KEY_3: printable=1; break; + case GLFW_KEY_4: printable=1; break; + case GLFW_KEY_5: printable=1; break; + case GLFW_KEY_6: printable=1; break; + case GLFW_KEY_7: printable=1; break; + case GLFW_KEY_8: printable=1; break; + case GLFW_KEY_9: printable=1; break; + case GLFW_KEY_0: printable=1; break; + case GLFW_KEY_SPACE: printable=1; break; + case GLFW_KEY_MINUS: printable=1; break; + case GLFW_KEY_EQUAL: printable=1; break; + case GLFW_KEY_LEFT_BRACKET: printable=1; break; + case GLFW_KEY_RIGHT_BRACKET: printable=1; break; + case GLFW_KEY_BACKSLASH: printable=1; break; + case GLFW_KEY_SEMICOLON: printable=1; break; + case GLFW_KEY_APOSTROPHE: printable=1; break; + case GLFW_KEY_GRAVE_ACCENT: printable=1; break; + case GLFW_KEY_COMMA: printable=1; break; + case GLFW_KEY_PERIOD: printable=1; break; + case GLFW_KEY_SLASH: printable=1; break; + case GLFW_KEY_WORLD_1: printable=1; break; + default: break; + } + + if (printable) + { + // We now need to search for the keyCode. + // This could be sped up through a table, but this function should not be called that frequently + // Valid key code range is [8,255], according to the XLib manual + for(keyCode = 8; keyCode<=255; ++keyCode) + { + if( translateKey(keyCode) == key ) + { + KeyCodeFound = keyCode; + break; + } + } + + if(KeyCodeFound>=0) + { + // get keyboard group in use via state + XkbStateRec state; + XkbGetState(_glfw.x11.display, XkbUseCoreKbd, &state); + + keySym = XkbKeycodeToKeysym(_glfw.x11.display, KeyCodeFound, state.group, 0); + ucsChar = _glfwKeySym2Unicode( keySym ); + if( ucsChar > 0 ) + { + // get current locale and set to for wctomb + locale = setlocale(LC_CTYPE,NULL); + setlocale(LC_CTYPE,""); + if(!_glfw.x11.keyName) + { + // only need to allocate once + _glfw.x11.keyName = calloc(MB_CUR_MAX+1,sizeof(char)); + } + length = wctomb(_glfw.x11.keyName, ucsChar); + + // reset locale + setlocale(LC_CTYPE,locale); + + _glfw.x11.keyName[length]='\0'; + if(length>0) + { + // need to ensure common chars are interpreted similarily: + if(length=1) + { + if(_glfw.x11.keyName[0]>='a' && _glfw.x11.keyName[0]<='z') + { + // Capitalize + _glfw.x11.keyName[0] += 'A'-'a'; + } + + switch(_glfw.x11.keyName[0]) + { + case ' ': return "SPACE"; + case '-': return "MINUS"; + case '=': return "EQUAL"; + case '[': return "LEFT BRACKET"; + case ']': return "RIGHT BRACKET"; + case '\\': return "BACKSLASH"; + case ';': return "SEMICOLON"; + case '\'': return "APOSTROPHE"; + case '`': return "GRAVE ACCENT"; + case ',': return "COMMA"; + case '.': return "PERIOD"; + case '/': return "SLASH"; + default: break; + } + } + return _glfw.x11.keyName; + } + } + + } + } + + + // Fallback to hard coded + switch (key) + { + // Printable keys + case GLFW_KEY_A: return "A"; + case GLFW_KEY_B: return "B"; + case GLFW_KEY_C: return "C"; + case GLFW_KEY_D: return "D"; + case GLFW_KEY_E: return "E"; + case GLFW_KEY_F: return "F"; + case GLFW_KEY_G: return "G"; + case GLFW_KEY_H: return "H"; + case GLFW_KEY_I: return "I"; + case GLFW_KEY_J: return "J"; + case GLFW_KEY_K: return "K"; + case GLFW_KEY_L: return "L"; + case GLFW_KEY_M: return "M"; + case GLFW_KEY_N: return "N"; + case GLFW_KEY_O: return "O"; + case GLFW_KEY_P: return "P"; + case GLFW_KEY_Q: return "Q"; + case GLFW_KEY_R: return "R"; + case GLFW_KEY_S: return "S"; + case GLFW_KEY_T: return "T"; + case GLFW_KEY_U: return "U"; + case GLFW_KEY_V: return "V"; + case GLFW_KEY_W: return "W"; + case GLFW_KEY_X: return "X"; + case GLFW_KEY_Y: return "Y"; + case GLFW_KEY_Z: return "Z"; + case GLFW_KEY_1: return "1"; + case GLFW_KEY_2: return "2"; + case GLFW_KEY_3: return "3"; + case GLFW_KEY_4: return "4"; + case GLFW_KEY_5: return "5"; + case GLFW_KEY_6: return "6"; + case GLFW_KEY_7: return "7"; + case GLFW_KEY_8: return "8"; + case GLFW_KEY_9: return "9"; + case GLFW_KEY_0: return "0"; + case GLFW_KEY_SPACE: return "SPACE"; + case GLFW_KEY_MINUS: return "MINUS"; + case GLFW_KEY_EQUAL: return "EQUAL"; + case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET"; + case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET"; + case GLFW_KEY_BACKSLASH: return "BACKSLASH"; + case GLFW_KEY_SEMICOLON: return "SEMICOLON"; + case GLFW_KEY_APOSTROPHE: return "APOSTROPHE"; + case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT"; + case GLFW_KEY_COMMA: return "COMMA"; + case GLFW_KEY_PERIOD: return "PERIOD"; + case GLFW_KEY_SLASH: return "SLASH"; + case GLFW_KEY_WORLD_1: return "WORLD 1"; + case GLFW_KEY_WORLD_2: return "WORLD 2"; + + // Function keys + case GLFW_KEY_ESCAPE: return "ESCAPE"; + case GLFW_KEY_F1: return "F1"; + case GLFW_KEY_F2: return "F2"; + case GLFW_KEY_F3: return "F3"; + case GLFW_KEY_F4: return "F4"; + case GLFW_KEY_F5: return "F5"; + case GLFW_KEY_F6: return "F6"; + case GLFW_KEY_F7: return "F7"; + case GLFW_KEY_F8: return "F8"; + case GLFW_KEY_F9: return "F9"; + case GLFW_KEY_F10: return "F10"; + case GLFW_KEY_F11: return "F11"; + case GLFW_KEY_F12: return "F12"; + case GLFW_KEY_F13: return "F13"; + case GLFW_KEY_F14: return "F14"; + case GLFW_KEY_F15: return "F15"; + case GLFW_KEY_F16: return "F16"; + case GLFW_KEY_F17: return "F17"; + case GLFW_KEY_F18: return "F18"; + case GLFW_KEY_F19: return "F19"; + case GLFW_KEY_F20: return "F20"; + case GLFW_KEY_F21: return "F21"; + case GLFW_KEY_F22: return "F22"; + case GLFW_KEY_F23: return "F23"; + case GLFW_KEY_F24: return "F24"; + case GLFW_KEY_F25: return "F25"; + case GLFW_KEY_UP: return "UP"; + case GLFW_KEY_DOWN: return "DOWN"; + case GLFW_KEY_LEFT: return "LEFT"; + case GLFW_KEY_RIGHT: return "RIGHT"; + case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT"; + case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT"; + case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL"; + case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL"; + case GLFW_KEY_LEFT_ALT: return "LEFT ALT"; + case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT"; + case GLFW_KEY_TAB: return "TAB"; + case GLFW_KEY_ENTER: return "ENTER"; + case GLFW_KEY_BACKSPACE: return "BACKSPACE"; + case GLFW_KEY_INSERT: return "INSERT"; + case GLFW_KEY_DELETE: return "DELETE"; + case GLFW_KEY_PAGE_UP: return "PAGE UP"; + case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN"; + case GLFW_KEY_HOME: return "HOME"; + case GLFW_KEY_END: return "END"; + case GLFW_KEY_KP_0: return "KEYPAD 0"; + case GLFW_KEY_KP_1: return "KEYPAD 1"; + case GLFW_KEY_KP_2: return "KEYPAD 2"; + case GLFW_KEY_KP_3: return "KEYPAD 3"; + case GLFW_KEY_KP_4: return "KEYPAD 4"; + case GLFW_KEY_KP_5: return "KEYPAD 5"; + case GLFW_KEY_KP_6: return "KEYPAD 6"; + case GLFW_KEY_KP_7: return "KEYPAD 7"; + case GLFW_KEY_KP_8: return "KEYPAD 8"; + case GLFW_KEY_KP_9: return "KEYPAD 9"; + case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE"; + case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTPLY"; + case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT"; + case GLFW_KEY_KP_ADD: return "KEYPAD ADD"; + case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL"; + case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL"; + case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER"; + case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN"; + case GLFW_KEY_NUM_LOCK: return "NUM LOCK"; + case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK"; + case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK"; + case GLFW_KEY_PAUSE: return "PAUSE"; + 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: break; + } + + return NULL; +} ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// diff --git a/tests/events.c b/tests/events.c index 320af621..b711b6ab 100644 --- a/tests/events.c +++ b/tests/events.c @@ -340,15 +340,36 @@ static void scroll_callback(GLFWwindow* window, double x, double y) printf("%08x at %0.3f: Scroll: %0.3f %0.3f\n", counter++, glfwGetTime(), x, y); } +// Work-around for windows console output of UTF-8 strings +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include "Windows.h" + void PlatformPrintKeyName(const char* keyName) + { + WCHAR target[80]; + MultiByteToWideChar(CP_UTF8, 0, keyName, -1, target, 80); + printf(" (%ls)", target ); + } +#else + void PlatformPrintKeyName(const char* keyName) + { + printf(" (%s)", keyName ); + } +#endif + static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { - const char* name = get_key_name(key); + const char* name = glfwGetKeyName(key); + const char* glfwName = get_key_name(key); printf("%08x at %0.3f: Key 0x%04x Scancode 0x%04x", counter++, glfwGetTime(), key, scancode); if (name) - printf(" (%s)", name); + PlatformPrintKeyName( name ); + + if(glfwName) + printf(" GLFW Key Code Name: [%s]", glfwName); if (mods) printf(" (with%s)", get_mods_name(mods));