From ef3ef30400150f015695df08780e589c6407e646 Mon Sep 17 00:00:00 2001 From: Daniel Otero Date: Thu, 12 Feb 2015 00:37:23 +0100 Subject: [PATCH 1/4] Add support for Compose and dead-keys with libxkbcommon --- README.md | 4 ++-- i3lock.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b587c27..6965a38 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ Requirements - libx11-dev - libx11-xcb-dev - libxkbfile-dev -- libxkbcommon >= 0.4.0 -- libxkbcommon-x11 >= 0.4.0 +- libxkbcommon >= 0.5.0 +- libxkbcommon-x11 >= 0.5.0 Running i3lock ------------- diff --git a/i3lock.c b/i3lock.c index 942781a..02e6794 100644 --- a/i3lock.c +++ b/i3lock.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,8 @@ bool show_failed_attempts = false; static struct xkb_state *xkb_state; static struct xkb_context *xkb_context; static struct xkb_keymap *xkb_keymap; +static struct xkb_compose_table *xkb_compose_table; +static struct xkb_compose_state *xkb_compose_state; static uint8_t xkb_base_event; static uint8_t xkb_base_error; @@ -137,6 +140,37 @@ static bool load_keymap(void) { return true; } +/* + * Loads the XKB compose table from the given locale. + * + */ +static bool load_compose_table(const char *locale) { + if (xkb_context == NULL) { + if ((xkb_context = xkb_context_new(0)) == NULL) { + fprintf(stderr, "[i3lock] could not create xkbcommon context\n"); + return false; + } + } + + xkb_compose_table_unref(xkb_compose_table); + + if ((xkb_compose_table = xkb_compose_table_new_from_locale(xkb_context, locale, 0)) == NULL) { + fprintf(stderr, "[i3lock] xkb_compose_table_new_from_locale failed\n"); + return false; + } + + struct xkb_compose_state *new_compose_state = xkb_compose_state_new(xkb_compose_table, 0); + if (new_compose_state == NULL) { + fprintf(stderr, "[i3lock] xkb_compose_state_new failed\n"); + return false; + } + + xkb_compose_state_unref(xkb_compose_state); + xkb_compose_state = new_compose_state; + + return true; +} + /* * Clears the memory which stored the password to be a bit safer against * cold-boot attacks. @@ -289,13 +323,36 @@ static void handle_key_press(xcb_key_press_event_t *event) { char buffer[128]; int n; bool ctrl; + bool composed = false; ksym = xkb_state_key_get_one_sym(xkb_state, event->detail); ctrl = xkb_state_mod_name_is_active(xkb_state, "Control", XKB_STATE_MODS_DEPRESSED); /* The buffer will be null-terminated, so n >= 2 for 1 actual character. */ memset(buffer, '\0', sizeof(buffer)); - n = xkb_keysym_to_utf8(ksym, buffer, sizeof(buffer)); + + if (xkb_compose_state && xkb_compose_state_feed(xkb_compose_state, ksym) == XKB_COMPOSE_FEED_ACCEPTED) { + switch (xkb_compose_state_get_status(xkb_compose_state)) { + case XKB_COMPOSE_NOTHING: + break; + case XKB_COMPOSE_COMPOSING: + return; + case XKB_COMPOSE_COMPOSED: + /* xkb_compose_state_get_utf8 doesn't include the terminating byte in the return value + * as xkb_keysym_to_utf8 does. Adding one makes the variable n consistent. */ + n = xkb_compose_state_get_utf8(xkb_compose_state, buffer, sizeof(buffer)) + 1; + //ksym = xkb_compose_state_get_one_sym(xkb_compose_state); // Not sure if it's needed + composed = true; + break; + case XKB_COMPOSE_CANCELLED: + xkb_compose_state_reset(xkb_compose_state); + break; + } + } + + if (!composed) { + n = xkb_keysym_to_utf8(ksym, buffer, sizeof(buffer)); + } switch (ksym) { case XKB_KEY_Return: @@ -824,6 +881,19 @@ int main(int argc, char *argv[]) { if (!load_keymap()) errx(EXIT_FAILURE, "Could not load keymap"); + const char *locale = getenv("LC_ALL"); + if (!locale) + locale = getenv("LC_CTYPE"); + if (!locale) + locale = getenv("LANG"); + if (!locale) { + if (debug_mode) + fprintf(stderr, "Can't detect your locale, fallback to C\n"); + locale = "C"; + } + + load_compose_table(locale); + xinerama_init(); xinerama_query_screens(); From 562e824246b9e81e65644660714db404035e693a Mon Sep 17 00:00:00 2001 From: Daniel Otero Date: Thu, 12 Feb 2015 11:12:38 +0100 Subject: [PATCH 2/4] Remove xkb_context initialization code from load_compose_table xkb_context is guaranteed to be initializated from load_keymap. --- i3lock.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/i3lock.c b/i3lock.c index 02e6794..b58297e 100644 --- a/i3lock.c +++ b/i3lock.c @@ -145,13 +145,6 @@ static bool load_keymap(void) { * */ static bool load_compose_table(const char *locale) { - if (xkb_context == NULL) { - if ((xkb_context = xkb_context_new(0)) == NULL) { - fprintf(stderr, "[i3lock] could not create xkbcommon context\n"); - return false; - } - } - xkb_compose_table_unref(xkb_compose_table); if ((xkb_compose_table = xkb_compose_table_new_from_locale(xkb_context, locale, 0)) == NULL) { From 9e48c74be19f7a27b2e01cf744f85b8b313a45b3 Mon Sep 17 00:00:00 2001 From: Daniel Otero Date: Thu, 12 Feb 2015 11:22:58 +0100 Subject: [PATCH 3/4] Avoid handle a keysym if the compose state gets cancelled This is the approach taken by libX11, and feels more consistent. --- i3lock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i3lock.c b/i3lock.c index b58297e..16e7911 100644 --- a/i3lock.c +++ b/i3lock.c @@ -339,7 +339,7 @@ static void handle_key_press(xcb_key_press_event_t *event) { break; case XKB_COMPOSE_CANCELLED: xkb_compose_state_reset(xkb_compose_state); - break; + return; } } From 86323f6e042a3d22218f64eba9c97c3cc0d4b04b Mon Sep 17 00:00:00 2001 From: Daniel Otero Date: Thu, 12 Feb 2015 21:38:37 +0100 Subject: [PATCH 4/4] Update the key symbol if the composing ends. --- i3lock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i3lock.c b/i3lock.c index 16e7911..7f8218e 100644 --- a/i3lock.c +++ b/i3lock.c @@ -334,7 +334,7 @@ static void handle_key_press(xcb_key_press_event_t *event) { /* xkb_compose_state_get_utf8 doesn't include the terminating byte in the return value * as xkb_keysym_to_utf8 does. Adding one makes the variable n consistent. */ n = xkb_compose_state_get_utf8(xkb_compose_state, buffer, sizeof(buffer)) + 1; - //ksym = xkb_compose_state_get_one_sym(xkb_compose_state); // Not sure if it's needed + ksym = xkb_compose_state_get_one_sym(xkb_compose_state); composed = true; break; case XKB_COMPOSE_CANCELLED: