#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 type, valid; int32_t x, y, h; u32int ks, mod; if(ipcreadn(fd, req, Ipcreqsz) < 0) return -1; type = ipcreqtype(req); switch(type){ case Ipclegacy: ipcunpackreq(req, want, &mod, &ks); kr->op = ipcreqreset(req) ? Keyreset : Keypress; kr->ks = ks; kr->mod = mod; break; case Ipccap: *want = (req[0] & Ipcreqwant) != 0; kr->op = Keycap; kr->ks = 0; kr->mod = 0; break; case Ipccaret: if(ipcreadn(fd, req + Ipcreqsz, Ipccaretsz - Ipcreqsz) < 0 || ipcunpackcaret(req, &valid, &x, &y, &h) < 0) return -1; *want = 0; kr->op = Keycaret; kr->ks = 0; kr->mod = 0; kr->caret.valid = valid; kr->caret.x = x; kr->caret.y = y; kr->caret.h = h; break; default: return -1; } return type; } static void clientthread(void *arg) { Channel *reply; int fd; Keyreq kr; Keyres res; Caret caret; uchar out[Ipcmaxresp]; char commit[Maxutf], preedit[Maxutf]; int cap, n, ncommit, negotiated, npreedit, type, want; uchar token; fd = (int)(uintptr)arg; threadsetname("client %d", fd); reply = chancreate(sizeof(Keyres), 0); kr.reply = reply; kr.owner = &fd; cap = Cclientpreedit; negotiated = 0; memset(&caret, 0, sizeof caret); kr.cap = cap; 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); chanrecv(reply, &res); if(type == Ipccaret) continue; ncommit = stoutf(&res.commit, commit, sizeof commit); npreedit = stoutf(&res.preedit, preedit, sizeof preedit); n = ipcpackresp(out, sizeof out, type == Ipccap ? 1 : res.eaten, commit, ncommit, preedit, npreedit, want); if(n < 0 || ipcsend(fd, out, n) < 0) break; } kr.op = Keyrelease; kr.cap = cap; kr.caret = caret; kr.ks = 0; kr.mod = 0; chansend(keyc, &kr); chanrecv(reply, &res); chanfree(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; } if(proccreate(clientthread, (void*)(uintptr)fd, 8192) < 0){ chanrecv(clientc, &token); close(fd); } } }