Add ds_keyboard 66/278166/1
authorSeunghun Lee <shiin.lee@samsung.com>
Mon, 18 Apr 2022 01:29:23 +0000 (10:29 +0900)
committerSooChan Lim <sc1.lim@samsung.com>
Mon, 18 Jul 2022 05:58:26 +0000 (14:58 +0900)
A ds_keyboard is for abstracting phisical keyboard device.

Change-Id: I97b3b02d37abd3adefb9da0b899a54bb58919784

include/libds/input_device.h
include/libds/interfaces/input_device.h
include/libds/interfaces/keyboard.h [new file with mode: 0644]
include/libds/keyboard.h [new file with mode: 0644]
packaging/libds.spec
src/libds/input_device.c
src/libds/keyboard.c [new file with mode: 0644]
src/libds/meson.build
src/libds/util.h
src/libds/util/shm.c

index 45e125b..6143e00 100644 (file)
@@ -5,6 +5,8 @@ struct ds_input_device;
 
 struct ds_pointer;
 
+struct ds_keyboard;
+
 enum ds_button_state
 {
     DS_BUTTON_RELEASED,
@@ -24,6 +26,9 @@ ds_input_device_get_type(struct ds_input_device *dev);
 struct ds_pointer *
 ds_input_device_get_pointer(struct ds_input_device *dev);
 
+struct ds_keyboard *
+ds_input_device_get_keyboard(struct ds_input_device *dev);
+
 void
 ds_input_device_add_destroy_listener(struct ds_input_device *dev,
         struct wl_listener *listener);
index f31594b..7828503 100644 (file)
@@ -4,6 +4,7 @@
 #include <wayland-server.h>
 #include <libds/input_device.h>
 #include <libds/pointer.h>
+#include <libds/keyboard.h>
 
 struct ds_input_device_interface
 {
@@ -22,6 +23,7 @@ struct ds_input_device
     union {
         void *_device;
         struct ds_pointer *pointer;
+        struct ds_keyboard *keyboard;
     };
 
     struct {
diff --git a/include/libds/interfaces/keyboard.h b/include/libds/interfaces/keyboard.h
new file mode 100644 (file)
index 0000000..f68c9a7
--- /dev/null
@@ -0,0 +1,89 @@
+#ifndef LIBDS_INTERFACES_KEYBOARD_H
+#define LIBDS_INTERFACES_KEYBOARD_H
+
+#include <wayland-server.h>
+#include <xkbcommon/xkbcommon.h>
+
+#include <libds/keyboard.h>
+
+#define DS_LED_COUNT 3
+
+enum ds_keyboard_led {
+    DS_LED_NUM_LOCK = 1 << 0,
+    DS_LED_CAPS_LOCK = 1 << 1,
+    DS_LED_SCROLL_LOCK = 1 << 2,
+};
+
+#define DS_MODIFIER_COUNT 8
+
+enum ds_keyboard_modifier {
+    DS_MODIFIER_SHIFT = 1 << 0,
+    DS_MODIFIER_CAPS = 1 << 1,
+    DS_MODIFIER_CTRL = 1 << 2,
+    DS_MODIFIER_ALT = 1 << 3,
+    DS_MODIFIER_MOD2 = 1 << 4,
+    DS_MODIFIER_MOD3 = 1 << 5,
+    DS_MODIFIER_LOGO = 1 << 6,
+    DS_MODIFIER_MOD5 = 1 << 7,
+};
+
+#define DS_KEYBOARD_KEYS_CAP 32
+
+struct ds_keyboard;
+
+struct ds_keyboard_interface
+{
+    void (*destroy)(struct ds_keyboard *keyboard);
+};
+
+struct ds_keyboard_modifiers
+{
+    xkb_mod_mask_t depressed;
+    xkb_mod_mask_t latched;
+    xkb_mod_mask_t locked;
+    xkb_mod_mask_t group;
+};
+
+struct ds_keyboard
+{
+    const struct ds_keyboard_interface *iface;
+
+    char *keymap_string;
+    size_t keymap_size;
+    int keymap_fd;
+    struct xkb_keymap *keymap;
+    struct xkb_state *xkb_state;
+    xkb_led_index_t led_indexes[DS_LED_COUNT];
+    xkb_mod_index_t mod_indexes[DS_MODIFIER_COUNT];
+
+    uint32_t keycodes[DS_KEYBOARD_KEYS_CAP];
+    size_t num_keycodes;
+    struct ds_keyboard_modifiers modifiers;
+
+    struct {
+        int32_t rate;
+        int32_t delay;
+    } repeat_info;
+
+    struct {
+        struct wl_signal destroy;
+        struct wl_signal key;
+        struct wl_signal modifiers;
+        struct wl_signal keymap;
+        struct wl_signal repeat_info;
+    } events;
+};
+
+void ds_keyboard_init(struct ds_keyboard *keyboard,
+        const struct ds_keyboard_interface *iface);
+
+void ds_keyboard_destroy(struct ds_keyboard *keyboard);
+
+void ds_keyboard_notify_key(struct ds_keyboard *keyboard,
+        struct ds_event_keyboard_key *event);
+
+void ds_keyboard_notify_modifiers(struct ds_keyboard *keyboard,
+        uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
+        uint32_t group);
+
+#endif
diff --git a/include/libds/keyboard.h b/include/libds/keyboard.h
new file mode 100644 (file)
index 0000000..e226cb4
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef LIBDS_KEYBOARD_H
+#define LIBDS_KEYBOARD_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <wayland-server.h>
+#include <xkbcommon/xkbcommon.h>
+
+struct ds_keyboard;
+
+struct ds_event_keyboard_key
+{
+    uint32_t time_msec;
+    uint32_t keycode;
+    bool update_state;
+    enum wl_keyboard_key_state state;
+};
+
+bool ds_keyboard_set_keymap(struct ds_keyboard *keyboard,
+        struct xkb_keymap *keymap);
+
+void ds_keyboard_set_repeat_info(struct ds_keyboard *keyboard,
+        int32_t rate, int32_t delay);
+
+uint32_t ds_keyboard_get_modifiers(struct ds_keyboard *keyboard);
+
+struct xkb_state *ds_keyboard_get_xkb_state(struct ds_keyboard *keyboard);
+
+void ds_keyboard_add_destroy_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener);
+
+void ds_keyboard_add_key_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener);
+
+void ds_keyboard_add_modifiers_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener);
+
+void ds_keyboard_add_keymap_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener);
+
+void ds_keyboard_add_repeat_info_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener);
+
+#endif
index 879a87d..806c75b 100644 (file)
@@ -13,6 +13,7 @@ BuildRequires:  pkgconfig(wayland-client)
 BuildRequires:  pkgconfig(wayland-protocols)
 BuildRequires:  pkgconfig(pixman-1)
 BuildRequires:  pkgconfig(libdrm)
+BuildRequires:  pkgconfig(xkbcommon)
 
 BuildRequires:  pkgconfig(libtdm)
 BuildRequires:  pkgconfig(libtbm)
index db84100..351361f 100644 (file)
@@ -6,6 +6,7 @@
 #include "libds/log.h"
 #include "libds/interfaces/input_device.h"
 #include "libds/interfaces/pointer.h"
+#include "libds/interfaces/keyboard.h"
 
 WL_EXPORT enum ds_input_device_type
 ds_input_device_get_type(struct ds_input_device *dev)
@@ -24,6 +25,17 @@ ds_input_device_get_pointer(struct ds_input_device *dev)
     return dev->pointer;
 }
 
+WL_EXPORT struct ds_keyboard *
+ds_input_device_get_keyboard(struct ds_input_device *dev)
+{
+    if (dev->type != DS_INPUT_DEVICE_KEYBOARD) {
+        ds_err("Given ds_input_device is not a keyboard device");
+        return NULL;
+    }
+
+    return dev->keyboard;
+}
+
 WL_EXPORT void
 ds_input_device_add_destroy_listener(struct ds_input_device *dev,
         struct wl_listener *listener)
@@ -54,6 +66,9 @@ ds_input_device_destroy(struct ds_input_device *dev)
             case DS_INPUT_DEVICE_POINTER:
                 ds_pointer_destroy(dev->pointer);
                 break;
+            case DS_INPUT_DEVICE_KEYBOARD:
+                ds_keyboard_destroy(dev->keyboard);
+                break;
             default:
                 ds_err("Warning: leaking memory %p %p %d",
                         dev->_device, dev, dev->type);
diff --git a/src/libds/keyboard.c b/src/libds/keyboard.c
new file mode 100644 (file)
index 0000000..d122e81
--- /dev/null
@@ -0,0 +1,304 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <wayland-server.h>
+
+#include "libds/log.h"
+#include "libds/interfaces/keyboard.h"
+#include "util.h"
+
+static bool keyboard_modifier_update(struct ds_keyboard *keyboard);
+static void keyboard_key_update(struct ds_keyboard *keyboard,
+        struct ds_event_keyboard_key *event);
+static void keyboard_led_update(struct ds_keyboard *keyboard);
+
+WL_EXPORT bool
+ds_keyboard_set_keymap(struct ds_keyboard *keyboard, struct xkb_keymap *keymap)
+{
+    char *tmp_keymap_string;
+    void *dst;
+    int rw_fd, ro_fd;
+    xkb_keycode_t keycode;
+    const char *led_names[DS_LED_COUNT] = {
+        XKB_LED_NAME_NUM,
+        XKB_LED_NAME_CAPS,
+        XKB_LED_NAME_SCROLL,
+    };
+    const char *mod_names[DS_MODIFIER_COUNT] = {
+        XKB_MOD_NAME_SHIFT,
+        XKB_MOD_NAME_CAPS,
+        XKB_MOD_NAME_CTRL,
+        XKB_MOD_NAME_ALT,
+        XKB_MOD_NAME_NUM,
+        "Mod3",
+        XKB_MOD_NAME_LOGO,
+        "Mod5",
+    };
+
+    if (keyboard->keymap)
+        xkb_keymap_unref(keyboard->keymap);
+
+    keyboard->keymap = xkb_keymap_ref(keymap);
+
+    if (keyboard->xkb_state)
+        xkb_state_unref(keyboard->xkb_state);
+
+    keyboard->xkb_state = xkb_state_new(keyboard->keymap);
+    if (!keyboard->xkb_state) {
+        ds_err("Failed to create XKB state");
+        goto err_state;
+    }
+
+    for (size_t i = 0; i < DS_LED_COUNT; i++) {
+        keyboard->led_indexes[i] =
+            xkb_map_led_get_index(keyboard->keymap, led_names[i]);
+    }
+
+    for (size_t i = 0; i < DS_MODIFIER_COUNT; i++) {
+        keyboard->mod_indexes[i] =
+            xkb_map_mod_get_index(keyboard->keymap, mod_names[i]);
+    }
+
+    tmp_keymap_string = xkb_keymap_get_as_string(keyboard->keymap,
+            XKB_KEYMAP_FORMAT_TEXT_V1);
+    if (!tmp_keymap_string) {
+        ds_err("Failed to get string version of keymap");
+        goto err_keymap_string;
+    }
+
+    if (keyboard->keymap_string)
+        free(keyboard->keymap_string);
+    keyboard->keymap_string = tmp_keymap_string;
+    keyboard->keymap_size = strlen(keyboard->keymap_string) + 1;
+
+    if (!allocate_shm_file_pair(keyboard->keymap_size, &rw_fd, &ro_fd)) {
+        ds_err("Failed to allocate shm_file for keymap");
+        goto err_shm_file;
+    }
+
+    dst = mmap(NULL, keyboard->keymap_size, PROT_READ | PROT_WRITE,
+            MAP_SHARED, rw_fd, 0);
+    if (dst == MAP_FAILED) {
+        ds_log_errno(DS_ERR, "mmap failed");
+        goto err_mmap;
+    }
+
+    memcpy(dst, keyboard->keymap_string, keyboard->keymap_size);
+    munmap(dst, keyboard->keymap_size);
+    close(rw_fd);
+
+    if (keyboard->keymap_fd >= 0)
+        close(keyboard->keymap_fd);
+    keyboard->keymap_fd = ro_fd;
+
+    for (size_t i = 0; i < keyboard->num_keycodes; i++) {
+        keycode = keyboard->keycodes[i] + 8;
+        xkb_state_update_key(keyboard->xkb_state, keycode, XKB_KEY_DOWN);
+    }
+
+    keyboard_modifier_update(keyboard);
+
+    wl_signal_emit(&keyboard->events.keymap, keyboard);
+
+    return true;
+
+err_mmap:
+    close(rw_fd);
+    close(ro_fd);
+err_shm_file:
+    free(keyboard->keymap_string);
+    keyboard->keymap_string = NULL;
+err_keymap_string:
+    xkb_state_unref(keyboard->xkb_state);
+    keyboard->xkb_state = NULL;
+err_state:
+    xkb_keymap_unref(keymap);
+    keyboard->keymap = NULL;
+
+    return false;
+}
+
+WL_EXPORT void
+ds_keyboard_set_repeat_info(struct ds_keyboard *keyboard,
+        int32_t rate, int32_t delay)
+{
+    if (keyboard->repeat_info.rate == rate &&
+            keyboard->repeat_info.delay == delay)
+        return;
+    keyboard->repeat_info.rate = rate;
+    keyboard->repeat_info.delay = delay;
+
+    wl_signal_emit(&keyboard->events.repeat_info, keyboard);
+}
+
+WL_EXPORT uint32_t
+ds_keyboard_get_modifiers(struct ds_keyboard *keyboard)
+{
+    xkb_mod_mask_t mask;
+    uint32_t modifiers = 0;
+
+    mask = keyboard->modifiers.depressed | keyboard->modifiers.latched;
+    for (size_t i = 0; i < DS_MODIFIER_COUNT; i++) {
+        if (keyboard->mod_indexes[i] != XKB_MOD_INVALID &&
+                (mask & (1 << keyboard->mod_indexes[i])))
+            modifiers |= (1 << i);
+    }
+
+    return modifiers;
+}
+
+WL_EXPORT struct xkb_state *
+ds_keyboard_get_xkb_state(struct ds_keyboard *keyboard)
+{
+    return keyboard->xkb_state;
+}
+
+WL_EXPORT void
+ds_keyboard_notify_key(struct ds_keyboard *keyboard,
+        struct ds_event_keyboard_key *event)
+{
+    uint32_t keycode;
+    enum xkb_key_direction dir;
+    bool updated;
+
+    keyboard_key_update(keyboard, event);
+
+    wl_signal_emit(&keyboard->events.key, event);
+
+    if (!keyboard->xkb_state)
+        return;
+
+    if (event->update_state) {
+        keycode = event->keycode + 8;
+        dir = (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) ?
+            XKB_KEY_DOWN : XKB_KEY_UP;
+        xkb_state_update_key(keyboard->xkb_state, keycode, dir);
+    }
+
+    updated = keyboard_modifier_update(keyboard);
+    if (updated)
+        wl_signal_emit(&keyboard->events.modifiers, keyboard);
+
+    keyboard_led_update(keyboard);
+}
+
+WL_EXPORT void
+ds_keyboard_notify_modifiers(struct ds_keyboard *keyboard,
+        uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
+        uint32_t group)
+{
+    bool updated;
+
+    if (!keyboard->xkb_state)
+        return;
+
+    xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched,
+            mods_locked, 0, 0, group);
+
+    updated = keyboard_modifier_update(keyboard);
+    if (updated)
+        wl_signal_emit(&keyboard->events.modifiers, keyboard);
+
+    keyboard_led_update(keyboard);
+}
+
+WL_EXPORT void
+ds_keyboard_add_destroy_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&keyboard->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_keyboard_add_key_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&keyboard->events.key, listener);
+}
+
+WL_EXPORT void
+ds_keyboard_add_modifiers_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&keyboard->events.modifiers, listener);
+}
+
+WL_EXPORT void
+ds_keyboard_add_keymap_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&keyboard->events.keymap, listener);
+}
+
+WL_EXPORT void
+ds_keyboard_add_repeat_info_listener(struct ds_keyboard *keyboard,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&keyboard->events.repeat_info, listener);
+}
+
+void
+ds_keyboard_init(struct ds_keyboard *keyboard,
+        const struct ds_keyboard_interface *iface)
+{
+    keyboard->iface = iface;
+    keyboard->keymap_fd = -1;
+
+    wl_signal_init(&keyboard->events.destroy);
+    wl_signal_init(&keyboard->events.key);
+    wl_signal_init(&keyboard->events.modifiers);
+    wl_signal_init(&keyboard->events.keymap);
+    wl_signal_init(&keyboard->events.repeat_info);
+}
+
+void
+ds_keyboard_destroy(struct ds_keyboard *keyboard)
+{
+    if (keyboard->iface && keyboard->iface->destroy)
+        keyboard->iface->destroy(keyboard);
+    else
+        free(keyboard);
+}
+
+static bool
+keyboard_modifier_update(struct ds_keyboard *keyboard)
+{
+    xkb_mod_mask_t depressed, latched, locked, group;
+
+    if (!keyboard->xkb_state)
+        return false;
+
+    depressed = xkb_state_serialize_mods(keyboard->xkb_state,
+            XKB_STATE_MODS_DEPRESSED);
+    latched = xkb_state_serialize_mods(keyboard->xkb_state,
+            XKB_STATE_MODS_LATCHED);
+    locked = xkb_state_serialize_mods(keyboard->xkb_state,
+            XKB_STATE_MODS_LOCKED);
+    group = xkb_state_serialize_layout(keyboard->xkb_state,
+            XKB_STATE_LAYOUT_EFFECTIVE);
+    if (depressed == keyboard->modifiers.depressed &&
+            latched == keyboard->modifiers.latched &&
+            locked == keyboard->modifiers.locked &&
+            group == keyboard->modifiers.group)
+        return false;
+
+    keyboard->modifiers.depressed = depressed;
+    keyboard->modifiers.latched = latched;
+    keyboard->modifiers.locked = locked;
+    keyboard->modifiers.group = group;
+
+    return true;
+}
+
+static void keyboard_key_update(struct ds_keyboard *keyboard,
+        struct ds_event_keyboard_key *event)
+{
+    // TODO
+}
+
+static void keyboard_led_update(struct ds_keyboard *keyboard)
+{
+    // TODO
+}
index a08dfdc..4d1ae39 100644 (file)
@@ -20,6 +20,7 @@ libds_files = [
   'backend.c',
   'input_device.c',
   'pointer.c',
+  'keyboard.c',
 ]
 
 protocols = {
@@ -52,12 +53,16 @@ math = meson.get_compiler('c').find_library('m')
 wayland_server = dependency('wayland-server', required: true)
 pixman = dependency('pixman-1', required: true)
 libdrm = dependency('libdrm', required: true)
+xkbcommon = dependency('xkbcommon', required: true)
+rt = meson.get_compiler('c').find_library('rt')
 
 libds_deps = [
   math,
   wayland_server,
   pixman,
   libdrm,
+  xkbcommon,
+  rt,
 ]
 
 subdir('backend')
index 3b7448b..1c9326a 100644 (file)
@@ -10,4 +10,7 @@ timespec_to_msec(const struct timespec *a);
 int
 allocate_shm_file(size_t size);
 
+bool
+allocate_shm_file_pair(size_t size, int *rw_fd_ptr, int *ro_fd_ptr);
+
 #endif
index c8c84e3..4abd229 100644 (file)
  */
 
 #define _POSIX_C_SOURCE 200809L
-
+#include <stdbool.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <time.h>
 #include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define RANDNAME_PATTERN "/libds-XXXXXX"
 
 int
 os_fd_set_cloexec(int fd)
@@ -172,3 +177,72 @@ allocate_shm_file(off_t size)
 
        return fd;
 }
+
+static void
+randname(char *buf)
+{
+    struct timespec ts;
+    long r;
+
+    clock_gettime(CLOCK_REALTIME, &ts);
+    r = ts.tv_nsec;
+    for (int i = 0; i < 6; i++) {
+        buf[i] = 'A' + (r & 15) + (r & 16) * 2;
+        r >>= 5;
+    }
+}
+
+static int
+excl_shm_open(char *name)
+{
+    int retries = 100;
+    int fd;
+
+    do {
+        randname(name + strlen(RANDNAME_PATTERN) - 6);
+
+        --retries;
+
+        fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+        if (fd >= 0)
+            return fd;
+    } while (retries > 0 && errno == EEXIST);
+
+    return -1;
+}
+
+bool
+allocate_shm_file_pair(size_t size, int *rw_fd_ptr, int *ro_fd_ptr)
+{
+    char name[] = RANDNAME_PATTERN;
+    int rw_fd, ro_fd;
+    int ret;
+    
+    rw_fd = excl_shm_open(name);
+    if (rw_fd < 0)
+        return false;
+
+    ro_fd = shm_open(name, O_RDONLY, 0);
+    if (ro_fd < 0) {
+        shm_unlink(name);
+        close(rw_fd);
+        return false;
+    }
+
+    shm_unlink(name);
+
+    do {
+        ret = ftruncate(rw_fd, size);
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret < 0) {
+        close(rw_fd);
+        close(ro_fd);
+        return false;
+    }
+
+    *rw_fd_ptr = rw_fd;
+    *ro_fd_ptr = ro_fd;
+
+    return true;
+}