plan9port/src/cmd/devdraw/srv.c

551 lines
10 KiB
C
Raw Normal View History

/*
* Window system protocol server.
*/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <memdraw.h>
#include <memlayer.h>
#include <keyboard.h>
#include <mouse.h>
#include <cursor.h>
#include <drawfcall.h>
#include "devdraw.h"
static void runmsg(Client*, Wsysmsg*);
static void replymsg(Client*, Wsysmsg*);
static void matchkbd(Client*);
static void matchmouse(Client*);
2020-01-11 06:10:14 -05:00
static void serveproc(void*);
static void listenproc(void*);
Client *client0;
int trace = 0;
2020-01-11 06:10:14 -05:00
static char *srvname;
static int afd;
static char adir[40];
2020-01-10 00:11:55 -05:00
static void
usage(void)
{
fprint(2, "usage: devdraw (don't run directly)\n");
threadexitsall("usage");
}
void
2020-01-10 00:11:55 -05:00
threadmain(int argc, char **argv)
{
char *p;
2020-01-10 00:11:55 -05:00
ARGBEGIN{
case 'D': /* for good ps -a listings */
break;
case 'f': /* fall through for backward compatibility */
case 'g':
case 'b':
break;
2020-01-11 06:10:14 -05:00
case 's':
// TODO: Update usage, man page.
srvname = EARGF(usage());
break;
2020-01-10 00:11:55 -05:00
default:
usage();
}ARGEND
memimageinit();
fmtinstall('H', encodefmt);
if((p = getenv("DEVDRAWTRACE")) != nil)
trace = atoi(p);
2020-01-11 06:10:14 -05:00
if(srvname == nil) {
client0 = mallocz(sizeof(Client), 1);
if(client0 == nil){
fprint(2, "initdraw: allocating client0: out of memory");
abort();
}
client0->displaydpi = 100;
client0->rfd = 3;
client0->wfd = 4;
/*
* Move the protocol off stdin/stdout so that
* any inadvertent prints don't screw things up.
*/
dup(0,3);
dup(1,4);
close(0);
close(1);
open("/dev/null", OREAD);
open("/dev/null", OWRITE);
2020-01-10 00:11:55 -05:00
}
2020-01-11 06:10:14 -05:00
fmtinstall('W', drawfcallfmt);
2020-01-10 00:11:55 -05:00
gfx_main();
}
void
gfx_started(void)
{
2020-01-11 06:10:14 -05:00
char *addr;
if(srvname == nil) {
// Legacy mode: serving single client on pipes.
proccreate(serveproc, client0, 0);
return;
}
// Server mode.
addr = smprint("unix!%s/%s", getns(), srvname);
if(addr == nil)
sysfatal("out of memory");
if((afd = announce(addr, adir)) < 0)
sysfatal("announce %s: %r", addr);
proccreate(listenproc, nil, 0);
2020-01-10 00:11:55 -05:00
}
static void
2020-01-11 06:10:14 -05:00
listenproc(void *v)
{
Client *c;
int fd;
char dir[40];
USED(v);
for(;;) {
fd = listen(adir, dir);
if(fd < 0)
sysfatal("listen: %r");
c = mallocz(sizeof(Client), 1);
if(c == nil){
fprint(2, "initdraw: allocating client0: out of memory");
abort();
}
c->displaydpi = 100;
c->rfd = fd;
c->wfd = fd;
proccreate(serveproc, c, 0);
}
}
static void
serveproc(void *v)
2020-01-10 00:11:55 -05:00
{
Client *c;
uchar buf[4], *mbuf;
int nmbuf, n, nn;
Wsysmsg m;
2020-01-10 00:11:55 -05:00
c = v;
mbuf = nil;
nmbuf = 0;
while((n = read(c->rfd, buf, 4)) == 4){
GET(buf, n);
if(n > nmbuf){
free(mbuf);
mbuf = malloc(4+n);
if(mbuf == nil)
2020-01-11 06:10:14 -05:00
sysfatal("out of memory");
nmbuf = n;
}
memmove(mbuf, buf, 4);
nn = readn(c->rfd, mbuf+4, n-4);
2020-01-11 06:10:14 -05:00
if(nn != n-4) {
fprint(2, "serveproc: eof during message\n");
break;
}
/* pick off messages one by one */
2020-01-11 06:10:14 -05:00
if(convM2W(mbuf, nn+4, &m) <= 0) {
fprint(2, "serveproc: cannot convert message\n");
break;
}
if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m);
runmsg(c, &m);
}
2020-01-10 00:11:55 -05:00
2020-01-11 06:10:14 -05:00
if(c == client0) {
rpc_shutdown();
threadexitsall(nil);
}
}
static void
replyerror(Client *c, Wsysmsg *m)
{
char err[256];
rerrstr(err, sizeof err);
m->type = Rerror;
m->error = err;
replymsg(c, m);
}
/*
* Handle a single wsysmsg.
* Might queue for later (kbd, mouse read)
*/
static void
runmsg(Client *c, Wsysmsg *m)
{
static uchar buf[65536];
int n;
Memimage *i;
switch(m->type){
2020-01-11 06:10:14 -05:00
case Tctxt:
c->wsysid = strdup(m->id);
replymsg(c, m);
break;
case Tinit:
2020-01-10 00:11:55 -05:00
i = rpc_attach(c, m->label, m->winsize);
if(i == nil) {
replyerror(c, m);
break;
}
2020-01-10 00:11:55 -05:00
draw_initdisplaymemimage(c, i);
replymsg(c, m);
break;
case Trdmouse:
2020-01-10 00:11:55 -05:00
qlock(&c->eventlk);
2020-01-11 06:10:14 -05:00
if((c->mousetags.wi+1)%nelem(c->mousetags.t) == c->mousetags.ri) {
qunlock(&c->eventlk);
werrstr("too many queued mouse reads");
replyerror(c, m);
break;
}
c->mousetags.t[c->mousetags.wi++] = m->tag;
if(c->mousetags.wi == nelem(c->mousetags.t))
c->mousetags.wi = 0;
c->mouse.stall = 0;
matchmouse(c);
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
break;
case Trdkbd:
2020-01-10 00:11:55 -05:00
qlock(&c->eventlk);
2020-01-11 06:10:14 -05:00
if((c->kbdtags.wi+1)%nelem(c->kbdtags.t) == c->kbdtags.ri) {
qunlock(&c->eventlk);
werrstr("too many queued keyboard reads");
replyerror(c, m);
break;
}
c->kbdtags.t[c->kbdtags.wi++] = m->tag;
if(c->kbdtags.wi == nelem(c->kbdtags.t))
c->kbdtags.wi = 0;
c->kbd.stall = 0;
matchkbd(c);
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
break;
case Tmoveto:
c->impl->rpc_setmouse(c, m->mouse.xy);
replymsg(c, m);
break;
case Tcursor:
if(m->arrowcursor)
c->impl->rpc_setcursor(c, nil, nil);
else {
scalecursor(&m->cursor2, &m->cursor);
c->impl->rpc_setcursor(c, &m->cursor, &m->cursor2);
}
replymsg(c, m);
break;
case Tcursor2:
if(m->arrowcursor)
c->impl->rpc_setcursor(c, nil, nil);
else
c->impl->rpc_setcursor(c, &m->cursor, &m->cursor2);
replymsg(c, m);
break;
case Tbouncemouse:
c->impl->rpc_bouncemouse(c, m->mouse);
replymsg(c, m);
break;
case Tlabel:
c->impl->rpc_setlabel(c, m->label);
replymsg(c, m);
break;
case Trdsnarf:
m->snarf = rpc_getsnarf();
replymsg(c, m);
free(m->snarf);
break;
case Twrsnarf:
2020-01-10 00:11:55 -05:00
rpc_putsnarf(m->snarf);
replymsg(c, m);
break;
case Trddraw:
n = m->count;
if(n > sizeof buf)
n = sizeof buf;
2020-01-10 00:11:55 -05:00
n = draw_dataread(c, buf, n);
if(n < 0)
replyerror(c, m);
else{
m->count = n;
m->data = buf;
replymsg(c, m);
}
break;
case Twrdraw:
2020-01-10 00:11:55 -05:00
if(draw_datawrite(c, m->data, m->count) < 0)
replyerror(c, m);
else
replymsg(c, m);
break;
case Ttop:
c->impl->rpc_topwin(c);
replymsg(c, m);
break;
case Tresize:
c->impl->rpc_resizewindow(c, m->rect);
replymsg(c, m);
break;
}
}
/*
* Reply to m.
*/
static void
replymsg(Client *c, Wsysmsg *m)
{
int n;
/* T -> R msg */
if(m->type%2 == 0)
m->type++;
if(trace) fprint(2, "%ud [%d] -> %W\n", nsec()/1000000, threadid(), m);
/* copy to output buffer */
n = sizeW2M(m);
2020-01-10 00:11:55 -05:00
qlock(&c->wfdlk);
if(n > c->nmbuf){
free(c->mbuf);
c->mbuf = malloc(n);
if(c->mbuf == nil)
sysfatal("out of memory");
2020-01-10 00:11:55 -05:00
c->nmbuf = n;
}
2020-01-10 00:11:55 -05:00
convW2M(m, c->mbuf, n);
if(write(c->wfd, c->mbuf, n) != n)
2020-01-11 06:10:14 -05:00
fprint(2, "client write: %r\n");
2020-01-10 00:11:55 -05:00
qunlock(&c->wfdlk);
}
/*
* Match queued kbd reads with queued kbd characters.
*/
static void
matchkbd(Client *c)
{
Wsysmsg m;
if(c->kbd.stall)
return;
while(c->kbd.ri != c->kbd.wi && c->kbdtags.ri != c->kbdtags.wi){
m.type = Rrdkbd;
m.tag = c->kbdtags.t[c->kbdtags.ri++];
if(c->kbdtags.ri == nelem(c->kbdtags.t))
c->kbdtags.ri = 0;
m.rune = c->kbd.r[c->kbd.ri++];
if(c->kbd.ri == nelem(c->kbd.r))
c->kbd.ri = 0;
replymsg(c, &m);
}
}
// matchmouse matches queued mouse reads with queued mouse events.
2020-01-10 00:11:55 -05:00
// It must be called with c->eventlk held.
static void
matchmouse(Client *c)
{
Wsysmsg m;
2020-01-10 00:11:55 -05:00
if(canqlock(&c->eventlk)) {
fprint(2, "misuse of matchmouse\n");
abort();
}
while(c->mouse.ri != c->mouse.wi && c->mousetags.ri != c->mousetags.wi){
m.type = Rrdmouse;
m.tag = c->mousetags.t[c->mousetags.ri++];
if(c->mousetags.ri == nelem(c->mousetags.t))
c->mousetags.ri = 0;
m.mouse = c->mouse.m[c->mouse.ri];
m.resized = c->mouse.resized;
c->mouse.resized = 0;
/*
if(m.resized)
fprint(2, "sending resize\n");
*/
c->mouse.ri++;
if(c->mouse.ri == nelem(c->mouse.m))
c->mouse.ri = 0;
replymsg(c, &m);
}
}
void
gfx_mouseresized(Client *c)
{
gfx_mousetrack(c, -1, -1, -1, -1);
}
void
gfx_mousetrack(Client *c, int x, int y, int b, uint ms)
{
Mouse *m;
2020-01-10 00:11:55 -05:00
qlock(&c->eventlk);
if(x == -1 && y == -1 && b == -1 && ms == -1) {
Mouse *copy;
// repeat last mouse event for resize
if(c->mouse.ri == 0)
copy = &c->mouse.m[nelem(c->mouse.m)-1];
else
copy = &c->mouse.m[c->mouse.ri-1];
x = copy->xy.x;
y = copy->xy.y;
b = copy->buttons;
ms = copy->msec;
c->mouse.resized = 1;
}
if(x < c->mouserect.min.x)
x = c->mouserect.min.x;
if(x > c->mouserect.max.x)
x = c->mouserect.max.x;
if(y < c->mouserect.min.y)
y = c->mouserect.min.y;
if(y > c->mouserect.max.y)
y = c->mouserect.max.y;
// If reader has stopped reading, don't bother.
// If reader is completely caught up, definitely queue.
// Otherwise, queue only button change events.
if(!c->mouse.stall)
if(c->mouse.wi == c->mouse.ri || c->mouse.last.buttons != b){
m = &c->mouse.last;
m->xy.x = x;
m->xy.y = y;
m->buttons = b;
m->msec = ms;
c->mouse.m[c->mouse.wi] = *m;
if(++c->mouse.wi == nelem(c->mouse.m))
c->mouse.wi = 0;
if(c->mouse.wi == c->mouse.ri){
c->mouse.stall = 1;
c->mouse.ri = 0;
c->mouse.wi = 1;
c->mouse.m[0] = *m;
}
matchmouse(c);
}
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
}
// kputc adds ch to the keyboard buffer.
2020-01-10 00:11:55 -05:00
// It must be called with c->eventlk held.
static void
kputc(Client *c, int ch)
{
2020-01-10 00:11:55 -05:00
if(canqlock(&c->eventlk)) {
fprint(2, "misuse of kputc\n");
abort();
}
c->kbd.r[c->kbd.wi++] = ch;
if(c->kbd.wi == nelem(c->kbd.r))
c->kbd.wi = 0;
if(c->kbd.ri == c->kbd.wi)
c->kbd.stall = 1;
matchkbd(c);
}
// gfx_abortcompose stops any pending compose sequence,
// because a mouse button has been clicked.
// It is called from the graphics thread with no locks held.
void
gfx_abortcompose(Client *c)
{
2020-01-10 00:11:55 -05:00
qlock(&c->eventlk);
if(c->kbd.alting) {
c->kbd.alting = 0;
c->kbd.nk = 0;
}
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
}
// gfx_keystroke records a single-rune keystroke.
// It is called from the graphics thread with no locks held.
void
gfx_keystroke(Client *c, int ch)
{
int i;
2020-01-10 00:11:55 -05:00
qlock(&c->eventlk);
if(ch == Kalt){
c->kbd.alting = !c->kbd.alting;
c->kbd.nk = 0;
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
return;
}
if(ch == Kcmd+'r') {
if(c->forcedpi)
c->forcedpi = 0;
else if(c->displaydpi >= 200)
c->forcedpi = 100;
else
c->forcedpi = 225;
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
c->impl->rpc_resizeimg(c);
return;
}
if(!c->kbd.alting){
kputc(c, ch);
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
return;
}
if(c->kbd.nk >= nelem(c->kbd.k)) // should not happen
c->kbd.nk = 0;
c->kbd.k[c->kbd.nk++] = ch;
2020-01-10 00:11:55 -05:00
ch = latin1(c->kbd.k, c->kbd.nk);
if(ch > 0){
c->kbd.alting = 0;
kputc(c, ch);
c->kbd.nk = 0;
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
return;
}
if(ch == -1){
c->kbd.alting = 0;
for(i=0; i<c->kbd.nk; i++)
kputc(c, c->kbd.k[i]);
c->kbd.nk = 0;
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
return;
}
// need more input
2020-01-10 00:11:55 -05:00
qunlock(&c->eventlk);
return;
}