compose: one Compose table for the XIM and IBus frontends

An IBus client throws away a dead key its engine did not take — the GTK
module's own comment says so, and Qt does the same — so é and ü were
lost in every IBus application, while XIM composed them with a table of
its own.  That table moves to compose.c, which both frontends now use;
xim.c is the shorter for it.

A finished sequence is text, not a key: the engine is asked to hand back
what it had pending, and the composed text follows it, so the composed
character can no longer land before the syllable typed before it.
This commit is contained in:
2026-08-17 12:16:07 +09:00
parent a0e83f98c6
commit ef7fb627c6
13 changed files with 149 additions and 64 deletions

59
xim.c
View File

@@ -8,7 +8,6 @@
#include <xcb/xcb_aux.h>
#include <xcb/xkb.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h>
#include <xkbcommon/xkbcommon-names.h>
#include <xkbcommon/xkbcommon-x11.h>
#include <xcb-imdkit/imdkit.h>
@@ -35,7 +34,6 @@ struct Ic
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;
@@ -97,26 +95,6 @@ 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)
{
@@ -401,39 +379,27 @@ static void
kpress(Ic *state, xcb_key_press_event_t *ev)
{
Keyres res;
char buf[Maxutf];
char buf[Maxutf], text[Maxutf];
u32int key, sym;
int composed, n;
int n;
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;
}
if(composekey(sym, text, sizeof text))
return;
key = ipckeysym(sym, xkb_keysym_to_utf32(sym));
if(keymeaningful(key))
place(state);
keypress(state, key, ev->state, &res);
if(text[0] != '\0')
/* Composed text follows whatever was pending. */
sendrequest(state, Keyreset, 0, 0, &res);
else
keypress(state, key, ev->state, &res);
n = stoutf(&res.commit, buf, sizeof buf);
commit(state, buf, n);
updatepreedit(state, &res.preedit);
if(res.eaten)
;
else if(composed){
/* The event holds the last key of the sequence, not its result. */
n = xkb_compose_state_get_utf8(compose, buf, sizeof buf);
commit(state, buf, min(n, (int)sizeof buf - 1));
}else
if(text[0] != '\0')
commit(state, text, strlen(text));
else if(!res.eaten)
xcb_im_forward_event(xim, state->xic, ev);
xcb_flush(conn);
}
@@ -554,7 +520,6 @@ 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,