From b889aa784129b3e7d1219a55a1008bd6518183db Mon Sep 17 00:00:00 2001 From: Andrew Corrigan Date: Thu, 24 Apr 2014 01:10:58 -0400 Subject: [PATCH] Automatically detect Xkb at run-time. Uses XGetKeyboardMapping as a fallback. Fixes #282. --- README.md | 1 + src/x11_init.c | 257 ++++++++++++++++++++++++--------------------- src/x11_platform.h | 1 + 3 files changed, 139 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 7fd42ac5..9d1c9ffc 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ The following dependencies are needed by the examples and test programs: the error callback - [Win32] Bugfix: Some keys were reported based on the current layout instead of their physical location + - [X11] Added run-time support for systems lacking the XKB extension - [X11] Made GLX 1.3 the minimum supported version - [X11] Bugfix: The case of finding no usable CRTCs was not detected - [X11] Bugfix: Detection of broken Nvidia RandR gamma support did not verify diff --git a/src/x11_init.c b/src/x11_init.c index ab07fe32..89da8f85 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -40,39 +40,55 @@ static int translateKey(int keyCode) { int keySym; + int keysyms_per_keycode_return; + KeySym *keysyms; // Valid key code range is [8,255], according to the XLib manual if (keyCode < 8 || keyCode > 255) return GLFW_KEY_UNKNOWN; - // Try secondary keysym, for numeric keypad keys - // Note: This way we always force "NumLock = ON", which is intentional - // since the returned key code should correspond to a physical - // location. - keySym = XkbKeycodeToKeysym(_glfw.x11.display, keyCode, 0, 1); - switch (keySym) + if(_glfw.x11.xkb.available) { - case XK_KP_0: return GLFW_KEY_KP_0; - case XK_KP_1: return GLFW_KEY_KP_1; - case XK_KP_2: return GLFW_KEY_KP_2; - case XK_KP_3: return GLFW_KEY_KP_3; - case XK_KP_4: return GLFW_KEY_KP_4; - case XK_KP_5: return GLFW_KEY_KP_5; - case XK_KP_6: return GLFW_KEY_KP_6; - case XK_KP_7: return GLFW_KEY_KP_7; - case XK_KP_8: return GLFW_KEY_KP_8; - case XK_KP_9: return GLFW_KEY_KP_9; - case XK_KP_Separator: - case XK_KP_Decimal: return GLFW_KEY_KP_DECIMAL; - case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; - case XK_KP_Enter: return GLFW_KEY_KP_ENTER; - default: break; + // Try secondary keysym, for numeric keypad keys + // Note: This way we always force "NumLock = ON", which is intentional + // since the returned key code should correspond to a physical + // location. + keySym = XkbKeycodeToKeysym(_glfw.x11.display, keyCode, 0, 1); + switch (keySym) + { + case XK_KP_0: return GLFW_KEY_KP_0; + case XK_KP_1: return GLFW_KEY_KP_1; + case XK_KP_2: return GLFW_KEY_KP_2; + case XK_KP_3: return GLFW_KEY_KP_3; + case XK_KP_4: return GLFW_KEY_KP_4; + case XK_KP_5: return GLFW_KEY_KP_5; + case XK_KP_6: return GLFW_KEY_KP_6; + case XK_KP_7: return GLFW_KEY_KP_7; + case XK_KP_8: return GLFW_KEY_KP_8; + case XK_KP_9: return GLFW_KEY_KP_9; + case XK_KP_Separator: + case XK_KP_Decimal: return GLFW_KEY_KP_DECIMAL; + case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; + case XK_KP_Enter: return GLFW_KEY_KP_ENTER; + 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). + keySym = XkbKeycodeToKeysym(_glfw.x11.display, keyCode, 0, 0); + } + else + { + keysyms = + XGetKeyboardMapping(_glfw.x11.display, + keyCode, + 1, + &keysyms_per_keycode_return); + keySym = keysyms[0]; + XFree(keysyms); } - // 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). - keySym = XkbKeycodeToKeysym(_glfw.x11.display, keyCode, 0, 0); switch (keySym) { case XK_Escape: return GLFW_KEY_ESCAPE; @@ -217,7 +233,8 @@ static int translateKey(int keyCode) // static void updateKeyCodeLUT(void) { - int i, keyCode, keyCodeGLFW; + int keyCode; + int keyCodeGLFW, i; char name[XkbKeyNameLength + 1]; XkbDescPtr descr; @@ -225,85 +242,88 @@ static void updateKeyCodeLUT(void) for (keyCode = 0; keyCode < 256; keyCode++) _glfw.x11.keyCodeLUT[keyCode] = GLFW_KEY_UNKNOWN; - // Use XKB to determine physical key locations independently of the current - // keyboard layout - - // Get keyboard description - descr = XkbGetKeyboard(_glfw.x11.display, - XkbAllComponentsMask, - XkbUseCoreKbd); - - // Find the X11 key code -> GLFW key code mapping - for (keyCode = descr->min_key_code; keyCode <= descr->max_key_code; ++keyCode) + if(_glfw.x11.xkb.available) { - // Get the key name - for (i = 0; i < XkbKeyNameLength; i++) - name[i] = descr->names->keys[keyCode].name[i]; + // Use XKB to determine physical key locations independently of the current + // keyboard layout - name[XkbKeyNameLength] = 0; + // Get keyboard description + descr = XkbGetKeyboard(_glfw.x11.display, + XkbAllComponentsMask, + XkbUseCoreKbd); - // Map the key name to a GLFW key code. Note: We only map printable - // keys here, and we use the US keyboard layout. The rest of the - // keys (function keys) are mapped using traditional KeySym - // translations. - if (strcmp(name, "TLDE") == 0) keyCodeGLFW = GLFW_KEY_GRAVE_ACCENT; - else if (strcmp(name, "AE01") == 0) keyCodeGLFW = GLFW_KEY_1; - else if (strcmp(name, "AE02") == 0) keyCodeGLFW = GLFW_KEY_2; - else if (strcmp(name, "AE03") == 0) keyCodeGLFW = GLFW_KEY_3; - else if (strcmp(name, "AE04") == 0) keyCodeGLFW = GLFW_KEY_4; - else if (strcmp(name, "AE05") == 0) keyCodeGLFW = GLFW_KEY_5; - else if (strcmp(name, "AE06") == 0) keyCodeGLFW = GLFW_KEY_6; - else if (strcmp(name, "AE07") == 0) keyCodeGLFW = GLFW_KEY_7; - else if (strcmp(name, "AE08") == 0) keyCodeGLFW = GLFW_KEY_8; - else if (strcmp(name, "AE09") == 0) keyCodeGLFW = GLFW_KEY_9; - else if (strcmp(name, "AE10") == 0) keyCodeGLFW = GLFW_KEY_0; - else if (strcmp(name, "AE11") == 0) keyCodeGLFW = GLFW_KEY_MINUS; - else if (strcmp(name, "AE12") == 0) keyCodeGLFW = GLFW_KEY_EQUAL; - else if (strcmp(name, "AD01") == 0) keyCodeGLFW = GLFW_KEY_Q; - else if (strcmp(name, "AD02") == 0) keyCodeGLFW = GLFW_KEY_W; - else if (strcmp(name, "AD03") == 0) keyCodeGLFW = GLFW_KEY_E; - else if (strcmp(name, "AD04") == 0) keyCodeGLFW = GLFW_KEY_R; - else if (strcmp(name, "AD05") == 0) keyCodeGLFW = GLFW_KEY_T; - else if (strcmp(name, "AD06") == 0) keyCodeGLFW = GLFW_KEY_Y; - else if (strcmp(name, "AD07") == 0) keyCodeGLFW = GLFW_KEY_U; - else if (strcmp(name, "AD08") == 0) keyCodeGLFW = GLFW_KEY_I; - else if (strcmp(name, "AD09") == 0) keyCodeGLFW = GLFW_KEY_O; - else if (strcmp(name, "AD10") == 0) keyCodeGLFW = GLFW_KEY_P; - else if (strcmp(name, "AD11") == 0) keyCodeGLFW = GLFW_KEY_LEFT_BRACKET; - else if (strcmp(name, "AD12") == 0) keyCodeGLFW = GLFW_KEY_RIGHT_BRACKET; - else if (strcmp(name, "AC01") == 0) keyCodeGLFW = GLFW_KEY_A; - else if (strcmp(name, "AC02") == 0) keyCodeGLFW = GLFW_KEY_S; - else if (strcmp(name, "AC03") == 0) keyCodeGLFW = GLFW_KEY_D; - else if (strcmp(name, "AC04") == 0) keyCodeGLFW = GLFW_KEY_F; - else if (strcmp(name, "AC05") == 0) keyCodeGLFW = GLFW_KEY_G; - else if (strcmp(name, "AC06") == 0) keyCodeGLFW = GLFW_KEY_H; - else if (strcmp(name, "AC07") == 0) keyCodeGLFW = GLFW_KEY_J; - else if (strcmp(name, "AC08") == 0) keyCodeGLFW = GLFW_KEY_K; - else if (strcmp(name, "AC09") == 0) keyCodeGLFW = GLFW_KEY_L; - else if (strcmp(name, "AC10") == 0) keyCodeGLFW = GLFW_KEY_SEMICOLON; - else if (strcmp(name, "AC11") == 0) keyCodeGLFW = GLFW_KEY_APOSTROPHE; - else if (strcmp(name, "AB01") == 0) keyCodeGLFW = GLFW_KEY_Z; - else if (strcmp(name, "AB02") == 0) keyCodeGLFW = GLFW_KEY_X; - else if (strcmp(name, "AB03") == 0) keyCodeGLFW = GLFW_KEY_C; - else if (strcmp(name, "AB04") == 0) keyCodeGLFW = GLFW_KEY_V; - else if (strcmp(name, "AB05") == 0) keyCodeGLFW = GLFW_KEY_B; - else if (strcmp(name, "AB06") == 0) keyCodeGLFW = GLFW_KEY_N; - else if (strcmp(name, "AB07") == 0) keyCodeGLFW = GLFW_KEY_M; - else if (strcmp(name, "AB08") == 0) keyCodeGLFW = GLFW_KEY_COMMA; - else if (strcmp(name, "AB09") == 0) keyCodeGLFW = GLFW_KEY_PERIOD; - else if (strcmp(name, "AB10") == 0) keyCodeGLFW = GLFW_KEY_SLASH; - else if (strcmp(name, "BKSL") == 0) keyCodeGLFW = GLFW_KEY_BACKSLASH; - else if (strcmp(name, "LSGT") == 0) keyCodeGLFW = GLFW_KEY_WORLD_1; - else keyCodeGLFW = GLFW_KEY_UNKNOWN; + // Find the X11 key code -> GLFW key code mapping + for (keyCode = descr->min_key_code; keyCode <= descr->max_key_code; ++keyCode) + { + // Get the key name + for (i = 0; i < XkbKeyNameLength; i++) + name[i] = descr->names->keys[keyCode].name[i]; - // Update the key code LUT - if ((keyCode >= 0) && (keyCode < 256)) - _glfw.x11.keyCodeLUT[keyCode] = keyCodeGLFW; + name[XkbKeyNameLength] = 0; + + // Map the key name to a GLFW key code. Note: We only map printable + // keys here, and we use the US keyboard layout. The rest of the + // keys (function keys) are mapped using traditional KeySym + // translations. + if (strcmp(name, "TLDE") == 0) keyCodeGLFW = GLFW_KEY_GRAVE_ACCENT; + else if (strcmp(name, "AE01") == 0) keyCodeGLFW = GLFW_KEY_1; + else if (strcmp(name, "AE02") == 0) keyCodeGLFW = GLFW_KEY_2; + else if (strcmp(name, "AE03") == 0) keyCodeGLFW = GLFW_KEY_3; + else if (strcmp(name, "AE04") == 0) keyCodeGLFW = GLFW_KEY_4; + else if (strcmp(name, "AE05") == 0) keyCodeGLFW = GLFW_KEY_5; + else if (strcmp(name, "AE06") == 0) keyCodeGLFW = GLFW_KEY_6; + else if (strcmp(name, "AE07") == 0) keyCodeGLFW = GLFW_KEY_7; + else if (strcmp(name, "AE08") == 0) keyCodeGLFW = GLFW_KEY_8; + else if (strcmp(name, "AE09") == 0) keyCodeGLFW = GLFW_KEY_9; + else if (strcmp(name, "AE10") == 0) keyCodeGLFW = GLFW_KEY_0; + else if (strcmp(name, "AE11") == 0) keyCodeGLFW = GLFW_KEY_MINUS; + else if (strcmp(name, "AE12") == 0) keyCodeGLFW = GLFW_KEY_EQUAL; + else if (strcmp(name, "AD01") == 0) keyCodeGLFW = GLFW_KEY_Q; + else if (strcmp(name, "AD02") == 0) keyCodeGLFW = GLFW_KEY_W; + else if (strcmp(name, "AD03") == 0) keyCodeGLFW = GLFW_KEY_E; + else if (strcmp(name, "AD04") == 0) keyCodeGLFW = GLFW_KEY_R; + else if (strcmp(name, "AD05") == 0) keyCodeGLFW = GLFW_KEY_T; + else if (strcmp(name, "AD06") == 0) keyCodeGLFW = GLFW_KEY_Y; + else if (strcmp(name, "AD07") == 0) keyCodeGLFW = GLFW_KEY_U; + else if (strcmp(name, "AD08") == 0) keyCodeGLFW = GLFW_KEY_I; + else if (strcmp(name, "AD09") == 0) keyCodeGLFW = GLFW_KEY_O; + else if (strcmp(name, "AD10") == 0) keyCodeGLFW = GLFW_KEY_P; + else if (strcmp(name, "AD11") == 0) keyCodeGLFW = GLFW_KEY_LEFT_BRACKET; + else if (strcmp(name, "AD12") == 0) keyCodeGLFW = GLFW_KEY_RIGHT_BRACKET; + else if (strcmp(name, "AC01") == 0) keyCodeGLFW = GLFW_KEY_A; + else if (strcmp(name, "AC02") == 0) keyCodeGLFW = GLFW_KEY_S; + else if (strcmp(name, "AC03") == 0) keyCodeGLFW = GLFW_KEY_D; + else if (strcmp(name, "AC04") == 0) keyCodeGLFW = GLFW_KEY_F; + else if (strcmp(name, "AC05") == 0) keyCodeGLFW = GLFW_KEY_G; + else if (strcmp(name, "AC06") == 0) keyCodeGLFW = GLFW_KEY_H; + else if (strcmp(name, "AC07") == 0) keyCodeGLFW = GLFW_KEY_J; + else if (strcmp(name, "AC08") == 0) keyCodeGLFW = GLFW_KEY_K; + else if (strcmp(name, "AC09") == 0) keyCodeGLFW = GLFW_KEY_L; + else if (strcmp(name, "AC10") == 0) keyCodeGLFW = GLFW_KEY_SEMICOLON; + else if (strcmp(name, "AC11") == 0) keyCodeGLFW = GLFW_KEY_APOSTROPHE; + else if (strcmp(name, "AB01") == 0) keyCodeGLFW = GLFW_KEY_Z; + else if (strcmp(name, "AB02") == 0) keyCodeGLFW = GLFW_KEY_X; + else if (strcmp(name, "AB03") == 0) keyCodeGLFW = GLFW_KEY_C; + else if (strcmp(name, "AB04") == 0) keyCodeGLFW = GLFW_KEY_V; + else if (strcmp(name, "AB05") == 0) keyCodeGLFW = GLFW_KEY_B; + else if (strcmp(name, "AB06") == 0) keyCodeGLFW = GLFW_KEY_N; + else if (strcmp(name, "AB07") == 0) keyCodeGLFW = GLFW_KEY_M; + else if (strcmp(name, "AB08") == 0) keyCodeGLFW = GLFW_KEY_COMMA; + else if (strcmp(name, "AB09") == 0) keyCodeGLFW = GLFW_KEY_PERIOD; + else if (strcmp(name, "AB10") == 0) keyCodeGLFW = GLFW_KEY_SLASH; + else if (strcmp(name, "BKSL") == 0) keyCodeGLFW = GLFW_KEY_BACKSLASH; + else if (strcmp(name, "LSGT") == 0) keyCodeGLFW = GLFW_KEY_WORLD_1; + else keyCodeGLFW = GLFW_KEY_UNKNOWN; + + // Update the key code LUT + if ((keyCode >= 0) && (keyCode < 256)) + _glfw.x11.keyCodeLUT[keyCode] = keyCodeGLFW; + } + + // Free the keyboard description + XkbFreeKeyboard(descr, 0, True); } - // Free the keyboard description - XkbFreeKeyboard(descr, 0, True); - // Translate the un-translated key codes using traditional X11 KeySym // lookups for (keyCode = 0; keyCode < 256; keyCode++) @@ -496,30 +516,27 @@ static GLboolean initExtensions(void) // Check if Xkb is supported on this display _glfw.x11.xkb.versionMajor = 1; _glfw.x11.xkb.versionMinor = 0; - if (!XkbQueryExtension(_glfw.x11.display, - &_glfw.x11.xkb.majorOpcode, - &_glfw.x11.xkb.eventBase, - &_glfw.x11.xkb.errorBase, - &_glfw.x11.xkb.versionMajor, - &_glfw.x11.xkb.versionMinor)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: The keyboard extension is not available"); - return GL_FALSE; - } + _glfw.x11.xkb.available = + XkbQueryExtension(_glfw.x11.display, + &_glfw.x11.xkb.majorOpcode, + &_glfw.x11.xkb.eventBase, + &_glfw.x11.xkb.errorBase, + &_glfw.x11.xkb.versionMajor, + &_glfw.x11.xkb.versionMinor); - if (!XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported)) + if(_glfw.x11.xkb.available) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to set detectable key repeat"); - return GL_FALSE; - } + if (!XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported)) + { + // X11: Failed to set detectable key repeat + _glfw.x11.xkb.available = GL_FALSE; + } - if (!supported) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Detectable key repeat is not supported"); - return GL_FALSE; + if (!supported) + { + // X11: Detectable key repeat is not supported + _glfw.x11.xkb.available = GL_FALSE; + } } // Update the key code LUT diff --git a/src/x11_platform.h b/src/x11_platform.h index 785b2ead..5d090347 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -175,6 +175,7 @@ typedef struct _GLFWlibraryX11 } randr; struct { + GLboolean available; int majorOpcode; int eventBase; int errorBase;