This commit is contained in:
Yukari Hafner 2026-09-07 09:52:16 +00:00 • committed by GitHub
commit f210e8528f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 451 additions and 163 deletions

View file

@ -313,6 +313,7 @@ video tutorials.
- Jonas Ådahl
- Lasse Öörni
- Leonard König
- Yukari Hafner (Shinmera)
- All the unmentioned and anonymous contributors in the GLFW community, for bug
reports, patches, feedback, testing and encouragement

View file

@ -120,8 +120,9 @@ information on what to include when reporting a bug.
## Changelog since 3.5
None.
- Added `glfwGetKeyNameModifiers` to obtain the native key translation including
active modifier sets in order to accurately display key state and handle key
chords in the presence of shift and other modifiers (#2685)
## Contact

View file

@ -233,14 +233,21 @@ void character_callback(GLFWwindow* window, unsigned int codepoint)
### Key names {#input_key_name}
If you wish to refer to keys by name, you can query the keyboard layout
dependent name of printable keys with @ref glfwGetKeyName.
dependent name of printable keys with @ref glfwGetKeyName
and @ref glfwGetKeyNameModifiers.
```c
const char* key_name = glfwGetKeyName(GLFW_KEY_W, 0);
show_tutorial_hint("Press %s to move forward", key_name);
```
This function can handle both [keys and scancodes](@ref input_key). If the
```c
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
printf("You pressed: %s\n", glfwGetKeyNameModifiers(key, scancode, mods));
}
```
These functions 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.

View file

@ -5,6 +5,12 @@
## New features {#features}
### Key translations with modifiers {#glfwGetKeyNameModifiers}
GLFW now provides the @ref glfwGetKeyNameModifiers function to obtain the native
key translation including active modifier sets in order to accurately display
key state and handle key chords in the presence of shift and other modifiers.
## Caveats {#caveats}
## Deprecations {#deprecations}
@ -15,6 +21,8 @@
### New functions {#new_functions}
- @ref glfwGetKeyNameModifiers
### New types {#new_types}
### New constants {#new_constants}

View file

@ -560,6 +560,11 @@ extern "C" {
* GLFW_LOCK_KEY_MODS input mode is set.
*/
#define GLFW_MOD_NUM_LOCK 0x0020
/*! @brief If this bit is set the Alt-Gr or right alt key were held down.
*
* If this bit is set the Alt-Gr or right alt key were held down.
*/
#define GLFW_MOD_ALT_GR 0x0040
/*! @} */
@ -4838,6 +4843,82 @@ GLFWAPI int glfwRawMouseMotionSupported(void);
*
* @remark The contents of the returned string may change when a keyboard
* layout change event is received.
*
* @remark This function is the same as calling @ref glfwGetKeyNameModifiers
* with the `modifiers` set to `0`.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the library is terminated.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref input_key_name
* @sa @ref glfwGetKeyNameModifiers
*
* @since Added in version 3.2.
*
* @ingroup input
*/
GLFWAPI const char* glfwGetKeyName(int key, int scancode);
/*! @brief Returns the layout-specific name of the specified printable key
* modifier combination.
*
* This function returns the name of the specified printable key, when pressed
* together with the given set of modifiers, encoded as UTF-8. This is
* intended for displaying key bindings and chords to the user.
*
* __Do not use this function__ for [text input](@ref input_char). You will
* break text input for many languages even if it happens to work for yours.
* This applies even though the modifiers are respected. Keyboard input
* methods often apply transformations that go beyond a single key combination.
*
* If the key is `GLFW_KEY_UNKNOWN`, the scancode is used to identify the key,
* otherwise the scancode is ignored. If you specify a non-printable key, or
* `GLFW_KEY_UNKNOWN` and a scancode that maps to a non-printable key, this
* function returns `NULL` but does not emit an error.
*
* This behavior allows you to always pass in the arguments in the
* [key callback](@ref input_key) without modification.
*
* The printable keys are:
* - `GLFW_KEY_APOSTROPHE`
* - `GLFW_KEY_COMMA`
* - `GLFW_KEY_MINUS`
* - `GLFW_KEY_PERIOD`
* - `GLFW_KEY_SLASH`
* - `GLFW_KEY_SEMICOLON`
* - `GLFW_KEY_EQUAL`
* - `GLFW_KEY_LEFT_BRACKET`
* - `GLFW_KEY_RIGHT_BRACKET`
* - `GLFW_KEY_BACKSLASH`
* - `GLFW_KEY_WORLD_1`
* - `GLFW_KEY_WORLD_2`
* - `GLFW_KEY_0` to `GLFW_KEY_9`
* - `GLFW_KEY_A` to `GLFW_KEY_Z`
* - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9`
* - `GLFW_KEY_KP_DECIMAL`
* - `GLFW_KEY_KP_DIVIDE`
* - `GLFW_KEY_KP_MULTIPLY`
* - `GLFW_KEY_KP_SUBTRACT`
* - `GLFW_KEY_KP_ADD`
* - `GLFW_KEY_KP_EQUAL`
*
* Names for printable keys depend on keyboard layout, while names for
* non-printable keys are the same across layouts but depend on the application
* language and should be localized along with other user interface text.
*
* @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`.
* @param[in] scancode The scancode of the key to query.
* @param[in] modifiers The modifier set of the key combination to query.
* @return The UTF-8 encoded, layout-specific name of the key combination, or
* `NULL`.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_INVALID_VALUE, @ref GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
*
* @remark The contents of the returned string may change when a keyboard
* layout change event is received.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the library is terminated.
@ -4846,11 +4927,11 @@ GLFWAPI int glfwRawMouseMotionSupported(void);
*
* @sa @ref input_key_name
*
* @since Added in version 3.2.
* @since Added in version 3.5.
*
* @ingroup input
*/
GLFWAPI const char* glfwGetKeyName(int key, int scancode);
GLFWAPI const char* glfwGetKeyNameModifiers(int key, int scancode, int modifiers);
/*! @brief Returns the platform-specific scancode of the specified key.
*

View file

@ -259,7 +259,7 @@ void _glfwPostEmptyEventCocoa(void);
void _glfwGetCursorPosCocoa(_GLFWwindow* window, double* xpos, double* ypos);
void _glfwSetCursorPosCocoa(_GLFWwindow* window, double xpos, double ypos);
void _glfwSetCursorModeCocoa(_GLFWwindow* window, int mode);
const char* _glfwGetScancodeNameCocoa(int scancode);
const char* _glfwGetScancodeNameCocoa(int scancode, int modifiers);
int _glfwGetKeyScancodeCocoa(int key);
GLFWbool _glfwCreateCursorCocoa(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot);
GLFWbool _glfwCreateStandardCursorCocoa(_GLFWcursor* cursor, int shape);

View file

@ -159,6 +159,26 @@ static int translateFlags(NSUInteger flags)
return mods;
}
// Translates GLFW key modifiers into macOS ones
//
static int translateFlagsInverse(int mods)
{
NSUInteger flags = 0;
if (mods & GLFW_MOD_SHIFT)
flags |= NSEventModifierFlagShift;
if (mods & GLFW_MOD_CONTROL)
flags |= NSEventModifierFlagControl;
if (mods & GLFW_MOD_ALT)
flags |= NSEventModifierFlagOption;
if (mods & GLFW_MOD_SUPER)
flags |= NSEventModifierFlagCommand;
if (mods & GLFW_MOD_CAPS_LOCK)
flags |= NSEventModifierFlagCapsLock;
return flags;
}
// Translates a macOS keycode to a GLFW keycode
//
static int translateKey(unsigned int key)
@ -1682,7 +1702,7 @@ void _glfwSetCursorModeCocoa(_GLFWwindow* window, int mode)
} // autoreleasepool
}
const char* _glfwGetScancodeNameCocoa(int scancode)
const char* _glfwGetScancodeNameCocoa(int scancode, int modifiers)
{
@autoreleasepool {
@ -1703,7 +1723,7 @@ const char* _glfwGetScancodeNameCocoa(int scancode)
if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes],
scancode,
kUCKeyActionDisplay,
0,
translateFlagsInverse(modifiers),
LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit,
&deadKeyState,

View file

@ -47,7 +47,8 @@
GLFW_MOD_ALT | \
GLFW_MOD_SUPER | \
GLFW_MOD_CAPS_LOCK | \
GLFW_MOD_NUM_LOCK)
GLFW_MOD_NUM_LOCK | \
GLFW_MOD_ALT_GR)
// Initializes the platform joystick API if it has not been already
//
@ -701,7 +702,7 @@ GLFWAPI int glfwRawMouseMotionSupported(void)
return _glfw.platform.rawMouseMotionSupported();
}
GLFWAPI const char* glfwGetKeyName(int key, int scancode)
GLFWAPI const char* glfwGetKeyNameModifiers(int key, int scancode, int modifiers)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@ -723,7 +724,12 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode)
scancode = _glfw.platform.getKeyScancode(key);
}
return _glfw.platform.getScancodeName(scancode);
return _glfw.platform.getScancodeName(scancode, modifiers);
}
GLFWAPI const char* glfwGetKeyName(int key, int scancode)
{
return glfwGetKeyNameModifiers(key, scancode, 0);
}
GLFWAPI int glfwGetKeyScancode(int key)

View file

@ -696,7 +696,7 @@ struct _GLFWplatform
GLFWbool (*createStandardCursor)(_GLFWcursor*,int);
void (*destroyCursor)(_GLFWcursor*);
void (*setCursor)(_GLFWwindow*,_GLFWcursor*);
const char* (*getScancodeName)(int);
const char* (*getScancodeName)(int, int);
int (*getKeyScancode)(int);
void (*setClipboardString)(const char*);
const char* (*getClipboardString)(void);

View file

@ -267,7 +267,7 @@ void _glfwDestroyCursorNull(_GLFWcursor* cursor);
void _glfwSetCursorNull(_GLFWwindow* window, _GLFWcursor* cursor);
void _glfwSetClipboardStringNull(const char* string);
const char* _glfwGetClipboardStringNull(void);
const char* _glfwGetScancodeNameNull(int scancode);
const char* _glfwGetScancodeNameNull(int scancode, int modifiers);
int _glfwGetKeyScancodeNull(int key);
EGLenum _glfwGetEGLPlatformNull(EGLint** attribs);

View file

@ -569,7 +569,140 @@ EGLNativeWindowType _glfwGetEGLNativeWindowNull(_GLFWwindow* window)
return 0;
}
const char* _glfwGetScancodeNameNull(int scancode)
// Store the string representations of the scan codes.
// This relies on the index in the array corresponding to the
// GLFW_NULL_SC_* constant. For each element, it defines the different
// names depending on that key's shift level, which can be either 0
// (no modifiers), 1 (shift held), 2 (ctrl+alt/altgr held), or 3 (both
// shift and altgr held).
//
// The used key names are from the en-US ANSI keyboard layout.
const char* keyNames[GLFW_NULL_SC_LAST+1][4] = {
// 0 = neutral, 1 = shift, 2 = altgr, 3 = shift+altgr
{/**/ NULL, NULL, NULL, NULL},
{/*SPACE*/ " ", " ", " ", " "},
{/*APOSTROPHE*/ "'", "\"", NULL, NULL},
{/*COMMA*/ ",", "<", NULL, NULL},
{/*MINUS*/ "-", "_", NULL, NULL},
{/*PERIOD*/ ".", ">", NULL, NULL},
{/*SLASH*/ "/", "?", NULL, NULL},
{/*0*/ "0", ")", NULL, NULL},
{/*1*/ "1", "!", NULL, NULL},
{/*2*/ "2", "@", NULL, NULL},
{/*3*/ "3", "#", NULL, NULL},
{/*4*/ "4", "$", NULL, NULL},
{/*5*/ "5", "%", NULL, NULL},
{/*6*/ "6", "^", NULL, NULL},
{/*7*/ "7", "&", NULL, NULL},
{/*8*/ "8", "*", NULL, NULL},
{/*9*/ "9", "(", NULL, NULL},
{/*SEMICOLON*/ ";", ":", NULL, NULL},
{/*EQUAL*/ "=", "+", NULL, NULL},
{/*LEFT_BRACKET*/ "[", "{", NULL, NULL},
{/*BACKSLASH*/ "\\", "|", NULL, NULL},
{/*RIGHT_BRACKET*/ "]", "}", NULL, NULL},
{/*GRAVE_ACCENT*/ NULL, NULL, NULL, NULL},
{/*WORLD_1*/ "\\", NULL, NULL, NULL},
{/*WORLD_2*/ "\\", NULL, NULL, NULL},
{/*ESCAPE*/ NULL, NULL, NULL, NULL},
{/*ENTER*/ NULL, NULL, NULL, NULL},
{/*TAB*/ NULL, NULL, NULL, NULL},
{/*BACKSPACE*/ NULL, NULL, NULL, NULL},
{/*INSERT*/ NULL, NULL, NULL, NULL},
{/*DELETE*/ NULL, NULL, NULL, NULL},
{/*RIGHT*/ NULL, NULL, NULL, NULL},
{/*LEFT*/ NULL, NULL, NULL, NULL},
{/*DOWN*/ NULL, NULL, NULL, NULL},
{/*UP*/ NULL, NULL, NULL, NULL},
{/*PAGE_UP*/ NULL, NULL, NULL, NULL},
{/*PAGE_DOWN*/ NULL, NULL, NULL, NULL},
{/*HOME*/ NULL, NULL, NULL, NULL},
{/*END*/ NULL, NULL, NULL, NULL},
{/*CAPS_LOCK*/ NULL, NULL, NULL, NULL},
{/*SCROLL_LOCK*/ NULL, NULL, NULL, NULL},
{/*NUM_LOCK*/ NULL, NULL, NULL, NULL},
{/*PRINT_SCREEN*/ NULL, NULL, NULL, NULL},
{/*PAUSE*/ NULL, NULL, NULL, NULL},
{/*A*/ "a", "A", NULL, NULL},
{/*B*/ "b", "B", NULL, NULL},
{/*C*/ "c", "C", NULL, NULL},
{/*D*/ "d", "D", NULL, NULL},
{/*E*/ "e", "E", NULL, NULL},
{/*F*/ "f", "F", NULL, NULL},
{/*G*/ "g", "G", NULL, NULL},
{/*H*/ "h", "H", NULL, NULL},
{/*I*/ "i", "I", NULL, NULL},
{/*J*/ "j", "J", NULL, NULL},
{/*K*/ "k", "K", NULL, NULL},
{/*L*/ "l", "L", NULL, NULL},
{/*M*/ "m", "M", NULL, NULL},
{/*N*/ "n", "N", NULL, NULL},
{/*O*/ "o", "O", NULL, NULL},
{/*P*/ "p", "P", NULL, NULL},
{/*Q*/ "q", "Q", NULL, NULL},
{/*R*/ "r", "R", NULL, NULL},
{/*S*/ "s", "S", NULL, NULL},
{/*T*/ "t", "T", NULL, NULL},
{/*U*/ "u", "U", NULL, NULL},
{/*V*/ "v", "V", NULL, NULL},
{/*W*/ "w", "W", NULL, NULL},
{/*X*/ "x", "X", NULL, NULL},
{/*Y*/ "y", "Y", NULL, NULL},
{/*Z*/ "z", "Z", NULL, NULL},
{/*F1*/ NULL, NULL, NULL, NULL},
{/*F2*/ NULL, NULL, NULL, NULL},
{/*F3*/ NULL, NULL, NULL, NULL},
{/*F4*/ NULL, NULL, NULL, NULL},
{/*F5*/ NULL, NULL, NULL, NULL},
{/*F6*/ NULL, NULL, NULL, NULL},
{/*F7*/ NULL, NULL, NULL, NULL},
{/*F8*/ NULL, NULL, NULL, NULL},
{/*F9*/ NULL, NULL, NULL, NULL},
{/*F10*/ NULL, NULL, NULL, NULL},
{/*F11*/ NULL, NULL, NULL, NULL},
{/*F12*/ NULL, NULL, NULL, NULL},
{/*F13*/ NULL, NULL, NULL, NULL},
{/*F14*/ NULL, NULL, NULL, NULL},
{/*F15*/ NULL, NULL, NULL, NULL},
{/*F16*/ NULL, NULL, NULL, NULL},
{/*F17*/ NULL, NULL, NULL, NULL},
{/*F18*/ NULL, NULL, NULL, NULL},
{/*F19*/ NULL, NULL, NULL, NULL},
{/*F20*/ NULL, NULL, NULL, NULL},
{/*F21*/ NULL, NULL, NULL, NULL},
{/*F22*/ NULL, NULL, NULL, NULL},
{/*F23*/ NULL, NULL, NULL, NULL},
{/*F24*/ NULL, NULL, NULL, NULL},
{/*F25*/ NULL, NULL, NULL, NULL},
{/*KP_0*/ "0", NULL, NULL, NULL},
{/*KP_1*/ "1", NULL, NULL, NULL},
{/*KP_2*/ "2", NULL, NULL, NULL},
{/*KP_3*/ "3", NULL, NULL, NULL},
{/*KP_4*/ "4", NULL, NULL, NULL},
{/*KP_5*/ "5", NULL, NULL, NULL},
{/*KP_6*/ "6", NULL, NULL, NULL},
{/*KP_7*/ "7", NULL, NULL, NULL},
{/*KP_8*/ "8", NULL, NULL, NULL},
{/*KP_9*/ "9", NULL, NULL, NULL},
{/*KP_DECIMAL*/ ".", NULL, NULL, NULL},
{/*KP_DIVIDE*/ "/", NULL, NULL, NULL},
{/*KP_MULTIPLY*/ "*", NULL, NULL, NULL},
{/*KP_SUBTRACT*/ "-", NULL, NULL, NULL},
{/*KP_ADD*/ "+", NULL, NULL, NULL},
{/*KP_ENTER*/ NULL, NULL, NULL, NULL},
{/*KP_EQUAL*/ "=", NULL, NULL, NULL},
{/*LEFT_SHIFT*/ NULL, NULL, NULL, NULL},
{/*LEFT_CONTROL*/ NULL, NULL, NULL, NULL},
{/*LEFT_ALT*/ NULL, NULL, NULL, NULL},
{/*LEFT_SUPER*/ NULL, NULL, NULL, NULL},
{/*RIGHT_SHIFT*/ NULL, NULL, NULL, NULL},
{/*RIGHT_CONTROL*/ NULL, NULL, NULL, NULL},
{/*RIGHT_ALT*/ NULL, NULL, NULL, NULL},
{/*RIGHT_SUPER*/ NULL, NULL, NULL, NULL},
{/*MENUP*/ NULL, NULL, NULL, NULL},
};
const char* _glfwGetScancodeNameNull(int scancode, int modifiers)
{
if (scancode < GLFW_NULL_SC_FIRST || scancode > GLFW_NULL_SC_LAST)
{
@ -577,123 +710,12 @@ const char* _glfwGetScancodeNameNull(int scancode)
return NULL;
}
switch (scancode)
{
case GLFW_NULL_SC_APOSTROPHE:
return "'";
case GLFW_NULL_SC_COMMA:
return ",";
case GLFW_NULL_SC_MINUS:
case GLFW_NULL_SC_KP_SUBTRACT:
return "-";
case GLFW_NULL_SC_PERIOD:
case GLFW_NULL_SC_KP_DECIMAL:
return ".";
case GLFW_NULL_SC_SLASH:
case GLFW_NULL_SC_KP_DIVIDE:
return "/";
case GLFW_NULL_SC_SEMICOLON:
return ";";
case GLFW_NULL_SC_EQUAL:
case GLFW_NULL_SC_KP_EQUAL:
return "=";
case GLFW_NULL_SC_LEFT_BRACKET:
return "[";
case GLFW_NULL_SC_RIGHT_BRACKET:
return "]";
case GLFW_NULL_SC_KP_MULTIPLY:
return "*";
case GLFW_NULL_SC_KP_ADD:
return "+";
case GLFW_NULL_SC_BACKSLASH:
case GLFW_NULL_SC_WORLD_1:
case GLFW_NULL_SC_WORLD_2:
return "\\";
case GLFW_NULL_SC_0:
case GLFW_NULL_SC_KP_0:
return "0";
case GLFW_NULL_SC_1:
case GLFW_NULL_SC_KP_1:
return "1";
case GLFW_NULL_SC_2:
case GLFW_NULL_SC_KP_2:
return "2";
case GLFW_NULL_SC_3:
case GLFW_NULL_SC_KP_3:
return "3";
case GLFW_NULL_SC_4:
case GLFW_NULL_SC_KP_4:
return "4";
case GLFW_NULL_SC_5:
case GLFW_NULL_SC_KP_5:
return "5";
case GLFW_NULL_SC_6:
case GLFW_NULL_SC_KP_6:
return "6";
case GLFW_NULL_SC_7:
case GLFW_NULL_SC_KP_7:
return "7";
case GLFW_NULL_SC_8:
case GLFW_NULL_SC_KP_8:
return "8";
case GLFW_NULL_SC_9:
case GLFW_NULL_SC_KP_9:
return "9";
case GLFW_NULL_SC_A:
return "a";
case GLFW_NULL_SC_B:
return "b";
case GLFW_NULL_SC_C:
return "c";
case GLFW_NULL_SC_D:
return "d";
case GLFW_NULL_SC_E:
return "e";
case GLFW_NULL_SC_F:
return "f";
case GLFW_NULL_SC_G:
return "g";
case GLFW_NULL_SC_H:
return "h";
case GLFW_NULL_SC_I:
return "i";
case GLFW_NULL_SC_J:
return "j";
case GLFW_NULL_SC_K:
return "k";
case GLFW_NULL_SC_L:
return "l";
case GLFW_NULL_SC_M:
return "m";
case GLFW_NULL_SC_N:
return "n";
case GLFW_NULL_SC_O:
return "o";
case GLFW_NULL_SC_P:
return "p";
case GLFW_NULL_SC_Q:
return "q";
case GLFW_NULL_SC_R:
return "r";
case GLFW_NULL_SC_S:
return "s";
case GLFW_NULL_SC_T:
return "t";
case GLFW_NULL_SC_U:
return "u";
case GLFW_NULL_SC_V:
return "v";
case GLFW_NULL_SC_W:
return "w";
case GLFW_NULL_SC_X:
return "x";
case GLFW_NULL_SC_Y:
return "y";
case GLFW_NULL_SC_Z:
return "z";
}
return NULL;
int level = 0;
if (modifiers & GLFW_MOD_SHIFT)
level |= 1;
if ((modifiers & GLFW_MOD_CONTROL) && (modifiers & GLFW_MOD_ALT))
level |= 2;
return keyNames[scancode][level];
}
int _glfwGetKeyScancodeNull(int key)

View file

@ -488,7 +488,7 @@ void _glfwInputErrorWin32(int error, const char* description)
//
void _glfwUpdateKeyNamesWin32(void)
{
int key;
int key, level;
BYTE state[256] = {0};
memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames));
@ -518,26 +518,36 @@ void _glfwUpdateKeyNamesWin32(void)
else
vk = MapVirtualKeyW(scancode, MAPVK_VSC_TO_VK);
length = ToUnicode(vk, scancode, state,
chars, sizeof(chars) / sizeof(WCHAR),
0);
if (length == -1)
for (level = 0; level < 8; ++level)
{
// This is a dead key, so we need a second simulated key press
// to make it output its own character (usually a diacritic)
int mods = _GLFWmodelevels[level];
state[(int) VK_SHIFT] = (mods & GLFW_MOD_SHIFT)? 0xff : 0x00;
state[(int) VK_CONTROL] = (mods & GLFW_MOD_CONTROL)? 0xff : 0x00;
state[(int) VK_MENU] = (mods & GLFW_MOD_ALT)? 0xff : 0x00;
state[(int) VK_CAPITAL] = (mods & GLFW_MOD_CAPS_LOCK)? 0xff : 0x00;
state[(int) VK_RMENU] = (mods & GLFW_MOD_ALT_GR)? 0xff : 0x00;
length = ToUnicode(vk, scancode, state,
chars, sizeof(chars) / sizeof(WCHAR),
0);
if (length == -1)
{
// This is a dead key, so we need a second simulated key press
// to make it output its own character (usually a diacritic)
length = ToUnicode(vk, scancode, state,
chars, sizeof(chars) / sizeof(WCHAR),
0);
}
if (length < 1)
continue;
WideCharToMultiByte(CP_UTF8, 0, chars, 1,
_glfw.win32.keynames[key][level],
sizeof(_glfw.win32.keynames[key][level]),
NULL, NULL);
}
if (length < 1)
continue;
WideCharToMultiByte(CP_UTF8, 0, chars, 1,
_glfw.win32.keynames[key],
sizeof(_glfw.win32.keynames[key]),
NULL, NULL);
}
}

View file

@ -377,6 +377,17 @@ typedef struct _GLFWwindowWin32
WCHAR highSurrogate;
} _GLFWwindowWin32;
const static int _GLFWmodelevels[] = {
0,
GLFW_MOD_SHIFT,
GLFW_MOD_CAPS_LOCK,
GLFW_MOD_SHIFT | GLFW_MOD_CAPS_LOCK,
GLFW_MOD_CONTROL | GLFW_MOD_ALT,
GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SHIFT,
GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_CAPS_LOCK,
GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SHIFT | GLFW_MOD_CAPS_LOCK
};
// Win32-specific global data
//
typedef struct _GLFWlibraryWin32
@ -390,7 +401,7 @@ typedef struct _GLFWlibraryWin32
char* clipboardString;
short int keycodes[512];
short int scancodes[GLFW_KEY_LAST + 1];
char keynames[GLFW_KEY_LAST + 1][5];
char keynames[GLFW_KEY_LAST + 1][8][5];
// Where to place the cursor when re-enabled
double restoreCursorPosX, restoreCursorPosY;
// The window whose disabled cursor mode is active
@ -527,7 +538,7 @@ void _glfwPostEmptyEventWin32(void);
void _glfwGetCursorPosWin32(_GLFWwindow* window, double* xpos, double* ypos);
void _glfwSetCursorPosWin32(_GLFWwindow* window, double xpos, double ypos);
void _glfwSetCursorModeWin32(_GLFWwindow* window, int mode);
const char* _glfwGetScancodeNameWin32(int scancode);
const char* _glfwGetScancodeNameWin32(int scancode, int modifiers);
int _glfwGetKeyScancodeWin32(int key);
GLFWbool _glfwCreateCursorWin32(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot);
GLFWbool _glfwCreateStandardCursorWin32(_GLFWcursor* cursor, int shape);

View file

@ -419,6 +419,8 @@ static int getKeyMods(void)
mods |= GLFW_MOD_CAPS_LOCK;
if (GetKeyState(VK_NUMLOCK) & 1)
mods |= GLFW_MOD_NUM_LOCK;
if (GetKeyState(VK_RMENU) & 1)
mods |= GLFW_MOD_ALT_GR;
return mods;
}
@ -2255,7 +2257,7 @@ void _glfwSetCursorModeWin32(_GLFWwindow* window, int mode)
updateCursorImage(window);
}
const char* _glfwGetScancodeNameWin32(int scancode)
const char* _glfwGetScancodeNameWin32(int scancode, int modifiers)
{
if (scancode < 0 || scancode > (KF_EXTENDED | 0xff))
{
@ -2267,7 +2269,20 @@ const char* _glfwGetScancodeNameWin32(int scancode)
if (key == GLFW_KEY_UNKNOWN)
return NULL;
return _glfw.win32.keynames[key];
// Determine corresponding modifier level
int level = 0;
if (modifiers)
{
for (level = 0; level < 8; ++level)
{
if ((_GLFWmodelevels[level] & modifiers) == modifiers)
break;
}
if (level == 8)
level = 0;
}
return _glfw.win32.keynames[key][level];
}
int _glfwGetKeyScancodeWin32(int key)

View file

@ -691,7 +691,13 @@ int _glfwInitWayland(void)
_glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_unref");
_glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
_glfw.wl.xkb.keymap_mod_get_mask2 = (PFN_xkb_keymap_mod_get_mask2)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_mask2");
_glfw.wl.xkb.keymap_key_get_mods_for_level = (PFN_xkb_keymap_key_get_mods_for_level)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_key_get_mods_for_level");
_glfw.wl.xkb.keymap_num_levels_for_key = (PFN_xkb_keymap_num_levels_for_key)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_num_levels_for_key");
_glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats");
_glfw.wl.xkb.keymap_key_get_syms_by_level = (PFN_xkb_keymap_key_get_syms_by_level)

View file

@ -185,6 +185,9 @@ typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*);
typedef xkb_mod_mask_t (* PFN_xkb_keymap_mod_get_mask2)(struct xkb_keymap*, const xkb_mod_index_t);
typedef xkb_level_index_t (* PFN_xkb_keymap_num_levels_for_key)(struct xkb_keymap*, const xkb_keycode_t, const xkb_layout_index_t);
typedef size_t (* PFN_xkb_keymap_key_get_mods_for_level)(struct xkb_keymap*, const xkb_keycode_t, const xkb_layout_index_t, const xkb_level_index_t, xkb_mod_mask_t *, const size_t);
typedef int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t);
typedef int (* PFN_xkb_keymap_key_get_syms_by_level)(struct xkb_keymap*,xkb_keycode_t,xkb_layout_index_t,xkb_level_index_t,const xkb_keysym_t**);
typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*);
@ -200,6 +203,9 @@ typedef int (* PFN_xkb_keysym_to_utf8)(xkb_keysym_t, char*, size_t);
#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string
#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref
#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index
#define xkb_keymap_mod_get_mask2 _glfw.wl.xkb.keymap_mod_get_mask2
#define xkb_keymap_num_levels_for_key _glfw.wl.xkb.keymap_num_levels_for_key
#define xkb_keymap_key_get_mods_for_level _glfw.wl.xkb.keymap_key_get_mods_for_level
#define xkb_keymap_key_repeats _glfw.wl.xkb.keymap_key_repeats
#define xkb_keymap_key_get_syms_by_level _glfw.wl.xkb.keymap_key_get_syms_by_level
#define xkb_state_new _glfw.wl.xkb.state_new
@ -517,6 +523,7 @@ typedef struct _GLFWlibraryWayland
xkb_mod_index_t superIndex;
xkb_mod_index_t capsLockIndex;
xkb_mod_index_t numLockIndex;
xkb_mod_index_t altGrIndex;
unsigned int modifiers;
PFN_xkb_context_new context_new;
@ -524,8 +531,11 @@ typedef struct _GLFWlibraryWayland
PFN_xkb_keymap_new_from_string keymap_new_from_string;
PFN_xkb_keymap_unref keymap_unref;
PFN_xkb_keymap_mod_get_index keymap_mod_get_index;
PFN_xkb_keymap_mod_get_mask2 keymap_mod_get_mask2;
PFN_xkb_keymap_key_repeats keymap_key_repeats;
PFN_xkb_keymap_key_get_syms_by_level keymap_key_get_syms_by_level;
PFN_xkb_keymap_num_levels_for_key keymap_num_levels_for_key;
PFN_xkb_keymap_key_get_mods_for_level keymap_key_get_mods_for_level;
PFN_xkb_state_new state_new;
PFN_xkb_state_unref state_unref;
PFN_xkb_state_key_get_syms state_key_get_syms;
@ -700,7 +710,7 @@ void _glfwPostEmptyEventWayland(void);
void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos);
void _glfwSetCursorPosWayland(_GLFWwindow* window, double xpos, double ypos);
void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode);
const char* _glfwGetScancodeNameWayland(int scancode);
const char* _glfwGetScancodeNameWayland(int scancode, int modifiers);
int _glfwGetKeyScancodeWayland(int key);
GLFWbool _glfwCreateCursorWayland(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot);
GLFWbool _glfwCreateStandardCursorWayland(_GLFWcursor* cursor, int shape);

View file

@ -1909,6 +1909,7 @@ static void keyboardHandleKeymap(void* userData,
_glfw.wl.xkb.superIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
_glfw.wl.xkb.capsLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
_glfw.wl.xkb.numLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
_glfw.wl.xkb.altGrIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod3");
}
static void keyboardHandleEnter(void* userData,
@ -2033,7 +2034,8 @@ static void keyboardHandleModifiers(void* userData,
{ _glfw.wl.xkb.shiftIndex, GLFW_MOD_SHIFT },
{ _glfw.wl.xkb.superIndex, GLFW_MOD_SUPER },
{ _glfw.wl.xkb.capsLockIndex, GLFW_MOD_CAPS_LOCK },
{ _glfw.wl.xkb.numLockIndex, GLFW_MOD_NUM_LOCK }
{ _glfw.wl.xkb.numLockIndex, GLFW_MOD_NUM_LOCK },
{ _glfw.wl.xkb.altGrIndex, GLFW_MOD_ALT_GR }
};
for (size_t i = 0; i < sizeof(modifiers) / sizeof(modifiers[0]); i++)
@ -2978,7 +2980,31 @@ void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
_glfwSetCursorWayland(window, window->cursor);
}
const char* _glfwGetScancodeNameWayland(int scancode)
// Translates an xkb modifier state mask
//
static xkb_mod_mask_t translateStateInverse(int mods)
{
xkb_mod_mask_t mask = 0;
if (mods & GLFW_MOD_SHIFT)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.shiftIndex);
if (mods & GLFW_MOD_CONTROL)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.controlIndex);
if (mods & GLFW_MOD_ALT)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.altIndex);
if (mods & GLFW_MOD_SUPER)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.superIndex);
if (mods & GLFW_MOD_CAPS_LOCK)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.capsLockIndex);
if (mods & GLFW_MOD_NUM_LOCK)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.numLockIndex);
if (mods & GLFW_MOD_ALT_GR)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.altGrIndex);
return mask;
}
const char* _glfwGetScancodeNameWayland(int scancode, int modifiers)
{
if (scancode < 0 || scancode > 255)
{
@ -3002,11 +3028,44 @@ const char* _glfwGetScancodeNameWayland(int scancode)
return NULL;
}
xkb_level_index_t found_level = 0;
if (0 < modifiers)
{
// Figure out the corresponding level set for the modifier set
// that the user requested. This involves a relatively annoying
// loop of trying things out. Could cache this information if
// lookup speed is of concern.
xkb_mod_mask_t mask = translateStateInverse(modifiers);
const int num_levels = xkb_keymap_num_levels_for_key(_glfw.wl.xkb.keymap,
keycode,
layout);
xkb_level_index_t level;
for (level = 0; level < num_levels && found_level == 0; ++level)
{
size_t num_masks;
xkb_mod_mask_t masks[128];
num_masks = xkb_keymap_key_get_mods_for_level(_glfw.wl.xkb.keymap,
keycode,
layout,
level,
masks,
128);
for (int i=0; i < num_masks; ++i)
{
if ((masks[i] & mask) == mask)
{
found_level = level;
break;
}
}
}
}
const xkb_keysym_t* keysyms = NULL;
xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap,
keycode,
layout,
0,
found_level,
&keysyms);
if (keysyms == NULL)
{

View file

@ -1498,6 +1498,8 @@ int _glfwInitX11(void)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbGetState");
_glfw.x11.xkb.KeycodeToKeysym = (PFN_XkbKeycodeToKeysym)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbKeycodeToKeysym");
_glfw.x11.xkb.LookupKeysym = (PFN_XkbLookupKeysym)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbLookupKeySym");
_glfw.x11.xkb.QueryExtension = (PFN_XkbQueryExtension)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbQueryExtension");
_glfw.x11.xkb.SelectEventDetails = (PFN_XkbSelectEventDetails)

View file

@ -188,6 +188,7 @@ typedef XkbDescPtr (* PFN_XkbGetMap)(Display*,unsigned int,unsigned int);
typedef Status (* PFN_XkbGetNames)(Display*,unsigned int,XkbDescPtr);
typedef Status (* PFN_XkbGetState)(Display*,unsigned int,XkbStatePtr);
typedef KeySym (* PFN_XkbKeycodeToKeysym)(Display*,KeyCode,int,int);
typedef Bool (* PFN_XkbLookupKeysym)(Display*,KeyCode,unsigned int,unsigned int*,KeySym*);
typedef Bool (* PFN_XkbQueryExtension)(Display*,int*,int*,int*,int*,int*);
typedef Bool (* PFN_XkbSelectEventDetails)(Display*,unsigned int,unsigned int,unsigned long,unsigned long);
typedef Bool (* PFN_XkbSetDetectableAutoRepeat)(Display*,Bool,Bool*);
@ -288,6 +289,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char
#define XkbGetNames _glfw.x11.xkb.GetNames
#define XkbGetState _glfw.x11.xkb.GetState
#define XkbKeycodeToKeysym _glfw.x11.xkb.KeycodeToKeysym
#define XkbLookupKeysym _glfw.x11.xkb.LookupKeysym
#define XkbQueryExtension _glfw.x11.xkb.QueryExtension
#define XkbSelectEventDetails _glfw.x11.xkb.SelectEventDetails
#define XkbSetDetectableAutoRepeat _glfw.x11.xkb.SetDetectableAutoRepeat
@ -782,6 +784,7 @@ typedef struct _GLFWlibraryX11
PFN_XkbGetNames GetNames;
PFN_XkbGetState GetState;
PFN_XkbKeycodeToKeysym KeycodeToKeysym;
PFN_XkbLookupKeysym LookupKeysym;
PFN_XkbQueryExtension QueryExtension;
PFN_XkbSelectEventDetails SelectEventDetails;
PFN_XkbSetDetectableAutoRepeat SetDetectableAutoRepeat;
@ -945,7 +948,7 @@ void _glfwPostEmptyEventX11(void);
void _glfwGetCursorPosX11(_GLFWwindow* window, double* xpos, double* ypos);
void _glfwSetCursorPosX11(_GLFWwindow* window, double xpos, double ypos);
void _glfwSetCursorModeX11(_GLFWwindow* window, int mode);
const char* _glfwGetScancodeNameX11(int scancode);
const char* _glfwGetScancodeNameX11(int scancode, int modifiers);
int _glfwGetKeyScancodeX11(int key);
GLFWbool _glfwCreateCursorX11(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot);
GLFWbool _glfwCreateStandardCursorX11(_GLFWcursor* cursor, int shape);

View file

@ -229,10 +229,36 @@ static int translateState(int state)
mods |= GLFW_MOD_CAPS_LOCK;
if (state & Mod2Mask)
mods |= GLFW_MOD_NUM_LOCK;
if (state & Mod3Mask)
mods |= GLFW_MOD_ALT_GR;
return mods;
}
// Translates an X event modifier state mask
//
static int translateStateInverse(int mods)
{
int state = 0;
if (mods & GLFW_MOD_SHIFT)
state |= ShiftMask;
if (mods & GLFW_MOD_CONTROL)
state |= ControlMask;
if (mods & GLFW_MOD_ALT)
state |= Mod1Mask;
if (mods & GLFW_MOD_SUPER)
state |= Mod4Mask;
if (mods & GLFW_MOD_CAPS_LOCK)
state |= LockMask;
if (mods & GLFW_MOD_NUM_LOCK)
state |= Mod2Mask;
if (mods & GLFW_MOD_ALT_GR)
state |= Mod3Mask;
return state;
}
// Translates an X11 key code to a GLFW key token
//
static int translateKey(int scancode)
@ -2924,7 +2950,7 @@ void _glfwSetCursorModeX11(_GLFWwindow* window, int mode)
XFlush(_glfw.x11.display);
}
const char* _glfwGetScancodeNameX11(int scancode)
const char* _glfwGetScancodeNameX11(int scancode, int modifiers)
{
if (!_glfw.x11.xkb.available)
return NULL;
@ -2939,9 +2965,9 @@ const char* _glfwGetScancodeNameX11(int scancode)
if (key == GLFW_KEY_UNKNOWN)
return NULL;
const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display,
scancode, _glfw.x11.xkb.group, 0);
if (keysym == NoSymbol)
unsigned int state = translateStateInverse(modifiers);
KeySym keysym;
if (XkbLookupKeysym(_glfw.x11.display, scancode, state, &state, &keysym) != True)
return NULL;
const uint32_t codepoint = _glfwKeySym2UnicodeX11(keysym);

View file

@ -411,7 +411,7 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action,
}
else
{
const char* name = glfwGetKeyName(key, scancode);
const char* name = glfwGetKeyNameModifiers(key, scancode, mods);
if (name)
{
printf("%08x to %i at %0.3f: Key 0x%04x (%s) Scancode 0x%04x Name %s (with%s) was %s\n",