fix(engine): a reset or a lost focus commits the pending text
Clicking elsewhere, changing focus, or any client reset dropped the composition in the GTK module and IBus (only XIM ResetIC handed it back), so typing 안녕 and clicking Send lost 녕. The engine now returns the pending text — a moved-to candidate first, as Enter would — on Keyreset and Keyrelease; the GTK module asks for it before closing on focus-out and commits it, IBus commits it on FocusOut, Reset and a switch to a password field, and XIM commits it on focus loss and hands it back on ResetIC without a separate capability probe.
This commit is contained in:
@@ -244,7 +244,8 @@ engine_active_owner_lifecycle(struct ct *t)
|
||||
checkstr(t, "stale release preserves owner", "にゃ", &shown);
|
||||
CT_EQ_PTR(t, &b, activeowner);
|
||||
|
||||
ownerrequest(&b, Keyrelease, 0, 0);
|
||||
res = ownerrequest(&b, Keyrelease, 0, 0);
|
||||
checkstr(t, "release hands the reading back", "にゃ", &res.commit);
|
||||
CT_EQ_PTR(t, nil, activeowner);
|
||||
CT_EQ_INT(t, 0, im.pre.n);
|
||||
CT_EQ_INT(t, 0, im.raw.n);
|
||||
@@ -274,6 +275,7 @@ engine_active_owner_reset(struct ct *t)
|
||||
checkstr(t, "preedit before reset", "か", &res.preedit);
|
||||
res = ownerrequest(&owner, Keyreset, 0, 0);
|
||||
CT_EQ_PTR(t, &owner, activeowner);
|
||||
checkstr(t, "reset hands the reading back", "か", &res.commit);
|
||||
CT_EQ_INT(t, 0, res.preedit.n);
|
||||
CT_CHECK(t, !caret.valid);
|
||||
res = ownerrequest(&owner, Keypress, 'n', 0);
|
||||
|
||||
@@ -820,7 +820,9 @@ main(int argc, char **argv)
|
||||
closes = closecount(&srv);
|
||||
gtk_im_context_focus_out(ctx);
|
||||
Check(waitclose(&srv, closes + 1), "focus-out did not close connection");
|
||||
Check(eventcount(&srv) == first, "focus-out sent a request before closing");
|
||||
Check(eventcount(&srv) == first + 1 &&
|
||||
getevent(&srv, first).type == Ereset,
|
||||
"focus-out did not hand the pending text back before closing");
|
||||
Check(strcmp(log.event, "CE") == 0, "focus-out clear order was %s", log.event);
|
||||
|
||||
/* Dispose is repeatable, closes the connection, and emits no preedit. */
|
||||
|
||||
@@ -27,6 +27,7 @@ struct Log
|
||||
int rfirst;
|
||||
int rcommit;
|
||||
int rdone;
|
||||
int flushed;
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -51,6 +52,11 @@ legacy(IBusInputContext *ctx, IBusText *text, guint cursor,
|
||||
log = arg;
|
||||
log->legacy++;
|
||||
s = ibus_text_get_text(text);
|
||||
if(log->repeat == 3){
|
||||
if(s[0] != '\0' || cursor != 0 || visible)
|
||||
log->invalid = 1;
|
||||
return;
|
||||
}
|
||||
if(log->repeat != 0){
|
||||
revent(log, 'P');
|
||||
if(log->repeat == 1){
|
||||
@@ -134,6 +140,16 @@ commit(IBusInputContext *ctx, IBusText *text, void *arg)
|
||||
log->commit++;
|
||||
s = ibus_text_get_text(text);
|
||||
attrs = ibus_text_get_attributes(text);
|
||||
if(log->repeat == 3){
|
||||
/* Focus loss hands the pending syllable back as a commit. */
|
||||
if(strcmp(s, "ㅋ") == 0)
|
||||
log->flushed = 1;
|
||||
else
|
||||
log->invalid = 1;
|
||||
if(log->loop != NULL && log->waiting != NULL && *log->waiting)
|
||||
g_main_loop_quit(log->loop);
|
||||
return;
|
||||
}
|
||||
if(log->repeat != 0){
|
||||
revent(log, 'K');
|
||||
if(log->repeat != 2 || strcmp(s, "ㅋ") != 0 || attrs == NULL ||
|
||||
@@ -253,9 +269,10 @@ main(int argc, char **argv)
|
||||
log.rn = 0;
|
||||
ok = ok && ibus_input_context_process_key_event(a, 'z', 0, 0) &&
|
||||
waitflag(&log, &log.rdone) && strcmp(log.revent, "PKP") == 0;
|
||||
log.repeat = 0;
|
||||
log.repeat = 3;
|
||||
ibus_input_context_focus_out(a);
|
||||
if(log.legacy == 0 || log.modern != 0 || log.commit != 2 ||
|
||||
ok = ok && waitflag(&log, &log.flushed);
|
||||
if(log.legacy == 0 || log.modern != 0 || log.commit != 3 ||
|
||||
log.invalid || !log.done || !log.atext || !log.aclear ||
|
||||
log.clearctx != a || !log.rdone)
|
||||
ok = 0;
|
||||
@@ -265,12 +282,12 @@ main(int argc, char **argv)
|
||||
g_object_unref(bus);
|
||||
if(!ok){
|
||||
fprintf(stderr,
|
||||
"ibus_client_smoke: legacy=%d modern=%d commit=%d invalid=%d preedit=%d committed=%d transfer-text=%d a-clear=%d repeat=%s\n",
|
||||
"ibus_client_smoke: legacy=%d modern=%d commit=%d invalid=%d preedit=%d committed=%d transfer-text=%d a-clear=%d repeat=%s flushed=%d\n",
|
||||
log.legacy, log.modern, log.commit, log.invalid,
|
||||
log.sawpreedit, log.sawcommit, log.atext, log.aclear,
|
||||
log.revent);
|
||||
log.revent, log.flushed);
|
||||
return 1;
|
||||
}
|
||||
printf("official libibus client preedit, commit, and owner clear: ok\n");
|
||||
printf("official libibus client preedit, commit, owner clear, and focus-out hand-back: ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -135,11 +135,12 @@ expectresponse(int fd, int want, int eaten, char *commit, char *preedit,
|
||||
}
|
||||
|
||||
static int
|
||||
requestreset(int fd, int want, int eaten, char *preedit, char *where)
|
||||
requestreset(int fd, int want, int eaten, char *commit, char *preedit,
|
||||
char *where)
|
||||
{
|
||||
if(!sendreset(fd, want))
|
||||
return fail("send %s: %s", where, strerror(errno));
|
||||
return expectresponse(fd, want, eaten, "", preedit, where);
|
||||
return expectresponse(fd, want, eaten, commit, preedit, where);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -257,7 +258,7 @@ runsmoke(char *path)
|
||||
requestkey(client, 1, Mctrl, 'n', 1, "", "",
|
||||
"select Japanese") &&
|
||||
requestkey(client, 1, 0, 'k', 1, "", "k", "preedit") &&
|
||||
requestreset(client, 1, 1, "", "reset");
|
||||
requestreset(client, 1, 1, "k", "", "reset");
|
||||
close(client);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -163,14 +163,15 @@ allowrequest(Pump *p)
|
||||
}
|
||||
|
||||
static int
|
||||
readreply(struct ct *t, Testclient *client, int want, char *preedit)
|
||||
readreply(struct ct *t, Testclient *client, int want, char *wantcommit,
|
||||
char *preedit)
|
||||
{
|
||||
char commit[Ipcfieldmax+1];
|
||||
Ipcresp resp;
|
||||
|
||||
if(ipcreadresp(client->peer, want, commit, preedit, &resp) < 0)
|
||||
return CT_ERRORF(t, "response read failed: %s", strerror(errno));
|
||||
CT_EQ_STR(t, "", commit);
|
||||
CT_EQ_STR(t, wantcommit, commit);
|
||||
return resp.eaten;
|
||||
}
|
||||
|
||||
@@ -257,14 +258,14 @@ server_connection_ownership(struct ct *t)
|
||||
req = nextrequest(t, &f.pump, Keypress);
|
||||
aowner = req.owner;
|
||||
allowrequest(&f.pump);
|
||||
CT_CHECK(t, readreply(t, &a, 1, preedit));
|
||||
CT_CHECK(t, readreply(t, &a, 1, "", preedit));
|
||||
CT_EQ_STR(t, "k", preedit);
|
||||
if(!sendkey(t, &a, 1, 0, 'a'))
|
||||
goto cleanup;
|
||||
req = nextrequest(t, &f.pump, Keypress);
|
||||
CT_EQ_PTR(t, aowner, req.owner);
|
||||
allowrequest(&f.pump);
|
||||
CT_CHECK(t, readreply(t, &a, 1, preedit));
|
||||
CT_CHECK(t, readreply(t, &a, 1, "", preedit));
|
||||
CT_EQ_STR(t, "か", preedit);
|
||||
CT_EQ_PTR(t, aowner, testengineowner());
|
||||
|
||||
@@ -274,7 +275,7 @@ server_connection_ownership(struct ct *t)
|
||||
bowner = req.owner;
|
||||
CT_CHECK(t, bowner != aowner);
|
||||
allowrequest(&f.pump);
|
||||
CT_CHECK(t, readreply(t, &b, 1, preedit));
|
||||
CT_CHECK(t, readreply(t, &b, 1, "", preedit));
|
||||
CT_EQ_STR(t, "ん", preedit);
|
||||
CT_EQ_PTR(t, bowner, testengineowner());
|
||||
|
||||
@@ -283,14 +284,14 @@ 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));
|
||||
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, Kmodfirst))
|
||||
goto cleanup;
|
||||
req = nextrequest(t, &f.pump, Keypress);
|
||||
allowrequest(&f.pump);
|
||||
CT_CHECK(t, !readreply(t, &b, 1, preedit));
|
||||
CT_CHECK(t, !readreply(t, &b, 1, "", preedit));
|
||||
CT_EQ_STR(t, "ん", preedit);
|
||||
|
||||
disconnectclient(t, &f.pump, &a, aowner);
|
||||
@@ -299,12 +300,12 @@ server_connection_ownership(struct ct *t)
|
||||
goto cleanup;
|
||||
req = nextrequest(t, &f.pump, Keypress);
|
||||
allowrequest(&f.pump);
|
||||
CT_CHECK(t, readreply(t, &b, 1, preedit));
|
||||
CT_CHECK(t, readreply(t, &b, 1, "", preedit));
|
||||
if(!sendkey(t, &b, 1, 0, 'a'))
|
||||
goto cleanup;
|
||||
req = nextrequest(t, &f.pump, Keypress);
|
||||
allowrequest(&f.pump);
|
||||
CT_CHECK(t, readreply(t, &b, 1, preedit));
|
||||
CT_CHECK(t, readreply(t, &b, 1, "", preedit));
|
||||
CT_EQ_STR(t, "にゃ", preedit);
|
||||
|
||||
if(!sendreset(t, &b, 1))
|
||||
@@ -312,7 +313,7 @@ server_connection_ownership(struct ct *t)
|
||||
req = nextrequest(t, &f.pump, Keyreset);
|
||||
CT_EQ_PTR(t, bowner, req.owner);
|
||||
allowrequest(&f.pump);
|
||||
CT_CHECK(t, readreply(t, &b, 1, preedit));
|
||||
CT_CHECK(t, readreply(t, &b, 1, "にゃ", preedit));
|
||||
CT_EQ_STR(t, "", preedit);
|
||||
CT_EQ_PTR(t, bowner, testengineowner());
|
||||
|
||||
@@ -321,7 +322,7 @@ server_connection_ownership(struct ct *t)
|
||||
req = nextrequestcap(t, &f.pump, Keypress, 0);
|
||||
CT_EQ_PTR(t, bowner, req.owner);
|
||||
allowrequest(&f.pump);
|
||||
CT_CHECK(t, readreply(t, &b, 0, preedit));
|
||||
CT_CHECK(t, readreply(t, &b, 0, "", preedit));
|
||||
errno = 0;
|
||||
n = recv(b.peer, &byte, 1, MSG_PEEK|MSG_DONTWAIT);
|
||||
CT_EQ_INT(t, -1, n);
|
||||
@@ -331,7 +332,7 @@ server_connection_ownership(struct ct *t)
|
||||
goto cleanup;
|
||||
req = nextrequest(t, &f.pump, Keypress);
|
||||
allowrequest(&f.pump);
|
||||
CT_CHECK(t, readreply(t, &b, 1, preedit));
|
||||
CT_CHECK(t, readreply(t, &b, 1, "", preedit));
|
||||
CT_EQ_STR(t, "か", preedit);
|
||||
|
||||
disconnectclient(t, &f.pump, &b, bowner);
|
||||
@@ -393,7 +394,7 @@ server_extension_stream(struct ct *t)
|
||||
aowner = req.owner;
|
||||
CT_EQ_PTR(t, nil, testengineowner());
|
||||
allowrequest(&f.pump);
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit));
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 1, "", preedit));
|
||||
CT_EQ_STR(t, "", preedit);
|
||||
|
||||
ipcpackcaret(frame, 1, -101, -7, 23);
|
||||
@@ -419,7 +420,7 @@ server_extension_stream(struct ct *t)
|
||||
CT_EQ_INT(t, -7, req.caret.y);
|
||||
CT_EQ_INT(t, 23, req.caret.h);
|
||||
allowrequest(&f.pump);
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit));
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 1, "", preedit));
|
||||
CT_EQ_STR(t, "k", preedit);
|
||||
CT_EQ_PTR(t, aowner, testengineowner());
|
||||
|
||||
@@ -430,7 +431,7 @@ server_extension_stream(struct ct *t)
|
||||
CT_EQ_PTR(t, aowner, req.owner);
|
||||
CT_EQ_INT(t, 1, req.caret.valid);
|
||||
allowrequest(&f.pump);
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 0, preedit));
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 0, "", preedit));
|
||||
|
||||
/* Reset is still the old six-byte frame and does not discard caret. */
|
||||
ipcpackreset(frame, 0);
|
||||
@@ -443,14 +444,14 @@ server_extension_stream(struct ct *t)
|
||||
CT_EQ_INT(t, -7, req.caret.y);
|
||||
CT_EQ_INT(t, 23, req.caret.h);
|
||||
allowrequest(&f.pump);
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 0, preedit));
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 0, "k", preedit));
|
||||
|
||||
ipcpackcap(frame, Cclientpreedit);
|
||||
if(!sendframe(t, &a, frame, Ipcreqsz, 0))
|
||||
goto cleanup;
|
||||
req = nextrequest(t, &f.pump, Keycap);
|
||||
allowrequest(&f.pump);
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit));
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 1, "", preedit));
|
||||
CT_EQ_STR(t, "", preedit);
|
||||
ipcpackreq(frame, 1, 0, 'n');
|
||||
if(!sendframe(t, &a, frame, Ipcreqsz, 0))
|
||||
@@ -459,7 +460,7 @@ server_extension_stream(struct ct *t)
|
||||
CT_EQ_INT(t, 1, req.caret.valid);
|
||||
CT_EQ_INT(t, -101, req.caret.x);
|
||||
allowrequest(&f.pump);
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit));
|
||||
CT_EQ_INT(t, 1, readreply(t, &a, 1, "", preedit));
|
||||
CT_EQ_STR(t, "ん", preedit);
|
||||
|
||||
disconnectclient(t, &f.pump, &a, aowner);
|
||||
@@ -471,7 +472,7 @@ server_extension_stream(struct ct *t)
|
||||
bowner = req.owner;
|
||||
CT_EQ_INT(t, 0, req.caret.valid);
|
||||
allowrequest(&f.pump);
|
||||
CT_EQ_INT(t, 1, readreply(t, &b, 1, preedit));
|
||||
CT_EQ_INT(t, 1, readreply(t, &b, 1, "", preedit));
|
||||
CT_EQ_STR(t, "k", preedit);
|
||||
|
||||
disconnectclient(t, &f.pump, &b, bowner);
|
||||
@@ -532,7 +533,7 @@ server_rejects_unknown_extension(struct ct *t)
|
||||
req = nextrequest(t, &f.pump, Keypress);
|
||||
owner = req.owner;
|
||||
allowrequest(&f.pump);
|
||||
CT_EQ_INT(t, 1, readreply(t, &good, 1, preedit));
|
||||
CT_EQ_INT(t, 1, readreply(t, &good, 1, "", preedit));
|
||||
CT_EQ_STR(t, "k", preedit);
|
||||
disconnectclient(t, &f.pump, &good, owner);
|
||||
|
||||
|
||||
@@ -901,7 +901,6 @@ xim_adapter_callback_cleanup(struct ct *t)
|
||||
memset(&reply, 0, sizeof reply);
|
||||
hdr.major_opcode = XCB_XIM_RESET_IC;
|
||||
callback(nil, nil, ic, &hdr, nil, &reply, nil);
|
||||
nexttrace(t, &f, Keycap, &state);
|
||||
nexttrace(t, &f, Keyreset, &state);
|
||||
notrace(t, &f);
|
||||
checkct(t, "reset reply", reply.committed_string,
|
||||
|
||||
Reference in New Issue
Block a user