#include "dat.h" #include "fn.h" #include #include 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); } }