Files
strans/srv.c

151 lines
3.0 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 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);
}
}
}