Add glfwGetKeyName

Fixes #464.
This commit is contained in:
Camilla Berglund 2015-07-02 14:24:50 +02:00
parent 67c6a45e0e
commit 9c315412e1
19 changed files with 273 additions and 16 deletions

View File

@ -344,17 +344,20 @@ if (_GLFW_COCOA AND _GLFW_NSGL)
find_library(IOKIT_FRAMEWORK IOKit) find_library(IOKIT_FRAMEWORK IOKit)
find_library(CORE_FOUNDATION_FRAMEWORK CoreFoundation) find_library(CORE_FOUNDATION_FRAMEWORK CoreFoundation)
find_library(CORE_VIDEO_FRAMEWORK CoreVideo) find_library(CORE_VIDEO_FRAMEWORK CoreVideo)
find_library(CARBON_FRAMEWORK Carbon)
mark_as_advanced(COCOA_FRAMEWORK mark_as_advanced(COCOA_FRAMEWORK
IOKIT_FRAMEWORK IOKIT_FRAMEWORK
CORE_FOUNDATION_FRAMEWORK CORE_FOUNDATION_FRAMEWORK
CORE_VIDEO_FRAMEWORK) CORE_VIDEO_FRAMEWORK
CARBON_FRAMEWORK)
list(APPEND glfw_LIBRARIES "${COCOA_FRAMEWORK}" list(APPEND glfw_LIBRARIES "${COCOA_FRAMEWORK}"
"${IOKIT_FRAMEWORK}" "${IOKIT_FRAMEWORK}"
"${CORE_FOUNDATION_FRAMEWORK}" "${CORE_FOUNDATION_FRAMEWORK}"
"${CORE_VIDEO_FRAMEWORK}") "${CORE_VIDEO_FRAMEWORK}"
"${CARBON_FRAMEWORK}")
set(glfw_PKG_DEPS "") set(glfw_PKG_DEPS "")
set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo") set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo -framework Carbon")
endif() endif()
#-------------------------------------------------------------------- #--------------------------------------------------------------------

View File

@ -66,6 +66,8 @@ used by the tests and examples and are not required to build the library.
- Added `glfwSetWindowSizeLimits` and `glfwSetWindowAspectRatio` for setting - Added `glfwSetWindowSizeLimits` and `glfwSetWindowAspectRatio` for setting
absolute and relative window size limits absolute and relative window size limits
- Added `glfwGetKeyName` for querying the layout-specific name of printable
keys
- Added `GLFW_NO_API` for creating window without contexts - Added `GLFW_NO_API` for creating window without contexts
- Added `GLFW_CONTEXT_NO_ERROR` context hint for `GL_KHR_no_error` support - Added `GLFW_CONTEXT_NO_ERROR` context hint for `GL_KHR_no_error` support
- Added `GLFW_TRUE` and `GLFW_FALSE` as client API independent boolean values - Added `GLFW_TRUE` and `GLFW_FALSE` as client API independent boolean values

View File

@ -192,6 +192,22 @@ void charmods_callback(GLFWwindow* window, unsigned int codepoint, int mods)
@endcode @endcode
@subsection input_key_name Key names
If you wish to refer to keys by name, you can query the keyboard layout
dependent name of printable keys with @ref glfwGetKeyName.
@code
const char* key_name = glfwGetKeyName(GLFW_KEY_W, 0);
show_tutorial_hint("Press %s to move forward", key_name);
@endcode
This function can handle both [keys and scancodes](@ref input_key). If the
specified key is `GLFW_KEY_UNKNOWN` then the scancode is used, otherwise it is
ignored. This matches the behavior of the key callback, meaning the callback
arguments can always be passed unmodified to this function.
@section input_mouse Mouse input @section input_mouse Mouse input
Mouse input comes in many forms, including cursor motion, button presses and Mouse input comes in many forms, including cursor motion, button presses and

View File

@ -11,6 +11,12 @@ GLFW now supports setting both absolute and relative window size limits with
@ref glfwSetWindowSizeLimits and @ref glfwSetWindowAspectRatio. @ref glfwSetWindowSizeLimits and @ref glfwSetWindowAspectRatio.
@subsection news_32_keyname Localized key names
GLFW now supports querying the localized name of printable keys with @ref
glfwGetKeyName, either by key token or by scancode.
@section news_31 New features in 3.1 @section news_31 New features in 3.1
These are the release highlights. For a full list of changes see the These are the release highlights. For a full list of changes see the

View File

@ -414,6 +414,7 @@ extern "C" {
#define GLFW_KEY_RIGHT_ALT 346 #define GLFW_KEY_RIGHT_ALT 346
#define GLFW_KEY_RIGHT_SUPER 347 #define GLFW_KEY_RIGHT_SUPER 347
#define GLFW_KEY_MENU 348 #define GLFW_KEY_MENU 348
#define GLFW_KEY_LAST GLFW_KEY_MENU #define GLFW_KEY_LAST GLFW_KEY_MENU
/*! @} */ /*! @} */
@ -2588,6 +2589,33 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
*/ */
GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value);
/*! @brief Returns the localized name of the specified printable key.
*
* This function returns the localized name of the specified printable key.
*
* If the key is `GLFW_KEY_UNKNOWN`, the scancode is used, otherwise the
* scancode is ignored.
*
* @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`.
* @param[in] scancode The scancode of the key to query.
* @return The localized name of the key.
*
* @par Pointer Lifetime
* The returned string is allocated and freed by GLFW. You should not free it
* yourself. It is valid until the next call to @ref glfwGetKeyName, or until
* the library is terminated.
*
* @par Thread Safety
* This function may only be called from the main thread.
*
* @sa @ref input_key_name
*
* @since Added in GLFW 3.2.
*
* @ingroup input
*/
GLFWAPI const char* glfwGetKeyName(int key, int scancode);
/*! @brief Returns the last reported state of a keyboard key for the specified /*! @brief Returns the last reported state of a keyboard key for the specified
* window. * window.
* *
@ -2607,6 +2635,8 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value);
* The [modifier key bit masks](@ref mods) are not key tokens and cannot be * The [modifier key bit masks](@ref mods) are not key tokens and cannot be
* used with this function. * used with this function.
* *
* __Do not use this function__ to implement [text input](@ref input_char).
*
* @param[in] window The desired window. * @param[in] window The desired window.
* @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is
* not a valid key for this function. * not a valid key for this function.

View File

@ -72,7 +72,10 @@ static void changeToResourcesDirectory(void)
// //
static void createKeyTables(void) static void createKeyTables(void)
{ {
int scancode;
memset(_glfw.ns.publicKeys, -1, sizeof(_glfw.ns.publicKeys)); memset(_glfw.ns.publicKeys, -1, sizeof(_glfw.ns.publicKeys));
memset(_glfw.ns.nativeKeys, -1, sizeof(_glfw.ns.nativeKeys));
_glfw.ns.publicKeys[0x1D] = GLFW_KEY_0; _glfw.ns.publicKeys[0x1D] = GLFW_KEY_0;
_glfw.ns.publicKeys[0x12] = GLFW_KEY_1; _glfw.ns.publicKeys[0x12] = GLFW_KEY_1;
@ -188,6 +191,13 @@ static void createKeyTables(void)
_glfw.ns.publicKeys[0x51] = GLFW_KEY_KP_EQUAL; _glfw.ns.publicKeys[0x51] = GLFW_KEY_KP_EQUAL;
_glfw.ns.publicKeys[0x43] = GLFW_KEY_KP_MULTIPLY; _glfw.ns.publicKeys[0x43] = GLFW_KEY_KP_MULTIPLY;
_glfw.ns.publicKeys[0x4E] = GLFW_KEY_KP_SUBTRACT; _glfw.ns.publicKeys[0x4E] = GLFW_KEY_KP_SUBTRACT;
for (scancode = 0; scancode < 256; scancode++)
{
// Store the reverse translation for faster key name lookup
if (_glfw.ns.publicKeys[scancode] >= 0)
_glfw.ns.nativeKeys[_glfw.ns.publicKeys[scancode]] = scancode;
}
} }
@ -211,6 +221,17 @@ int _glfwPlatformInit(void)
CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0); CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0);
// TODO: Catch kTISNotifySelectedKeyboardInputSourceChanged and update
_glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();
if (!_glfw.ns.inputSource)
return GLFW_FALSE;
_glfw.ns.unicodeData = TISGetInputSourceProperty(_glfw.ns.inputSource,
kTISPropertyUnicodeKeyLayoutData);
if (!_glfw.ns.unicodeData)
return GLFW_FALSE;
if (!_glfwInitContextAPI()) if (!_glfwInitContextAPI())
return GLFW_FALSE; return GLFW_FALSE;
@ -222,6 +243,12 @@ int _glfwPlatformInit(void)
void _glfwPlatformTerminate(void) void _glfwPlatformTerminate(void)
{ {
if (_glfw.ns.inputSource)
{
CFRelease(_glfw.ns.inputSource);
_glfw.ns.inputSource = NULL;
}
if (_glfw.ns.eventSource) if (_glfw.ns.eventSource)
{ {
CFRelease(_glfw.ns.eventSource); CFRelease(_glfw.ns.eventSource);

View File

@ -30,8 +30,10 @@
#include <stdint.h> #include <stdint.h>
#if defined(__OBJC__) #if defined(__OBJC__)
#import <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#else #else
#include <Carbon/Carbon.h>
#include <ApplicationServices/ApplicationServices.h> #include <ApplicationServices/ApplicationServices.h>
typedef void* id; typedef void* id;
#endif #endif
@ -73,13 +75,17 @@ typedef struct _GLFWwindowNS
// //
typedef struct _GLFWlibraryNS typedef struct _GLFWlibraryNS
{ {
CGEventSourceRef eventSource; CGEventSourceRef eventSource;
id delegate; id delegate;
id autoreleasePool; id autoreleasePool;
id cursor; id cursor;
TISInputSourceRef inputSource;
id unicodeData;
short int publicKeys[256]; char keyName[64];
char* clipboardString; short int publicKeys[256];
short int nativeKeys[GLFW_KEY_LAST + 1];
char* clipboardString;
} _GLFWlibraryNS; } _GLFWlibraryNS;

View File

@ -1168,6 +1168,48 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
CGAssociateMouseAndMouseCursorPosition(true); CGAssociateMouseAndMouseCursorPosition(true);
} }
const char* _glfwPlatformGetKeyName(int key, int scancode)
{
if (key != GLFW_KEY_UNKNOWN)
scancode = _glfw.ns.nativeKeys[key];
if (!_glfwIsPrintable(_glfw.ns.publicKeys[scancode]))
return NULL;
UInt32 deadKeyState = 0;
UniChar characters[8];
UniCharCount characterCount = 0;
if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes],
scancode,
kUCKeyActionDisplay,
0,
LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit,
&deadKeyState,
sizeof(characters) / sizeof(characters[0]),
&characterCount,
characters) != noErr)
{
return NULL;
}
if (!characterCount)
return NULL;
CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
characters,
characterCount,
kCFAllocatorNull);
CFStringGetCString(string,
_glfw.ns.keyName,
sizeof(_glfw.ns.keyName),
kCFStringEncodingUTF8);
CFRelease(string);
return _glfw.ns.keyName;
}
int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
const GLFWimage* image, const GLFWimage* image,
int xhot, int yhot) int xhot, int yhot)

View File

@ -223,6 +223,18 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
} }
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
GLFWbool _glfwIsPrintable(int key)
{
return (key >= GLFW_KEY_APOSTROPHE && key <= GLFW_KEY_WORLD_2) ||
(key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) ||
key == GLFW_KEY_KP_EQUAL;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW public API ////// ////// GLFW public API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -270,6 +282,12 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
} }
} }
GLFWAPI const char* glfwGetKeyName(int key, int scancode)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return _glfwPlatformGetKeyName(key, scancode);
}
GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) GLFWAPI int glfwGetKey(GLFWwindow* handle, int key)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle; _GLFWwindow* window = (_GLFWwindow*) handle;

View File

@ -428,6 +428,11 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos);
*/ */
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode);
/*! @copydoc glfwGetKeyName
* @ingroup platform
*/
const char* _glfwPlatformGetKeyName(int key, int scancode);
/*! @copydoc glfwGetMonitors /*! @copydoc glfwGetMonitors
* @ingroup platform * @ingroup platform
*/ */
@ -900,4 +905,8 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor);
*/ */
void _glfwFreeMonitors(_GLFWmonitor** monitors, int count); void _glfwFreeMonitors(_GLFWmonitor** monitors, int count);
/*! @ingroup utility
*/
int _glfwIsPrintable(int key);
#endif // _glfw3_internal_h_ #endif // _glfw3_internal_h_

View File

@ -802,6 +802,13 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
"Mir: Unsupported function %s", __PRETTY_FUNCTION__); "Mir: Unsupported function %s", __PRETTY_FUNCTION__);
} }
const char* _glfwPlatformGetKeyName(int key, int scancode)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
return NULL;
}
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,

View File

@ -130,7 +130,10 @@ static void terminateLibraries(void)
// //
static void createKeyTables(void) static void createKeyTables(void)
{ {
int scancode;
memset(_glfw.win32.publicKeys, -1, sizeof(_glfw.win32.publicKeys)); memset(_glfw.win32.publicKeys, -1, sizeof(_glfw.win32.publicKeys));
memset(_glfw.win32.nativeKeys, -1, sizeof(_glfw.win32.nativeKeys));
_glfw.win32.publicKeys[0x00B] = GLFW_KEY_0; _glfw.win32.publicKeys[0x00B] = GLFW_KEY_0;
_glfw.win32.publicKeys[0x002] = GLFW_KEY_1; _glfw.win32.publicKeys[0x002] = GLFW_KEY_1;
@ -252,6 +255,12 @@ static void createKeyTables(void)
_glfw.win32.publicKeys[0x11C] = GLFW_KEY_KP_ENTER; _glfw.win32.publicKeys[0x11C] = GLFW_KEY_KP_ENTER;
_glfw.win32.publicKeys[0x037] = GLFW_KEY_KP_MULTIPLY; _glfw.win32.publicKeys[0x037] = GLFW_KEY_KP_MULTIPLY;
_glfw.win32.publicKeys[0x04A] = GLFW_KEY_KP_SUBTRACT; _glfw.win32.publicKeys[0x04A] = GLFW_KEY_KP_SUBTRACT;
for (scancode = 0; scancode < 512; scancode++)
{
if (_glfw.win32.publicKeys[scancode] > 0)
_glfw.win32.nativeKeys[_glfw.win32.publicKeys[scancode]] = scancode;
}
} }

View File

@ -182,7 +182,9 @@ typedef struct _GLFWlibraryWin32
{ {
DWORD foregroundLockTimeout; DWORD foregroundLockTimeout;
char* clipboardString; char* clipboardString;
char keyName[64];
short int publicKeys[512]; short int publicKeys[512];
short int nativeKeys[GLFW_KEY_LAST + 1];
// winmm.dll // winmm.dll
struct { struct {

View File

@ -1180,6 +1180,30 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
SetCursor(NULL); SetCursor(NULL);
} }
const char* _glfwPlatformGetKeyName(int key, int scancode)
{
WCHAR name[16];
if (key != GLFW_KEY_UNKNOWN)
scancode = _glfw.win32.nativeKeys[key];
if (!_glfwIsPrintable(_glfw.win32.publicKeys[scancode]))
return NULL;
if (!GetKeyNameTextW(scancode << 16, name, sizeof(name) / sizeof(WCHAR)))
return NULL;
if (!WideCharToMultiByte(CP_UTF8, 0, name, -1,
_glfw.win32.keyName,
sizeof(_glfw.win32.keyName),
NULL, NULL))
{
return NULL;
}
return _glfw.win32.keyName;
}
int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
const GLFWimage* image, const GLFWimage* image,
int xhot, int yhot) int xhot, int yhot)

View File

@ -436,6 +436,12 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
_glfwPlatformSetCursor(window, window->wl.currentCursor); _glfwPlatformSetCursor(window, window->wl.currentCursor);
} }
const char* _glfwPlatformGetKeyName(int key, int scancode)
{
// TODO
return NULL;
}
int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
const GLFWimage* image, const GLFWimage* image,
int xhot, int yhot) int xhot, int yhot)

View File

@ -234,6 +234,7 @@ static void createKeyTables(void)
int scancode, key; int scancode, key;
memset(_glfw.x11.publicKeys, -1, sizeof(_glfw.x11.publicKeys)); memset(_glfw.x11.publicKeys, -1, sizeof(_glfw.x11.publicKeys));
memset(_glfw.x11.nativeKeys, -1, sizeof(_glfw.x11.nativeKeys));
if (_glfw.x11.xkb.available) if (_glfw.x11.xkb.available)
{ {
@ -312,12 +313,16 @@ static void createKeyTables(void)
XkbFreeClientMap(desc, 0, True); XkbFreeClientMap(desc, 0, True);
} }
// Translate the un-translated key codes using traditional X11 KeySym
// lookups
for (scancode = 0; scancode < 256; scancode++) for (scancode = 0; scancode < 256; scancode++)
{ {
// Translate the un-translated key codes using traditional X11 KeySym
// lookups
if (_glfw.x11.publicKeys[scancode] < 0) if (_glfw.x11.publicKeys[scancode] < 0)
_glfw.x11.publicKeys[scancode] = translateKeyCode(scancode); _glfw.x11.publicKeys[scancode] = translateKeyCode(scancode);
// Store the reverse translation for faster key name lookup
if (_glfw.x11.publicKeys[scancode] > 0)
_glfw.x11.nativeKeys[_glfw.x11.publicKeys[scancode]] = scancode;
} }
} }

View File

@ -121,8 +121,12 @@ typedef struct _GLFWlibraryX11
int errorCode; int errorCode;
// Clipboard string (while the selection is owned) // Clipboard string (while the selection is owned)
char* clipboardString; char* clipboardString;
// Key name string
char keyName[64];
// X11 keycode to GLFW key LUT // X11 keycode to GLFW key LUT
short int publicKeys[256]; short int publicKeys[256];
// GLFW key to X11 keycode LUT
short int nativeKeys[GLFW_KEY_LAST + 1];
// Window manager atoms // Window manager atoms
Atom WM_PROTOCOLS; Atom WM_PROTOCOLS;

View File

@ -1939,6 +1939,34 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
} }
} }
const char* _glfwPlatformGetKeyName(int key, int scancode)
{
KeySym keysym;
int extra;
if (!_glfw.x11.xkb.available)
return NULL;
if (key != GLFW_KEY_UNKNOWN)
scancode = _glfw.x11.nativeKeys[key];
if (!_glfwIsPrintable(_glfw.x11.publicKeys[scancode]))
return NULL;
keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
if (keysym == NoSymbol)
return NULL;
XkbTranslateKeySym(_glfw.x11.display, &keysym, 0,
_glfw.x11.keyName, sizeof(_glfw.x11.keyName),
&extra);
if (!strlen(_glfw.x11.keyName))
return NULL;
return _glfw.x11.keyName;
}
int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
const GLFWimage* image, const GLFWimage* image,
int xhot, int yhot) int xhot, int yhot)

View File

@ -360,12 +360,25 @@ static void scroll_callback(GLFWwindow* window, double x, double y)
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{ {
Slot* slot = glfwGetWindowUserPointer(window); Slot* slot = glfwGetWindowUserPointer(window);
const char* name = glfwGetKeyName(key, scancode);
printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (with%s) was %s\n", if (name)
counter++, slot->number, glfwGetTime(), key, scancode, {
get_key_name(key), printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (%s) (with%s) was %s\n",
get_mods_name(mods), counter++, slot->number, glfwGetTime(), key, scancode,
get_action_name(action)); get_key_name(key),
name,
get_mods_name(mods),
get_action_name(action));
}
else
{
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) if (action != GLFW_PRESS)
return; return;