From c102c87d55ae263d33b3c8ad91a2b5c99ce975ce Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 17 Aug 2026 11:58:08 +0900 Subject: [PATCH] ibus: a key stands for the FocusIn a client may never send A context that had not sent FocusIn had every key rejected outright, so the input method was silently dead in a client that omits or delays it, and in the documented case of two applications whose focus events cross. ibus-daemon and fcitx5 both treat a key as focus; strans, which plays the daemon here, now does too. A release still does not focus, and the engine's own owner rule is unchanged. --- ibus.c | 7 ++++++- tests/ibus_test.c | 16 ++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/ibus.c b/ibus.c index ae04ed3..aa065bb 100644 --- a/ibus.c +++ b/ibus.c @@ -526,11 +526,16 @@ dropconncontexts(DBusConnection *conn) dropcontext(&contexts[i]); } +/* + * A key is proof of focus: some clients never send FocusIn, and the + * focus events of two applications can cross. + */ static int processkey(Ictx *ctx, u32int sym, u32int state, Keyres *res) { - if(state & Relmask || !ctx->focused || hidden(ctx)) + if(state & Relmask || hidden(ctx)) return 0; + ctx->focused = 1; sendrequest(ctx, Keypress, ipckeysym(sym, xkb_keysym_to_utf32(sym)), ipcmod(state), res); if(preowner != nil && preowner != ctx) diff --git a/tests/ibus_test.c b/tests/ibus_test.c index 8aa6bc5..ec4013b 100644 --- a/tests/ibus_test.c +++ b/tests/ibus_test.c @@ -281,25 +281,21 @@ ibus_context_lifecycle(struct ct *t) CT_EQ_PTR(t, b, findcontext(f.c2, "/context/one")); CT_EQ_PTR(t, nil, findcontext(f.c1, "/context/missing")); - memset(&res, 0, sizeof res); - CT_CHECK(t, !processkey(a, 'x', 0, &res)); - CT_CHECK(t, !res.eaten); - notrace(t, &f); - CT_EQ_PTR(t, nil, testengineowner()); - setcursor(a, -10, -20, 14); notrace(t, &f); CT_CHECK(t, a->caret.valid); - a->focused = 1; memset(&res, 0, sizeof res); CT_CHECK(t, !processkey(a, 'x', Relmask, &res)); CT_CHECK(t, !res.eaten); notrace(t, &f); + CT_CHECK(t, !a->focused); CT_EQ_PTR(t, nil, testengineowner()); + /* A key stands for the FocusIn a client may never send. */ memset(&res, 0, sizeof res); CT_CHECK(t, processkey(a, 'x', 0, &res)); req = nexttrace(t, &f, Keypress, a); + CT_CHECK(t, a->focused); CT_CHECK(t, !res.eaten); CT_CHECK(t, req.caret.valid); CT_EQ_INT(t, -10, req.caret.x); @@ -430,12 +426,8 @@ ibus_active_release_lifecycle(struct ct *t) reused = newcontext(f.c1, "/context/prune"); CT_EQ_PTR(t, ctx, reused); CT_EQ_PTR(t, nil, testengineowner()); - memset(&res, 0, sizeof res); - CT_CHECK(t, !processkey(reused, 'x', 0, &res)); - CT_CHECK(t, !res.eaten); - notrace(t, &f); - reused->focused = 1; contextkey(t, &f, reused, 'n', 0); + CT_CHECK(t, reused->focused); CT_EQ_PTR(t, reused, testengineowner()); dropconncontexts(f.c1); nexttrace(t, &f, Keyrelease, reused);