浏览代码

Automatically detect Xkb at run-time.

Uses XGetKeyboardMapping as a fallback.  Fixes #282.
Andrew Corrigan 11 年之前
父节点
当前提交
b889aa7841
共有 3 个文件被更改,包括 142 次插入123 次删除
  1. 1 0
      README.md
  2. 140 123
      src/x11_init.c
  3. 1 0
      src/x11_platform.h

+ 1 - 0
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

+ 140 - 123
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,84 +242,87 @@ 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
+    if(_glfw.x11.xkb.available)
+    {
+        // Use XKB to determine physical key locations independently of the current
+        // keyboard layout
 
-    // Get keyboard description
-    descr = XkbGetKeyboard(_glfw.x11.display,
-                            XkbAllComponentsMask,
-                            XkbUseCoreKbd);
+        // 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)
-    {
-        // Get the key name
-        for (i = 0;  i < XkbKeyNameLength;  i++)
-            name[i] = descr->names->keys[keyCode].name[i];
-
-        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;
-    }
+        // 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];
+
+            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
@@ -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))
+    _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(_glfw.x11.xkb.available)
     {
-        _glfwInputError(GLFW_PLATFORM_ERROR,
-                        "X11: The keyboard extension is not available");
-        return GL_FALSE;
-    }
-
-    if (!XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported))
-    {
-        _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

+ 1 - 0
src/x11_platform.h

@@ -175,6 +175,7 @@ typedef struct _GLFWlibraryX11
     } randr;
 
     struct {
+        GLboolean   available;
         int         majorOpcode;
         int         eventBase;
         int         errorBase;