Two attached files:

- 9p-srv.c is a devdraw(1) version which uses 9pclient(3) to talk with
Plan 9-like devices and use them for its windows.

- p9p.patch is a patch that has to be applied to the p9p tree to use
this devdraw version.

The drawing device expects to find draw(3), cons(3) and mouse(3) files
in the $WSYS directory, as can be found in /dev in a Plan 9 terminal
(including 9vx or drawterm) or /mnt/wsys when using rio. Then, those
devices are used to run p9p programs.

I wrote 9p-srv as an experiment to use wsys(4) [1] with p9p
applications, but have found it quite useful to open p9p programs -
like 9term or sam - in rio windows (for example, from 9vx).

Although it could be argued that this is not the most efficient thing
to do writing it was quite funny, and much easier than expected.

Installation (once the patch has been applied and p9p built):

        cp 9p-srv.c $PLAN9/src/cmd/devdraw
        cd $PLAN9/src/cmd/devdraw
        9c 9p-srv.c && 9l -o devdraw.9p 9p-srv.o
        cp devdraw.9p $PLAN9/bin

Now, to run 9term in a rio window:

        # From 9vx's rio:
        ; mount $wsys /n/w new
        ; bind -a '#i' /n/w
        ; aux/listen1 -t 'tcp!*!12345' /bin/exportfs -r /n/w

        # From unix:
        $ WSYS='tcp!127.0.0.1!12345'
        $ DEVDRAW=devdraw.9p
        $ 9term

These files are also included with wsys(4), in the util directory
(instructions are in the README). To use the drawing device with
wsys(4) just set WSYS to "unix!/tmp/ns.$USERNAME.$DISPLAY/wsys".
However, it is not working so well as with rio. The reason is probably
some bug in wsys, and not in 9p-srv. I hope to fix it at some point,
but is not an easy bug to track.

If (after some more testing) the 9p drawing device could be included
with p9p that would be great. If it has to stay in the wsys repository
that is fine too but, could the p9p patch be applied at least? The two
small changes shouldn't be a problem for the other versions of
devdraw.

Comments are welcomed.


[1] http://bitbucket.org/yiyus/devwsys-prev/ (there is also a blog
about the development of wsys(4) at
http://summerofdevdraw.blogspot.com/ and a WIP short paper is included
in the proceedings of the last iwp9).

-- 
- yiyus || JGL .
/*
 * Window system protocol server.
 * Use thread(3) and 9pclient(3) to connect
 * to a wsys service as described in rio(4).
 */

#include <u.h>
#include <libc.h>
#include <thread.h>
#include <9pclient.h>
#include <draw.h>
#include <memdraw.h>
#include <memlayer.h>
#include <keyboard.h>
#include <mouse.h>
#include <cursor.h>
#include <drawfcall.h>

#define STACK (32*1024)

void runmsg(Wsysmsg*);
void replymsg(Wsysmsg*);
void kbdthread(void*);
void mousethread(void*);

int chatty = 0;
int drawsleep;
int fd;

Channel *kbdchan;
Channel *mousechan;

void
usage(void)
{
	fprint(2, "usage: devdraw (don't run directly)\n");
	threadexitsall(0);
}

void
bell(void *v, char *msg)
{
	if(strcmp(msg, "alarm") == 0)
		drawsleep = drawsleep ? 0 : 1000;
	noted(NCONT);
}

void
threadmain(int argc, char **argv)
{
	char *addr, *defaddr, *ns;
	uchar buf[4], *mbuf;
	int nmbuf, n, nn;
	Ioproc *io;
	Wsysmsg m;

	/*
	 * 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);

	fmtinstall('H', encodefmt);
	fmtinstall('W', drawfcallfmt);

	ARGBEGIN{
	case 'd':
		chatty9pclient++;
		break;
	case 'D':
		chatty++;
		break;
	default:
		usage();
	}ARGEND

	/*
	 * Ignore arguments.  They're only for good ps -a listings.
	 */
	
	notify(bell);

	/*
	 * Connect to 9P server defined by $WSYS
	 * or unix!$NAMESPACE/wsys.
	 */
	addr = getenv("WSYS");
	defaddr = nil;
	if(addr == 0){
		ns = getns();
		if(ns == nil)
			sysfatal("no name space");
		defaddr = smprint("unix!%s/wsys", ns);
		addr = defaddr;
	}
	fd = dial(addr, 0, 0, 0);
	if(fd < 0)
		sysfatal("dial %s: %r", addr);
	if(addr == defaddr)
		free(addr);

	mbuf = nil;
	nmbuf = 0;
	io = ioproc();
	while((n = ioread(io, 3, buf, 4)) == 4){
		GET(buf, n);
		if(n > nmbuf){
			free(mbuf);
			mbuf = malloc(4+n);
			if(mbuf == nil)
				sysfatal("malloc: %r");
			nmbuf = n;
		}
		memmove(mbuf, buf, 4);
		nn = readn(3, mbuf+4, n-4);
		if(nn != n-4)
			sysfatal("eof during message");

		/* pick off messages one by one */
		if(convM2W(mbuf, nn+4, &m) <= 0)
			sysfatal("cannot convert message");
		if(chatty) fprint(2, "<- %W\n", &m);
		runmsg(&m);
	}
	threadexitsall(0);
}

void
replyerror(Wsysmsg *m)
{
	char err[256];
	
	rerrstr(err, sizeof err);
	m->type = Rerror;
	m->error = err;
	replymsg(m);
}


/* 
 * Handle a single wsysmsg. 
 * Might queue for later (kbd, mouse read)
 */
void
runmsg(Wsysmsg *m)
{
	uchar buf[65536];
	int n;
	CFid *f;
	Rectangle r;
	static int border;
	static int id = -1;
	static CFid *fcons, *fmouse, *fctl, *fdata, *frddraw;
	static CFsys *fsys;

	switch(m->type){
	case Tinit:
		sprint((char*)buf, "new %s", m->winsize);
		if((fsys = fsmount(fd, (char*)buf)) == nil)
			sysfatal("fsmount: %r");

		fcons = fsopen(fsys, "cons", OREAD);
		fmouse = fsopen(fsys, "mouse", ORDWR);

		f = fsopen(fsys, "label", OWRITE);
		fsprint(f, m->label);
		fsclose(f);

		/*
		 * Read keyboard and mouse events
		 * from different threads.
		 * Use a buffered chan as tag queue.
		 */
		kbdchan = chancreate(sizeof(uchar), 12);
		mousechan = chancreate(sizeof(uchar), 12);
		f= fsopen(fsys, "consctl", OWRITE);
		fsprint(f, "rawon");
		threadcreate(kbdthread, fcons, STACK);
		threadcreate(mousethread, fmouse, STACK);

		/*
		 * Open draw(3) files and register
		 * image named winname
		 */
		fctl = fsopen(fsys, "draw/new", ORDWR);
		fsread(fctl, buf, 12*12);
		n = atoi((char*)buf);
		sprint((char*)buf, "draw/%d/data", n);
		fdata = fsopen(fsys, (char*)buf, ORDWR);
		
		replymsg(m);
		break;

	case Trdmouse:
		if(nbsend(mousechan, &m->tag) == 0)
			sysfatal("too many queued mouse reads");
		break;

	case Trdkbd:
		if(nbsend(kbdchan, &m->tag) == 0)
			sysfatal("too many queued mouse reads");
		break;

	case Tmoveto:
		fsprint(fmouse, "m %11d %11d %11d %11d", m->mouse.xy.x, m->mouse.xy.y, m->mouse.buttons, m->mouse.msec);
		replymsg(m);
		break;

	case Tcursor:
		f = fsopen(fsys, "cursor", OWRITE);
		if(m->arrowcursor)
			fswrite(f, buf, 0);
		else{
			PUT(&buf[0], m->cursor.offset.x);
			PUT(&buf[4], m->cursor.offset.y);
			for(n = 0; n < 32; n++){
				buf[2*4+n] = m->cursor.clr[n];
				buf[2*4+32+n] = m->cursor.set[n];
			}
			fswrite(f, buf, 72);
		}
		fsclose(f);
		replymsg(m);
		break;

	case Tbouncemouse:
		/* Ignore */
		replymsg(m);
		break;

	case Tlabel:
		f = fsopen(fsys, "label", OWRITE);
		fsprint(f, m->label);
		fsclose(f);
		replymsg(m);
		break;

	case Trdsnarf:
		f = fsopen(fsys, "snarf", OREAD);
		fsread(f, buf, sizeof buf);
		fsclose(f);
		m->snarf = strdup((char*)buf);
		replymsg(m);
		break;

	case Twrsnarf:
		f = fsopen(fsys, "snarf", OWRITE);
		fsprint(f, m->snarf);
		fsclose(f);
		replymsg(m);
		break;

	case Trddraw:
		if((n = fsread(frddraw, buf, sizeof buf)) < 0){
			replyerror(m);
			break;
		}
		/*
		 * If it is not a "noborder" image lie to p9p's libdraw saying
		 * that the image is smaller, to respect the window border.
		 */
		if(frddraw == fctl){
			if(border){
				r = Rect(
					atoi((char*)&buf[4*12]),
					atoi((char*)&buf[5*12]),
					atoi((char*)&buf[6*12]),
					atoi((char*)&buf[7*12])
				);
				r = insetrect(r, Borderwidth);
				sprint((char*)&buf[4*12], "%11d %11d %11d %11d",
					r.min.x, r.min.y, r.max.x, r.max.y);
			}
		}
		m->count = n;
		m->data = buf;
		frddraw = fdata;
		replymsg(m);
		break;

	case Twrdraw:
		/*
		 * In the drawfcall protocol:
		 * 	J: Install screen image as image 0
		 * 	I: get "screen" (image 0) information
		 *
		 * Instead, we read the screen image name
		 * from winname and associate it to a devdraw
		 * image with the command 'n', which we
		 * will free after ctl is read.
		 */
		SET(n);
		if(m->count == 2 && m->data[0] == 'J' && m->data[1] == 'I'){
			id = 1;
			buf[0] = 'f';
			BPLONG(&buf[1], id);
			fswrite(fdata, buf, 1+4);
			f = fsopen(fsys, "winname", OREAD);
			buf[0] = 'n';
			n = fsread(f, &buf[1+4+1], 64);
			border = (strncmp((char*)&buf[1+4+1], "noborder", 8) != 0);
			buf[1+4] = n;
			fsclose(f);
			BPLONG(&buf[1], id);
			n = fswrite(fdata, buf, 1+4+1+n);
			frddraw = fctl;
		} else
			n = fswrite(fdata, m->data, m->count);
		if(n < 0)
			replyerror(m);
		else
			replymsg(m);
		break;
	
	case Ttop:
		f = fsopen(fsys, "wctl", OWRITE);
		fsprint(f, "top");
		fsclose(f);
		replymsg(m);
		break;
	
	case Tresize:
		f = fsopen(fsys, "wctl", OWRITE);
		fsprint(f, "resize %d %d", Dx(m->rect), Dy(m->rect));
		fsclose(f);
		replymsg(m);
		break;
	}
}

/*
 * Reply to m.
 */
QLock replylock;
void
replymsg(Wsysmsg *m)
{
	int n;
	static uchar *mbuf;
	static int nmbuf;

	/* T -> R msg */
	if(m->type%2 == 0)
		m->type++;

	if(chatty) fprint(2, "-> %W\n", m);
	/* copy to output buffer */
	n = sizeW2M(m);

	qlock(&replylock);
	if(n > nmbuf){
		free(mbuf);
		mbuf = malloc(n);
		if(mbuf == nil)
			sysfatal("out of memory");
		nmbuf = n;
	}
	convW2M(m, mbuf, n);
	if(write(4, mbuf, n) != n)
		sysfatal("write: %r");
	qunlock(&replylock);
}

void
kbdthread(void *arg)
{
	CFid *f;
	Rune r;
	Wsysmsg m;

	f = arg;
	m.type = Rrdkbd;
	while(1){
		recv(kbdchan, &m.tag);
		r = 0;
		if(fsread(f, &r, 2) < 1)
			sysfatal("short keyboard read");
		m.rune = r;
		replymsg(&m);
	}
}

void
mousethread(void *arg)
{
	char buf[49];
	CFid *f;
	Wsysmsg m;

	f = arg;
	m.type = Rrdmouse;
	while(1){
		recv(mousechan, &m.tag);
		if(fsread(f, buf, 49) < 49)
			sysfatal("short mouse read");
		m.resized = 0;
		if(buf[0] == 'r')
			m.resized = 1;
		m.mouse.xy.x = atoi(buf+1+0*12);
		m.mouse.xy.y = atoi(buf+1+1*12);
		m.mouse.buttons = atoi(buf+1+2*12);
		m.mouse.msec = atoi(buf+1+3*12);
		replymsg(&m);
	}
}

diff -r 16d4081236af src/libdraw/init.c
--- a/src/libdraw/init.c	Wed Apr 27 13:18:07 2011 -0400
+++ b/src/libdraw/init.c	Wed Nov 02 18:07:13 2011 +0100
@@ -145,7 +145,7 @@
 	}
 
 	image->display = d;
-	image->id = 0;
+	image->id = atoi(info+1*12);
 	image->chan = strtochan(info+2*12);
 	image->depth = chantodepth(image->chan);
 	image->repl = atoi(info+3*12);
@@ -206,6 +206,12 @@
 		return nil;
 	}
 	disp->srvfd = -1;
+	/*
+	 * The image id 1 is used by devdraw.9p to identify
+	 * the image associated to winname. Since the counter
+	 * is preincremented before use, 1 is a safe value.
+	 */
+	disp->imageid = 1;
 	image = nil;
 	if(0){
     Error2:

Reply via email to