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.
This commit is contained in:
2026-08-16 15:47:47 +09:00
parent 66ea2892f8
commit 43b469f70b
6 changed files with 49 additions and 76 deletions

View File

@@ -223,36 +223,6 @@ srvconnect(Im *im)
return 0; 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 static void
simplecommit(GtkIMContext *ctx, const char *s, Im *im) simplecommit(GtkIMContext *ctx, const char *s, Im *im)
{ {
@@ -327,8 +297,8 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev)
} }
if(im->simpleactive) if(im->simpleactive)
return simplefilter(ctx, ev, 0); return simplefilter(ctx, ev, 0);
key = kget(ev->keyval); key = ipckey(ev->keyval, gdk_keyval_to_unicode(ev->keyval));
mod = mget(ev->state); mod = ipcmod(ev->state);
if(im->private || key == 0) if(im->private || key == 0)
return simplefilter(ctx, ev, 0); return simplefilter(ctx, ev, 0);
if(srvconnect(im) < 0) if(srvconnect(im) < 0)

30
ibus.c
View File

@@ -17,7 +17,6 @@ enum
Maxwatches = 2*Maxconns + 1, Maxwatches = 2*Maxconns + 1,
Maxcontexts = Maxclients, Maxcontexts = Maxclients,
Relmask = 1<<30, Relmask = 1<<30,
Ibussupermask = 1<<26,
/* IBus wire constants; the daemon does not link libibus. */ /* IBus wire constants; the daemon does not link libibus. */
Ibuscappreedit = 1<<0, Ibuscappreedit = 1<<0,
Ibuspurposepassword = 8, Ibuspurposepassword = 8,
@@ -266,32 +265,6 @@ togglewatch(DBusWatch *w, void *_)
USED(_); 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* static Ictx*
findcontext(DBusConnection *conn, const char *path) 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)) if(state & Relmask || !ctx->focused || hidden(ctx))
return 0; 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) if(preowner != nil && preowner != ctx)
checkpreowner(); checkpreowner();
return 1; return 1;

25
ipc.c
View File

@@ -223,6 +223,31 @@ Bad:
return -1; 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 void
ipcpackreq(unsigned char req[Ipcreqsz], int want, uint32_t mod, ipcpackreq(unsigned char req[Ipcreqsz], int want, uint32_t mod,
uint32_t key) uint32_t key)

4
ipc.h
View File

@@ -38,11 +38,13 @@ enum
Kmodfirst = Kspec|0xe1, Kmodfirst = Kspec|0xe1,
Kmodlast = Kspec|0xee, Kmodlast = Kspec|0xee,
/* X core modifier bits; GDK and IBus also flag Super at bit 26. */
Mshift = 1<<0, Mshift = 1<<0,
Mctrl = 1<<2, Mctrl = 1<<2,
Malt = 1<<3, Malt = 1<<3,
Msuper = 1<<6, Msuper = 1<<6,
Mmask = Mshift|Mctrl|Malt|Msuper, Mmask = Mshift|Mctrl|Malt|Msuper,
Msupervirt = 1<<26,
Ipcreqsz = 6, Ipcreqsz = 6,
Ipccaretsz = 16, Ipccaretsz = 16,
@@ -70,6 +72,8 @@ struct Ipcresp
size_t preeditlen; 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 ipcpackreq(unsigned char[Ipcreqsz], int, uint32_t, uint32_t);
void ipcpackreset(unsigned char[Ipcreqsz], int); void ipcpackreset(unsigned char[Ipcreqsz], int);
void ipcpackcap(unsigned char[Ipcreqsz], int); void ipcpackcap(unsigned char[Ipcreqsz], int);

View File

@@ -46,6 +46,18 @@ ipc_masks_modifiers(struct ct *t)
CT_EQ_UINT(t, 0, mod); CT_EQ_UINT(t, 0, mod);
CT_EQ_UINT(t, 0, key); CT_EQ_UINT(t, 0, key);
CT_CHECK(t, ipcreqreset(buf)); 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 void

View File

@@ -249,12 +249,6 @@ out:
return ok; return ok;
} }
static u32int
kget(uint8_t kc, u16int state)
{
return keymaplookup(kstate, kc, state);
}
static xcb_screen_t* static xcb_screen_t*
getscreen(int scr) getscreen(int scr)
{ {
@@ -466,7 +460,7 @@ sendrequest(Ic *state, int op, u32int key, u32int mod, Keyres *res)
kr.cap = state->cap; kr.cap = state->cap;
kr.op = op; kr.op = op;
kr.ks = key; kr.ks = key;
kr.mod = mod & Mmask; kr.mod = ipcmod(mod);
kr.caret = state->caret; kr.caret = state->caret;
kr.reply = replyc; kr.reply = replyc;
chansend(keyc, &kr); chansend(keyc, &kr);
@@ -557,17 +551,11 @@ kpress(Ic *state, xcb_key_press_event_t *ev)
{ {
Keyres res; Keyres res;
char buf[Maxutf]; char buf[Maxutf];
u32int key, rune; u32int key;
int n, wasvalid; int n, wasvalid;
key = kget(ev->detail, ev->state); key = keymaplookup(kstate, ev->detail, ev->state);
rune = xkb_keysym_to_utf32(key); key = ipckey(key, xkb_keysym_to_utf32(key));
if(rune >= ' ' && rune != 0x7f)
key = rune;
else if(key >= 0xff00 && key <= 0xffff)
key = Kspec + (key - 0xff00);
else
key = rune;
if(meaningful(key)){ if(meaningful(key)){
wasvalid = state->caret.valid; wasvalid = state->caret.valid;
refreshplace(state); refreshplace(state);