diff options
author | Simon Ser <contact@emersion.fr> | 2020-03-11 11:21:12 +0100 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2020-03-11 11:21:12 +0100 |
commit | cf9e78a43f9a0dde33a86d965b0799662ecfa823 (patch) | |
tree | 03aed0c575e18ddf438eced51915e7d3921e748b | |
parent | 614b795c1eccece05abf02836d76c39ba6d27575 (diff) |
Grab keyboard, feed keys to libhangul
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | include/wlhangul.h | 11 | ||||
-rw-r--r-- | main.c | 149 | ||||
-rw-r--r-- | meson.build | 5 | ||||
-rw-r--r-- | utf8.c | 56 |
5 files changed, 219 insertions, 4 deletions
@@ -4,7 +4,7 @@ A Hangul input method for Wayland. ## Building -Depends on Wayland and libhangul. +Depends on libhangul, Wayland and libxkbcommon. meson build/ ninja -C build/ diff --git a/include/wlhangul.h b/include/wlhangul.h index 0d62431..f92afcd 100644 --- a/include/wlhangul.h +++ b/include/wlhangul.h @@ -21,6 +21,17 @@ struct wlhangul_seat { HangulInputContext *input_context; struct zwp_input_method_v2 *input_method; + + struct xkb_context *xkb_context; + struct xkb_keymap *xkb_keymap; + struct xkb_state *xkb_state; + + bool active; + uint32_t serial; + bool pending_activate, pending_deactivate; + struct zwp_input_method_keyboard_grab_v2 *keyboard_grab; }; +char *ucsstr_to_str(const ucschar *ucsstr); + #endif @@ -1,16 +1,145 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> +#include <unistd.h> #include <wayland-client.h> +#include <xkbcommon/xkbcommon.h> #include "wlhangul.h" #include "input-method-unstable-v2-client-protocol.h" +static void handle_key(void *data, + struct zwp_input_method_keyboard_grab_v2 *keyboard_grab, + uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { + struct wlhangul_seat *seat = data; + xkb_keycode_t xkb_key = key + 8; + + if (seat->xkb_state == NULL) { + return; + } + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) { + return; + } + + bool handled; + xkb_keysym_t sym = xkb_state_key_get_one_sym(seat->xkb_state, xkb_key); + switch (sym) { + case XKB_KEY_Escape: + seat->state->running = false; + handled = true; + break; + case XKB_KEY_BackSpace: + handled = hangul_ic_backspace(seat->input_context); + break; + // TODO: XKB_KEY_Hangul + default:; + uint32_t ch = xkb_state_key_get_utf32(seat->xkb_state, xkb_key); + handled = hangul_ic_process(seat->input_context, ch); + break; + } + + const ucschar *commit_ucsstr = + hangul_ic_get_commit_string(seat->input_context); + if (commit_ucsstr[0] != 0) { + char *commit_str = ucsstr_to_str(commit_ucsstr); + zwp_input_method_v2_commit_string(seat->input_method, commit_str); + free(commit_str); + } + + const ucschar *preedit_ucsstr = + hangul_ic_get_preedit_string(seat->input_context); + char *preedit_str = ucsstr_to_str(preedit_ucsstr); + zwp_input_method_v2_set_preedit_string(seat->input_method, + preedit_str, 0, strlen(preedit_str)); + free(preedit_str); + + zwp_input_method_v2_commit(seat->input_method, seat->serial); + + if (!handled) { + printf("key: %d\n", xkb_key); + // TODO: forward key + } +} + +static void handle_modifiers(void *data, + struct zwp_input_method_keyboard_grab_v2 *keyboard_grab, + uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + struct wlhangul_seat *seat = data; + + if (seat->xkb_state == NULL) { + return; + } + + xkb_state_update_mask(seat->xkb_state, mods_depressed, + mods_latched, mods_locked, 0, 0, group); +} + +static void handle_keymap(void *data, + struct zwp_input_method_keyboard_grab_v2 *keyboard_grab, + uint32_t format, int32_t fd, uint32_t size) { + struct wlhangul_seat *seat = data; + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + return; + } + + char *str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (str == MAP_FAILED) { + perror("mmap"); + close(fd); + return; + } + + if (seat->xkb_keymap != NULL) { + xkb_keymap_unref(seat->xkb_keymap); + } + seat->xkb_keymap = xkb_keymap_new_from_string(seat->xkb_context, str, + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(str, size); + close(fd); + + if (seat->xkb_keymap == NULL) { + fprintf(stderr, "Failed to compile keymap\n"); + return; + } + + if (seat->xkb_state != NULL) { + xkb_state_unref(seat->xkb_state); + } + seat->xkb_state = xkb_state_new(seat->xkb_keymap); + if (seat->xkb_state == NULL) { + fprintf(stderr, "Failed to create XKB state\n"); + return; + } +} + +static void handle_repeat_info(void *data, + struct zwp_input_method_keyboard_grab_v2 *keyboard_grab, int32_t rate, + int32_t delay) { + // TODO +} + +static const struct zwp_input_method_keyboard_grab_v2_listener + keyboard_grab_listener = { + .key = handle_key, + .modifiers = handle_modifiers, + .keymap = handle_keymap, + .repeat_info = handle_repeat_info, +}; + static void handle_activate(void *data, struct zwp_input_method_v2 *input_method) { + struct wlhangul_seat *seat = data; + seat->pending_activate = true; } static void handle_deactivate(void *data, struct zwp_input_method_v2 *input_method) { + struct wlhangul_seat *seat = data; + seat->pending_deactivate = true; } static void handle_surrounding_text(void *data, @@ -28,7 +157,23 @@ static void handle_content_type(void *data, } static void handle_done(void *data, struct zwp_input_method_v2 *input_method) { - // TODO + struct wlhangul_seat *seat = data; + seat->serial++; + + if (seat->pending_activate && !seat->active) { + seat->keyboard_grab = zwp_input_method_v2_grab_keyboard(input_method); + zwp_input_method_keyboard_grab_v2_add_listener(seat->keyboard_grab, + &keyboard_grab_listener, seat); + seat->active = true; + } else if (seat->pending_deactivate && seat->active) { + zwp_input_method_keyboard_grab_v2_release(seat->keyboard_grab); + hangul_ic_reset(seat->input_context); + seat->keyboard_grab = NULL; + seat->active = false; + } + + seat->pending_activate = false; + seat->pending_deactivate = false; } static void handle_unavailable(void *data, @@ -49,6 +194,7 @@ static struct wlhangul_seat *create_seat(struct wlhangul_state *state, struct wl_seat *wl_seat) { struct wlhangul_seat *seat = calloc(1, sizeof(*seat)); seat->wl_seat = wl_seat; + seat->state = state; wl_list_insert(&state->seats, &seat->link); return seat; } @@ -102,6 +248,7 @@ int main(int argc, char *argv[]) { state.input_method_manager, seat->wl_seat); zwp_input_method_v2_add_listener(seat->input_method, &input_method_listener, seat); + seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); } state.running = true; diff --git a/meson.build b/meson.build index 9b20f9a..83ac4d3 100644 --- a/meson.build +++ b/meson.build @@ -23,8 +23,9 @@ wlhangul_inc = include_directories('include') libhangul = dependency('libhangul') wayland_client = dependency('wayland-client') wayland_protocols = dependency('wayland-protocols') +xkbcommon = dependency('xkbcommon') -wlhangul_files = files('main.c') +wlhangul_files = files('main.c', 'utf8.c') subdir('protocol') @@ -32,6 +33,6 @@ executable( meson.project_name(), wlhangul_files, include_directories: wlhangul_inc, - dependencies: [libhangul, wayland_client], + dependencies: [libhangul, wayland_client, xkbcommon], install: true, ) @@ -0,0 +1,56 @@ +#include <assert.h> +#include <stdlib.h> +#include "wlhangul.h" + +static size_t ucschar_len(ucschar ch) { + if (ch < 0x80) { + return 1; + } else if (ch < 0x800) { + return 2; + } else if (ch < 0x10000) { + return 3; + } else if (ch < 0x110000) { + return 4; + } + return 0; +} + +static size_t ucschar_to_char(char out[static 4], ucschar ch) { + size_t len = ucschar_len(ch); + switch (len) { + case 1: + out[0] = (char)ch; + break; + case 2: + out[0] = (ch >> 6) | 0xC0; + out[1] = (ch & 0x3F) | 0x80; + break; + case 3: + out[0] = (ch >> 12) | 0xE0; + out[1] = ((ch >> 6) & 0x3F) | 0x80; + out[2] = (ch & 0x3F) | 0x80; + break; + case 4: + out[0] = (ch >> 18) | 0xF0; + out[1] = ((ch >> 12) & 0x3F) | 0x80; + out[2] = ((ch >> 6) & 0x3F) | 0x80; + out[3] = (ch & 0x3F) | 0x80; + break; + } + return len; +} + +char *ucsstr_to_str(const ucschar *ucsstr) { + size_t len = 0; + for (size_t i = 0; ucsstr[i] != 0; i++) { + len += ucschar_len(ucsstr[i]); + } + char *str = malloc(len + 1); + size_t n = 0; + for (size_t i = 0; ucsstr[i] != 0; i++) { + n += ucschar_to_char(&str[n], ucsstr[i]); + } + assert(n == len); + str[n] = '\0'; + return str; +} |