Files
strans/srv.c
Hojun-Cho eb0f88f764 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.
2026-08-16 15:53:08 +09:00

128 lines
2.7 KiB
C

#include "dat.h"
#include "fn.h"
#include <sys/stat.h>
#include <sys/un.h>
static char adir[256];
static Channel *clientc;
static int
srvreadreq(int fd, Keyreq *kr, int *want)
{
uchar req[Ipccaretsz];
int valid;
int32_t x, y, h;
if(ipcreadn(fd, req, Ipcreqsz) < 0)
return -1;
*want = 0;
kr->ks = 0;
kr->mod = 0;
switch(ipcreqtype(req)){
case Ipckey:
ipcunpackreq(req, want, &kr->mod, &kr->ks);
kr->op = ipcreqreset(req) ? Keyreset : Keypress;
return 0;
case Ipccap:
*want = (req[0] & Ipcreqwant) != 0;
kr->op = Keycap;
return 0;
case Ipccaret:
if(ipcreadn(fd, req + Ipcreqsz, Ipccaretsz - Ipcreqsz) < 0 ||
ipcunpackcaret(req, &valid, &x, &y, &h) < 0)
return -1;
kr->op = Keycaret;
kr->caret.valid = valid;
kr->caret.x = x;
kr->caret.y = y;
kr->caret.h = h;
return 0;
}
return -1;
}
/*
* 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
clientthread(void *arg)
{
Keyreq kr;
Keyres res;
uchar out[Ipcmaxresp], token;
char commit[Maxutf], preedit[Maxutf];
int fd, n, ncommit, npreedit, want;
fd = (int)(uintptr)arg;
threadsetname("client %d", fd);
memset(&kr, 0, sizeof kr);
kr.reply = chancreate(sizeof(Keyres), 0);
kr.owner = &fd;
kr.cap = Cclientpreedit;
while(srvreadreq(fd, &kr, &want) >= 0){
if(kr.op != Keycaret)
kr.cap = want ? Cclientpreedit : 0;
chansend(keyc, &kr);
chanrecv(kr.reply, &res);
if(kr.op == Keycaret)
continue;
ncommit = stoutf(&res.commit, commit, sizeof commit);
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);
if(n < 0 || ipcsend(fd, out, n) < 0)
break;
}
kr.op = Keyrelease;
kr.ks = 0;
kr.mod = 0;
chansend(keyc, &kr);
chanrecv(kr.reply, &res);
chanfree(kr.reply);
close(fd);
chanrecv(clientc, &token);
}
void
srvinit(void)
{
char *addr, path[sizeof(((struct sockaddr_un*)0)->sun_path)];
if(ipcpath(path, sizeof path) < 0)
die("IPC path is too long");
addr = smprint("unix!%s", path);
if(addr == nil)
die("out of memory");
if(announce(addr, adir) < 0)
die("IPC endpoint is already in use: %r");
free(addr);
if(chmod(path, 0600) < 0)
die("can't protect IPC endpoint: %s", path);
}
void
srvthread(void*)
{
char ldir[40];
int fd;
uchar token;
threadsetname("srv");
token = 0;
clientc = chancreate(sizeof token, Maxclients);
for(;;){
fd = listen(adir, ldir);
if(fd < 0)
continue;
if(channbsend(clientc, &token) <= 0){
close(fd);
continue;
}
proccreate(clientthread, (void*)(uintptr)fd, 8192);
}
}