ipc, srv, gtk: the client's text goes over and a take-back comes back

The engine can reach a Hanja reading back into the text the client
already holds, but only if the frontend hands that text over and can
take some of it away again.  GTK 3 has both: retrieve-surrounding
brings the text around the cursor and delete-surrounding removes runes
before it, and a widget that answers neither leaves the text empty, so
nothing is ever reached into or taken from it.

The wire grows a control frame for the text, sent like the caret only
when it changes, and one byte in every response for the runes to take
back.  That byte moves the length fields along, so the version goes to
2: an old daemon and a new module, either way round, fail the handshake
and the module falls through to GtkIMContextSimple rather than misread
a frame.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-18 14:31:21 +09:00
parent f7277f54a3
commit 910bf51347
11 changed files with 286 additions and 41 deletions

View File

@@ -177,34 +177,37 @@ ipc_runtime_path(struct ct *t)
void
ipc_response_pack_boundaries(struct ct *t)
{
static const uchar withpre[] = { 1, 1, 0, 'A', 2, 0, 'x', 'y' };
static const uchar withoutpre[] = { 1, 1, 0, 'A' };
static const uchar withpre[] = { 1, 3, 1, 0, 'A', 2, 0, 'x', 'y' };
static const uchar withoutpre[] = { 1, 0, 1, 0, 'A' };
uchar out[Ipcmaxresp+1], field[Ipcfieldmax];
int n;
n = ipcpackresp(out, sizeof out, 7, "A", 1, "xy", 2, 1);
n = ipcpackresp(out, sizeof out, 7, 3, "A", 1, "xy", 2, 1);
CT_EQ_INT(t, sizeof withpre, n);
CT_EQ_MEM(t, withpre, out, sizeof withpre);
n = ipcpackresp(out, sizeof out, 1, "A", 1, "xy", 2, 0);
n = ipcpackresp(out, sizeof out, 1, 0, "A", 1, "xy", 2, 0);
CT_EQ_INT(t, sizeof withoutpre, n);
CT_EQ_MEM(t, withoutpre, out, sizeof withoutpre);
n = ipcpackresp(out, sizeof out, 0, nil, 0, nil, 0, 1);
n = ipcpackresp(out, sizeof out, 0, 0, nil, 0, nil, 0, 1);
CT_EQ_INT(t, Ipcresphdrsz + Ipclensz, n);
memset(field, 'x', sizeof field);
memset(out, 0xa5, sizeof out);
n = ipcpackresp(out, Ipcmaxresp, 1,
n = ipcpackresp(out, Ipcmaxresp, 1, 0,
(char*)field, sizeof field, (char*)field, sizeof field, 1);
CT_EQ_INT(t, Ipcmaxresp, n);
CT_EQ_INT(t, 0, out[1]);
CT_EQ_INT(t, 1, out[2]);
CT_EQ_INT(t, 0, out[3+Ipcfieldmax]);
CT_EQ_INT(t, 1, out[4+Ipcfieldmax]);
CT_EQ_INT(t, 0, out[2]);
CT_EQ_INT(t, 1, out[3]);
CT_EQ_INT(t, 0, out[4+Ipcfieldmax]);
CT_EQ_INT(t, 1, out[5+Ipcfieldmax]);
CT_EQ_INT(t, 0xa5, out[Ipcmaxresp]);
CT_EQ_INT(t, -1, ipcpackresp(out, Ipcmaxresp-1, 1,
CT_EQ_INT(t, -1, ipcpackresp(out, Ipcmaxresp-1, 1, 0,
(char*)field, sizeof field, (char*)field, sizeof field, 1));
CT_EQ_INT(t, -1, ipcpackresp(out, sizeof out, 0,
CT_EQ_INT(t, -1, ipcpackresp(out, sizeof out, 0, 0,
(char*)field, Ipcfieldmax+1, "", 0, 0));
/* A take-back is a rune count and never leaves its byte. */
CT_EQ_INT(t, -1, ipcpackresp(out, sizeof out, 0, -1, "", 0, "", 0, 0));
CT_EQ_INT(t, -1, ipcpackresp(out, sizeof out, 0, 256, "", 0, "", 0, 0));
}
void
@@ -215,8 +218,8 @@ ipc_response_empty_and_preedit(struct ct *t)
Ipcresp resp;
int fd[2], nfirst, nsecond;
nfirst = ipcpackresp(first, sizeof first, 0, "", 0, "", 0, 0);
nsecond = ipcpackresp(second, sizeof second, 1,
nfirst = ipcpackresp(first, sizeof first, 0, 0, "", 0, "", 0, 0);
nsecond = ipcpackresp(second, sizeof second, 1, 0,
"go", 2, "kana", 4, 1);
if(!CT_CHECK(t, nfirst > 0 && nsecond > 0))
return;
@@ -252,9 +255,9 @@ ipc_response_max_and_drain(struct ct *t)
int fd[2], nfirst, nsecond;
memset(field, 'x', sizeof field);
nfirst = ipcpackresp(first, sizeof first, 1,
nfirst = ipcpackresp(first, sizeof first, 1, 0,
(char*)field, sizeof field, (char*)field, sizeof field, 1);
nsecond = ipcpackresp(second, sizeof second, 0, "ok", 2, "", 0, 0);
nsecond = ipcpackresp(second, sizeof second, 0, 0, "ok", 2, "", 0, 0);
if(!CT_CHECK(t, nfirst == Ipcmaxresp && nsecond > 0))
return;
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
@@ -285,7 +288,7 @@ ipc_response_fragmented_and_truncated(struct ct *t)
Ipcresp resp;
int fd[2], i, n;
n = ipcpackresp(frame, sizeof frame, 1, "abc", 3, "xy", 2, 1);
n = ipcpackresp(frame, sizeof frame, 1, 2, "abc", 3, "xy", 2, 1);
if(!CT_CHECK(t, n > 0))
return;
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){