diff --git a/strans.c b/strans.c index 8954ffc..4f45137 100644 --- a/strans.c +++ b/strans.c @@ -3,6 +3,8 @@ static Im im; static void *activeowner; +static void *lostowner; /* the context the engine was taken from */ +static Str lost; static int clientpre; /* the owner draws its own preedit */ static Caret caret; static Drawcmd lastdraw; @@ -663,6 +665,20 @@ flush(Str *com) reset(); } +/* + * Focus can be lost after the next context's keys arrive, so the engine + * changes hands with text still pending. The context it was taken from + * gets that text back when it is next heard from. + */ +static void +takelost(void *owner, Str *com) +{ + if(owner != lostowner) + return; + sappend(com, &lost); + lostowner = nil; +} + /* * A search takes over the keys. Hanja converts the pending syllable, so * it becomes the query; anything else pending is committed. @@ -881,6 +897,7 @@ init(void) memset(&search, 0, sizeof search); memset(&caret, 0, sizeof caret); activeowner = nil; + lostowner = nil; clientpre = 0; memset(&lastdraw, 0, sizeof lastdraw); } @@ -902,6 +919,7 @@ imhandlekey(Keyreq *kr) res.eaten = 1; switch(kr->op){ case Keyrelease: + takelost(kr->owner, &res.commit); if(kr->owner == activeowner){ flush(&res.commit); activeowner = nil; @@ -909,6 +927,7 @@ imhandlekey(Keyreq *kr) } break; case Keyreset: + takelost(kr->owner, &res.commit); if(kr->owner == activeowner) flush(&res.commit); break; @@ -927,7 +946,9 @@ imhandlekey(Keyreq *kr) clientpre = kr->clientpre; if(keymeaningful(kr->ks)){ if(kr->owner != activeowner){ - reset(); + sclear(&lost); + flush(&lost); + lostowner = lost.n > 0 ? activeowner : nil; activeowner = kr->owner; clientpre = kr->clientpre; } diff --git a/tests/engine_test.c b/tests/engine_test.c index f153e59..15b6e4a 100644 --- a/tests/engine_test.c +++ b/tests/engine_test.c @@ -234,6 +234,7 @@ engine_active_owner_lifecycle(struct ct *t) checkstr(t, "takeover resets old preedit", "ん", &res.preedit); CT_EQ_PTR(t, &b, activeowner); res = ownerrequest(&a, Keyreset, 0, 0); + checkstr(t, "the taken owner gets its reading back", "か", &res.commit); CT_EQ_INT(t, 0, res.preedit.n); impre(&im, &shown); checkstr(t, "stale reset preserves owner", "ん", &shown); diff --git a/tests/server_test.c b/tests/server_test.c index 66571f3..7f2640e 100644 --- a/tests/server_test.c +++ b/tests/server_test.c @@ -284,7 +284,8 @@ server_connection_ownership(struct ct *t) req = nextrequest(t, &f.pump, Keyreset); CT_EQ_PTR(t, aowner, req.owner); allowrequest(&f.pump); - CT_CHECK(t, readreply(t, &a, 1, "", preedit)); + /* The reset hands back what the takeover took from this client. */ + CT_CHECK(t, readreply(t, &a, 1, "か", preedit)); CT_EQ_STR(t, "", preedit); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 1, 0, 0)) diff --git a/tests/xim_adapter_test.c b/tests/xim_adapter_test.c index b4864bc..5840084 100644 --- a/tests/xim_adapter_test.c +++ b/tests/xim_adapter_test.c @@ -1059,9 +1059,12 @@ xim_adapter_callback_owner_loss(struct ct *t) checkpreowner(); notrace(t, &f); - /* Late XIM lifecycle messages remain idempotent after owner cleanup. */ + /* The release hands back the reading the takeover took. */ release(&state); nexttrace(t, &f, Keyrelease, &state); + CT_EQ_INT(t, 5, nwirecalls); + CT_EQ_INT(t, Wcommit, wirecalls[4].op); + /* Late XIM lifecycle messages remain idempotent after owner cleanup. */ release(&state); notrace(t, &f); memset(&hdr, 0, sizeof hdr); @@ -1071,7 +1074,7 @@ xim_adapter_callback_owner_loss(struct ct *t) hdr.major_opcode = XCB_XIM_DESTROY_IC; callback(nil, nil, ic, &hdr, nil, nil, nil); notrace(t, &f); - CT_EQ_INT(t, 4, nwirecalls); + CT_EQ_INT(t, 5, nwirecalls); memset(&req, 0, sizeof req); req.owner = &foreign;