diff --git a/fn.h b/fn.h index 738edd3..b9f4745 100644 --- a/fn.h +++ b/fn.h @@ -1,6 +1,7 @@ void die(char*, ...); int sinit(Str*, char*, int); +void stail(Str*, char*, int); void sclear(Str*); void sputr(Str*, Rune); void spopr(Str*); diff --git a/gtk/main.c b/gtk/main.c index c6819ff..851a037 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -24,6 +24,9 @@ struct Im int cursorvalid; int caretsent; unsigned char sent[Ipccaretsz]; + char surround[Ipcfieldmax]; + int nsurround; + int surroundsent; }; typedef struct ImClass ImClass; @@ -65,6 +68,7 @@ srvdrop(Im *im, int notify) im->fd = -1; im->ext = 0; im->caretsent = 0; + im->surroundsent = 0; if(notify) setpreedit(im, "", 0); else{ @@ -157,6 +161,58 @@ sendcaret(Im *im) return 0; } +/* + * The text just before the cursor, as much of it as a reading can use, + * cut on a rune boundary. A widget that keeps none leaves it empty, and + * then the daemon never asks for any of it back. + */ +static void +readsurround(Im *im, GtkIMContext *ctx) +{ + char *text, *p; + gint cursor; + int start; + + im->nsurround = 0; + if(!gtk_im_context_get_surrounding(ctx, &text, &cursor)) + return; + if(cursor < 0 || cursor > (gint)strlen(text)){ + g_free(text); + return; + } + start = cursor > Ipcfieldmax ? cursor - Ipcfieldmax : 0; + for(p = text + start; p < text + cursor && (*p & 0xc0) == 0x80; p++) + ; + im->nsurround = text + cursor - p; + memcpy(im->surround, p, im->nsurround); + g_free(text); +} + +/* Sent only when it changes, as the caret is. */ +static int +sendsurround(Im *im, GtkIMContext *ctx) +{ + unsigned char hdr[Ipcreqsz]; + char was[Ipcfieldmax]; + int nwas; + + if(im->fd < 0 || !im->ext) + return 0; + nwas = im->nsurround; + memcpy(was, im->surround, nwas); + readsurround(im, ctx); + if(im->surroundsent && nwas == im->nsurround && + memcmp(was, im->surround, nwas) == 0) + return 0; + ipcpacksurround(hdr, im->nsurround); + if(ipcsend(im->fd, hdr, sizeof hdr) < 0 || + (im->nsurround > 0 && + ipcsend(im->fd, im->surround, im->nsurround) < 0)) + return -1; + im->surroundsent = 1; + return 0; +} + static int srvconnect(Im *im) { @@ -286,7 +342,7 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev) if(srvconnect(im) < 0) return simplefilter(ctx, ev, 0); /* A retained GDK window may have moved since the last cursor report. */ - if(sendcaret(im) < 0){ + if(sendcaret(im) < 0 || sendsurround(im, ctx) < 0){ srvclose(im); return simplefilter(ctx, ev, 0); } @@ -298,6 +354,11 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev) } if(im->usepreedit && commit[0] != '\0' && im->prelen > 0) setpreedit(im, "", 0); + /* The reading reached into the widget's own text: give it back. */ + if(resp.del > 0){ + gtk_im_context_delete_surrounding(ctx, -resp.del, resp.del); + im->surroundsent = 0; + } if(commit[0] != '\0') g_signal_emit_by_name(ctx, "commit", commit); if(im->usepreedit) diff --git a/ipc.c b/ipc.c index b3a107a..30ce0b2 100644 --- a/ipc.c +++ b/ipc.c @@ -252,6 +252,22 @@ ipcpackcaret(unsigned char req[Ipccaretsz], int valid, int32_t x, } } +void +ipcpacksurround(unsigned char req[Ipcreqsz], size_t n) +{ + memset(req, 0, Ipcreqsz); + req[0] = Ipcext; + req[1] = Ipcversion; + req[2] = Ipcopsurround; + putlen(req + 4, n); +} + +size_t +ipcsurroundlen(const unsigned char req[Ipcreqsz]) +{ + return getlen(req + 4); +} + void ipcunpackreq(const unsigned char req[Ipcreqsz], int *want, uint32_t *mod, uint32_t *key) @@ -281,6 +297,11 @@ ipcreqtype(const unsigned char req[Ipcreqsz]) if(req[0] != Ipcext || req[3] > 1) return Ipcunknown; return Ipccaret; + case Ipcopsurround: + if(req[0] != Ipcext || req[3] != 0 || + getlen(req + 4) > Ipcfieldmax) + return Ipcunknown; + return Ipcsurround; } return Ipcunknown; } @@ -305,19 +326,21 @@ ipcreqreset(const unsigned char req[Ipcreqsz]) } int -ipcpackresp(unsigned char *dst, size_t cap, int eaten, +ipcpackresp(unsigned char *dst, size_t cap, int eaten, int del, const char *commit, size_t ncommit, const char *preedit, size_t npreedit, int want) { size_t n; - if(ncommit > Ipcfieldmax || npreedit > Ipcfieldmax) + if(ncommit > Ipcfieldmax || npreedit > Ipcfieldmax || + del < 0 || del > 0xff) return -1; n = Ipcresphdrsz + ncommit + (want ? Ipclensz + npreedit : 0); if(cap < n) return -1; dst[0] = eaten != 0; - putlen(dst + 1, ncommit); + dst[1] = del; + putlen(dst + 2, ncommit); memcpy(dst + Ipcresphdrsz, commit, ncommit); if(want){ putlen(dst + Ipcresphdrsz + ncommit, npreedit); @@ -398,7 +421,7 @@ readfield(int fd, char *dst, int64_t until) int ipcreadresp(int fd, int want, char *commit, char *preedit, Ipcresp *resp) { - unsigned char eaten; + unsigned char head[2]; int64_t until; int n; @@ -406,13 +429,14 @@ ipcreadresp(int fd, int want, char *commit, char *preedit, Ipcresp *resp) preedit[0] = '\0'; memset(resp, 0, sizeof *resp); until = deadline(); - if(readwait(fd, &eaten, 1, until) < 0) + if(readwait(fd, head, sizeof head, until) < 0) return -1; - if(eaten > 1){ + if(head[0] > 1){ errno = EPROTO; return -1; } - resp->eaten = eaten; + resp->eaten = head[0]; + resp->del = head[1]; n = readfield(fd, commit, until); if(n < 0) return -1; diff --git a/ipc.h b/ipc.h index 660f96f..41bc9a0 100644 --- a/ipc.h +++ b/ipc.h @@ -8,9 +8,11 @@ * The server identifies the connection as the engine owner; that identity is * not sent on the wire. * Only Mmask modifier bits are retained. - * Response: [eaten, commit-length-low, commit-length-high, commit...], - * followed, when requested, by + * Response: [eaten, take-back, commit-length-low, commit-length-high, + * commit...], followed, when requested, by * [preedit-length-low, preedit-length-high, preedit...]. + * Take-back is the count of runes of the client's own text, just before the + * cursor, to remove before inserting the commit. * Lengths are little-endian byte counts; fields are at most Ipcfieldmax bytes, * so callers read them into char[Ipcfieldmax+1]. * @@ -19,6 +21,10 @@ * frames are understood. * Caret control is sixteen bytes: [0x80, version, 1, valid, x, y, h], * with the signed coordinates and height encoded as little-endian 32-bit words. + * Surrounding-text control is six bytes, [0x80, version, 2, 0, length-low, + * length-high], and then that many bytes of the text just before the cursor. + * It changes what rides along with the requests that follow and is not + * answered. */ enum @@ -26,9 +32,10 @@ enum Ipcreqwant = 1<<0, Ipcreqreset = 1<<1, Ipcext = 1<<7, - Ipcversion = 1, + Ipcversion = 2, Ipcopcap = 0, Ipcopcaret = 1, + Ipcopsurround = 2, Kspec = 0x110000, Kback = Kspec|0x08, @@ -57,7 +64,7 @@ enum Ipcreqsz = 6, Ipccaretsz = 16, Ipclensz = 2, - Ipcresphdrsz = 1 + Ipclensz, + Ipcresphdrsz = 2 + Ipclensz, Ipcfieldmax = 256, Ipcmaxresp = Ipcresphdrsz + Ipcfieldmax + Ipclensz + Ipcfieldmax, Ipcwaitms = 250, @@ -69,12 +76,14 @@ enum Ipckey, Ipccap, Ipccaret, + Ipcsurround, }; typedef struct Ipcresp Ipcresp; struct Ipcresp { int eaten; + int del; /* runes of the client's own text to take back first */ /* Bytes copied to each caller buffer, excluding its trailing NUL. */ size_t commitlen; size_t preeditlen; @@ -86,11 +95,13 @@ void ipcpackreq(unsigned char[Ipcreqsz], int, uint32_t, uint32_t); void ipcpackreset(unsigned char[Ipcreqsz], int); void ipcpackcap(unsigned char[Ipcreqsz], int); void ipcpackcaret(unsigned char[Ipccaretsz], int, int32_t, int32_t, int32_t); +void ipcpacksurround(unsigned char[Ipcreqsz], size_t); +size_t ipcsurroundlen(const unsigned char[Ipcreqsz]); void ipcunpackreq(const unsigned char[Ipcreqsz], int*, uint32_t*, uint32_t*); int ipcunpackcaret(const unsigned char[Ipccaretsz], int*, int32_t*, int32_t*, int32_t*); int ipcreqtype(const unsigned char[Ipcreqsz]); int ipcreqreset(const unsigned char[Ipcreqsz]); -int ipcpackresp(unsigned char*, size_t, int, const char*, size_t, const char*, size_t, int); +int ipcpackresp(unsigned char*, size_t, int, int, const char*, size_t, const char*, size_t, int); int ipcreadn(int, void*, size_t); int ipcsend(int, const void*, size_t); int ipcreadresp(int, int, char*, char*, Ipcresp*); diff --git a/srv.c b/srv.c index 1cd9b86..8bb57cc 100644 --- a/srv.c +++ b/srv.c @@ -31,11 +31,17 @@ srvnote(void *v, char *note) return 0; } +/* + * 0 for a request to make, 1 for a frame that only changes what rides + * along with the requests after it, -1 for anything else. + */ static int srvreadreq(int fd, Keyreq *kr, int *want) { uchar req[Ipccaretsz]; + char text[Ipcfieldmax]; int valid; + size_t n; int32_t x, y, h; if(ipcreadn(fd, req, Ipcreqsz) < 0) @@ -62,6 +68,12 @@ srvreadreq(int fd, Keyreq *kr, int *want) kr->caret.y = y; kr->caret.h = h; return 0; + case Ipcsurround: + n = ipcsurroundlen(req); + if(n > 0 && ipcreadn(fd, text, n) < 0) + return -1; + stail(&kr->surround, text, n); + return 1; } return -1; } @@ -78,7 +90,7 @@ clientthread(void *arg) Keyres res; uchar out[Ipcmaxresp], token; char commit[Maxutf], preedit[Maxutf]; - int fd, n, ncommit, npreedit, want; + int fd, n, ncommit, npreedit, rv, want; fd = (int)(uintptr)arg; threadsetname("client %d", fd); @@ -86,7 +98,9 @@ clientthread(void *arg) kr.reply = chancreate(sizeof(Keyres), 0); kr.owner = &fd; kr.clientpre = 1; - while(srvreadreq(fd, &kr, &want) >= 0){ + while((rv = srvreadreq(fd, &kr, &want)) >= 0){ + if(rv != 0) + continue; if(kr.op != Keycaret) kr.clientpre = want; chansend(keyc, &kr); @@ -97,7 +111,7 @@ clientthread(void *arg) npreedit = stoutf(&res.preedit, preedit, sizeof preedit); /* A capability reply is always eaten: it marks the extension. */ n = ipcpackresp(out, sizeof out, kr.op == Keycap || res.eaten, - commit, ncommit, preedit, npreedit, want); + res.del, commit, ncommit, preedit, npreedit, want); if(n < 0 || ipcsend(fd, out, n) < 0) break; } diff --git a/str.c b/str.c index 72fa02f..0e0f024 100644 --- a/str.c +++ b/str.c @@ -24,6 +24,21 @@ sinit(Str *s, char *src, int n) return 1; } +/* The last runes of n bytes of UTF-8, as many as a Str holds. */ +void +stail(Str *s, char *src, int n) +{ + Rune r; + char *p; + int nr; + + nr = utfnlen(src, n); + for(p = src; nr > Maxrunes; nr--) + p += chartorune(&r, p); + if(!sinit(s, p, n - (p - src))) + sclear(s); +} + void sclear(Str *s) { diff --git a/tests/daemon_restart_test.c b/tests/daemon_restart_test.c index 599bfd4..7f6ec27 100644 --- a/tests/daemon_restart_test.c +++ b/tests/daemon_restart_test.c @@ -297,8 +297,8 @@ rejectold(Test *t, char *where) static int openreplacement(Test *t) { - unsigned char selectjp[] = {1, 0, 0, 0, 0}; - unsigned char keyk[] = {1, 0, 0, 1, 0, 'k'}; + unsigned char selectjp[] = {1, 0, 0, 0, 0, 0}; + unsigned char keyk[] = {1, 0, 0, 0, 1, 0, 'k'}; char path[96]; t->ipcnew = connectsocket(t->l.socket, nowms() + Calltimeout); diff --git a/tests/gtk_live_test.c b/tests/gtk_live_test.c index 873d54c..e41c7d8 100644 --- a/tests/gtk_live_test.c +++ b/tests/gtk_live_test.c @@ -32,6 +32,7 @@ enum Ecaret, Ekey, Ereset, + Esurround, }; enum @@ -55,6 +56,7 @@ struct Event int32_t x; int32_t y; int32_t h; + char text[Ipcfieldmax+1]; }; struct Server @@ -69,6 +71,7 @@ struct Server int eaten; int stall; int response; + int del; int nclose; char pre[Ipcfieldmax+1]; Event ev[Maxevent]; @@ -83,6 +86,10 @@ struct Siglog char shown[Ipcfieldmax+1]; char commit[Ipcfieldmax+1]; int prechanged; + char around[64]; /* what the widget answers retrieve-surrounding */ + int aroundcursor; + int deloffset; + int deln; }; static void @@ -110,13 +117,14 @@ sendresponse(Server *s, int fd, int eaten, int want, int key) unsigned char buf[Ipcmaxresp]; const char *commit; char pre[Ipcfieldmax+1]; - int n, response, stall; + int del, n, response, stall; size_t ncommit, npreedit; pthread_mutex_lock(&s->lock); memcpy(pre, s->pre, sizeof pre); response = s->response; stall = s->stall; + del = key ? s->del : 0; pthread_mutex_unlock(&s->lock); if(stall) return 1; @@ -135,7 +143,7 @@ sendresponse(Server *s, int fd, int eaten, int want, int key) pre[2] = 'y'; npreedit = 3; } - n = ipcpackresp(buf, sizeof buf, eaten, commit, ncommit, + n = ipcpackresp(buf, sizeof buf, eaten, del, commit, ncommit, pre, npreedit, want); return n >= 0 && ipcsend(fd, buf, n) == 0; } @@ -146,6 +154,7 @@ request(Server *s, int fd) unsigned char buf[Ipccaretsz]; uint32_t mod, key; Event e; + size_t n; int old, type, want; if(ipcreadn(fd, buf, Ipcreqsz) < 0) @@ -168,6 +177,14 @@ request(Server *s, int fd) e.type = Ecaret; record(s, &e); return 1; + case Ipcsurround: + n = ipcsurroundlen(buf); + if(n > 0 && ipcreadn(fd, e.text, n) < 0) + return 0; + e.text[n] = '\0'; + e.type = Esurround; + record(s, &e); + return 1; case Ipckey: ipcunpackreq(buf, &want, &mod, &key); e.want = want; @@ -513,6 +530,25 @@ clearlog(Siglog *l) memset(l, 0, sizeof *l); } +/* The widget's half of the surrounding-text contract. */ +static gboolean +retrievearound(GtkIMContext *ctx, Siglog *l) +{ + if(l->around[0] == '\0') + return FALSE; + gtk_im_context_set_surrounding(ctx, l->around, -1, l->aroundcursor); + return TRUE; +} + +static gboolean +deletearound(GtkIMContext *ctx, gint offset, gint n, Siglog *l) +{ + (void)ctx; + l->deloffset = offset; + l->deln = n; + return TRUE; +} + static GtkIMContext* newcontext(Siglog *l) { @@ -520,6 +556,8 @@ newcontext(Siglog *l) ctx = gtk_im_multicontext_new(); gtk_im_multicontext_set_context_id(GTK_IM_MULTICONTEXT(ctx), "strans"); + g_signal_connect(ctx, "retrieve-surrounding", G_CALLBACK(retrievearound), l); + g_signal_connect(ctx, "delete-surrounding", G_CALLBACK(deletearound), l); g_signal_connect(ctx, "preedit-start", G_CALLBACK(prestart), l); g_signal_connect(ctx, "preedit-changed", G_CALLBACK(prechange), l); g_signal_connect(ctx, "preedit-end", G_CALLBACK(preend), l); @@ -667,7 +705,7 @@ main(int argc, char **argv) Check(eventcount(&srv) == 0, "cursor reporting opened the socket"); setpre(&srv, ""); Check(key(ctx, win, GDK_KEY_a), "initial key was not eaten"); - Check(waitcount(&srv, 3), "initial protocol frames timed out"); + Check(waitcount(&srv, 4), "initial protocol frames timed out"); e = getevent(&srv, 0); Check(e.type == Ecap && e.want == 1, "default preedit capability missing"); e = getevent(&srv, 1); @@ -682,6 +720,9 @@ main(int argc, char **argv) e.x, e.y, e.h, (ox + rect.x) * scale, (oy + rect.y) * scale, rect.height * scale); e = getevent(&srv, 2); + Check(e.type == Esurround && e.text[0] == '\0', + "a widget that keeps no text sent some"); + e = getevent(&srv, 3); Check(e.type == Ekey && e.want == 1, "initial key framing changed"); Check(log.n == 0, "initial empty preedit emitted %s", log.event); setpre(&srv, "ㅋ"); @@ -804,6 +845,38 @@ main(int argc, char **argv) "visible-entry key did not reach the daemon"); gtk_im_context_set_client_window(ctx, win); + /* The widget's own text goes over, and a take-back comes back. */ + setpre(&srv, ""); + clearlog(&log); + snprintf(log.around, sizeof log.around, "%s", "가나한"); + log.aroundcursor = 9; + first = eventcount(&srv); + Check(key(ctx, win, GDK_KEY_j), "surrounding-text key was not eaten"); + Check(waitcount(&srv, first + 2), "surrounding text did not reach the daemon"); + e = getevent(&srv, first); + Check(e.type == Esurround && strcmp(e.text, "가나한") == 0, + "the daemon was sent %s, not the text before the cursor", e.text); + Check(getevent(&srv, first + 1).type == Ekey, + "the text did not ride ahead of the key"); + /* Sent again only when it changes. */ + first = eventcount(&srv); + Check(key(ctx, win, GDK_KEY_j), "second surrounding-text key was not eaten"); + Check(waitcount(&srv, first + 1) && getevent(&srv, first).type == Ekey, + "unchanged surrounding text was sent again"); + setreply(&srv, 1, Rnormal, 0); + pthread_mutex_lock(&srv.lock); + srv.del = 2; + pthread_mutex_unlock(&srv.lock); + log.deln = 0; + Check(key(ctx, win, GDK_KEY_j), "take-back key was not eaten"); + Check(log.deloffset == -2 && log.deln == 2, + "the widget was told to take back %d runes at %d", log.deln, + log.deloffset); + pthread_mutex_lock(&srv.lock); + srv.del = 0; + pthread_mutex_unlock(&srv.lock); + log.around[0] = '\0'; + setpre(&srv, "reset"); clearlog(&log); Check(key(ctx, win, GDK_KEY_e), "reset setup key was not eaten"); diff --git a/tests/ipc_live_test.c b/tests/ipc_live_test.c index be74c5a..f04951e 100644 --- a/tests/ipc_live_test.c +++ b/tests/ipc_live_test.c @@ -23,6 +23,7 @@ typedef struct Response Response; struct Response { int eaten; + int del; char commit[Ipcfieldmax+1]; char preedit[Ipcfieldmax+1]; }; @@ -50,7 +51,8 @@ readresponseuntil(int fd, int want, Response *res, int64_t deadline, return rv; } res->eaten = hdr[0] != 0; - n = getlen(hdr + 1); + res->del = hdr[1]; + n = getlen(hdr + 2); if(n > Ipcfieldmax){ fail("invalid commit length %zu", n); return -1; @@ -119,6 +121,18 @@ sendcap(int fd, int cap) return ipcsend(fd, req, sizeof req) == 0; } +static int +sendsurround(int fd, char *text) +{ + unsigned char req[Ipcreqsz]; + size_t n; + + n = strlen(text); + ipcpacksurround(req, n); + return ipcsend(fd, req, sizeof req) == 0 && + (n == 0 || ipcsend(fd, text, n) == 0); +} + static int expectresponse(int fd, int want, int eaten, char *commit, char *preedit, char *where) @@ -152,6 +166,33 @@ requestkey(int fd, int want, uint32_t mod, uint32_t key, int eaten, return expectresponse(fd, want, eaten, commit, preedit, where); } +/* + * The Korean habit: 한자 typed, then the Hanja key. The 한 is the + * client's by then, so the daemon must ask for it back and answer with + * the whole word. + */ +static int +requesthanja(int fd) +{ + Response res; + + if(!sendsurround(fd, "저는 한")) + return fail("send surrounding text: %s", strerror(errno)); + if(!requestkey(fd, 1, Mctrl, 's', 1, "", "", "select Korean") || + !requestkey(fd, 1, 0, 'w', 1, "", "ㅈ", "jamo") || + !requestkey(fd, 1, 0, 'k', 1, "", "자", "syllable") || + !requestkey(fd, 1, Mctrl, 'h', 1, "", "자", "Hanja search")) + return 0; + if(!sendkey(fd, 1, 0, Kret)) + return fail("send Hanja pick: %s", strerror(errno)); + if(readresponseuntil(fd, 1, &res, nowms() + Calltimeout, 0) != 1) + return 0; + if(strcmp(res.commit, "漢字") != 0 || res.del != 1) + return fail("Hanja pick committed %s and took back %d", + res.commit, res.del); + return 1; +} + static int tryclient(char *path, int64_t deadline, int *client) { @@ -258,7 +299,8 @@ runsmoke(char *path) requestkey(client, 1, Mctrl, 'n', 1, "", "", "select Japanese") && requestkey(client, 1, 0, 'k', 1, "", "k", "preedit") && - requestreset(client, 1, 1, "k", "", "reset"); + requestreset(client, 1, 1, "k", "", "reset") && + requesthanja(client); close(client); return ok; } diff --git a/tests/ipc_test.c b/tests/ipc_test.c index b81a95f..ab8ff95 100644 --- a/tests/ipc_test.c +++ b/tests/ipc_test.c @@ -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){ diff --git a/tests/live.c b/tests/live.c index 0fac6f2..0e0059d 100644 --- a/tests/live.c +++ b/tests/live.c @@ -767,7 +767,8 @@ ipcrequest(int fd, uint32_t mod, uint32_t key, unsigned char *want, int ipcprobe(int fd, char *where) { - unsigned char empty[] = {0, 0, 0, 0, 0}; + /* eaten, take-back, no commit, no preedit */ + unsigned char empty[] = {0, 0, 0, 0, 0, 0}; return ipcrequest(fd, 0, 0, empty, sizeof empty, where); }