From 9dc116a035d860a1c5709ba7307a7b84908602d3 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 11 Aug 2026 19:34:47 +0900 Subject: [PATCH] harden ipc and frontend recovery --- bench/Makefile | 4 +- bench/main.c | 32 +++------ dat.h | 12 +++- fn.h | 1 + gtk/Makefile | 4 +- gtk/main.c | 102 ++++++++++++++------------- ibus.c | 62 ++++++----------- ipc.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++ ipc.h | 41 ++++++++++- srv.c | 59 ++++++++++++---- strans.c | 36 ++++------ wayland.c | 97 ++++++++++++-------------- xim/Makefile | 8 ++- xim/main.c | 96 ++++++++++++++++--------- 14 files changed, 495 insertions(+), 245 deletions(-) create mode 100644 ipc.c diff --git a/bench/Makefile b/bench/Makefile index e9ff193..502dbca 100644 --- a/bench/Makefile +++ b/bench/Makefile @@ -1,8 +1,8 @@ CC = cc CFLAGS = -Wall -O2 -bench: main.c - $(CC) $(CFLAGS) -o $@ main.c +bench: main.c ../ipc.c ../ipc.h + $(CC) $(CFLAGS) -I.. -o $@ main.c ../ipc.c clean: rm -f bench diff --git a/bench/main.c b/bench/main.c index f180dba..3bd4e51 100644 --- a/bench/main.c +++ b/bench/main.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -5,13 +6,7 @@ #include #include #include - -enum { - Kback = 0xf008, - Kret = 0xf00d, - Kesc = 0xf01b, - Mctrl = 1<<2, -}; +#include "../ipc.h" typedef struct Key Key; struct Key { @@ -93,8 +88,7 @@ dial(void) die("socket"); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), - "/tmp/strans.%d", getuid()); + snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid()); if(connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) die("connect"); } @@ -102,28 +96,20 @@ dial(void) static void sendkey(int key, int mod) { - unsigned char req[4]; + unsigned char req[Ipcreqsz]; - req[0] = 0; - req[1] = mod; - req[2] = key & 0xff; - req[3] = (key >> 8) & 0xff; - if(write(fd, req, 4) != 4) + ipcpackreq(req, 0, mod, key); + if(ipcsend(fd, req, sizeof req) < 0) die("write"); } static int readresp(void) { - unsigned char buf[256]; - int n; + char buf[Ipcfieldmax+1]; + Ipcresp resp; - if(read(fd, buf, 2) != 2) - return -1; - n = buf[1]; - if(n > 0 && read(fd, buf + 2, n) != n) - return -1; - return 0; + return ipcreadresp(fd, 0, buf, sizeof buf, NULL, 0, &resp); } static double diff --git a/dat.h b/dat.h index fb34450..01a3846 100644 --- a/dat.h +++ b/dat.h @@ -126,12 +126,20 @@ struct Drawcmd }; typedef struct Keyreq Keyreq; +typedef struct Keyres Keyres; +struct Keyres +{ + int eaten; + Str commit; + Str preedit; +}; + struct Keyreq { - int fd; u32int ks; u32int mod; - int want; /* nonzero: append preedit after commit */ + int want; /* nonzero: include preedit in reply */ + Channel *reply; }; typedef struct Dictreq Dictreq; diff --git a/fn.h b/fn.h index d33dba9..dac431d 100644 --- a/fn.h +++ b/fn.h @@ -36,6 +36,7 @@ void backko(Im*); void backvi(Im*); void dictsend(Im*, Str*); +int srvreadkey(int, Keyreq*); void srvthread(void*); void ibusthread(void*); void waylandthread(void*); diff --git a/gtk/Makefile b/gtk/Makefile index 4065493..d406d16 100644 --- a/gtk/Makefile +++ b/gtk/Makefile @@ -2,8 +2,8 @@ CFLAGS = -Wall -O2 -I.. `pkg-config --cflags gtk+-3.0` LDFLAGS = `pkg-config --libs gtk+-3.0` DSTDIR ?= $(shell find /usr/lib* -type d -path "*/gtk-3.0/*/immodules" 2>/dev/null | head -1) -im-strans.so: main.c - $(CC) -shared -fPIC $(CFLAGS) -o $@ $< $(LDFLAGS) +im-strans.so: main.c ../ipc.c ../ipc.h + $(CC) -shared -fPIC $(CFLAGS) -o $@ main.c ../ipc.c $(LDFLAGS) docker: main.c Dockerfile docker build -t strans-gtk . diff --git a/gtk/main.c b/gtk/main.c index 326822d..de30a91 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -12,7 +12,7 @@ struct Im { GtkIMContext parent; int fd; - char pre[256]; + char pre[Ipcfieldmax+1]; int prelen; }; @@ -36,52 +36,61 @@ srvconnect(Im *im) return; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/strans.%d", getuid()); + snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid()); if(connect(im->fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){ close(im->fd); im->fd = -1; } } +static void +srvclose(Im *im) +{ + int was; + + if(im->fd >= 0) + close(im->fd); + im->fd = -1; + was = im->prelen; + im->pre[0] = '\0'; + im->prelen = 0; + if(was > 0){ + g_signal_emit_by_name(im, "preedit-changed"); + g_signal_emit_by_name(im, "preedit-end"); + } +} + static int readresp(Im *im, char *buf, int bufsz) { - unsigned char hdr[2], pl; - int n, was; + Ipcresp resp; + int was; - if(read(im->fd, hdr, 2) != 2) - return -1; - n = hdr[1]; - buf[0] = '\0'; - if(n > 0 && n < bufsz){ - if(read(im->fd, buf, n) != n) - return -1; - buf[n] = '\0'; - } - if(read(im->fd, &pl, 1) != 1) - return -1; - if(pl >= sizeof(im->pre)) - return -1; was = im->prelen; - if(pl > 0 && read(im->fd, im->pre, pl) != pl) + if(ipcreadresp(im->fd, 1, buf, bufsz, im->pre, + sizeof(im->pre), &resp) < 0) return -1; - im->pre[pl] = '\0'; - im->prelen = pl; - if(was == 0 && pl > 0) + im->prelen = resp.npreedit; + if(was == 0 && im->prelen > 0) g_signal_emit_by_name(im, "preedit-start"); - if(was != 0 || pl != 0) + if(was != 0 || im->prelen != 0) g_signal_emit_by_name(im, "preedit-changed"); - if(was > 0 && pl == 0) + if(was > 0 && im->prelen == 0) g_signal_emit_by_name(im, "preedit-end"); - return hdr[0]; + return resp.eaten; } static uint32_t kget(uint32_t gdk) { - if(gdk >= 0xff00) + uint32_t u; + + u = gdk_keyval_to_unicode(gdk); + if((gdk & 0xff000000) == 0x01000000 && u != 0) + return u; + if(gdk >= 0xff00 && gdk <= 0xffff) return Kspec + (gdk - 0xff00); - return gdk_keyval_to_unicode(gdk); + return u; } static uint32_t @@ -104,17 +113,17 @@ mget(uint32_t state) static void sendreset(Im *im) { - unsigned char buf[4]; - char resp[64]; + unsigned char buf[Ipcreqsz]; + char resp[Ipcfieldmax+1]; - if(im->fd < 0) + if(im->fd < 0){ + srvclose(im); return; - buf[0] = 1; - buf[1] = 0; - buf[2] = Kesc & 0xff; - buf[3] = Kesc >> 8; - if(send(im->fd, buf, 4, MSG_NOSIGNAL) == 4) - readresp(im, resp, sizeof(resp)); + } + ipcpackreq(buf, 1, 0, Kesc); + if(ipcsend(im->fd, buf, sizeof buf) < 0 || + readresp(im, resp, sizeof(resp)) < 0) + srvclose(im); } static gboolean @@ -137,8 +146,8 @@ static gboolean kpress(GtkIMContext *ctx, GdkEventKey *ev) { Im *im; - unsigned char buf[4]; - char resp[64]; + unsigned char buf[Ipcreqsz]; + char resp[Ipcfieldmax+1]; uint32_t key, mod; int r; GtkInputPurpose purpose; @@ -154,21 +163,18 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev) if(purpose == GTK_INPUT_PURPOSE_PASSWORD || purpose == GTK_INPUT_PURPOSE_PIN) return plaincommit(ctx, key, mod); srvconnect(im); - if(im->fd < 0) + if(im->fd < 0){ + srvclose(im); return plaincommit(ctx, key, mod); - buf[0] = 1; - buf[1] = mod; - buf[2] = key & 0xff; - buf[3] = key >> 8; - if(send(im->fd, buf, 4, MSG_NOSIGNAL) != 4){ - close(im->fd); - im->fd = -1; + } + ipcpackreq(buf, 1, mod, key); + if(ipcsend(im->fd, buf, sizeof buf) < 0){ + srvclose(im); return plaincommit(ctx, key, mod); } r = readresp(im, resp, sizeof(resp)); if(r < 0){ - close(im->fd); - im->fd = -1; + srvclose(im); return plaincommit(ctx, key, mod); } if(resp[0] != '\0') @@ -228,6 +234,8 @@ static void init(Im *im) { im->fd = -1; + im->pre[0] = '\0'; + im->prelen = 0; } static void diff --git a/ibus.c b/ibus.c index 1469125..825e740 100644 --- a/ibus.c +++ b/ibus.c @@ -8,6 +8,7 @@ #include #include #include +#include enum { @@ -30,6 +31,7 @@ static DBusServer *srv; static char addrfile[256]; static int icctr; static int busctr; +static Channel *replyc; static DBusHandlerResult onmsg(DBusConnection*, DBusMessage*, void*); @@ -182,9 +184,14 @@ togglewatch(DBusWatch *w, void *_) static u32int kget(u32int sym) { + u32int c; + + c = xkb_keysym_to_utf32(sym); + if(c >= ' ' && c != 0x7f) + return c; if(sym >= 0xff00 && sym <= 0xffff) return Kspec + (sym - 0xff00); - return sym; + return c; } static u32int @@ -201,53 +208,24 @@ mget(u32int state) } static void -sendkey(u32int ks, u32int mod, char *com, int csz, char *pre, int psz, - int *eaten) +sendkey(u32int ks, u32int mod, Keyres *res) { Keyreq kr; - int p[2]; - uchar hdr[2]; - uchar pl; - int n; - *eaten = 0; - com[0] = '\0'; - pre[0] = '\0'; - if(pipe(p) < 0) - return; - kr.fd = p[1]; kr.ks = ks; kr.mod = mod; kr.want = 1; + kr.reply = replyc; chansend(keyc, &kr); - if(read(p[0], hdr, 2) != 2) - goto out; - *eaten = hdr[0]; - n = hdr[1]; - if(n > 0 && n < csz){ - if(read(p[0], com, n) != n) - goto out; - com[n] = '\0'; - } - if(read(p[0], &pl, 1) != 1) - goto out; - if(pl > 0 && pl < psz){ - if(read(p[0], pre, pl) != pl) - goto out; - pre[pl] = '\0'; - } -out: - close(p[0]); - close(p[1]); + chanrecv(replyc, res); } static void sendreset(void) { - char com[64], pre[256]; - int eaten; + Keyres res; - sendkey(Kesc, 0, com, sizeof(com), pre, sizeof(pre), &eaten); + sendkey(Kesc, 0, &res); } static void @@ -305,7 +283,7 @@ emitpreedit(DBusConnection *c, const char *path, const char *text) return; dbus_message_iter_init_append(sig, &it); appendibustext(&it, text); - cursor = utflen(text); + cursor = utflen((char*)text); visible = text[0] != '\0' ? TRUE : FALSE; dbus_message_iter_append_basic(&it, DBUS_TYPE_UINT32, &cursor); dbus_message_iter_append_basic(&it, DBUS_TYPE_BOOLEAN, &visible); @@ -353,8 +331,8 @@ handlekey(DBusConnection *c, DBusMessage *m) DBusMessage *r; DBusError err; dbus_uint32_t sym, code, state; - char commit[256], preedit[256]; - int eaten; + Keyres res; + char commit[Maxutf], preedit[Maxutf]; dbus_bool_t b; u32int ks, mod; @@ -378,13 +356,14 @@ handlekey(DBusConnection *c, DBusMessage *m) } ks = kget(sym); mod = mget(state); - sendkey(ks, mod, commit, sizeof(commit), preedit, sizeof(preedit), - &eaten); + sendkey(ks, mod, &res); + stoutf(&res.commit, commit, sizeof commit); + stoutf(&res.preedit, preedit, sizeof preedit); if(commit[0] != '\0') emitcommit(c, dbus_message_get_path(m), commit); emitpreedit(c, dbus_message_get_path(m), preedit); r = dbus_message_new_method_return(m); - b = eaten ? TRUE : FALSE; + b = res.eaten ? TRUE : FALSE; dbus_message_append_args(r, DBUS_TYPE_BOOLEAN, &b, DBUS_TYPE_INVALID); dbus_connection_send(c, r, nil); dbus_message_unref(r); @@ -581,6 +560,7 @@ ibusthread(void *_) threadsetname("ibus"); if(ibusinit() < 0) return; + replyc = chancreate(sizeof(Keyres), 0); for(;;){ n = 0; for(i = 0; i < nwatches && n < Maxwatches; i++){ diff --git a/ipc.c b/ipc.c new file mode 100644 index 0000000..8bf166f --- /dev/null +++ b/ipc.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include "ipc.h" + +#if !defined(MSG_NOSIGNAL) && !defined(SO_NOSIGPIPE) +#error "ipcsend requires MSG_NOSIGNAL or SO_NOSIGPIPE" +#endif + +static void +putlen(unsigned char p[Ipclensz], size_t n) +{ + p[0] = n; + p[1] = n >> 8; +} + +static size_t +getlen(const unsigned char p[Ipclensz]) +{ + return p[0] | (p[1] << 8); +} + +void +ipcpackreq(unsigned char req[Ipcreqsz], int want, unsigned int mod, + unsigned int key) +{ + req[0] = want != 0; + req[1] = mod; + req[2] = key; + req[3] = key >> 8; + req[4] = key >> 16; + req[5] = key >> 24; +} + +void +ipcunpackreq(const unsigned char req[Ipcreqsz], int *want, unsigned int *mod, + unsigned int *key) +{ + *want = req[0] != 0; + *mod = req[1]; + *key = (unsigned int)req[2] | + ((unsigned int)req[3] << 8) | + ((unsigned int)req[4] << 16) | + ((unsigned int)req[5] << 24); +} + +int +ipcpackresp(unsigned char *dst, size_t cap, int eaten, + const char *commit, size_t ncommit, const char *preedit, size_t npreedit, + int want) +{ + size_t n; + + if(ncommit > Ipcfieldmax || npreedit > Ipcfieldmax) + return -1; + if((ncommit > 0 && commit == NULL) || + (want && npreedit > 0 && preedit == NULL)) + return -1; + n = Ipcresphdrsz + ncommit + (want ? Ipclensz + npreedit : 0); + if(dst == NULL || cap < n) + return -1; + dst[0] = eaten != 0; + putlen(dst + 1, ncommit); + if(ncommit > 0) + memcpy(dst + Ipcresphdrsz, commit, ncommit); + if(want){ + putlen(dst + Ipcresphdrsz + ncommit, npreedit); + if(npreedit > 0) + memcpy(dst + Ipcresphdrsz + ncommit + Ipclensz, + preedit, npreedit); + } + return n; +} + +int +ipcreadn(int fd, void *buf, size_t n) +{ + unsigned char *p; + ssize_t r; + + p = buf; + while(n > 0){ + r = read(fd, p, n); + if(r < 0 && errno == EINTR) + continue; + if(r <= 0) + return -1; + p += r; + n -= r; + } + return 0; +} + +int +ipcsend(int fd, const void *buf, size_t n) +{ + const unsigned char *p; + ssize_t r; + int flags; +#if !defined(MSG_NOSIGNAL) && defined(SO_NOSIGPIPE) + int one, sr; +#endif + + p = buf; +#ifdef MSG_NOSIGNAL + flags = MSG_NOSIGNAL; +#else + flags = 0; +#ifdef SO_NOSIGPIPE + one = 1; + do + sr = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof one); + while(sr < 0 && errno == EINTR); + if(sr < 0) + return -1; +#endif +#endif + while(n > 0){ + r = send(fd, p, n, flags); + if(r < 0 && errno == EINTR) + continue; + if(r <= 0) + return -1; + p += r; + n -= r; + } + return 0; +} + +static int +readfield(int fd, size_t n, char *dst, size_t cap) +{ + unsigned char discard[128]; + size_t keep, part; + + if(cap > 0 && dst == NULL) + return -1; + keep = 0; + if(cap > 0) + keep = n >= cap ? cap - 1 : n; + if(keep > 0 && ipcreadn(fd, dst, keep) < 0) + return -1; + if(cap > 0) + dst[keep] = '\0'; + n -= keep; + while(n > 0){ + part = n < sizeof discard ? n : sizeof discard; + if(ipcreadn(fd, discard, part) < 0) + return -1; + n -= part; + } + return 0; +} + +int +ipcreadresp(int fd, int want, char *commit, size_t ccap, + char *preedit, size_t pcap, Ipcresp *resp) +{ + unsigned char hdr[Ipcresphdrsz], npreedit[Ipclensz]; + + if(resp == NULL || (ccap > 0 && commit == NULL) || + (pcap > 0 && preedit == NULL)) + return -1; + if(ccap > 0) + commit[0] = '\0'; + if(pcap > 0) + preedit[0] = '\0'; + memset(resp, 0, sizeof *resp); + if(ipcreadn(fd, hdr, sizeof hdr) < 0) + return -1; + resp->eaten = hdr[0] != 0; + resp->ncommit = getlen(hdr + 1); + if(resp->ncommit > Ipcfieldmax) + return -1; + if(readfield(fd, resp->ncommit, commit, ccap) < 0) + return -1; + if(!want) + return 0; + if(ipcreadn(fd, npreedit, sizeof npreedit) < 0) + return -1; + resp->npreedit = getlen(npreedit); + if(resp->npreedit > Ipcfieldmax) + return -1; + return readfield(fd, resp->npreedit, preedit, pcap); +} diff --git a/ipc.h b/ipc.h index 90a15c2..7224d89 100644 --- a/ipc.h +++ b/ipc.h @@ -1,9 +1,21 @@ -// req: 4 bytes [type, mod, key lo, key hi] -// resp: 2+n bytes [type, n, ...] +#ifndef STRANS_IPC_H +#define STRANS_IPC_H + +#include + +#define IPCPATH "/tmp/strans.2.%d" + +/* + * Request: [want-preedit, modifiers, key byte 0, ..., key byte 3]. + * Response: [eaten, commit-length-low, commit-length-high, commit...], + * followed, when requested, by + * [preedit-length-low, preedit-length-high, preedit...]. + * Lengths are little-endian byte counts; fields are at most Ipcfieldmax bytes. + */ enum { - Kspec = 0xf000, + Kspec = 0x110000, Kback = Kspec|0x08, Ktab = Kspec|0x09, Kret = Kspec|0x0d, @@ -17,4 +29,27 @@ enum Mctrl = 1<<2, Malt = 1<<3, Msuper = 1<<6, + + Ipcreqsz = 6, + Ipclensz = 2, + Ipcresphdrsz = 1 + Ipclensz, + Ipcfieldmax = 256, + Ipcmaxresp = Ipcresphdrsz + Ipcfieldmax + Ipclensz + Ipcfieldmax, }; + +typedef struct Ipcresp Ipcresp; +struct Ipcresp +{ + int eaten; + size_t ncommit; + size_t npreedit; +}; + +void ipcpackreq(unsigned char[Ipcreqsz], int, unsigned int, unsigned int); +void ipcunpackreq(const unsigned char[Ipcreqsz], int*, unsigned int*, unsigned int*); +int ipcpackresp(unsigned char*, size_t, 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*, size_t, char*, size_t, Ipcresp*); + +#endif diff --git a/srv.c b/srv.c index 7fc0793..41e4b93 100644 --- a/srv.c +++ b/srv.c @@ -2,30 +2,53 @@ #include "fn.h" static char adir[40]; +static Channel *clientc; -static void -sendkey(int fd, u32int ks, u32int mod, int want) +int +srvreadkey(int fd, Keyreq *kr) { - Keyreq kr; + uchar req[Ipcreqsz]; + u32int ks, mod; + int want; - kr.fd = fd; - kr.ks = ks; - kr.mod = mod; - kr.want = want; - chansend(keyc, &kr); + if(ipcreadn(fd, req, sizeof req) < 0) + return -1; + ipcunpackreq(req, &want, &mod, &ks); + kr->ks = ks; + kr->mod = mod; + kr->want = want; + return 0; } static void clientthread(void *arg) { + Channel *reply; int fd; - uchar req[4]; + Keyreq kr; + Keyres res; + uchar out[Ipcmaxresp]; + char commit[Maxutf], preedit[Maxutf]; + int n, ncommit, npreedit; + uchar token; fd = (int)(uintptr)arg; threadsetname("client %d", fd); - while(read(fd, req, 4) == 4) - sendkey(fd, req[2] | (req[3] << 8), req[1], req[0]); + reply = chancreate(sizeof(Keyres), 0); + kr.reply = reply; + while(srvreadkey(fd, &kr) == 0){ + chansend(keyc, &kr); + chanrecv(reply, &res); + ncommit = stoutf(&res.commit, commit, sizeof commit); + npreedit = stoutf(&res.preedit, preedit, sizeof preedit); + n = ipcpackresp(out, sizeof out, res.eaten, + commit, ncommit, preedit, npreedit, kr.want); + if(n < 0 || ipcsend(fd, out, n) < 0) + break; + } + chanfree(reply); close(fd); + chanrecv(clientc, &token); } static void @@ -33,7 +56,7 @@ srvinit(void) { char addr[64]; - snprint(addr, sizeof(addr), "unix!/tmp/strans.%d", getuid()); + snprint(addr, sizeof(addr), "unix!" IPCPATH, getuid()); remove(addr + 5); if(announce(addr, adir) < 0) die("announce: %r"); @@ -44,13 +67,23 @@ srvthread(void*) { char ldir[40]; int fd; + uchar token; threadsetname("srv"); srvinit(); + token = 0; + clientc = chancreate(sizeof token, Maxclients); for(;;){ fd = listen(adir, ldir); if(fd < 0) continue; - proccreate(clientthread, (void*)(uintptr)fd, 8192); + if(channbsend(clientc, &token) <= 0){ + close(fd); + continue; + } + if(proccreate(clientthread, (void*)(uintptr)fd, 8192) < 0){ + chanrecv(clientc, &token); + close(fd); + } } } diff --git a/strans.c b/strans.c index c132ada..9566007 100644 --- a/strans.c +++ b/strans.c @@ -308,14 +308,24 @@ init(void) im.l = getlang(LangEN); } +static void +imhandlekey(Keyreq *kr) +{ + Keyres res; + + sclear(&res.commit); + sclear(&res.preedit); + res.eaten = keystroke(kr->ks, kr->mod, &res.commit); + if(kr->want) + res.preedit = im.pre; + chansend(kr->reply, &res); +} + void imthread(void*) { Keyreq kr; Dictres res; - Str com; - uchar out[256]; - int n, len, r; Alt alts[] = { {keyc, &kr, CHANRCV, nil}, {dictresc, &res, CHANRCV, nil}, @@ -327,24 +337,7 @@ imthread(void*) for(;;){ switch(alt(alts)){ case 0: - sclear(&com); - r = keystroke(kr.ks, kr.mod, &com); - out[0] = r; - out[1] = 0; - n = 2; - if(com.n > 0){ - len = stoutf(&com, (char*)(out+2), sizeof(out)-2); - out[1] = len; - n += len; - } - if(kr.want){ - len = im.pre.n > 0 ? - stoutf(&im.pre, (char*)(out+n+1), - sizeof(out)-n-1) : 0; - out[n] = len; - n += 1 + len; - } - write(kr.fd, out, n); + imhandlekey(&kr); break; case 1: if(scmp(&res.key, &im.pre) == 0){ @@ -385,4 +378,3 @@ mapinit(char *dir) langs[i].map = trieopen(path); } } - diff --git a/wayland.c b/wayland.c index 4d60ce2..eb2cbe2 100644 --- a/wayland.c +++ b/wayland.c @@ -25,55 +25,40 @@ static struct xkb_state *xkbst; static int active; static int pending; static u32int imserial; +static Channel *replyc; static void -sendkey(u32int ks, u32int mod, char *com, int csz, char *pre, int psz, - int *eaten) +sendkey(u32int ks, u32int mod, Keyres *res) { Keyreq kr; - int p[2]; - uchar hdr[2]; - uchar pl; - int n; - *eaten = 0; - com[0] = '\0'; - pre[0] = '\0'; - if(pipe(p) < 0) - return; - kr.fd = p[1]; kr.ks = ks; kr.mod = mod; kr.want = 1; + kr.reply = replyc; chansend(keyc, &kr); - if(read(p[0], hdr, 2) != 2) - goto out; - *eaten = hdr[0]; - n = hdr[1]; - if(n > 0 && n < csz){ - if(read(p[0], com, n) != n) - goto out; - com[n] = '\0'; - } - if(read(p[0], &pl, 1) != 1) - goto out; - if(pl > 0 && pl < psz){ - if(read(p[0], pre, pl) != pl) - goto out; - pre[pl] = '\0'; - } -out: - close(p[0]); - close(p[1]); + chanrecv(replyc, res); } static void sendreset(void) { - char com[64], pre[256]; - int eaten; + Keyres res; - sendkey(Kesc, 0, com, sizeof(com), pre, sizeof(pre), &eaten); + sendkey(Kesc, 0, &res); +} + +static u32int +kget(xkb_keysym_t ks) +{ + u32int c; + + c = xkb_keysym_to_utf32(ks); + if(c >= ' ' && c != 0x7f) + return c; + if(ks >= 0xff00 && ks <= 0xffff) + return Kspec + (ks - 0xff00); + return c; } static u32int @@ -104,29 +89,32 @@ kpress(uint32_t time, uint32_t keycode, uint32_t state) { xkb_keysym_t ks; u32int k, mod; - char com[64], pre[256]; - int eaten, plen; + Keyres res; + char commit[Maxutf], preedit[Maxutf]; + int plen; if(state != WL_KEYBOARD_KEY_STATE_PRESSED || xkbst == nil){ zwp_virtual_keyboard_v1_key(vk, time, keycode, state); return; } ks = xkb_state_key_get_one_sym(xkbst, keycode + 8); - k = ks >= 0xff00 ? Kspec + (ks - 0xff00) : xkb_keysym_to_utf32(ks); + k = kget(ks); if(k == 0){ zwp_virtual_keyboard_v1_key(vk, time, keycode, state); return; } mod = mget(); - sendkey(k, mod, com, sizeof(com), pre, sizeof(pre), &eaten); - if(com[0] != '\0'){ - zwp_input_method_v2_commit_string(im, com); + sendkey(k, mod, &res); + stoutf(&res.commit, commit, sizeof commit); + stoutf(&res.preedit, preedit, sizeof preedit); + if(commit[0] != '\0'){ + zwp_input_method_v2_commit_string(im, commit); zwp_input_method_v2_commit(im, imserial); } - plen = strlen(pre); - zwp_input_method_v2_set_preedit_string(im, pre, plen, plen); + plen = strlen(preedit); + zwp_input_method_v2_set_preedit_string(im, preedit, plen, plen); zwp_input_method_v2_commit(im, imserial); - if(!eaten) + if(!res.eaten) zwp_virtual_keyboard_v1_key(vk, time, keycode, state); } @@ -158,6 +146,7 @@ kg_keymap(void *data, struct zwp_input_method_keyboard_grab_v2 *g, munmap(s, size); } zwp_virtual_keyboard_v1_keymap(vk, format, fd, size); + close(fd); } static void @@ -215,14 +204,6 @@ im_deactivate(void *data, struct zwp_input_method_v2 *m) (void)data; (void)m; pending = 0; - if(active){ - active = 0; - sendreset(); - if(grab != nil){ - zwp_input_method_keyboard_grab_v2_release(grab); - grab = nil; - } - } } static void @@ -266,6 +247,13 @@ im_done(void *data, struct zwp_input_method_v2 *m) if(grab != nil) zwp_input_method_keyboard_grab_v2_add_listener(grab, &grab_listener, nil); + }else if(!pending && active){ + active = 0; + sendreset(); + if(grab != nil){ + zwp_input_method_keyboard_grab_v2_release(grab); + grab = nil; + } } } @@ -292,9 +280,9 @@ reg_global(void *data, struct wl_registry *r, uint32_t name, const char *iface, uint32_t version) { (void)data; - (void)version; if(strcmp(iface, wl_seat_interface.name) == 0) - seat = wl_registry_bind(r, name, &wl_seat_interface, 5); + seat = wl_registry_bind(r, name, &wl_seat_interface, + version < 5 ? version : 5); else if(strcmp(iface, zwp_input_method_manager_v2_interface.name) == 0) immgr = wl_registry_bind(r, name, &zwp_input_method_manager_v2_interface, 1); @@ -345,6 +333,9 @@ waylandthread(void *_) im = zwp_input_method_manager_v2_get_input_method(immgr, seat); zwp_input_method_v2_add_listener(im, &im_listener, nil); vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmgr, seat); + replyc = chancreate(sizeof(Keyres), 0); while(wl_display_dispatch(dpy) != -1) ; + chanfree(replyc); + replyc = nil; } diff --git a/xim/Makefile b/xim/Makefile index ffd6dd1..bc71fa3 100644 --- a/xim/Makefile +++ b/xim/Makefile @@ -1,5 +1,7 @@ CC = cc -CFLAGS = -Wall -Wextra -O2 -I.. -Ixcb-imdkit/src +XKB_CFLAGS := $(shell pkg-config --cflags xkbcommon) +XKB_LIBS := $(shell pkg-config --libs xkbcommon) +CFLAGS = -Wall -Wextra -O2 -I.. -Ixcb-imdkit/src $(XKB_CFLAGS) PROG = strans-xim SRCS = $(wildcard *.c) @@ -9,8 +11,8 @@ LIBS = xcb-imdkit/libxcb-imdkit.a xcb-util/libxcb-util.a all: $(PROG) -$(PROG): $(OBJS) $(LIBS) - $(CC) -o $@ $(OBJS) $(LIBS) -lxcb +$(PROG): $(OBJS) $(LIBS) ../ipc.c ../ipc.h + $(CC) $(CFLAGS) -o $@ $(OBJS) ../ipc.c $(LIBS) -lxcb $(XKB_LIBS) $(OBJS): ../ipc.h diff --git a/xim/main.c b/xim/main.c index 86b6dac..8f5fbb8 100644 --- a/xim/main.c +++ b/xim/main.c @@ -5,13 +5,14 @@ #include #include #include +#include #include "imdkit.h" #include "encoding.h" #include "ipc.h" static xcb_connection_t *conn; static xcb_im_t *xim; -static int srvfd; +static int srvfd = -1; static xcb_keysym_t *kmap; static uint8_t minkc, maxkc; static uint8_t symsper; @@ -105,50 +106,74 @@ commit(xcb_im_input_context_t *ic, char *s, int len) } static void +srvclose(void) +{ + if(srvfd < 0) + return; + close(srvfd); + srvfd = -1; +} + +static int srvconnect(void) { struct sockaddr_un addr; + int fd; - srvfd = socket(AF_UNIX, SOCK_STREAM, 0); - if(srvfd < 0) - die("socket failed"); + if(srvfd >= 0) + return 0; + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if(fd < 0) + return -1; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/strans.%d", getuid()); - if(connect(srvfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) - die("can't connect to strans"); + snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid()); + if(connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){ + close(fd); + return -1; + } + srvfd = fd; + return 0; } static int readresp(xcb_im_input_context_t *ic) { - unsigned char buf[64]; - int n; + char buf[Ipcfieldmax+1]; + Ipcresp resp; - if(read(srvfd, buf, 2) != 2) + if(ipcreadresp(srvfd, 0, buf, sizeof buf, NULL, 0, &resp) < 0) return -1; - n = buf[1]; - if(n > 0 && (size_t)n < sizeof(buf) && read(srvfd, buf+2, n) == n) - commit(ic, (char*)(buf+2), n); - return buf[0]; + if(buf[0] != '\0') + commit(ic, buf, strlen(buf)); + return resp.eaten; } static void kpress(xcb_im_input_context_t *ic, xcb_key_press_event_t *ev) { - unsigned char buf[4]; - uint32_t key; + unsigned char buf[Ipcreqsz]; + uint32_t key, rune; + int eaten; key = kget(ev->detail, ev->state); - if(key >= 0xff00) + rune = xkb_keysym_to_utf32(key); + if(rune >= ' ' && rune != 0x7f) + key = rune; + else if(key >= 0xff00 && key <= 0xffff) key = Kspec + (key - 0xff00); - buf[0] = 0; - buf[1] = ev->state; - buf[2] = key; - buf[3] = key >> 8; - if(write(srvfd, buf, 4) != 4) - die("write failed"); - if(readresp(ic) == 0) + else + key = rune; + ipcpackreq(buf, 0, ev->state, key); + eaten = 0; + if(srvconnect() == 0){ + if(ipcsend(srvfd, buf, sizeof buf) < 0 || + (eaten = readresp(ic)) < 0){ + srvclose(); + eaten = 0; + } + } + if(eaten == 0) xcb_im_forward_event(xim, ic, ev); xcb_flush(conn); } @@ -156,15 +181,13 @@ kpress(xcb_im_input_context_t *ic, xcb_key_press_event_t *ev) static void reset(xcb_im_input_context_t *ic) { - unsigned char buf[4]; - - buf[0] = 0; - buf[1] = 0; - buf[2] = Kesc & 0xff; - buf[3] = Kesc >> 8; - if(write(srvfd, buf, 4) != 4) - die("write failed"); - readresp(ic); + unsigned char buf[Ipcreqsz]; + + ipcpackreq(buf, 0, 0, Kesc); + if(srvconnect() < 0) + return; + if(ipcsend(srvfd, buf, sizeof buf) < 0 || readresp(ic) < 0) + srvclose(); } static void @@ -173,6 +196,11 @@ callback(xcb_im_t *im, xcb_im_client_t *client, xcb_im_input_context_t *ic, { xcb_key_press_event_t *ev; + (void)im; + (void)client; + (void)frame; + (void)user; + switch(hdr->major_opcode){ case XCB_XIM_FORWARD_EVENT: ev = arg; @@ -220,7 +248,6 @@ main(void) { xcb_generic_event_t *ev; - srvconnect(); ximinit(); for(;;){ ev = xcb_wait_for_event(conn); @@ -229,5 +256,6 @@ main(void) xcb_im_filter_event(xim, ev); free(ev); } + srvclose(); return 0; }