summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2020-03-11 11:21:12 +0100
committerSimon Ser <contact@emersion.fr>2020-03-11 11:21:12 +0100
commitcf9e78a43f9a0dde33a86d965b0799662ecfa823 (patch)
tree03aed0c575e18ddf438eced51915e7d3921e748b
parent614b795c1eccece05abf02836d76c39ba6d27575 (diff)
Grab keyboard, feed keys to libhangul
-rw-r--r--README.md2
-rw-r--r--include/wlhangul.h11
-rw-r--r--main.c149
-rw-r--r--meson.build5
-rw-r--r--utf8.c56
5 files changed, 219 insertions, 4 deletions
diff --git a/README.md b/README.md
index 03a0fd5..e7f54e4 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/main.c b/main.c
index a220e78..5b2f09c 100644
--- a/main.c
+++ b/main.c
@@ -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,
)
diff --git a/utf8.c b/utf8.c
new file mode 100644
index 0000000..677ee6b
--- /dev/null
+++ b/utf8.c
@@ -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;
+}