From 48d3e165c3935cb95f6a156fb3d7e970c7a60f12 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sun, 16 Aug 2026 21:39:22 +0900 Subject: [PATCH] xim: dead keys and Compose Xlib does no local composing for a client on an input method server, so under XIM a dead key was forwarded as a bare keysym and vanished. The XIM frontend now feeds keys through xkbcommon's Compose table for the daemon's locale and commits the composed character itself. --- xim/xim.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/xim/xim.c b/xim/xim.c index 6fc925a..2ce4ae5 100644 --- a/xim/xim.c +++ b/xim/xim.c @@ -2,10 +2,12 @@ #include "fn.h" #include +#include #include #include #include #include +#include #include #include #include @@ -38,6 +40,7 @@ enum static xcb_connection_t *conn; static xcb_im_t *xim; static struct xkb_state *kstate; +static struct xkb_compose_state *compose; static uint8_t xkbevent; static xcb_window_t rootwin; static Ic *ics; @@ -96,6 +99,26 @@ out: return ok; } +/* Dead keys and Compose: Xlib leaves them to the input method server. */ +static void +cinit(void) +{ + struct xkb_context *context; + struct xkb_compose_table *table; + char *locale; + + locale = setlocale(LC_CTYPE, ""); + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if(context == nil) + return; + table = xkb_compose_table_new_from_locale(context, + locale != nil ? locale : "C", XKB_COMPOSE_COMPILE_NO_FLAGS); + if(table != nil) + compose = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(table); + xkb_context_unref(context); +} + static void kwatch(void) { @@ -361,11 +384,25 @@ kpress(Ic *state, xcb_key_press_event_t *ev) { Keyres res; char buf[Maxutf]; - u32int key; - int n, wasvalid; + u32int key, sym; + int composed, n, wasvalid; - key = keymaplookup(kstate, ev->detail, ev->state); - key = ipckeysym(key, xkb_keysym_to_utf32(key)); + sym = keymaplookup(kstate, ev->detail, ev->state); + composed = 0; + if(compose != nil && + xkb_compose_state_feed(compose, sym) == XKB_COMPOSE_FEED_ACCEPTED) + switch(xkb_compose_state_get_status(compose)){ + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return; + case XKB_COMPOSE_COMPOSED: + sym = xkb_compose_state_get_one_sym(compose); + composed = 1; + break; + default: + break; + } + key = ipckeysym(sym, xkb_keysym_to_utf32(sym)); if(keymeaningful(key)){ wasvalid = state->caret.valid; place(state); @@ -376,7 +413,13 @@ kpress(Ic *state, xcb_key_press_event_t *ev) n = stoutf(&res.commit, buf, sizeof buf); commit(state, buf, n); updatepreedit(state, &res.preedit); - if(!res.eaten) + if(res.eaten) + ; + else if(composed){ + /* The event holds the last key of the sequence, not its result. */ + n = xkb_keysym_to_utf8(sym, buf, sizeof buf); + commit(state, buf, max(n - 1, 0)); + }else xcb_im_forward_event(xim, state->xic, ev); xcb_flush(conn); } @@ -498,6 +541,7 @@ ximinit(void) ximlog("cannot read keyboard mapping"); return -1; } + cinit(); kwatch(); win = xcb_generate_id(conn); xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, screen->root,