ipc: strip machinery no peer uses; keep client state in one Keyreq

ipc.c: an AF_UNIX nonblocking connect completes at once or fails with
EAGAIN, so the EINPROGRESS/poll/SO_ERROR path and the fcntl juggling
were dead; the deadline plumbing checked a clock that cannot fail and
re-tested the deadline before every transfer although only waitfd
blocks; readfield's truncation and discard loop served the unit test,
since every caller owns char[Ipcfieldmax+1]; NULL-argument checks on
in-tree encoders are gone (peer validation stays). The primary key frame
is Ipckey, not 'legacy'; ipckeysym names the keysym mapping.

srv.c: the per-connection capability and caret already lived in the
persistent Keyreq; the mirror locals and 'negotiated' flag guarded a
protocol rule no client relied on. proccreate never fails in libthread.

gtk: focus-out no longer round-trips a reset before closing the socket
that releases the engine; the caret dedup compares the packed frame;
srvconnect's preedit no-ops and the insimple flag are gone.
This commit is contained in:
2026-08-16 15:53:08 +09:00
parent 43b469f70b
commit eb0f88f764
11 changed files with 205 additions and 394 deletions

View File

@@ -128,10 +128,10 @@ sendkey(int key, int mod)
static int static int
readresp(void) readresp(void)
{ {
char buf[Ipcfieldmax+1]; char commit[Ipcfieldmax+1], preedit[Ipcfieldmax+1];
Ipcresp resp; Ipcresp resp;
return ipcreadresp(fd, 0, buf, sizeof buf, NULL, 0, &resp); return ipcreadresp(fd, 0, commit, preedit, &resp);
} }
static double static double

View File

@@ -1,5 +1,4 @@
#include <errno.h> #include <errno.h>
#include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -19,7 +18,6 @@ struct Im
int ext; int ext;
int private; int private;
int simpleactive; int simpleactive;
int insimple;
int simpledone; int simpledone;
char pre[Ipcfieldmax+1]; char pre[Ipcfieldmax+1];
int prelen; int prelen;
@@ -27,10 +25,7 @@ struct Im
GdkRectangle cursor; GdkRectangle cursor;
int cursorvalid; int cursorvalid;
int caretsent; int caretsent;
int sentvalid; unsigned char sent[Ipccaretsz];
int32_t sentx;
int32_t senty;
int32_t senth;
}; };
typedef struct ImClass ImClass; typedef struct ImClass ImClass;
@@ -86,22 +81,14 @@ srvclose(Im *im)
srvdrop(im, 1); srvdrop(im, 1);
} }
/* Both buffers are char[Ipcfieldmax+1]. */
static int static int
validtext(const char *s, size_t n) readresp(Im *im, int want, char *commit, char *pre, Ipcresp *resp)
{ {
return s[n] == '\0' && memchr(s, '\0', n) == NULL && if(ipcreadresp(im->fd, want, commit, pre, resp) < 0)
g_utf8_validate(s, n, NULL);
}
static int
readresp(Im *im, int want, char *commit, int ncommit, char *pre, int npre,
Ipcresp *resp)
{
if(ipcreadresp(im->fd, want, commit, ncommit, pre, npre,
resp) < 0)
return -1; return -1;
if(!validtext(commit, resp->commitlen) || if(!g_utf8_validate(commit, resp->commitlen, NULL) ||
(want && !validtext(pre, resp->preeditlen))){ (want && !g_utf8_validate(pre, resp->preeditlen, NULL))){
errno = EPROTO; errno = EPROTO;
return -1; return -1;
} }
@@ -117,76 +104,58 @@ dropwindow(Im *im)
im->cursorvalid = 0; im->cursorvalid = 0;
} }
/* Root-relative caret in device pixels; returns whether it is known. */
static int static int
caretget(Im *im, int *valid, int32_t *x, int32_t *y, int32_t *h) caretget(Im *im, int32_t *x, int32_t *y, int32_t *h)
{ {
gint rx, ry, scale; gint rx, ry, scale;
int64_t sx, sy, sh; int64_t sx, sy, sh;
*valid = 0;
*x = *y = *h = 0; *x = *y = *h = 0;
if(im->win != NULL && gdk_window_is_destroyed(im->win)) if(im->win != NULL && gdk_window_is_destroyed(im->win))
dropwindow(im); dropwindow(im);
if(!im->cursorvalid || im->win == NULL){ if(!im->cursorvalid || im->win == NULL || im->cursor.height < 0)
im->cursorvalid = 0;
return 0; return 0;
}
#ifdef GDK_WINDOWING_X11 #ifdef GDK_WINDOWING_X11
if(!GDK_IS_X11_WINDOW(im->win)){ if(!GDK_IS_X11_WINDOW(im->win))
im->cursorvalid = 0;
return 0; return 0;
}
#else #else
im->cursorvalid = 0;
return 0; return 0;
#endif #endif
if(im->cursor.height < 0){
im->cursorvalid = 0;
return 0;
}
scale = gdk_window_get_scale_factor(im->win); scale = gdk_window_get_scale_factor(im->win);
if(scale <= 0){ if(scale <= 0)
im->cursorvalid = 0;
return 0; return 0;
}
/* GDK translates the client-relative point before device scaling. */ /* GDK translates the client-relative point before device scaling. */
gdk_window_get_root_coords(im->win, 0, 0, &rx, &ry); gdk_window_get_root_coords(im->win, 0, 0, &rx, &ry);
sx = ((int64_t)rx + im->cursor.x) * scale; sx = ((int64_t)rx + im->cursor.x) * scale;
sy = ((int64_t)ry + im->cursor.y) * scale; sy = ((int64_t)ry + im->cursor.y) * scale;
sh = (int64_t)im->cursor.height * scale; sh = (int64_t)im->cursor.height * scale;
if(sx < INT32_MIN || sx > INT32_MAX || if(sx < INT32_MIN || sx > INT32_MAX ||
sy < INT32_MIN || sy > INT32_MAX || sh > INT32_MAX){ sy < INT32_MIN || sy > INT32_MAX || sh > INT32_MAX)
im->cursorvalid = 0;
return 0; return 0;
}
*valid = 1;
*x = sx; *x = sx;
*y = sy; *y = sy;
*h = sh; *h = sh;
return 0; return 1;
} }
static int static int
sendcaret(Im *im) sendcaret(Im *im)
{ {
unsigned char buf[Ipccaretsz]; unsigned char buf[Ipccaretsz];
int valid;
int32_t x, y, h; int32_t x, y, h;
int valid;
if(im->fd < 0 || !im->ext) if(im->fd < 0 || !im->ext)
return 0; return 0;
caretget(im, &valid, &x, &y, &h); valid = caretget(im, &x, &y, &h);
if(im->caretsent && valid == im->sentvalid && ipcpackcaret(buf, valid, x, y, h);
(!valid || (x == im->sentx && y == im->senty && h == im->senth))) if(im->caretsent && memcmp(buf, im->sent, sizeof buf) == 0)
return 0; return 0;
if(ipcpackcaret(buf, valid, x, y, h) < 0 || if(ipcsend(im->fd, buf, sizeof buf) < 0)
ipcsend(im->fd, buf, sizeof buf) < 0)
return -1; return -1;
memcpy(im->sent, buf, sizeof buf);
im->caretsent = 1; im->caretsent = 1;
im->sentvalid = valid;
im->sentx = x;
im->senty = y;
im->senth = h;
return 0; return 0;
} }
@@ -206,17 +175,12 @@ srvconnect(Im *im)
/* Marker zero is an old daemon's harmless key-zero response. */ /* Marker zero is an old daemon's harmless key-zero response. */
ipcpackcap(buf, im->usepreedit); ipcpackcap(buf, im->usepreedit);
if(ipcsend(im->fd, buf, sizeof buf) < 0 || if(ipcsend(im->fd, buf, sizeof buf) < 0 ||
readresp(im, im->usepreedit, commit, sizeof commit, readresp(im, im->usepreedit, commit, pre, &resp) < 0){
pre, sizeof pre, &resp) < 0){
srvclose(im); srvclose(im);
return -1; return -1;
} }
if(im->usepreedit)
setpreedit(im, pre, resp.preeditlen);
im->ext = resp.eaten != 0; im->ext = resp.eaten != 0;
if(!im->usepreedit) if(sendcaret(im) < 0){
setpreedit(im, "", 0);
if(im->ext && sendcaret(im) < 0){
srvclose(im); srvclose(im);
return -1; return -1;
} }
@@ -228,7 +192,6 @@ simplecommit(GtkIMContext *ctx, const char *s, Im *im)
{ {
(void)ctx; (void)ctx;
(void)s; (void)s;
if(im->insimple)
im->simpledone = 1; im->simpledone = 1;
} }
@@ -236,10 +199,13 @@ static void
simpleend(GtkIMContext *ctx, Im *im) simpleend(GtkIMContext *ctx, Im *im)
{ {
(void)ctx; (void)ctx;
if(im->insimple)
im->simpledone = 1; im->simpledone = 1;
} }
/*
* GtkIMContextSimple composes dead keys and Compose sequences; it keeps
* the key stream until it commits, ends its preedit, or rejects a press.
*/
static gboolean static gboolean
simplefilter(GtkIMContext *ctx, GdkEventKey *ev, int release) simplefilter(GtkIMContext *ctx, GdkEventKey *ev, int release)
{ {
@@ -249,9 +215,7 @@ simplefilter(GtkIMContext *ctx, GdkEventKey *ev, int release)
im = (Im*)ctx; im = (Im*)ctx;
im->simpleactive = 1; im->simpleactive = 1;
im->simpledone = 0; im->simpledone = 0;
im->insimple = 1;
r = parentim->filter_keypress(ctx, ev); r = parentim->filter_keypress(ctx, ev);
im->insimple = 0;
if((!r && !release) || im->simpledone) if((!r && !release) || im->simpledone)
im->simpleactive = 0; im->simpleactive = 0;
return r; return r;
@@ -271,8 +235,7 @@ sendreset(Im *im)
} }
ipcpackreset(buf, im->usepreedit); ipcpackreset(buf, im->usepreedit);
if(ipcsend(im->fd, buf, sizeof buf) < 0 || if(ipcsend(im->fd, buf, sizeof buf) < 0 ||
readresp(im, im->usepreedit, commit, sizeof commit, readresp(im, im->usepreedit, commit, pre, &resp) < 0){
pre, sizeof pre, &resp) < 0){
srvclose(im); srvclose(im);
return; return;
} }
@@ -297,7 +260,7 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev)
} }
if(im->simpleactive) if(im->simpleactive)
return simplefilter(ctx, ev, 0); return simplefilter(ctx, ev, 0);
key = ipckey(ev->keyval, gdk_keyval_to_unicode(ev->keyval)); key = ipckeysym(ev->keyval, gdk_keyval_to_unicode(ev->keyval));
mod = ipcmod(ev->state); mod = ipcmod(ev->state);
if(im->private || key == 0) if(im->private || key == 0)
return simplefilter(ctx, ev, 0); return simplefilter(ctx, ev, 0);
@@ -310,8 +273,7 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev)
} }
ipcpackreq(buf, im->usepreedit, mod, key); ipcpackreq(buf, im->usepreedit, mod, key);
if(ipcsend(im->fd, buf, sizeof buf) < 0 || if(ipcsend(im->fd, buf, sizeof buf) < 0 ||
readresp(im, im->usepreedit, commit, sizeof commit, readresp(im, im->usepreedit, commit, pre, &resp) < 0){
pre, sizeof pre, &resp) < 0){
srvclose(im); srvclose(im);
return simplefilter(ctx, ev, 0); return simplefilter(ctx, ev, 0);
} }
@@ -371,8 +333,7 @@ focusout(GtkIMContext *ctx)
if(parentim->focus_out != NULL) if(parentim->focus_out != NULL)
parentim->focus_out(ctx); parentim->focus_out(ctx);
im->simpleactive = 0; im->simpleactive = 0;
sendreset(im); /* Closing the connection releases the engine, which resets it. */
/* Closing the context connection releases engine ownership. */
srvclose(im); srvclose(im);
} }
@@ -398,8 +359,7 @@ setusepreedit(GtkIMContext *ctx, gboolean use)
return; return;
ipcpackcap(buf, use); ipcpackcap(buf, use);
if(ipcsend(im->fd, buf, sizeof buf) < 0 || if(ipcsend(im->fd, buf, sizeof buf) < 0 ||
readresp(im, use, commit, sizeof commit, readresp(im, use, commit, pre, &resp) < 0 ||
pre, sizeof pre, &resp) < 0 ||
!resp.eaten){ !resp.eaten){
srvclose(im); srvclose(im);
return; return;

2
ibus.c
View File

@@ -358,7 +358,7 @@ processkey(Ictx *ctx, u32int sym, u32int state, Keyres *res)
{ {
if(state & Relmask || !ctx->focused || hidden(ctx)) if(state & Relmask || !ctx->focused || hidden(ctx))
return 0; return 0;
sendrequest(ctx, Keypress, ipckey(sym, xkb_keysym_to_utf32(sym)), sendrequest(ctx, Keypress, ipckeysym(sym, xkb_keysym_to_utf32(sym)),
ipcmod(state), res); ipcmod(state), res);
if(preowner != nil && preowner != ctx) if(preowner != nil && preowner != ctx)
checkpreowner(); checkpreowner();

215
ipc.c
View File

@@ -1,7 +1,6 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <poll.h> #include <poll.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -17,57 +16,32 @@ nowms(void)
{ {
struct timespec ts; struct timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) < 0) clock_gettime(CLOCK_MONOTONIC, &ts);
return -1;
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
} }
static int64_t static int64_t
deadline(void) deadline(void)
{ {
int64_t now; return nowms() + Ipcwaitms;
now = nowms();
if(now < 0)
return -1;
return now + Ipcwaitms;
}
static int
checkdeadline(int64_t until)
{
int64_t now;
now = nowms();
if(now < 0)
return -1;
if(now >= until){
errno = ETIMEDOUT;
return -1;
}
return 0;
} }
static int static int
waitfd(int fd, short events, int64_t until) waitfd(int fd, short events, int64_t until)
{ {
struct pollfd pfd; struct pollfd pfd;
int64_t now, left; int64_t left;
int n; int n;
pfd.fd = fd; pfd.fd = fd;
pfd.events = events; pfd.events = events;
for(;;){ for(;;){
now = nowms(); left = until - nowms();
if(now < 0)
return -1;
left = until - now;
if(left <= 0){ if(left <= 0){
errno = ETIMEDOUT; errno = ETIMEDOUT;
return -1; return -1;
} }
pfd.revents = 0; n = poll(&pfd, 1, left);
n = poll(&pfd, 1, left > INT32_MAX ? INT32_MAX : (int)left);
if(n < 0 && errno == EINTR) if(n < 0 && errno == EINTR)
continue; continue;
if(n < 0) if(n < 0)
@@ -76,11 +50,6 @@ waitfd(int fd, short events, int64_t until)
errno = ETIMEDOUT; errno = ETIMEDOUT;
return -1; return -1;
} }
if(pfd.revents & POLLNVAL){
errno = EBADF;
return -1;
}
if(pfd.revents & (events|POLLERR|POLLHUP))
return 0; return 0;
} }
} }
@@ -93,8 +62,6 @@ readwait(int fd, void *buf, size_t n, int64_t until)
p = buf; p = buf;
while(n > 0){ while(n > 0){
if(checkdeadline(until) < 0)
return -1;
r = recv(fd, p, n, MSG_DONTWAIT); r = recv(fd, p, n, MSG_DONTWAIT);
if(r < 0 && errno == EINTR) if(r < 0 && errno == EINTR)
continue; continue;
@@ -143,15 +110,10 @@ put32(unsigned char *p, int32_t v)
static int32_t static int32_t
get32(const unsigned char *p) get32(const unsigned char *p)
{ {
uint32_t u; return (int32_t)((uint32_t)p[0] |
u = (uint32_t)p[0] |
((uint32_t)p[1] << 8) | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) | ((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24); ((uint32_t)p[3] << 24));
if(u <= INT32_MAX)
return u;
return -(int32_t)(~u) - 1;
} }
int int
@@ -160,8 +122,6 @@ ipcpath(char *dst, size_t cap)
const char *dir, *sep; const char *dir, *sep;
int n; int n;
if(dst == NULL || cap == 0)
return -1;
dir = getenv("XDG_RUNTIME_DIR"); dir = getenv("XDG_RUNTIME_DIR");
if(dir != NULL && dir[0] == '/'){ if(dir != NULL && dir[0] == '/'){
sep = dir[strlen(dir)-1] == '/' ? "" : "/"; sep = dir[strlen(dir)-1] == '/' ? "" : "/";
@@ -174,13 +134,16 @@ ipcpath(char *dst, size_t cap)
return 0; return 0;
} }
/*
* A nonblocking AF_UNIX connect completes at once or fails with EAGAIN
* when the daemon's backlog is full; the socket stays nonblocking because
* every transfer below polls with a deadline.
*/
int int
ipcconnect(void) ipcconnect(void)
{ {
struct sockaddr_un addr; struct sockaddr_un addr;
socklen_t nerr; int e, fd;
int e, err, fd, fdflags, flags;
int64_t until;
memset(&addr, 0, sizeof addr); memset(&addr, 0, sizeof addr);
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
@@ -188,47 +151,24 @@ ipcconnect(void)
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
return -1; return -1;
} }
fd = socket(AF_UNIX, SOCK_STREAM, 0); fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if(fd < 0) if(fd < 0)
return -1; return -1;
fdflags = fcntl(fd, F_GETFD);
if(fdflags < 0 || fcntl(fd, F_SETFD, fdflags|FD_CLOEXEC) < 0)
goto Bad;
flags = fcntl(fd, F_GETFL);
if(flags < 0 || fcntl(fd, F_SETFL, flags|O_NONBLOCK) < 0)
goto Bad;
until = deadline();
if(until < 0)
goto Bad;
if(connect(fd, (struct sockaddr*)&addr, sizeof addr) < 0){ if(connect(fd, (struct sockaddr*)&addr, sizeof addr) < 0){
if(errno != EINPROGRESS && errno != EALREADY && errno != EINTR)
goto Bad;
if(waitfd(fd, POLLOUT, until) < 0)
goto Bad;
nerr = sizeof err;
if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &nerr) < 0)
goto Bad;
if(err != 0){
errno = err;
goto Bad;
}
}
if(fcntl(fd, F_SETFL, flags) < 0)
goto Bad;
return fd;
Bad:
e = errno; e = errno;
close(fd); close(fd);
errno = e; errno = e;
return -1; return -1;
} }
return fd;
}
/* /*
* Engine key for an X keysym and its Unicode value: printable characters * Engine key for an X keysym and its Unicode value: printable characters
* as themselves, the function keysyms 0xff00-0xffff as Kspec+offset. * as themselves, the function keysyms 0xff00-0xffff as Kspec+offset.
*/ */
uint32_t uint32_t
ipckey(uint32_t sym, uint32_t unicode) ipckeysym(uint32_t sym, uint32_t unicode)
{ {
if(unicode >= ' ' && unicode != 0x7f) if(unicode >= ' ' && unicode != 0x7f)
return unicode; return unicode;
@@ -277,25 +217,20 @@ ipcpackcap(unsigned char req[Ipcreqsz], int want)
req[2] = Ipcopcap; req[2] = Ipcopcap;
} }
int void
ipcpackcaret(unsigned char req[Ipccaretsz], int valid, int32_t x, ipcpackcaret(unsigned char req[Ipccaretsz], int valid, int32_t x,
int32_t y, int32_t h) int32_t y, int32_t h)
{ {
if(valid != 0 && valid != 1)
return -1;
if(valid && h < 0)
return -1;
memset(req, 0, Ipccaretsz); memset(req, 0, Ipccaretsz);
req[0] = Ipcext; req[0] = Ipcext;
req[1] = Ipcversion; req[1] = Ipcversion;
req[2] = Ipcopcaret; req[2] = Ipcopcaret;
req[3] = valid; req[3] = valid != 0;
if(!valid) if(valid){
return 0;
put32(req + 4, x); put32(req + 4, x);
put32(req + 8, y); put32(req + 8, y);
put32(req + 12, h); put32(req + 12, h);
return 0; }
} }
void void
@@ -314,7 +249,7 @@ int
ipcreqtype(const unsigned char req[Ipcreqsz]) ipcreqtype(const unsigned char req[Ipcreqsz])
{ {
if((req[0] & Ipcext) == 0) if((req[0] & Ipcext) == 0)
return Ipclegacy; return Ipckey;
if(req[1] != Ipcversion) if(req[1] != Ipcversion)
return Ipcunknown; return Ipcunknown;
switch(req[2]){ switch(req[2]){
@@ -335,24 +270,12 @@ int
ipcunpackcaret(const unsigned char req[Ipccaretsz], int *valid, ipcunpackcaret(const unsigned char req[Ipccaretsz], int *valid,
int32_t *x, int32_t *y, int32_t *h) int32_t *x, int32_t *y, int32_t *h)
{ {
int32_t wireh; if(ipcreqtype(req) != Ipccaret || get32(req + 12) < 0)
if(valid == NULL || x == NULL || y == NULL || h == NULL ||
ipcreqtype(req) != Ipccaret)
return -1;
wireh = get32(req + 12);
if(wireh < 0)
return -1; return -1;
*valid = req[3]; *valid = req[3];
if(!*valid){ *x = *valid ? get32(req + 4) : 0;
*x = 0; *y = *valid ? get32(req + 8) : 0;
*y = 0; *h = *valid ? get32(req + 12) : 0;
*h = 0;
return 0;
}
*x = get32(req + 4);
*y = get32(req + 8);
*h = wireh;
return 0; return 0;
} }
@@ -371,21 +294,15 @@ ipcpackresp(unsigned char *dst, size_t cap, int eaten,
if(ncommit > Ipcfieldmax || npreedit > Ipcfieldmax) if(ncommit > Ipcfieldmax || npreedit > Ipcfieldmax)
return -1; return -1;
if((ncommit > 0 && commit == NULL) ||
(want && npreedit > 0 && preedit == NULL))
return -1;
n = Ipcresphdrsz + ncommit + (want ? Ipclensz + npreedit : 0); n = Ipcresphdrsz + ncommit + (want ? Ipclensz + npreedit : 0);
if(dst == NULL || cap < n) if(cap < n)
return -1; return -1;
dst[0] = eaten != 0; dst[0] = eaten != 0;
putlen(dst + 1, ncommit); putlen(dst + 1, ncommit);
if(ncommit > 0)
memcpy(dst + Ipcresphdrsz, commit, ncommit); memcpy(dst + Ipcresphdrsz, commit, ncommit);
if(want){ if(want){
putlen(dst + Ipcresphdrsz + ncommit, npreedit); putlen(dst + Ipcresphdrsz + ncommit, npreedit);
if(npreedit > 0) memcpy(dst + Ipcresphdrsz + ncommit + Ipclensz, preedit, npreedit);
memcpy(dst + Ipcresphdrsz + ncommit + Ipclensz,
preedit, npreedit);
} }
return n; return n;
} }
@@ -417,14 +334,8 @@ ipcsend(int fd, const void *buf, size_t n)
int64_t until; int64_t until;
p = buf; p = buf;
if(n == 0)
return 0;
until = deadline(); until = deadline();
if(until < 0)
return -1;
while(n > 0){ while(n > 0){
if(checkdeadline(until) < 0)
return -1;
r = send(fd, p, n, MSG_NOSIGNAL|MSG_DONTWAIT); r = send(fd, p, n, MSG_NOSIGNAL|MSG_DONTWAIT);
if(r < 0 && errno == EINTR) if(r < 0 && errno == EINTR)
continue; continue;
@@ -445,73 +356,53 @@ ipcsend(int fd, const void *buf, size_t n)
return 0; return 0;
} }
/* Reads a length-prefixed field into dst[Ipcfieldmax+1]. */
static int static int
readfield(int fd, size_t n, char *dst, size_t cap, size_t *copied, readfield(int fd, char *dst, int64_t until)
int64_t until)
{ {
unsigned char discard[128]; unsigned char len[Ipclensz];
size_t keep, part; size_t n;
if(cap > 0 && dst == NULL) if(readwait(fd, len, sizeof len, until) < 0)
return -1; return -1;
keep = 0; n = getlen(len);
if(cap > 0) if(n > Ipcfieldmax){
keep = n >= cap ? cap - 1 : n; errno = EPROTO;
if(keep > 0 && readwait(fd, dst, keep, until) < 0)
return -1; return -1;
if(cap > 0)
dst[keep] = '\0';
n -= keep;
while(n > 0){
part = n < sizeof discard ? n : sizeof discard;
if(readwait(fd, discard, part, until) < 0)
return -1;
n -= part;
} }
*copied = keep; if(readwait(fd, dst, n, until) < 0)
return 0; return -1;
dst[n] = '\0';
return n;
} }
int int
ipcreadresp(int fd, int want, char *commit, size_t ccap, ipcreadresp(int fd, int want, char *commit, char *preedit, Ipcresp *resp)
char *preedit, size_t pcap, Ipcresp *resp)
{ {
unsigned char hdr[Ipcresphdrsz], npreedit[Ipclensz]; unsigned char eaten;
size_t wirelen;
int64_t until; int64_t until;
int n;
if(resp == NULL || (ccap > 0 && commit == NULL) ||
(pcap > 0 && preedit == NULL))
return -1;
if(ccap > 0)
commit[0] = '\0'; commit[0] = '\0';
if(pcap > 0)
preedit[0] = '\0'; preedit[0] = '\0';
memset(resp, 0, sizeof *resp); memset(resp, 0, sizeof *resp);
until = deadline(); until = deadline();
if(until < 0 || readwait(fd, hdr, sizeof hdr, until) < 0) if(readwait(fd, &eaten, 1, until) < 0)
return -1; return -1;
if(hdr[0] > 1){ if(eaten > 1){
errno = EPROTO; errno = EPROTO;
return -1; return -1;
} }
resp->eaten = hdr[0]; resp->eaten = eaten;
wirelen = getlen(hdr + 1); n = readfield(fd, commit, until);
if(wirelen > Ipcfieldmax){ if(n < 0)
errno = EPROTO;
return -1;
}
if(readfield(fd, wirelen, commit, ccap, &resp->commitlen, until) < 0)
return -1; return -1;
resp->commitlen = n;
if(!want) if(!want)
return 0; return 0;
if(readwait(fd, npreedit, sizeof npreedit, until) < 0) n = readfield(fd, preedit, until);
return -1; if(n < 0)
wirelen = getlen(npreedit);
if(wirelen > Ipcfieldmax){
errno = EPROTO;
return -1; return -1;
} resp->preeditlen = n;
return readfield(fd, wirelen, preedit, pcap, &resp->preeditlen, return 0;
until);
} }

13
ipc.h
View File

@@ -2,7 +2,7 @@
#include <stdint.h> #include <stdint.h>
/* /*
* Legacy request: [flags, modifiers, key byte 0, ..., key byte 3]. * Key request: [flags, modifiers, key byte 0, ..., key byte 3].
* Flags request preedit and distinguish lifecycle reset from physical Escape. * Flags request preedit and distinguish lifecycle reset from physical Escape.
* The server identifies the connection as the engine owner; that identity is * The server identifies the connection as the engine owner; that identity is
* not sent on the wire. * not sent on the wire.
@@ -10,7 +10,8 @@
* Response: [eaten, commit-length-low, commit-length-high, commit...], * Response: [eaten, commit-length-low, commit-length-high, commit...],
* followed, when requested, by * followed, when requested, by
* [preedit-length-low, preedit-length-high, preedit...]. * [preedit-length-low, preedit-length-high, preedit...].
* Lengths are little-endian byte counts; fields are at most Ipcfieldmax bytes. * Lengths are little-endian byte counts; fields are at most Ipcfieldmax bytes,
* so callers read them into char[Ipcfieldmax+1].
* *
* Capability control is six bytes: [0x80|want, version, 0, 0, 0, 0]. * Capability control is six bytes: [0x80|want, version, 0, 0, 0, 0].
* Caret control is sixteen bytes: [0x80, version, 1, valid, x, y, h], * Caret control is sixteen bytes: [0x80, version, 1, valid, x, y, h],
@@ -58,7 +59,7 @@ enum
enum enum
{ {
Ipcunknown = -1, Ipcunknown = -1,
Ipclegacy, Ipckey,
Ipccap, Ipccap,
Ipccaret, Ipccaret,
}; };
@@ -72,12 +73,12 @@ struct Ipcresp
size_t preeditlen; size_t preeditlen;
}; };
uint32_t ipckey(uint32_t, uint32_t); uint32_t ipckeysym(uint32_t, uint32_t);
uint32_t ipcmod(uint32_t); uint32_t ipcmod(uint32_t);
void ipcpackreq(unsigned char[Ipcreqsz], int, uint32_t, uint32_t); void ipcpackreq(unsigned char[Ipcreqsz], int, uint32_t, uint32_t);
void ipcpackreset(unsigned char[Ipcreqsz], int); void ipcpackreset(unsigned char[Ipcreqsz], int);
void ipcpackcap(unsigned char[Ipcreqsz], int); void ipcpackcap(unsigned char[Ipcreqsz], int);
int ipcpackcaret(unsigned char[Ipccaretsz], int, int32_t, int32_t, int32_t); void ipcpackcaret(unsigned char[Ipccaretsz], int, int32_t, int32_t, int32_t);
void ipcunpackreq(const unsigned char[Ipcreqsz], int*, uint32_t*, uint32_t*); 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 ipcunpackcaret(const unsigned char[Ipccaretsz], int*, int32_t*, int32_t*, int32_t*);
int ipcreqtype(const unsigned char[Ipcreqsz]); int ipcreqtype(const unsigned char[Ipcreqsz]);
@@ -85,6 +86,6 @@ 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, const char*, size_t, const char*, size_t, int);
int ipcreadn(int, void*, size_t); int ipcreadn(int, void*, size_t);
int ipcsend(int, const void*, size_t); int ipcsend(int, const void*, size_t);
int ipcreadresp(int, int, char*, size_t, char*, size_t, Ipcresp*); int ipcreadresp(int, int, char*, char*, Ipcresp*);
int ipcpath(char*, size_t); int ipcpath(char*, size_t);
int ipcconnect(void); int ipcconnect(void);

17
main.c
View File

@@ -61,17 +61,12 @@ threadmain(int argc, char **argv)
mapinit(argv[1]); mapinit(argv[1]);
dictinit(argv[1]); dictinit(argv[1]);
srvinit(); srvinit();
if(proccreate(drawthread, nil, 16384) < 0) proccreate(drawthread, nil, 16384);
die("can't create draw worker"); proccreate(srvthread, nil, 16384);
if(proccreate(srvthread, nil, 16384) < 0) proccreate(ibusthread, nil, 32768);
die("can't create server worker");
if(proccreate(ibusthread, nil, 32768) < 0)
die("can't create IBus worker");
display = getenv("DISPLAY"); display = getenv("DISPLAY");
if(display != nil && display[0] != '\0' && if(display != nil && display[0] != '\0')
proccreate(ximthread, nil, 32768) < 0) proccreate(ximthread, nil, 32768);
die("xim: cannot create worker"); threadcreate(dictthread, nil, 16384);
if(threadcreate(dictthread, nil, 16384) < 0)
die("can't create dictionary worker");
imthread(nil); imthread(nil);
} }

85
srv.c
View File

@@ -11,98 +11,78 @@ static int
srvreadreq(int fd, Keyreq *kr, int *want) srvreadreq(int fd, Keyreq *kr, int *want)
{ {
uchar req[Ipccaretsz]; uchar req[Ipccaretsz];
int type, valid; int valid;
int32_t x, y, h; int32_t x, y, h;
u32int ks, mod;
if(ipcreadn(fd, req, Ipcreqsz) < 0) if(ipcreadn(fd, req, Ipcreqsz) < 0)
return -1; return -1;
type = ipcreqtype(req); *want = 0;
switch(type){ kr->ks = 0;
case Ipclegacy: kr->mod = 0;
ipcunpackreq(req, want, &mod, &ks); switch(ipcreqtype(req)){
case Ipckey:
ipcunpackreq(req, want, &kr->mod, &kr->ks);
kr->op = ipcreqreset(req) ? Keyreset : Keypress; kr->op = ipcreqreset(req) ? Keyreset : Keypress;
kr->ks = ks; return 0;
kr->mod = mod;
break;
case Ipccap: case Ipccap:
*want = (req[0] & Ipcreqwant) != 0; *want = (req[0] & Ipcreqwant) != 0;
kr->op = Keycap; kr->op = Keycap;
kr->ks = 0; return 0;
kr->mod = 0;
break;
case Ipccaret: case Ipccaret:
if(ipcreadn(fd, req + Ipcreqsz, Ipccaretsz - Ipcreqsz) < 0 || if(ipcreadn(fd, req + Ipcreqsz, Ipccaretsz - Ipcreqsz) < 0 ||
ipcunpackcaret(req, &valid, &x, &y, &h) < 0) ipcunpackcaret(req, &valid, &x, &y, &h) < 0)
return -1; return -1;
*want = 0;
kr->op = Keycaret; kr->op = Keycaret;
kr->ks = 0;
kr->mod = 0;
kr->caret.valid = valid; kr->caret.valid = valid;
kr->caret.x = x; kr->caret.x = x;
kr->caret.y = y; kr->caret.y = y;
kr->caret.h = h; kr->caret.h = h;
break; return 0;
default: }
return -1; return -1;
} }
return type;
}
/*
* One Keyreq persists for the connection: the negotiated capability and
* the last caret ride along with every request, and the socket closing
* releases the engine.
*/
static void static void
clientthread(void *arg) clientthread(void *arg)
{ {
Channel *reply;
int fd;
Keyreq kr; Keyreq kr;
Keyres res; Keyres res;
Caret caret; uchar out[Ipcmaxresp], token;
uchar out[Ipcmaxresp];
char commit[Maxutf], preedit[Maxutf]; char commit[Maxutf], preedit[Maxutf];
int cap, n, ncommit, negotiated, npreedit, type, want; int fd, n, ncommit, npreedit, want;
uchar token;
fd = (int)(uintptr)arg; fd = (int)(uintptr)arg;
threadsetname("client %d", fd); threadsetname("client %d", fd);
reply = chancreate(sizeof(Keyres), 0); memset(&kr, 0, sizeof kr);
kr.reply = reply; kr.reply = chancreate(sizeof(Keyres), 0);
kr.owner = &fd; kr.owner = &fd;
cap = Cclientpreedit; kr.cap = Cclientpreedit;
negotiated = 0; while(srvreadreq(fd, &kr, &want) >= 0){
memset(&caret, 0, sizeof caret); if(kr.op != Keycaret)
kr.cap = cap; kr.cap = want ? Cclientpreedit : 0;
kr.caret = caret;
while((type = srvreadreq(fd, &kr, &want)) >= 0){
if(type == Ipccap){
cap = want ? Cclientpreedit : 0;
negotiated = 1;
}else if(type == Ipclegacy && !negotiated)
cap = want ? Cclientpreedit : 0;
else if(type == Ipccaret)
caret = kr.caret;
kr.cap = cap;
kr.caret = caret;
chansend(keyc, &kr); chansend(keyc, &kr);
chanrecv(reply, &res); chanrecv(kr.reply, &res);
if(type == Ipccaret) if(kr.op == Keycaret)
continue; continue;
ncommit = stoutf(&res.commit, commit, sizeof commit); ncommit = stoutf(&res.commit, commit, sizeof commit);
npreedit = stoutf(&res.preedit, preedit, sizeof preedit); npreedit = stoutf(&res.preedit, preedit, sizeof preedit);
n = ipcpackresp(out, sizeof out, /* A capability reply is always eaten: it marks the extension. */
type == Ipccap ? 1 : res.eaten, n = ipcpackresp(out, sizeof out, kr.op == Keycap || res.eaten,
commit, ncommit, preedit, npreedit, want); commit, ncommit, preedit, npreedit, want);
if(n < 0 || ipcsend(fd, out, n) < 0) if(n < 0 || ipcsend(fd, out, n) < 0)
break; break;
} }
kr.op = Keyrelease; kr.op = Keyrelease;
kr.cap = cap;
kr.caret = caret;
kr.ks = 0; kr.ks = 0;
kr.mod = 0; kr.mod = 0;
chansend(keyc, &kr); chansend(keyc, &kr);
chanrecv(reply, &res); chanrecv(kr.reply, &res);
chanfree(reply); chanfree(kr.reply);
close(fd); close(fd);
chanrecv(clientc, &token); chanrecv(clientc, &token);
} }
@@ -142,9 +122,6 @@ srvthread(void*)
close(fd); close(fd);
continue; continue;
} }
if(proccreate(clientthread, (void*)(uintptr)fd, 8192) < 0){ proccreate(clientthread, (void*)(uintptr)fd, 8192);
chanrecv(clientc, &token);
close(fd);
}
} }
} }

View File

@@ -204,7 +204,7 @@ request(Server *s, int fd)
e.type = Ecaret; e.type = Ecaret;
record(s, &e); record(s, &e);
return 1; return 1;
case Ipclegacy: case Ipckey:
ipcunpackreq(buf, &want, &mod, &key); ipcunpackreq(buf, &want, &mod, &key);
e.want = want; e.want = want;
e.key = key; e.key = key;
@@ -854,9 +854,8 @@ main(int argc, char **argv)
first = eventcount(&srv); first = eventcount(&srv);
closes = closecount(&srv); closes = closecount(&srv);
gtk_im_context_focus_out(ctx); gtk_im_context_focus_out(ctx);
Check(waitcount(&srv, first + 1) && getevent(&srv, first).type == Ereset,
"focus-out reset missing");
Check(waitclose(&srv, closes + 1), "focus-out did not close connection"); Check(waitclose(&srv, closes + 1), "focus-out did not close connection");
Check(eventcount(&srv) == first, "focus-out sent a request before closing");
Check(strcmp(log.event, "CE") == 0, "focus-out clear order was %s", log.event); Check(strcmp(log.event, "CE") == 0, "focus-out clear order was %s", log.event);
/* Dispose is repeatable, closes the connection, and emits no preedit. */ /* Dispose is repeatable, closes the connection, and emits no preedit. */

View File

@@ -48,13 +48,13 @@ ipc_masks_modifiers(struct ct *t)
CT_CHECK(t, ipcreqreset(buf)); CT_CHECK(t, ipcreqreset(buf));
/* Frontends share one keysym and modifier mapping. */ /* Frontends share one keysym and modifier mapping. */
CT_EQ_UINT(t, 'a', ipckey('a', 'a')); CT_EQ_UINT(t, 'a', ipckeysym('a', 'a'));
CT_EQ_UINT(t, '1', ipckey(0xffb1, '1')); /* KP_1 */ CT_EQ_UINT(t, '1', ipckeysym(0xffb1, '1')); /* KP_1 */
CT_EQ_UINT(t, Kret, ipckey(0xff0d, '\r')); CT_EQ_UINT(t, Kret, ipckeysym(0xff0d, '\r'));
CT_EQ_UINT(t, Kback, ipckey(0xff08, 8)); CT_EQ_UINT(t, Kback, ipckeysym(0xff08, 8));
CT_EQ_UINT(t, Kspec + 0xff, ipckey(0xffff, 0x7f)); /* Delete */ CT_EQ_UINT(t, Kspec + 0xff, ipckeysym(0xffff, 0x7f)); /* Delete */
CT_EQ_UINT(t, 0x1f642, ipckey(0x101f642, 0x1f642)); CT_EQ_UINT(t, 0x1f642, ipckeysym(0x101f642, 0x1f642));
CT_EQ_UINT(t, 0, ipckey(0xfe03, 0)); /* ISO_Level3_Shift */ CT_EQ_UINT(t, 0, ipckeysym(0xfe03, 0)); /* ISO_Level3_Shift */
CT_EQ_UINT(t, Mshift|Mctrl, ipcmod(Mshift|Mctrl|(1<<1)|(1<<4))); CT_EQ_UINT(t, Mshift|Mctrl, ipcmod(Mshift|Mctrl|(1<<1)|(1<<4)));
CT_EQ_UINT(t, Msuper, ipcmod(1<<6)); CT_EQ_UINT(t, Msuper, ipcmod(1<<6));
CT_EQ_UINT(t, Msuper, ipcmod(1<<26)); CT_EQ_UINT(t, Msuper, ipcmod(1<<26));
@@ -96,7 +96,7 @@ ipc_control_and_caret_frames(struct ct *t)
CT_EQ_INT(t, 0, want); CT_EQ_INT(t, 0, want);
CT_EQ_UINT(t, 0, key); CT_EQ_UINT(t, 0, key);
CT_EQ_INT(t, 0, ipcpackcaret(buf, 1, -2, 0x01020304, 0x506)); ipcpackcaret(buf, 1, -2, 0x01020304, 0x506);
CT_EQ_MEM(t, caret, buf, sizeof caret); CT_EQ_MEM(t, caret, buf, sizeof caret);
CT_EQ_INT(t, Ipccaret, ipcreqtype(buf)); CT_EQ_INT(t, Ipccaret, ipcreqtype(buf));
if(CT_EQ_INT(t, 0, ipcunpackcaret(buf, &valid, &x, &y, &h))){ if(CT_EQ_INT(t, 0, ipcunpackcaret(buf, &valid, &x, &y, &h))){
@@ -105,17 +105,16 @@ ipc_control_and_caret_frames(struct ct *t)
CT_EQ_INT(t, 0x01020304, y); CT_EQ_INT(t, 0x01020304, y);
CT_EQ_INT(t, 0x506, h); CT_EQ_INT(t, 0x506, h);
} }
CT_EQ_INT(t, 0, ipcpackcaret(buf, 1, INT32_MIN, INT32_MAX, 0)); ipcpackcaret(buf, 1, INT32_MIN, INT32_MAX, 0);
if(CT_EQ_INT(t, 0, ipcunpackcaret(buf, &valid, &x, &y, &h))){ if(CT_EQ_INT(t, 0, ipcunpackcaret(buf, &valid, &x, &y, &h))){
CT_EQ_INT(t, INT32_MIN, x); CT_EQ_INT(t, INT32_MIN, x);
CT_EQ_INT(t, INT32_MAX, y); CT_EQ_INT(t, INT32_MAX, y);
CT_EQ_INT(t, 0, h); CT_EQ_INT(t, 0, h);
} }
CT_EQ_INT(t, 0, ipcpackcaret(buf, 0, -2, 3, 4)); ipcpackcaret(buf, 0, -2, 3, 4);
CT_EQ_MEM(t, nocaret, buf, sizeof nocaret); CT_EQ_MEM(t, nocaret, buf, sizeof nocaret);
if(CT_EQ_INT(t, 0, ipcunpackcaret(buf, &valid, &x, &y, &h))) if(CT_EQ_INT(t, 0, ipcunpackcaret(buf, &valid, &x, &y, &h)))
CT_EQ_INT(t, 0, valid); CT_EQ_INT(t, 0, valid);
CT_EQ_INT(t, -1, ipcpackcaret(buf, 1, 0, 0, -1));
memcpy(bad, caret, sizeof bad); memcpy(bad, caret, sizeof bad);
bad[1]++; bad[1]++;
@@ -139,9 +138,9 @@ ipc_control_and_caret_frames(struct ct *t)
CT_EQ_INT(t, Ipcunknown, ipcreqtype(bad)); CT_EQ_INT(t, Ipcunknown, ipcreqtype(bad));
ipcpackreq(buf, 1, 0, 'a'); ipcpackreq(buf, 1, 0, 'a');
CT_EQ_INT(t, Ipclegacy, ipcreqtype(buf)); CT_EQ_INT(t, Ipckey, ipcreqtype(buf));
ipcpackreset(buf, 1); ipcpackreset(buf, 1);
CT_EQ_INT(t, Ipclegacy, ipcreqtype(buf)); CT_EQ_INT(t, Ipckey, ipcreqtype(buf));
} }
void void
@@ -162,7 +161,6 @@ ipc_runtime_path(struct ct *t)
snprint(fallback, sizeof fallback, "/tmp/strans.%d", getuid()); snprint(fallback, sizeof fallback, "/tmp/strans.%d", getuid());
CT_EQ_STR(t, fallback, buf); CT_EQ_STR(t, fallback, buf);
CT_EQ_INT(t, -1, ipcpath(buf, 4)); CT_EQ_INT(t, -1, ipcpath(buf, 4));
CT_EQ_INT(t, -1, ipcpath(nil, sizeof buf));
if(saved != nil){ if(saved != nil){
setenv("XDG_RUNTIME_DIR", saved, 1); setenv("XDG_RUNTIME_DIR", saved, 1);
free(saved); free(saved);
@@ -200,22 +198,18 @@ ipc_response_pack_boundaries(struct ct *t)
CT_EQ_INT(t, -1, ipcpackresp(out, Ipcmaxresp-1, 1, CT_EQ_INT(t, -1, ipcpackresp(out, Ipcmaxresp-1, 1,
(char*)field, sizeof field, (char*)field, sizeof field, 1)); (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,
nil, 1, nil, 0, 0)); (char*)field, Ipcfieldmax+1, "", 0, 0));
CT_EQ_INT(t, -1, ipcpackresp(out, sizeof out, 0,
nil, 0, nil, 1, 1));
CT_EQ_INT(t, -1, ipcpackresp(out, sizeof out, 0,
(char*)field, Ipcfieldmax+1, nil, 0, 0));
} }
void void
ipc_response_empty_and_preedit(struct ct *t) ipc_response_empty_and_preedit(struct ct *t)
{ {
uchar first[Ipcmaxresp], second[Ipcmaxresp]; uchar first[Ipcmaxresp], second[Ipcmaxresp];
char commit[16], preedit[16]; char commit[Ipcfieldmax+1], preedit[Ipcfieldmax+1];
Ipcresp resp; Ipcresp resp;
int fd[2], nfirst, nsecond; int fd[2], nfirst, nsecond;
nfirst = ipcpackresp(first, sizeof first, 0, nil, 0, nil, 0, 0); nfirst = ipcpackresp(first, sizeof first, 0, "", 0, "", 0, 0);
nsecond = ipcpackresp(second, sizeof second, 1, nsecond = ipcpackresp(second, sizeof second, 1,
"go", 2, "kana", 4, 1); "go", 2, "kana", 4, 1);
if(!CT_CHECK(t, nfirst > 0 && nsecond > 0)) if(!CT_CHECK(t, nfirst > 0 && nsecond > 0))
@@ -226,15 +220,13 @@ ipc_response_empty_and_preedit(struct ct *t)
} }
if(CT_EQ_INT(t, 0, ipcsend(fd[0], first, nfirst)) && if(CT_EQ_INT(t, 0, ipcsend(fd[0], first, nfirst)) &&
CT_EQ_INT(t, 0, ipcsend(fd[0], second, nsecond)) && CT_EQ_INT(t, 0, ipcsend(fd[0], second, nsecond)) &&
CT_EQ_INT(t, 0, ipcreadresp(fd[1], 0, commit, sizeof commit, CT_EQ_INT(t, 0, ipcreadresp(fd[1], 0, commit, preedit, &resp))){
nil, 0, &resp))){
CT_EQ_INT(t, 0, resp.eaten); CT_EQ_INT(t, 0, resp.eaten);
CT_EQ_SIZE(t, 0, resp.commitlen); CT_EQ_SIZE(t, 0, resp.commitlen);
CT_EQ_SIZE(t, 0, resp.preeditlen); CT_EQ_SIZE(t, 0, resp.preeditlen);
CT_EQ_STR(t, "", commit); CT_EQ_STR(t, "", commit);
} }
if(CT_EQ_INT(t, 0, ipcreadresp(fd[1], 1, commit, sizeof commit, if(CT_EQ_INT(t, 0, ipcreadresp(fd[1], 1, commit, preedit, &resp))){
preedit, sizeof preedit, &resp))){
CT_EQ_INT(t, 1, resp.eaten); CT_EQ_INT(t, 1, resp.eaten);
CT_EQ_SIZE(t, 2, resp.commitlen); CT_EQ_SIZE(t, 2, resp.commitlen);
CT_EQ_SIZE(t, 4, resp.preeditlen); CT_EQ_SIZE(t, 4, resp.preeditlen);
@@ -249,14 +241,14 @@ void
ipc_response_max_and_drain(struct ct *t) ipc_response_max_and_drain(struct ct *t)
{ {
uchar first[Ipcmaxresp], second[Ipcmaxresp], field[Ipcfieldmax]; uchar first[Ipcmaxresp], second[Ipcmaxresp], field[Ipcfieldmax];
char commit[8], preedit[8]; char commit[Ipcfieldmax+1], preedit[Ipcfieldmax+1];
Ipcresp resp; Ipcresp resp;
int fd[2], nfirst, nsecond; int fd[2], nfirst, nsecond;
memset(field, 'x', sizeof field); memset(field, 'x', sizeof field);
nfirst = ipcpackresp(first, sizeof first, 1, nfirst = ipcpackresp(first, sizeof first, 1,
(char*)field, sizeof field, (char*)field, sizeof field, 1); (char*)field, sizeof field, (char*)field, sizeof field, 1);
nsecond = ipcpackresp(second, sizeof second, 0, "ok", 2, nil, 0, 0); nsecond = ipcpackresp(second, sizeof second, 0, "ok", 2, "", 0, 0);
if(!CT_CHECK(t, nfirst == Ipcmaxresp && nsecond > 0)) if(!CT_CHECK(t, nfirst == Ipcmaxresp && nsecond > 0))
return; return;
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){ if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
@@ -265,15 +257,15 @@ ipc_response_max_and_drain(struct ct *t)
} }
if(CT_EQ_INT(t, 0, ipcsend(fd[0], first, nfirst)) && if(CT_EQ_INT(t, 0, ipcsend(fd[0], first, nfirst)) &&
CT_EQ_INT(t, 0, ipcsend(fd[0], second, nsecond)) && CT_EQ_INT(t, 0, ipcsend(fd[0], second, nsecond)) &&
CT_EQ_INT(t, 0, ipcreadresp(fd[1], 1, commit, sizeof commit, CT_EQ_INT(t, 0, ipcreadresp(fd[1], 1, commit, preedit, &resp))){
preedit, sizeof preedit, &resp))){ CT_EQ_SIZE(t, Ipcfieldmax, resp.commitlen);
CT_EQ_SIZE(t, sizeof commit - 1, resp.commitlen); CT_EQ_SIZE(t, Ipcfieldmax, resp.preeditlen);
CT_EQ_SIZE(t, sizeof preedit - 1, resp.preeditlen); CT_EQ_MEM(t, field, commit, Ipcfieldmax);
CT_EQ_STR(t, "xxxxxxx", commit); CT_EQ_MEM(t, field, preedit, Ipcfieldmax);
CT_EQ_STR(t, "xxxxxxx", preedit); CT_EQ_INT(t, 0, commit[Ipcfieldmax]);
CT_EQ_INT(t, 0, preedit[Ipcfieldmax]);
} }
if(CT_EQ_INT(t, 0, ipcreadresp(fd[1], 0, commit, sizeof commit, if(CT_EQ_INT(t, 0, ipcreadresp(fd[1], 0, commit, preedit, &resp)))
nil, 0, &resp)))
CT_EQ_STR(t, "ok", commit); CT_EQ_STR(t, "ok", commit);
close(fd[0]); close(fd[0]);
close(fd[1]); close(fd[1]);
@@ -283,7 +275,7 @@ void
ipc_response_fragmented_and_truncated(struct ct *t) ipc_response_fragmented_and_truncated(struct ct *t)
{ {
uchar frame[Ipcmaxresp]; uchar frame[Ipcmaxresp];
char commit[8], preedit[8]; char commit[Ipcfieldmax+1], preedit[Ipcfieldmax+1];
Ipcresp resp; Ipcresp resp;
int fd[2], i, n; int fd[2], i, n;
@@ -299,8 +291,8 @@ ipc_response_fragmented_and_truncated(struct ct *t)
CT_ERRORF(t, "fragment send failed"); CT_ERRORF(t, "fragment send failed");
break; break;
} }
if(i == n && CT_EQ_INT(t, 0, ipcreadresp(fd[1], 1, if(i == n && CT_EQ_INT(t, 0, ipcreadresp(fd[1], 1, commit, preedit,
commit, sizeof commit, preedit, sizeof preedit, &resp))){ &resp))){
CT_EQ_STR(t, "abc", commit); CT_EQ_STR(t, "abc", commit);
CT_EQ_STR(t, "xy", preedit); CT_EQ_STR(t, "xy", preedit);
} }
@@ -313,8 +305,7 @@ ipc_response_fragmented_and_truncated(struct ct *t)
} }
CT_EQ_INT(t, 0, ipcsend(fd[0], frame, n-1)); CT_EQ_INT(t, 0, ipcsend(fd[0], frame, n-1));
shutdown(fd[0], SHUT_WR); shutdown(fd[0], SHUT_WR);
CT_EQ_INT(t, -1, ipcreadresp(fd[1], 1, commit, sizeof commit, CT_EQ_INT(t, -1, ipcreadresp(fd[1], 1, commit, preedit, &resp));
preedit, sizeof preedit, &resp));
close(fd[0]); close(fd[0]);
close(fd[1]); close(fd[1]);
@@ -325,8 +316,7 @@ ipc_response_fragmented_and_truncated(struct ct *t)
frame[0] = 2; frame[0] = 2;
CT_EQ_INT(t, 0, ipcsend(fd[0], frame, n)); CT_EQ_INT(t, 0, ipcsend(fd[0], frame, n));
errno = 0; errno = 0;
CT_EQ_INT(t, -1, ipcreadresp(fd[1], 1, commit, sizeof commit, CT_EQ_INT(t, -1, ipcreadresp(fd[1], 1, commit, preedit, &resp));
preedit, sizeof preedit, &resp));
CT_EQ_INT(t, EPROTO, errno); CT_EQ_INT(t, EPROTO, errno);
close(fd[0]); close(fd[0]);
close(fd[1]); close(fd[1]);
@@ -351,7 +341,7 @@ ipc_broken_peer_send(struct ct *t)
static void static void
ipcclientdeadlines(struct ct *t) ipcclientdeadlines(struct ct *t)
{ {
char fill[4096], commit[8]; char fill[4096], commit[Ipcfieldmax+1], preedit[Ipcfieldmax+1];
Ipcresp resp; Ipcresp resp;
int fd[2]; int fd[2];
int64_t start, elapsed; int64_t start, elapsed;
@@ -363,8 +353,7 @@ ipcclientdeadlines(struct ct *t)
} }
start = nowms(); start = nowms();
errno = 0; errno = 0;
CT_EQ_INT(t, -1, ipcreadresp(fd[0], 0, commit, sizeof commit, CT_EQ_INT(t, -1, ipcreadresp(fd[0], 0, commit, preedit, &resp));
nil, 0, &resp));
elapsed = nowms() - start; elapsed = nowms() - start;
CT_EQ_INT(t, ETIMEDOUT, errno); CT_EQ_INT(t, ETIMEDOUT, errno);
CT_CHECK(t, elapsed >= 0 && elapsed < 4*Ipcwaitms); CT_CHECK(t, elapsed >= 0 && elapsed < 4*Ipcwaitms);

View File

@@ -169,13 +169,12 @@ allowrequest(Enginegate *g)
} }
static int static int
readreply(struct ct *t, Testclient *client, int want, char *preedit, int npreedit) readreply(struct ct *t, Testclient *client, int want, char *preedit)
{ {
char commit[Maxutf]; char commit[Ipcfieldmax+1];
Ipcresp resp; Ipcresp resp;
if(ipcreadresp(client->peer, want, commit, sizeof commit, if(ipcreadresp(client->peer, want, commit, preedit, &resp) < 0)
preedit, npreedit, &resp) < 0)
return CT_ERRORF(t, "response read failed: %s", strerror(errno)); return CT_ERRORF(t, "response read failed: %s", strerror(errno));
CT_EQ_STR(t, "", commit); CT_EQ_STR(t, "", commit);
return resp.eaten; return resp.eaten;
@@ -247,7 +246,7 @@ server_connection_ownership(struct ct *t)
Keyreq req; Keyreq req;
Drawcmd dc; Drawcmd dc;
Str shown; Str shown;
char preedit[Maxutf]; char preedit[Ipcfieldmax+1];
void *aowner, *bowner, *cowner; void *aowner, *bowner, *cowner;
uchar byte, token; uchar byte, token;
ssize_t n; ssize_t n;
@@ -279,14 +278,14 @@ server_connection_ownership(struct ct *t)
req = nextrequest(t, &gate, Keypress); req = nextrequest(t, &gate, Keypress);
aowner = req.owner; aowner = req.owner;
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, readreply(t, &a, 1, preedit, sizeof preedit)); CT_CHECK(t, readreply(t, &a, 1, preedit));
CT_EQ_STR(t, "k", preedit); CT_EQ_STR(t, "k", preedit);
if(!sendkey(t, &a, 1, 0, 'a')) if(!sendkey(t, &a, 1, 0, 'a'))
goto cleanup; goto cleanup;
req = nextrequest(t, &gate, Keypress); req = nextrequest(t, &gate, Keypress);
CT_EQ_PTR(t, aowner, req.owner); CT_EQ_PTR(t, aowner, req.owner);
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, readreply(t, &a, 1, preedit, sizeof preedit)); CT_CHECK(t, readreply(t, &a, 1, preedit));
CT_EQ_STR(t, "", preedit); CT_EQ_STR(t, "", preedit);
CT_EQ_PTR(t, aowner, testengineowner()); CT_EQ_PTR(t, aowner, testengineowner());
@@ -296,7 +295,7 @@ server_connection_ownership(struct ct *t)
bowner = req.owner; bowner = req.owner;
CT_CHECK(t, bowner != aowner); CT_CHECK(t, bowner != aowner);
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); CT_CHECK(t, readreply(t, &b, 1, preedit));
CT_EQ_STR(t, "", preedit); CT_EQ_STR(t, "", preedit);
CT_EQ_PTR(t, bowner, testengineowner()); CT_EQ_PTR(t, bowner, testengineowner());
@@ -305,14 +304,14 @@ server_connection_ownership(struct ct *t)
req = nextrequest(t, &gate, Keyreset); req = nextrequest(t, &gate, Keyreset);
CT_EQ_PTR(t, aowner, req.owner); CT_EQ_PTR(t, aowner, req.owner);
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, readreply(t, &a, 1, preedit, sizeof preedit)); CT_CHECK(t, readreply(t, &a, 1, preedit));
CT_EQ_STR(t, "", preedit); CT_EQ_STR(t, "", preedit);
CT_EQ_PTR(t, bowner, testengineowner()); CT_EQ_PTR(t, bowner, testengineowner());
if(!sendkey(t, &b, 1, 0, Kmodfirst)) if(!sendkey(t, &b, 1, 0, Kmodfirst))
goto cleanup; goto cleanup;
req = nextrequest(t, &gate, Keypress); req = nextrequest(t, &gate, Keypress);
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, !readreply(t, &b, 1, preedit, sizeof preedit)); CT_CHECK(t, !readreply(t, &b, 1, preedit));
CT_EQ_STR(t, "", preedit); CT_EQ_STR(t, "", preedit);
disconnectclient(t, &gate, &a, aowner); disconnectclient(t, &gate, &a, aowner);
@@ -321,12 +320,12 @@ server_connection_ownership(struct ct *t)
goto cleanup; goto cleanup;
req = nextrequest(t, &gate, Keypress); req = nextrequest(t, &gate, Keypress);
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); CT_CHECK(t, readreply(t, &b, 1, preedit));
if(!sendkey(t, &b, 1, 0, 'a')) if(!sendkey(t, &b, 1, 0, 'a'))
goto cleanup; goto cleanup;
req = nextrequest(t, &gate, Keypress); req = nextrequest(t, &gate, Keypress);
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); CT_CHECK(t, readreply(t, &b, 1, preedit));
CT_EQ_STR(t, "にゃ", preedit); CT_EQ_STR(t, "にゃ", preedit);
if(!sendreset(t, &b, 1)) if(!sendreset(t, &b, 1))
@@ -334,7 +333,7 @@ server_connection_ownership(struct ct *t)
req = nextrequest(t, &gate, Keyreset); req = nextrequest(t, &gate, Keyreset);
CT_EQ_PTR(t, bowner, req.owner); CT_EQ_PTR(t, bowner, req.owner);
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); CT_CHECK(t, readreply(t, &b, 1, preedit));
CT_EQ_STR(t, "", preedit); CT_EQ_STR(t, "", preedit);
CT_EQ_PTR(t, bowner, testengineowner()); CT_EQ_PTR(t, bowner, testengineowner());
@@ -343,7 +342,7 @@ server_connection_ownership(struct ct *t)
req = nextrequestcap(t, &gate, Keypress, 0); req = nextrequestcap(t, &gate, Keypress, 0);
CT_EQ_PTR(t, bowner, req.owner); CT_EQ_PTR(t, bowner, req.owner);
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, readreply(t, &b, 0, nil, 0)); CT_CHECK(t, readreply(t, &b, 0, preedit));
errno = 0; errno = 0;
n = recv(b.peer, &byte, 1, MSG_PEEK|MSG_DONTWAIT); n = recv(b.peer, &byte, 1, MSG_PEEK|MSG_DONTWAIT);
CT_EQ_INT(t, -1, n); CT_EQ_INT(t, -1, n);
@@ -353,7 +352,7 @@ server_connection_ownership(struct ct *t)
goto cleanup; goto cleanup;
req = nextrequest(t, &gate, Keypress); req = nextrequest(t, &gate, Keypress);
allowrequest(&gate); allowrequest(&gate);
CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); CT_CHECK(t, readreply(t, &b, 1, preedit));
CT_EQ_STR(t, "", preedit); CT_EQ_STR(t, "", preedit);
disconnectclient(t, &gate, &b, bowner); disconnectclient(t, &gate, &b, bowner);
@@ -410,7 +409,7 @@ server_extension_stream(struct ct *t)
Keyreq req; Keyreq req;
Drawcmd dc; Drawcmd dc;
uchar frame[Ipccaretsz], token; uchar frame[Ipccaretsz], token;
char preedit[Maxutf]; char preedit[Ipcfieldmax+1];
void *aowner, *bowner; void *aowner, *bowner;
int gateactive; int gateactive;
@@ -442,10 +441,10 @@ server_extension_stream(struct ct *t)
aowner = req.owner; aowner = req.owner;
CT_EQ_PTR(t, nil, testengineowner()); CT_EQ_PTR(t, nil, testengineowner());
allowrequest(&gate); allowrequest(&gate);
CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit, sizeof preedit)); CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit));
CT_EQ_STR(t, "", preedit); CT_EQ_STR(t, "", preedit);
CT_EQ_INT(t, 0, ipcpackcaret(frame, 1, -101, -7, 23)); ipcpackcaret(frame, 1, -101, -7, 23);
if(!sendframe(t, &a, frame, Ipccaretsz, 1)) if(!sendframe(t, &a, frame, Ipccaretsz, 1))
goto cleanup; goto cleanup;
req = nextrequest(t, &gate, Keycaret); req = nextrequest(t, &gate, Keycaret);
@@ -468,7 +467,7 @@ server_extension_stream(struct ct *t)
CT_EQ_INT(t, -7, req.caret.y); CT_EQ_INT(t, -7, req.caret.y);
CT_EQ_INT(t, 23, req.caret.h); CT_EQ_INT(t, 23, req.caret.h);
allowrequest(&gate); allowrequest(&gate);
CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit, sizeof preedit)); CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit));
CT_EQ_STR(t, "k", preedit); CT_EQ_STR(t, "k", preedit);
CT_EQ_PTR(t, aowner, testengineowner()); CT_EQ_PTR(t, aowner, testengineowner());
@@ -479,7 +478,7 @@ server_extension_stream(struct ct *t)
CT_EQ_PTR(t, aowner, req.owner); CT_EQ_PTR(t, aowner, req.owner);
CT_EQ_INT(t, 1, req.caret.valid); CT_EQ_INT(t, 1, req.caret.valid);
allowrequest(&gate); allowrequest(&gate);
CT_EQ_INT(t, 1, readreply(t, &a, 0, nil, 0)); CT_EQ_INT(t, 1, readreply(t, &a, 0, preedit));
/* Reset is still the old six-byte frame and does not discard caret. */ /* Reset is still the old six-byte frame and does not discard caret. */
ipcpackreset(frame, 0); ipcpackreset(frame, 0);
@@ -492,14 +491,14 @@ server_extension_stream(struct ct *t)
CT_EQ_INT(t, -7, req.caret.y); CT_EQ_INT(t, -7, req.caret.y);
CT_EQ_INT(t, 23, req.caret.h); CT_EQ_INT(t, 23, req.caret.h);
allowrequest(&gate); allowrequest(&gate);
CT_EQ_INT(t, 1, readreply(t, &a, 0, nil, 0)); CT_EQ_INT(t, 1, readreply(t, &a, 0, preedit));
ipcpackcap(frame, Cclientpreedit); ipcpackcap(frame, Cclientpreedit);
if(!sendframe(t, &a, frame, Ipcreqsz, 0)) if(!sendframe(t, &a, frame, Ipcreqsz, 0))
goto cleanup; goto cleanup;
req = nextrequest(t, &gate, Keycap); req = nextrequest(t, &gate, Keycap);
allowrequest(&gate); allowrequest(&gate);
CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit, sizeof preedit)); CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit));
CT_EQ_STR(t, "", preedit); CT_EQ_STR(t, "", preedit);
ipcpackreq(frame, 1, 0, 'n'); ipcpackreq(frame, 1, 0, 'n');
if(!sendframe(t, &a, frame, Ipcreqsz, 0)) if(!sendframe(t, &a, frame, Ipcreqsz, 0))
@@ -508,7 +507,7 @@ server_extension_stream(struct ct *t)
CT_EQ_INT(t, 1, req.caret.valid); CT_EQ_INT(t, 1, req.caret.valid);
CT_EQ_INT(t, -101, req.caret.x); CT_EQ_INT(t, -101, req.caret.x);
allowrequest(&gate); allowrequest(&gate);
CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit, sizeof preedit)); CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit));
CT_EQ_STR(t, "", preedit); CT_EQ_STR(t, "", preedit);
disconnectclient(t, &gate, &a, aowner); disconnectclient(t, &gate, &a, aowner);
@@ -520,7 +519,7 @@ server_extension_stream(struct ct *t)
bowner = req.owner; bowner = req.owner;
CT_EQ_INT(t, 0, req.caret.valid); CT_EQ_INT(t, 0, req.caret.valid);
allowrequest(&gate); allowrequest(&gate);
CT_EQ_INT(t, 1, readreply(t, &b, 1, preedit, sizeof preedit)); CT_EQ_INT(t, 1, readreply(t, &b, 1, preedit));
CT_EQ_STR(t, "k", preedit); CT_EQ_STR(t, "k", preedit);
disconnectclient(t, &gate, &b, bowner); disconnectclient(t, &gate, &b, bowner);
@@ -554,7 +553,7 @@ server_rejects_unknown_extension(struct ct *t)
Keyreq req; Keyreq req;
Drawcmd dc; Drawcmd dc;
uchar frame[Ipccaretsz], token; uchar frame[Ipccaretsz], token;
char preedit[Maxutf]; char preedit[Ipcfieldmax+1];
void *owner; void *owner;
int gateactive; int gateactive;
@@ -580,7 +579,7 @@ server_rejects_unknown_extension(struct ct *t)
if(!startclient(t, &badver)) if(!startclient(t, &badver))
goto cleanup; goto cleanup;
CT_EQ_INT(t, 0, ipcpackcaret(frame, 1, 3, 4, 5)); ipcpackcaret(frame, 1, 3, 4, 5);
frame[1]++; frame[1]++;
if(!sendframe(t, &badver, frame, sizeof frame, 0)) if(!sendframe(t, &badver, frame, sizeof frame, 0))
goto cleanup; goto cleanup;
@@ -592,7 +591,7 @@ server_rejects_unknown_extension(struct ct *t)
if(!startclient(t, &badop)) if(!startclient(t, &badop))
goto cleanup; goto cleanup;
CT_EQ_INT(t, 0, ipcpackcaret(frame, 1, 3, 4, 5)); ipcpackcaret(frame, 1, 3, 4, 5);
frame[2]++; frame[2]++;
if(!sendframe(t, &badop, frame, sizeof frame, 1)) if(!sendframe(t, &badop, frame, sizeof frame, 1))
goto cleanup; goto cleanup;
@@ -608,7 +607,7 @@ server_rejects_unknown_extension(struct ct *t)
req = nextrequest(t, &gate, Keypress); req = nextrequest(t, &gate, Keypress);
owner = req.owner; owner = req.owner;
allowrequest(&gate); allowrequest(&gate);
CT_EQ_INT(t, 1, readreply(t, &good, 1, preedit, sizeof preedit)); CT_EQ_INT(t, 1, readreply(t, &good, 1, preedit));
CT_EQ_STR(t, "k", preedit); CT_EQ_STR(t, "k", preedit);
disconnectclient(t, &gate, &good, owner); disconnectclient(t, &gate, &good, owner);

View File

@@ -555,7 +555,7 @@ kpress(Ic *state, xcb_key_press_event_t *ev)
int n, wasvalid; int n, wasvalid;
key = keymaplookup(kstate, ev->detail, ev->state); key = keymaplookup(kstate, ev->detail, ev->state);
key = ipckey(key, xkb_keysym_to_utf32(key)); key = ipckeysym(key, xkb_keysym_to_utf32(key));
if(meaningful(key)){ if(meaningful(key)){
wasvalid = state->caret.valid; wasvalid = state->caret.valid;
refreshplace(state); refreshplace(state);