浏览代码

wayland: Add keyboard support

Adds libxkbcommon as a dependency when enabling the Wayland backend.
Jonas Ådahl 11 年之前
父节点
当前提交
9ac854b7bb
共有 5 个文件被更改,包括 297 次插入8 次删除
  1. 34 0
      CMake/modules/FindXKBCommon.cmake
  2. 5 0
      CMakeLists.txt
  3. 1 1
      src/CMakeLists.txt
  4. 245 7
      src/wl_init.c
  5. 12 0
      src/wl_platform.h

+ 34 - 0
CMake/modules/FindXKBCommon.cmake

@@ -0,0 +1,34 @@
+# - Try to find XKBCommon
+# Once done, this will define
+#
+#   XKBCOMMON_FOUND - System has XKBCommon
+#   XKBCOMMON_INCLUDE_DIRS - The XKBCommon include directories
+#   XKBCOMMON_LIBRARIES - The libraries needed to use XKBCommon
+#   XKBCOMMON_DEFINITIONS - Compiler switches required for using XKBCommon
+
+find_package(PkgConfig)
+pkg_check_modules(PC_XKBCOMMON QUIET xkbcommon)
+set(XKBCOMMON_DEFINITIONS ${PC_XKBCOMMON_CFLAGS_OTHER})
+
+find_path(XKBCOMMON_INCLUDE_DIR
+    NAMES xkbcommon/xkbcommon.h
+    HINTS ${PC_XKBCOMMON_INCLUDE_DIR} ${PC_XKBCOMMON_INCLUDE_DIRS}
+)
+
+find_library(XKBCOMMON_LIBRARY
+    NAMES xkbcommon
+    HINTS ${PC_XKBCOMMON_LIBRARY} ${PC_XKBCOMMON_LIBRARY_DIRS}
+)
+
+set(XKBCOMMON_LIBRARIES ${XKBCOMMON_LIBRARY})
+set(XKBCOMMON_LIBRARY_DIRS ${XKBCOMMON_LIBRARY_DIRS})
+set(XKBCOMMON_INCLUDE_DIRS ${XKBCOMMON_INCLUDE_DIR})
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(XKBCommon DEFAULT_MSG
+    XKBCOMMON_LIBRARY
+    XKBCOMMON_INCLUDE_DIR
+)
+
+mark_as_advanced(XKBCOMMON_LIBRARY XKBCOMMON_INCLUDE_DIR)
+

+ 5 - 0
CMakeLists.txt

@@ -301,6 +301,11 @@ if (_GLFW_WAYLAND)
     list(APPEND glfw_INCLUDE_DIRS ${WAYLAND_INCLUDE_DIR})
     list(APPEND glfw_LIBRARIES ${WAYLAND_LIBRARIES} -pthread)
 
+    find_package(XKBCommon REQUIRED)
+    set(GLFW_PKG_DEPS "${GLFW_PKG_DEPS} libxkbcommon")
+    list(APPEND glfw_INCLUDE_DIRS ${XKBCOMMON_INCLUDE_DIRS})
+    list(APPEND glfw_LIBRARIES ${XKBCOMMON_LIBRARY})
+
     find_library(MATH_LIBRARY m)
     mark_as_advanced(MATH_LIBRARY)
     if (MATH_LIBRARY)

+ 1 - 1
src/CMakeLists.txt

@@ -34,7 +34,7 @@ elseif (_GLFW_WAYLAND)
                      posix_time.h posix_tls.h)
     set(glfw_SOURCES ${common_SOURCES} wl_clipboard.c wl_gamma.c wl_init.c
                      wl_monitor.c wl_window.c linux_joystick.c posix_time.c
-                     posix_tls.c)
+                     posix_tls.c xkb_unicode.c xkb_unicode.h)
 endif()
 
 if (_GLFW_EGL)

+ 245 - 7
src/wl_init.c

@@ -30,10 +30,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
 #include <wayland-client.h>
 #include <wayland-client-protocol.h>
 #include <wayland-cursor.h>
 
+#include "xkb_unicode.h"
 
 static void handlePing(void* data,
                        struct wl_shell_surface* shellSurface,
@@ -119,13 +122,12 @@ static void pointerHandleButton(void* data,
      * codes. */
     glfwButton = button - BTN_LEFT;
 
-    /* TODO: modifiers */
     _glfwInputMouseClick(window,
                          glfwButton,
                          state == WL_POINTER_BUTTON_STATE_PRESSED
                                 ? GLFW_PRESS
                                 : GLFW_RELEASE,
-                         0);
+                         _glfw.wl.xkb.modifiers);
 }
 
 static void pointerHandleAxis(void* data,
@@ -174,7 +176,58 @@ static void keyboardHandleKeymap(void* data,
                                  int fd,
                                  uint32_t size)
 {
-    /* TODO */
+    struct xkb_keymap* keymap;
+    struct xkb_state* state;
+    char* mapStr;
+
+    if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
+    {
+        close(fd);
+        return;
+    }
+
+    mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+    if (mapStr == MAP_FAILED) {
+        close(fd);
+        return;
+    }
+
+    keymap = xkb_map_new_from_string(_glfw.wl.xkb.context,
+                                     mapStr,
+                                     XKB_KEYMAP_FORMAT_TEXT_V1,
+                                     0);
+    munmap(mapStr, size);
+    close(fd);
+
+    if (!keymap)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Failed to compile keymap");
+        return;
+    }
+
+    state = xkb_state_new(keymap);
+    if (!state)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Failed to create XKB state");
+        xkb_map_unref(keymap);
+        return;
+    }
+
+    xkb_keymap_unref(_glfw.wl.xkb.keymap);
+    xkb_state_unref(_glfw.wl.xkb.state);
+    _glfw.wl.xkb.keymap = keymap;
+    _glfw.wl.xkb.state = state;
+
+    _glfw.wl.xkb.control_mask =
+        1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Control");
+    _glfw.wl.xkb.alt_mask =
+        1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
+    _glfw.wl.xkb.shift_mask =
+        1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
+    _glfw.wl.xkb.super_mask =
+        1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
 }
 
 static void keyboardHandleEnter(void* data,
@@ -183,7 +236,10 @@ static void keyboardHandleEnter(void* data,
                                 struct wl_surface* surface,
                                 struct wl_array* keys)
 {
-    _glfwInputWindowFocus(wl_surface_get_user_data(surface), GL_TRUE);
+    _GLFWwindow* window = wl_surface_get_user_data(surface);
+
+    _glfw.wl.keyboardFocus = window;
+    _glfwInputWindowFocus(window, GL_TRUE);
 }
 
 static void keyboardHandleLeave(void* data,
@@ -191,7 +247,134 @@ static void keyboardHandleLeave(void* data,
                                 uint32_t serial,
                                 struct wl_surface* surface)
 {
-    _glfwInputWindowFocus(wl_surface_get_user_data(surface), GL_FALSE);
+    _GLFWwindow* window = _glfw.wl.keyboardFocus;
+
+    _glfw.wl.keyboardFocus = NULL;
+    _glfwInputWindowFocus(window, GL_FALSE);
+}
+
+static int toGLFWKeyCode(uint32_t key)
+{
+    switch (key)
+    {
+        case KEY_GRAVE:         return GLFW_KEY_GRAVE_ACCENT;
+        case KEY_1:             return GLFW_KEY_1;
+        case KEY_2:             return GLFW_KEY_2;
+        case KEY_3:             return GLFW_KEY_3;
+        case KEY_4:             return GLFW_KEY_4;
+        case KEY_5:             return GLFW_KEY_5;
+        case KEY_6:             return GLFW_KEY_6;
+        case KEY_7:             return GLFW_KEY_7;
+        case KEY_8:             return GLFW_KEY_8;
+        case KEY_9:             return GLFW_KEY_9;
+        case KEY_0:             return GLFW_KEY_0;
+        case KEY_MINUS:         return GLFW_KEY_MINUS;
+        case KEY_EQUAL:         return GLFW_KEY_EQUAL;
+        case KEY_Q:             return GLFW_KEY_Q;
+        case KEY_W:             return GLFW_KEY_W;
+        case KEY_E:             return GLFW_KEY_E;
+        case KEY_R:             return GLFW_KEY_R;
+        case KEY_T:             return GLFW_KEY_T;
+        case KEY_Y:             return GLFW_KEY_Y;
+        case KEY_U:             return GLFW_KEY_U;
+        case KEY_I:             return GLFW_KEY_I;
+        case KEY_O:             return GLFW_KEY_O;
+        case KEY_P:             return GLFW_KEY_P;
+        case KEY_LEFTBRACE:     return GLFW_KEY_LEFT_BRACKET;
+        case KEY_RIGHTBRACE:    return GLFW_KEY_RIGHT_BRACKET;
+        case KEY_A:             return GLFW_KEY_A;
+        case KEY_S:             return GLFW_KEY_S;
+        case KEY_D:             return GLFW_KEY_D;
+        case KEY_F:             return GLFW_KEY_F;
+        case KEY_G:             return GLFW_KEY_G;
+        case KEY_H:             return GLFW_KEY_H;
+        case KEY_J:             return GLFW_KEY_J;
+        case KEY_K:             return GLFW_KEY_K;
+        case KEY_L:             return GLFW_KEY_L;
+        case KEY_SEMICOLON:     return GLFW_KEY_SEMICOLON;
+        case KEY_APOSTROPHE:    return GLFW_KEY_APOSTROPHE;
+        case KEY_Z:             return GLFW_KEY_Z;
+        case KEY_X:             return GLFW_KEY_X;
+        case KEY_C:             return GLFW_KEY_C;
+        case KEY_V:             return GLFW_KEY_V;
+        case KEY_B:             return GLFW_KEY_B;
+        case KEY_N:             return GLFW_KEY_N;
+        case KEY_M:             return GLFW_KEY_M;
+        case KEY_COMMA:         return GLFW_KEY_COMMA;
+        case KEY_DOT:           return GLFW_KEY_PERIOD;
+        case KEY_SLASH:         return GLFW_KEY_SLASH;
+        case KEY_BACKSLASH:     return GLFW_KEY_BACKSLASH;
+        case KEY_ESC:           return GLFW_KEY_ESCAPE;
+        case KEY_TAB:           return GLFW_KEY_TAB;
+        case KEY_LEFTSHIFT:     return GLFW_KEY_LEFT_SHIFT;
+        case KEY_RIGHTSHIFT:    return GLFW_KEY_RIGHT_SHIFT;
+        case KEY_LEFTCTRL:      return GLFW_KEY_LEFT_CONTROL;
+        case KEY_RIGHTCTRL:     return GLFW_KEY_RIGHT_CONTROL;
+        case KEY_LEFTALT:       return GLFW_KEY_LEFT_ALT;
+        case KEY_RIGHTALT:      return GLFW_KEY_RIGHT_ALT;
+        case KEY_LEFTMETA:      return GLFW_KEY_LEFT_SUPER;
+        case KEY_RIGHTMETA:     return GLFW_KEY_RIGHT_SUPER;
+        case KEY_MENU:          return GLFW_KEY_MENU;
+        case KEY_NUMLOCK:       return GLFW_KEY_NUM_LOCK;
+        case KEY_CAPSLOCK:      return GLFW_KEY_CAPS_LOCK;
+        case KEY_PRINT:         return GLFW_KEY_PRINT_SCREEN;
+        case KEY_SCROLLLOCK:    return GLFW_KEY_SCROLL_LOCK;
+        case KEY_PAUSE:         return GLFW_KEY_PAUSE;
+        case KEY_DELETE:        return GLFW_KEY_DELETE;
+        case KEY_BACKSPACE:     return GLFW_KEY_BACKSPACE;
+        case KEY_ENTER:         return GLFW_KEY_ENTER;
+        case KEY_HOME:          return GLFW_KEY_HOME;
+        case KEY_END:           return GLFW_KEY_END;
+        case KEY_PAGEUP:        return GLFW_KEY_PAGE_UP;
+        case KEY_PAGEDOWN:      return GLFW_KEY_PAGE_DOWN;
+        case KEY_INSERT:        return GLFW_KEY_INSERT;
+        case KEY_LEFT:          return GLFW_KEY_LEFT;
+        case KEY_RIGHT:         return GLFW_KEY_RIGHT;
+        case KEY_DOWN:          return GLFW_KEY_DOWN;
+        case KEY_UP:            return GLFW_KEY_UP;
+        case KEY_F1:            return GLFW_KEY_F1;
+        case KEY_F2:            return GLFW_KEY_F2;
+        case KEY_F3:            return GLFW_KEY_F3;
+        case KEY_F4:            return GLFW_KEY_F4;
+        case KEY_F5:            return GLFW_KEY_F5;
+        case KEY_F6:            return GLFW_KEY_F6;
+        case KEY_F7:            return GLFW_KEY_F7;
+        case KEY_F8:            return GLFW_KEY_F8;
+        case KEY_F9:            return GLFW_KEY_F9;
+        case KEY_F10:           return GLFW_KEY_F10;
+        case KEY_F11:           return GLFW_KEY_F11;
+        case KEY_F12:           return GLFW_KEY_F12;
+        case KEY_F13:           return GLFW_KEY_F13;
+        case KEY_F14:           return GLFW_KEY_F14;
+        case KEY_F15:           return GLFW_KEY_F15;
+        case KEY_F16:           return GLFW_KEY_F16;
+        case KEY_F17:           return GLFW_KEY_F17;
+        case KEY_F18:           return GLFW_KEY_F18;
+        case KEY_F19:           return GLFW_KEY_F19;
+        case KEY_F20:           return GLFW_KEY_F20;
+        case KEY_F21:           return GLFW_KEY_F21;
+        case KEY_F22:           return GLFW_KEY_F22;
+        case KEY_F23:           return GLFW_KEY_F23;
+        case KEY_F24:           return GLFW_KEY_F24;
+        case KEY_KPSLASH:       return GLFW_KEY_KP_DIVIDE;
+        case KEY_KPDOT:         return GLFW_KEY_KP_MULTIPLY;
+        case KEY_KPMINUS:       return GLFW_KEY_KP_SUBTRACT;
+        case KEY_KPPLUS:        return GLFW_KEY_KP_ADD;
+        case KEY_KP0:           return GLFW_KEY_KP_0;
+        case KEY_KP1:           return GLFW_KEY_KP_1;
+        case KEY_KP2:           return GLFW_KEY_KP_2;
+        case KEY_KP3:           return GLFW_KEY_KP_3;
+        case KEY_KP4:           return GLFW_KEY_KP_4;
+        case KEY_KP5:           return GLFW_KEY_KP_5;
+        case KEY_KP6:           return GLFW_KEY_KP_6;
+        case KEY_KP7:           return GLFW_KEY_KP_7;
+        case KEY_KP8:           return GLFW_KEY_KP_8;
+        case KEY_KP9:           return GLFW_KEY_KP_9;
+        case KEY_KPCOMMA:       return GLFW_KEY_KP_DECIMAL;
+        case KEY_KPEQUAL:       return GLFW_KEY_KP_EQUAL;
+        case KEY_KPENTER:       return GLFW_KEY_KP_ENTER;
+        default:                return GLFW_KEY_UNKNOWN;
+    }
 }
 
 static void keyboardHandleKey(void* data,
@@ -201,7 +384,29 @@ static void keyboardHandleKey(void* data,
                               uint32_t key,
                               uint32_t state)
 {
-    /* TODO */
+    uint32_t code, num_syms;
+    long sym;
+    int keyCode;
+    int action;
+    const xkb_keysym_t *syms;
+    _GLFWwindow* window = _glfw.wl.keyboardFocus;
+
+    keyCode = toGLFWKeyCode(key);
+    action = state == WL_KEYBOARD_KEY_STATE_PRESSED
+            ? GLFW_PRESS : GLFW_RELEASE;
+
+    _glfwInputKey(window, keyCode, key, action,
+                  _glfw.wl.xkb.modifiers);
+
+    code = key + 8;
+    num_syms = xkb_key_get_syms(_glfw.wl.xkb.state, code, &syms);
+
+    if (num_syms == 1)
+    {
+        sym = _glfwKeySym2Unicode(syms[0]);
+        if (sym != -1)
+            _glfwInputChar(window, sym);
+    }
 }
 
 static void keyboardHandleModifiers(void* data,
@@ -212,7 +417,32 @@ static void keyboardHandleModifiers(void* data,
                                     uint32_t modsLocked,
                                     uint32_t group)
 {
-    /* TODO */
+    xkb_mod_mask_t mask;
+    unsigned int modifiers = 0;
+
+    if (!_glfw.wl.xkb.keymap)
+        return;
+
+    xkb_state_update_mask(_glfw.wl.xkb.state,
+                          modsDepressed,
+                          modsLatched,
+                          modsLocked,
+                          0,
+                          0,
+                          group);
+
+    mask = xkb_state_serialize_mods(_glfw.wl.xkb.state,
+                                    XKB_STATE_DEPRESSED |
+                                    XKB_STATE_LATCHED);
+    if (mask & _glfw.wl.xkb.control_mask)
+        modifiers |= GLFW_MOD_CONTROL;
+    if (mask & _glfw.wl.xkb.alt_mask)
+        modifiers |= GLFW_MOD_ALT;
+    if (mask & _glfw.wl.xkb.shift_mask)
+        modifiers |= GLFW_MOD_SHIFT;
+    if (mask & _glfw.wl.xkb.super_mask)
+        modifiers |= GLFW_MOD_SUPER;
+    _glfw.wl.xkb.modifiers = modifiers;
 }
 
 static const struct wl_keyboard_listener keyboardListener = {
@@ -318,6 +548,14 @@ int _glfwPlatformInit(void)
     _glfw.wl.monitors = calloc(4, sizeof(_GLFWmonitor*));
     _glfw.wl.monitorsSize = 4;
 
+    _glfw.wl.xkb.context = xkb_context_new(0);
+    if (!_glfw.wl.xkb.context)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Failed to initialize xkb context");
+        return GL_FALSE;
+    }
+
     // Sync so we got all registry objects
     wl_display_roundtrip(_glfw.wl.display);
 

+ 12 - 0
src/wl_platform.h

@@ -29,6 +29,7 @@
 
 
 #include <wayland-client.h>
+#include <xkbcommon/xkbcommon.h>
 
 #if defined(_GLFW_EGL)
  #include "egl_context.h"
@@ -76,6 +77,17 @@ typedef struct _GLFWlibraryWayland
     int                         monitorsCount;
     int                         monitorsSize;
 
+    struct {
+        struct xkb_context*     context;
+        struct xkb_keymap*      keymap;
+        struct xkb_state*       state;
+        xkb_mod_mask_t          control_mask;
+        xkb_mod_mask_t          alt_mask;
+        xkb_mod_mask_t          shift_mask;
+        xkb_mod_mask_t          super_mask;
+        unsigned int            modifiers;
+    } xkb;
+
     _GLFWwindow*                pointerFocus;
     _GLFWwindow*                keyboardFocus;
 } _GLFWlibraryWayland;