From 43b469f70bab6763b6244a3488ad4a80675d30fc Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sun, 16 Aug 2026 15:47:47 +0900 Subject: [PATCH] ipc: share one keysym and modifier mapping across frontends The keysym-to-engine-key rule lived in ibus.c, xim/xim.c and gtk/main.c, and the copies disagreed: GTK sent keypad digits as special keys while IBus and XIM sent '1'..'9'. Modifiers were rebuilt bit by bit in two places although Mmask already is the X core layout; only the virtual Super bit (26) that GDK and IBus set needs folding. ipckey/ipcmod in ipc.c serve the daemon, the module and the tests. --- gtk/main.c | 34 ++-------------------------------- ibus.c | 30 ++---------------------------- ipc.c | 25 +++++++++++++++++++++++++ ipc.h | 4 ++++ tests/ipc_test.c | 12 ++++++++++++ xim/xim.c | 20 ++++---------------- 6 files changed, 49 insertions(+), 76 deletions(-) diff --git a/gtk/main.c b/gtk/main.c index bda6be8..4ccfbab 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -223,36 +223,6 @@ srvconnect(Im *im) return 0; } -static uint32_t -kget(uint32_t gdk) -{ - uint32_t u; - - u = gdk_keyval_to_unicode(gdk); - if((gdk & 0xff000000) == 0x01000000 && u != 0) - return u; - if(gdk >= 0xff00 && gdk <= 0xffff) - return Kspec + (gdk - 0xff00); - return u; -} - -static uint32_t -mget(uint32_t state) -{ - uint32_t m; - - m = 0; - if(state & GDK_SHIFT_MASK) - m |= Mshift; - if(state & GDK_CONTROL_MASK) - m |= Mctrl; - if(state & GDK_MOD1_MASK) - m |= Malt; - if(state & GDK_SUPER_MASK) - m |= Msuper; - return m; -} - static void simplecommit(GtkIMContext *ctx, const char *s, Im *im) { @@ -327,8 +297,8 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev) } if(im->simpleactive) return simplefilter(ctx, ev, 0); - key = kget(ev->keyval); - mod = mget(ev->state); + key = ipckey(ev->keyval, gdk_keyval_to_unicode(ev->keyval)); + mod = ipcmod(ev->state); if(im->private || key == 0) return simplefilter(ctx, ev, 0); if(srvconnect(im) < 0) diff --git a/ibus.c b/ibus.c index ccff5bd..0ee1430 100644 --- a/ibus.c +++ b/ibus.c @@ -17,7 +17,6 @@ enum Maxwatches = 2*Maxconns + 1, Maxcontexts = Maxclients, Relmask = 1<<30, - Ibussupermask = 1<<26, /* IBus wire constants; the daemon does not link libibus. */ Ibuscappreedit = 1<<0, Ibuspurposepassword = 8, @@ -266,32 +265,6 @@ togglewatch(DBusWatch *w, void *_) USED(_); } -static u32int -kget(u32int sym) -{ - u32int c; - - c = xkb_keysym_to_utf32(sym); - if(c >= ' ' && c != 0x7f) - return c; - if(sym >= 0xff00 && sym <= 0xffff) - return Kspec + (sym - 0xff00); - return c; -} - -static u32int -mget(u32int state) -{ - u32int m; - - m = 0; - if(state & (1<<0)) m |= Mshift; - if(state & (1<<2)) m |= Mctrl; - if(state & (1<<3)) m |= Malt; - if(state & ((1<<6)|Ibussupermask)) m |= Msuper; - return m; -} - static Ictx* findcontext(DBusConnection *conn, const char *path) { @@ -385,7 +358,8 @@ processkey(Ictx *ctx, u32int sym, u32int state, Keyres *res) { if(state & Relmask || !ctx->focused || hidden(ctx)) return 0; - sendrequest(ctx, Keypress, kget(sym), mget(state), res); + sendrequest(ctx, Keypress, ipckey(sym, xkb_keysym_to_utf32(sym)), + ipcmod(state), res); if(preowner != nil && preowner != ctx) checkpreowner(); return 1; diff --git a/ipc.c b/ipc.c index 0ce6f35..8f48695 100644 --- a/ipc.c +++ b/ipc.c @@ -223,6 +223,31 @@ Bad: return -1; } +/* + * Engine key for an X keysym and its Unicode value: printable characters + * as themselves, the function keysyms 0xff00-0xffff as Kspec+offset. + */ +uint32_t +ipckey(uint32_t sym, uint32_t unicode) +{ + if(unicode >= ' ' && unicode != 0x7f) + return unicode; + if(sym >= 0xff00 && sym <= 0xffff) + return Kspec + (sym - 0xff00); + return unicode; +} + +uint32_t +ipcmod(uint32_t state) +{ + uint32_t m; + + m = state & Mmask; + if(state & Msupervirt) + m |= Msuper; + return m; +} + void ipcpackreq(unsigned char req[Ipcreqsz], int want, uint32_t mod, uint32_t key) diff --git a/ipc.h b/ipc.h index 5d27213..faea5ce 100644 --- a/ipc.h +++ b/ipc.h @@ -38,11 +38,13 @@ enum Kmodfirst = Kspec|0xe1, Kmodlast = Kspec|0xee, + /* X core modifier bits; GDK and IBus also flag Super at bit 26. */ Mshift = 1<<0, Mctrl = 1<<2, Malt = 1<<3, Msuper = 1<<6, Mmask = Mshift|Mctrl|Malt|Msuper, + Msupervirt = 1<<26, Ipcreqsz = 6, Ipccaretsz = 16, @@ -70,6 +72,8 @@ struct Ipcresp size_t preeditlen; }; +uint32_t ipckey(uint32_t, uint32_t); +uint32_t ipcmod(uint32_t); void ipcpackreq(unsigned char[Ipcreqsz], int, uint32_t, uint32_t); void ipcpackreset(unsigned char[Ipcreqsz], int); void ipcpackcap(unsigned char[Ipcreqsz], int); diff --git a/tests/ipc_test.c b/tests/ipc_test.c index b48543a..7ad236d 100644 --- a/tests/ipc_test.c +++ b/tests/ipc_test.c @@ -46,6 +46,18 @@ ipc_masks_modifiers(struct ct *t) CT_EQ_UINT(t, 0, mod); CT_EQ_UINT(t, 0, key); CT_CHECK(t, ipcreqreset(buf)); + + /* Frontends share one keysym and modifier mapping. */ + CT_EQ_UINT(t, 'a', ipckey('a', 'a')); + CT_EQ_UINT(t, '1', ipckey(0xffb1, '1')); /* KP_1 */ + CT_EQ_UINT(t, Kret, ipckey(0xff0d, '\r')); + CT_EQ_UINT(t, Kback, ipckey(0xff08, 8)); + CT_EQ_UINT(t, Kspec + 0xff, ipckey(0xffff, 0x7f)); /* Delete */ + CT_EQ_UINT(t, 0x1f642, ipckey(0x101f642, 0x1f642)); + CT_EQ_UINT(t, 0, ipckey(0xfe03, 0)); /* ISO_Level3_Shift */ + CT_EQ_UINT(t, Mshift|Mctrl, ipcmod(Mshift|Mctrl|(1<<1)|(1<<4))); + CT_EQ_UINT(t, Msuper, ipcmod(1<<6)); + CT_EQ_UINT(t, Msuper, ipcmod(1<<26)); } void diff --git a/xim/xim.c b/xim/xim.c index 0b71147..6de7125 100644 --- a/xim/xim.c +++ b/xim/xim.c @@ -249,12 +249,6 @@ out: return ok; } -static u32int -kget(uint8_t kc, u16int state) -{ - return keymaplookup(kstate, kc, state); -} - static xcb_screen_t* getscreen(int scr) { @@ -466,7 +460,7 @@ sendrequest(Ic *state, int op, u32int key, u32int mod, Keyres *res) kr.cap = state->cap; kr.op = op; kr.ks = key; - kr.mod = mod & Mmask; + kr.mod = ipcmod(mod); kr.caret = state->caret; kr.reply = replyc; chansend(keyc, &kr); @@ -557,17 +551,11 @@ kpress(Ic *state, xcb_key_press_event_t *ev) { Keyres res; char buf[Maxutf]; - u32int key, rune; + u32int key; int n, wasvalid; - key = kget(ev->detail, ev->state); - rune = xkb_keysym_to_utf32(key); - if(rune >= ' ' && rune != 0x7f) - key = rune; - else if(key >= 0xff00 && key <= 0xffff) - key = Kspec + (key - 0xff00); - else - key = rune; + key = keymaplookup(kstate, ev->detail, ev->state); + key = ipckey(key, xkb_keysym_to_utf32(key)); if(meaningful(key)){ wasvalid = state->caret.valid; refreshplace(state);