plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

srv.c (10538B)


      1 /*
      2  * Window system protocol server.
      3  */
      4 
      5 #include <u.h>
      6 #include <libc.h>
      7 #include <thread.h>
      8 #include <draw.h>
      9 #include <memdraw.h>
     10 #include <memlayer.h>
     11 #include <keyboard.h>
     12 #include <mouse.h>
     13 #include <cursor.h>
     14 #include <drawfcall.h>
     15 #include "devdraw.h"
     16 
     17 static void runmsg(Client*, Wsysmsg*);
     18 static void replymsg(Client*, Wsysmsg*);
     19 static void matchkbd(Client*);
     20 static void matchmouse(Client*);
     21 static void serveproc(void*);
     22 static void listenproc(void*);
     23 Client *client0;
     24 
     25 int trace = 0;
     26 static char *srvname;
     27 static int afd;
     28 static char adir[40];
     29 
     30 static void
     31 usage(void)
     32 {
     33 	fprint(2, "usage: devdraw (don't run directly)\n");
     34 	threadexitsall("usage");
     35 }
     36 
     37 void
     38 threadmain(int argc, char **argv)
     39 {
     40 	char *p;
     41 
     42 	ARGBEGIN{
     43 	case 'D':		/* for good ps -a listings */
     44 		break;
     45 	case 'f':		/* fall through for backward compatibility */
     46 	case 'g':
     47 	case 'b':
     48 		break;
     49 	case 's':
     50 		// TODO: Update usage, man page.
     51 		srvname = EARGF(usage());
     52 		break;
     53 	default:
     54 		usage();
     55 	}ARGEND
     56 
     57 	memimageinit();
     58 	fmtinstall('H', encodefmt);
     59 	if((p = getenv("DEVDRAWTRACE")) != nil)
     60 		trace = atoi(p);
     61 
     62 	if(srvname == nil) {
     63 		client0 = mallocz(sizeof(Client), 1);
     64 		if(client0 == nil){
     65 			fprint(2, "initdraw: allocating client0: out of memory");
     66 			abort();
     67 		}
     68 		client0->displaydpi = 100;
     69 		client0->rfd = 3;
     70 		client0->wfd = 4;
     71 
     72 		/*
     73 		 * Move the protocol off stdin/stdout so that
     74 		 * any inadvertent prints don't screw things up.
     75 		 */
     76 		dup(0,3);
     77 		dup(1,4);
     78 		close(0);
     79 		close(1);
     80 		open("/dev/null", OREAD);
     81 		open("/dev/null", OWRITE);
     82 	}
     83 
     84 	fmtinstall('W', drawfcallfmt);
     85 	gfx_main();
     86 }
     87 
     88 void
     89 gfx_started(void)
     90 {
     91 	char *ns, *addr;
     92 
     93 	if(srvname == nil) {
     94 		// Legacy mode: serving single client on pipes.
     95 		proccreate(serveproc, client0, 0);
     96 		return;
     97 	}
     98 
     99 	// Server mode.
    100 	if((ns = getns()) == nil)
    101 		sysfatal("out of memory");
    102 
    103 	addr = smprint("unix!%s/%s", ns, srvname);
    104 	free(ns);
    105 	if(addr == nil)
    106 		sysfatal("out of memory");
    107 
    108 	if((afd = announce(addr, adir)) < 0)
    109 		sysfatal("announce %s: %r", addr);
    110 
    111 	proccreate(listenproc, nil, 0);
    112 }
    113 
    114 static void
    115 listenproc(void *v)
    116 {
    117 	Client *c;
    118 	int fd;
    119 	char dir[40];
    120 
    121 	USED(v);
    122 
    123 	for(;;) {
    124 		fd = listen(adir, dir);
    125 		if(fd < 0)
    126 			sysfatal("listen: %r");
    127 		c = mallocz(sizeof(Client), 1);
    128 		if(c == nil){
    129 			fprint(2, "initdraw: allocating client0: out of memory");
    130 			abort();
    131 		}
    132 		c->displaydpi = 100;
    133 		c->rfd = fd;
    134 		c->wfd = fd;
    135 		proccreate(serveproc, c, 0);
    136 	}
    137 }
    138 
    139 static void
    140 serveproc(void *v)
    141 {
    142 	Client *c;
    143 	uchar buf[4], *mbuf;
    144 	int nmbuf, n, nn;
    145 	Wsysmsg m;
    146 
    147 	c = v;
    148 	mbuf = nil;
    149 	nmbuf = 0;
    150 	while((n = read(c->rfd, buf, 4)) == 4){
    151 		GET(buf, n);
    152 		if(n > nmbuf){
    153 			free(mbuf);
    154 			mbuf = malloc(4+n);
    155 			if(mbuf == nil)
    156 				sysfatal("out of memory");
    157 			nmbuf = n;
    158 		}
    159 		memmove(mbuf, buf, 4);
    160 		nn = readn(c->rfd, mbuf+4, n-4);
    161 		if(nn != n-4) {
    162 			fprint(2, "serveproc: eof during message\n");
    163 			break;
    164 		}
    165 
    166 		/* pick off messages one by one */
    167 		if(convM2W(mbuf, nn+4, &m) <= 0) {
    168 			fprint(2, "serveproc: cannot convert message\n");
    169 			break;
    170 		}
    171 		if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m);
    172 		runmsg(c, &m);
    173 	}
    174 
    175 	if(c == client0) {
    176 		rpc_shutdown();
    177 		threadexitsall(nil);
    178 	}
    179 }
    180 
    181 static void
    182 replyerror(Client *c, Wsysmsg *m)
    183 {
    184 	char err[256];
    185 
    186 	rerrstr(err, sizeof err);
    187 	m->type = Rerror;
    188 	m->error = err;
    189 	replymsg(c, m);
    190 }
    191 
    192 /*
    193  * Handle a single wsysmsg.
    194  * Might queue for later (kbd, mouse read)
    195  */
    196 static void
    197 runmsg(Client *c, Wsysmsg *m)
    198 {
    199 	static uchar buf[65536];
    200 	int n;
    201 	Memimage *i;
    202 
    203 	switch(m->type){
    204 	case Tctxt:
    205 		c->wsysid = strdup(m->id);
    206 		replymsg(c, m);
    207 		break;
    208 
    209 	case Tinit:
    210 		i = rpc_attach(c, m->label, m->winsize);
    211 		if(i == nil) {
    212 			replyerror(c, m);
    213 			break;
    214 		}
    215 		draw_initdisplaymemimage(c, i);
    216 		replymsg(c, m);
    217 		break;
    218 
    219 	case Trdmouse:
    220 		qlock(&c->eventlk);
    221 		if((c->mousetags.wi+1)%nelem(c->mousetags.t) == c->mousetags.ri) {
    222 			qunlock(&c->eventlk);
    223 			werrstr("too many queued mouse reads");
    224 			replyerror(c, m);
    225 			break;
    226 		}
    227 		c->mousetags.t[c->mousetags.wi++] = m->tag;
    228 		if(c->mousetags.wi == nelem(c->mousetags.t))
    229 			c->mousetags.wi = 0;
    230 		c->mouse.stall = 0;
    231 		matchmouse(c);
    232 		qunlock(&c->eventlk);
    233 		break;
    234 
    235 	case Trdkbd:
    236 	case Trdkbd4:
    237 		qlock(&c->eventlk);
    238 		if((c->kbdtags.wi+1)%nelem(c->kbdtags.t) == c->kbdtags.ri) {
    239 			qunlock(&c->eventlk);
    240 			werrstr("too many queued keyboard reads");
    241 			replyerror(c, m);
    242 			break;
    243 		}
    244 		c->kbdtags.t[c->kbdtags.wi++] = (m->tag<<1) | (m->type==Trdkbd4);
    245 		if(c->kbdtags.wi == nelem(c->kbdtags.t))
    246 			c->kbdtags.wi = 0;
    247 		c->kbd.stall = 0;
    248 		matchkbd(c);
    249 		qunlock(&c->eventlk);
    250 		break;
    251 
    252 	case Tmoveto:
    253 		c->impl->rpc_setmouse(c, m->mouse.xy);
    254 		replymsg(c, m);
    255 		break;
    256 
    257 	case Tcursor:
    258 		if(m->arrowcursor)
    259 			c->impl->rpc_setcursor(c, nil, nil);
    260 		else {
    261 			scalecursor(&m->cursor2, &m->cursor);
    262 			c->impl->rpc_setcursor(c, &m->cursor, &m->cursor2);
    263 		}
    264 		replymsg(c, m);
    265 		break;
    266 
    267 	case Tcursor2:
    268 		if(m->arrowcursor)
    269 			c->impl->rpc_setcursor(c, nil, nil);
    270 		else
    271 			c->impl->rpc_setcursor(c, &m->cursor, &m->cursor2);
    272 		replymsg(c, m);
    273 		break;
    274 
    275 	case Tbouncemouse:
    276 		c->impl->rpc_bouncemouse(c, m->mouse);
    277 		replymsg(c, m);
    278 		break;
    279 
    280 	case Tlabel:
    281 		c->impl->rpc_setlabel(c, m->label);
    282 		replymsg(c, m);
    283 		break;
    284 
    285 	case Trdsnarf:
    286 		m->snarf = rpc_getsnarf();
    287 		replymsg(c, m);
    288 		free(m->snarf);
    289 		break;
    290 
    291 	case Twrsnarf:
    292 		rpc_putsnarf(m->snarf);
    293 		replymsg(c, m);
    294 		break;
    295 
    296 	case Trddraw:
    297 		n = m->count;
    298 		if(n > sizeof buf)
    299 			n = sizeof buf;
    300 		n = draw_dataread(c, buf, n);
    301 		if(n < 0)
    302 			replyerror(c, m);
    303 		else{
    304 			m->count = n;
    305 			m->data = buf;
    306 			replymsg(c, m);
    307 		}
    308 		break;
    309 
    310 	case Twrdraw:
    311 		if(draw_datawrite(c, m->data, m->count) < 0)
    312 			replyerror(c, m);
    313 		else
    314 			replymsg(c, m);
    315 		break;
    316 
    317 	case Ttop:
    318 		c->impl->rpc_topwin(c);
    319 		replymsg(c, m);
    320 		break;
    321 
    322 	case Tresize:
    323 		c->impl->rpc_resizewindow(c, m->rect);
    324 		replymsg(c, m);
    325 		break;
    326 	}
    327 }
    328 
    329 /*
    330  * Reply to m.
    331  */
    332 static void
    333 replymsg(Client *c, Wsysmsg *m)
    334 {
    335 	int n;
    336 
    337 	/* T -> R msg */
    338 	if(m->type%2 == 0)
    339 		m->type++;
    340 
    341 	if(trace) fprint(2, "%ud [%d] -> %W\n", nsec()/1000000, threadid(), m);
    342 	/* copy to output buffer */
    343 	n = sizeW2M(m);
    344 
    345 	qlock(&c->wfdlk);
    346 	if(n > c->nmbuf){
    347 		free(c->mbuf);
    348 		c->mbuf = malloc(n);
    349 		if(c->mbuf == nil)
    350 			sysfatal("out of memory");
    351 		c->nmbuf = n;
    352 	}
    353 	convW2M(m, c->mbuf, n);
    354 	if(write(c->wfd, c->mbuf, n) != n)
    355 		fprint(2, "client write: %r\n");
    356 	qunlock(&c->wfdlk);
    357 }
    358 
    359 /*
    360  * Match queued kbd reads with queued kbd characters.
    361  */
    362 static void
    363 matchkbd(Client *c)
    364 {
    365 	int tag;
    366 	Wsysmsg m;
    367 
    368 	if(c->kbd.stall)
    369 		return;
    370 	while(c->kbd.ri != c->kbd.wi && c->kbdtags.ri != c->kbdtags.wi){
    371 		tag = c->kbdtags.t[c->kbdtags.ri++];
    372 		m.type = Rrdkbd;
    373 		if(tag&1)
    374 			m.type = Rrdkbd4;
    375 		m.tag = tag>>1;
    376 		if(c->kbdtags.ri == nelem(c->kbdtags.t))
    377 			c->kbdtags.ri = 0;
    378 		m.rune = c->kbd.r[c->kbd.ri++];
    379 		if(c->kbd.ri == nelem(c->kbd.r))
    380 			c->kbd.ri = 0;
    381 		replymsg(c, &m);
    382 	}
    383 }
    384 
    385 // matchmouse matches queued mouse reads with queued mouse events.
    386 // It must be called with c->eventlk held.
    387 static void
    388 matchmouse(Client *c)
    389 {
    390 	Wsysmsg m;
    391 
    392 	if(canqlock(&c->eventlk)) {
    393 		fprint(2, "misuse of matchmouse\n");
    394 		abort();
    395 	}
    396 
    397 	while(c->mouse.ri != c->mouse.wi && c->mousetags.ri != c->mousetags.wi){
    398 		m.type = Rrdmouse;
    399 		m.tag = c->mousetags.t[c->mousetags.ri++];
    400 		if(c->mousetags.ri == nelem(c->mousetags.t))
    401 			c->mousetags.ri = 0;
    402 		m.mouse = c->mouse.m[c->mouse.ri];
    403 		m.resized = c->mouse.resized;
    404 		c->mouse.resized = 0;
    405 		/*
    406 		if(m.resized)
    407 			fprint(2, "sending resize\n");
    408 		*/
    409 		c->mouse.ri++;
    410 		if(c->mouse.ri == nelem(c->mouse.m))
    411 			c->mouse.ri = 0;
    412 		replymsg(c, &m);
    413 	}
    414 }
    415 
    416 void
    417 gfx_mouseresized(Client *c)
    418 {
    419 	gfx_mousetrack(c, -1, -1, -1, -1);
    420 }
    421 
    422 void
    423 gfx_mousetrack(Client *c, int x, int y, int b, uint ms)
    424 {
    425 	Mouse *m;
    426 
    427 	qlock(&c->eventlk);
    428 	if(x == -1 && y == -1 && b == -1 && ms == -1) {
    429 		Mouse *copy;
    430 		// repeat last mouse event for resize
    431 		if(c->mouse.ri == 0)
    432 			copy = &c->mouse.m[nelem(c->mouse.m)-1];
    433 		else
    434 			copy = &c->mouse.m[c->mouse.ri-1];
    435 		x = copy->xy.x;
    436 		y = copy->xy.y;
    437 		b = copy->buttons;
    438 		ms = copy->msec;
    439 		c->mouse.resized = 1;
    440 	}
    441 	if(x < c->mouserect.min.x)
    442 		x = c->mouserect.min.x;
    443 	if(x > c->mouserect.max.x)
    444 		x = c->mouserect.max.x;
    445 	if(y < c->mouserect.min.y)
    446 		y = c->mouserect.min.y;
    447 	if(y > c->mouserect.max.y)
    448 		y = c->mouserect.max.y;
    449 
    450 	// If reader has stopped reading, don't bother.
    451 	// If reader is completely caught up, definitely queue.
    452 	// Otherwise, queue only button change events.
    453 	if(!c->mouse.stall)
    454 	if(c->mouse.wi == c->mouse.ri || c->mouse.last.buttons != b){
    455 		m = &c->mouse.last;
    456 		m->xy.x = x;
    457 		m->xy.y = y;
    458 		m->buttons = b;
    459 		m->msec = ms;
    460 
    461 		c->mouse.m[c->mouse.wi] = *m;
    462 		if(++c->mouse.wi == nelem(c->mouse.m))
    463 			c->mouse.wi = 0;
    464 		if(c->mouse.wi == c->mouse.ri){
    465 			c->mouse.stall = 1;
    466 			c->mouse.ri = 0;
    467 			c->mouse.wi = 1;
    468 			c->mouse.m[0] = *m;
    469 		}
    470 		matchmouse(c);
    471 	}
    472 	qunlock(&c->eventlk);
    473 }
    474 
    475 // kputc adds ch to the keyboard buffer.
    476 // It must be called with c->eventlk held.
    477 static void
    478 kputc(Client *c, int ch)
    479 {
    480 	if(canqlock(&c->eventlk)) {
    481 		fprint(2, "misuse of kputc\n");
    482 		abort();
    483 	}
    484 
    485 	c->kbd.r[c->kbd.wi++] = ch;
    486 	if(c->kbd.wi == nelem(c->kbd.r))
    487 		c->kbd.wi = 0;
    488 	if(c->kbd.ri == c->kbd.wi)
    489 		c->kbd.stall = 1;
    490 	matchkbd(c);
    491 }
    492 
    493 // gfx_abortcompose stops any pending compose sequence,
    494 // because a mouse button has been clicked.
    495 // It is called from the graphics thread with no locks held.
    496 void
    497 gfx_abortcompose(Client *c)
    498 {
    499 	qlock(&c->eventlk);
    500 	if(c->kbd.alting) {
    501 		c->kbd.alting = 0;
    502 		c->kbd.nk = 0;
    503 	}
    504 	qunlock(&c->eventlk);
    505 }
    506 
    507 // gfx_keystroke records a single-rune keystroke.
    508 // It is called from the graphics thread with no locks held.
    509 void
    510 gfx_keystroke(Client *c, int ch)
    511 {
    512 	int i;
    513 
    514 	qlock(&c->eventlk);
    515 	if(ch == Kalt){
    516 		c->kbd.alting = !c->kbd.alting;
    517 		c->kbd.nk = 0;
    518 		qunlock(&c->eventlk);
    519 		return;
    520 	}
    521 	if(ch == Kcmd+'r') {
    522 		if(c->forcedpi)
    523 			c->forcedpi = 0;
    524 		else if(c->displaydpi >= 200)
    525 			c->forcedpi = 100;
    526 		else
    527 			c->forcedpi = 225;
    528 		qunlock(&c->eventlk);
    529 		c->impl->rpc_resizeimg(c);
    530 		return;
    531 	}
    532 	if(!c->kbd.alting){
    533 		kputc(c, ch);
    534 		qunlock(&c->eventlk);
    535 		return;
    536 	}
    537 	if(c->kbd.nk >= nelem(c->kbd.k))      // should not happen
    538 		c->kbd.nk = 0;
    539 	c->kbd.k[c->kbd.nk++] = ch;
    540 	ch = latin1(c->kbd.k, c->kbd.nk);
    541 	if(ch > 0){
    542 		c->kbd.alting = 0;
    543 		kputc(c, ch);
    544 		c->kbd.nk = 0;
    545 		qunlock(&c->eventlk);
    546 		return;
    547 	}
    548 	if(ch == -1){
    549 		c->kbd.alting = 0;
    550 		for(i=0; i<c->kbd.nk; i++)
    551 			kputc(c, c->kbd.k[i]);
    552 		c->kbd.nk = 0;
    553 		qunlock(&c->eventlk);
    554 		return;
    555 	}
    556 	// need more input
    557 	qunlock(&c->eventlk);
    558 	return;
    559 }