shithub: drawfs

Download patch

ref: 1e84383ea66d572e979e1add131a2e5ed3cc757a
author: sirjofri <sirjofri@sirjofri.de>
date: Fri Jan 3 15:30:58 EST 2025

adds files

--- /dev/null
+++ b/drawfs/fns.h
@@ -1,0 +1,1 @@
+void initfs(char *srvname);
--- /dev/null
+++ b/drawfs/fs.c
@@ -1,0 +1,878 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+#include "dat.h"
+#include "fns.h"
+
+/* see: /sys/src/9/port/devdraw.c */
+
+enum {
+	Qroot,
+	/* potential files like cons and mouse */
+	Qwinname,
+	Qdevdraw,
+		Qnew,
+		Qclient,
+		Q2nd,
+		Qcolormap,
+		Qctl,
+		Qdata,
+		Qrefresh,
+	Qmax
+};
+
+typedef struct Qfile Qfile;
+struct Qfile {
+	char *name; /* filename, or nil if generated */
+	int type; /* type: QTDIR or QTFILE */
+	int clientref; /* needs client reference in Qpath */
+};
+
+static Qfile qfiles[] = {
+	[Qroot]     { ".",        QTDIR,  0 },
+	[Qwinname]  { "winname",  QTFILE, 0 },
+	[Qdevdraw]  { "draw",     QTDIR,  0 },
+	[Qnew]      { "new",      QTFILE, 0 },
+	[Qclient]   { nil,        QTDIR,  1 },
+	[Q2nd]      { "2nd",      QTDIR,  0 },
+	[Qcolormap] { "colormap", QTFILE, 1 },
+	[Qctl]      { "ctl",      QTFILE, 1 },
+	[Qdata]     { "data",     QTFILE, 1 },
+	[Qrefresh]  { "refresh",  QTFILE, 1 },
+};
+
+#define QSHIFT 4  /* location in qid of client # */
+
+#define QIDTYPE(q)  (((q)&0x0000000F)>>0)
+#define QID(q)   ((((ulong)(q).path)&0x0000000F)>>0)
+#define CLIENTPATH(q)    ((((ulong)q)&0x7ffffff0)>>QSHIFT)
+#define CLIENT(q)    CLIENTPATH((q).path)
+
+#define NHASH     (1<<5)
+#define HASHMASK  (NHASH-1)
+
+typedef struct Client Client;
+typedef struct Draw Draw;
+typedef struct DImage DImage;
+typedef struct DScreen DScreen;
+typedef struct CScreen CScreen;
+typedef struct FChar FChar;
+typedef struct Refresh Refresh;
+typedef struct Refx Refx;
+typedef struct DName DName;
+
+struct Draw
+{
+	int		clientid;
+	int		nclient;
+	Client**	client;
+	int		nname;
+	DName*		name;
+	int		vers;
+	int		softscreen;
+};
+
+struct Client
+{
+	Ref		r;
+	DImage*		dimage[NHASH];
+	CScreen*	cscreen;
+	Refresh*	refresh;
+	Rendez		refrend;
+	QLock		refq;
+	uchar*		readdata;
+	int		nreaddata;
+	int		busy;
+	int		clientid;
+	int		slot;
+	int		refreshme;
+	int		infoid;
+	int		op;
+};
+
+struct Refresh
+{
+	DImage*		dimage;
+	Rectangle	r;
+	Refresh*	next;
+};
+
+struct Refx
+{
+	Client*		client;
+	DImage*		dimage;
+};
+
+struct DName
+{
+	char		*name;
+	Client		*client;
+	DImage*		dimage;
+	int		vers;
+};
+
+struct FChar
+{
+	int		minx;	/* left edge of bits */
+	int		maxx;	/* right edge of bits */
+	uchar		miny;	/* first non-zero scan-line */
+	uchar		maxy;	/* last non-zero scan-line + 1 */
+	schar		left;	/* offset of baseline */
+	uchar		width;	/* width of baseline */
+};
+
+/*
+ * Reference counts in DImages:
+ *	one per open by original client
+ *	one per screen image or fill
+ * 	one per image derived from this one by name
+ */
+struct DImage
+{
+	int		id;
+	int		ref;
+	char		*name;
+	int		vers;
+	Memimage*	image;
+	int		ascent;
+	int		nfchar;
+	FChar*		fchar;
+	DScreen*	dscreen;	/* 0 if not a window */
+	DImage*		fromname;	/* image this one is derived from, by name */
+	DImage*		next;
+};
+
+struct CScreen
+{
+	DScreen*	dscreen;
+	CScreen*	next;
+};
+
+struct DScreen
+{
+	int		id;
+	int		public;
+	int		ref;
+	DImage		*dimage;
+	DImage		*dfill;
+	Memscreen*	screen;
+	Client*		owner;
+	DScreen*	next;
+};
+
+static	Draw		sdraw;
+	QLock	drawlock;
+
+static	Memimage	*screenimage;
+static	DImage*	screendimage;
+static	char	screenname[40];
+static	int	screennameid;
+
+static	Rectangle	flushrect;
+static	int		waste;
+static	DScreen*	dscreen;
+extern	void		flushmemscreen(Rectangle);
+	void		drawmesg(Client*, void*, int);
+	void		drawuninstall(Client*, int);
+	void		drawfreedimage(DImage*);
+	Client*		drawclientofpath(ulong);
+	DImage*	allocdimage(Memimage*);
+
+static	char Enodrawimage[] =	"unknown id for draw image";
+static	char Enodrawscreen[] =	"unknown id for draw screen";
+static	char Eshortdraw[] =	"short draw message";
+static	char Eshortread[] =	"draw read too short";
+static	char Eimageexists[] =	"image id in use";
+static	char Escreenexists[] =	"screen id in use";
+static	char Edrawmem[] =	"image memory allocation failed";
+static	char Ereadoutside[] =	"readimage outside image";
+static	char Ewriteoutside[] =	"writeimage outside image";
+static	char Enotfont[] =	"image not a font";
+static	char Eindex[] =		"character index out of range";
+static	char Enoclient[] =	"no such draw client";
+static	char Enameused[] =	"image name in use";
+static	char Enoname[] =	"no image with that name";
+static	char Eoldname[] =	"named image no longer valid";
+static	char Enamed[] = 	"image already has name";
+static	char Ewrongname[] = 	"wrong name for image";
+
+static	char Enomem[] = "no memory";
+static	char Ebadarg[] = "bad arguments";
+static	char Enodev[] = "no device";
+static	char Einuse[] = "device in use";
+
+static void
+dlock(void)
+{
+	qlock(&drawlock);
+}
+
+static int
+candlock(void)
+{
+	return canqlock(&drawlock);
+}
+
+static void
+dunlock(void)
+{
+	qunlock(&drawlock);
+}
+
+static int
+drawcmp(char *a, char *b, int n)
+{
+	if (strlen(a) != n)
+		return 1;
+	return memcmp(a, b, n);
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+	/* empty stub: only used for colormap */
+	USED(p, pr, pg, pb);
+}
+
+DImage*
+allocdimage(Memimage *i)
+{
+	DImage *d;
+	
+	d = malloc(sizeof(DImage));
+	if (!d)
+		return nil;
+	d->ref = 1;
+	d->name = 0;
+	d->vers = 0;
+	d->image = i;
+	d->dscreen = 0;
+	d->nfchar = 0;
+	d->fchar = 0;
+	d->fromname = 0;
+	return d;
+}
+
+void
+drawaddname(Client *client, DImage *di, int n, char *str)
+{
+	DName *name, *ename, *new, *t;
+	
+	name = sdraw.name;
+	ename = &name[sdraw.nname];
+	for (; name < ename; name++)
+		if (drawcmp(name->name, str, n) == 0) {
+			werrstr("name used");
+			return;
+		}
+	t = malloc((sdraw.nname+1)*sizeof(DName));
+	memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
+	free(sdraw.name);
+	sdraw.name = t;
+	new = &sdraw.name[sdraw.nname++];
+	new->name = malloc(n+1);
+	memmove(new->name, str, n);
+	new->name[n] = 0;
+	new->dimage = di;
+	new->client = client;
+	new->vers = ++sdraw.vers;
+}
+
+static DImage*
+makescreenimage(void)
+{
+	ulong chan;
+	DImage *di;
+	Memimage *i;
+	Rectangle r;
+	
+	r = Rect(0, 0, 300, 200);
+	chan = RGB24;
+	
+	i = allocmemimage(r, chan);
+	if (!i)
+		return nil;
+	
+	di = allocdimage(i);
+	if (!di) {
+		freememimage(i);
+		return nil;
+	}
+	snprint(screenname, sizeof screenname, "noborder.screen.%d", ++screennameid);
+	drawaddname(nil, di, strlen(screenname), screenname);
+	return di;
+}
+
+DName*
+drawlookupname(int n, char *str)
+{
+	DName *name, *ename;
+	
+	name = sdraw.name;
+	ename = &name[sdraw.nname];
+	for (; name < ename; name++)
+		if (drawcmp(name->name, str, n) == 0)
+			return name;
+	return 0;
+}
+
+int
+drawgoodname(DImage *d)
+{
+	DName *n;
+	
+	/* if window, validate the screen's own images */
+	if (d->dscreen)
+		if (drawgoodname(d->dscreen->dimage) == 0
+		 || drawgoodname(d->dscreen->dfill) == 0)
+			return 0;
+	if (d->name == nil)
+		return 1;
+	n = drawlookupname(strlen(d->name), d->name);
+	if (!n || n->vers != d->vers)
+		return 0;
+	return 1;
+}
+
+DImage*
+drawlookup(Client *client, int id, int checkname)
+{
+	DImage *d;
+	
+	d = client->dimage[id&HASHMASK];
+	while (d) {
+		if (d->id == id) {
+			if (checkname && !drawgoodname(d))
+				werrstr(Eoldname);
+			return d;
+		}
+		d = d->next;
+	}
+	return nil;
+}
+
+Client*
+drawclientofslot(int slot)
+{
+	Client *cl;
+	if (slot <= 0)
+		return nil;
+	if (slot > sdraw.nclient)
+		return nil;
+	cl = sdraw.client[slot-1];
+	if (!cl || cl->clientid == 0)
+		return nil;
+	return cl;
+}
+
+Client*
+drawclientofpath(ulong path)
+{
+	Client *cl;
+	int slot;
+	
+	slot = CLIENTPATH(path);
+	if (slot == 0)
+		return nil;
+	cl = sdraw.client[slot-1];
+	if (!cl || cl->clientid == 0)
+		return nil;
+	return cl;
+}
+
+Memimage*
+drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
+{
+	DImage *d;
+	
+	d = allocdimage(i);
+	if (!d)
+		return nil;
+	d->id = id;
+	d->dscreen = dscreen;
+	d->next = client->dimage[id&HASHMASK];
+	client->dimage[id&HASHMASK] = d;
+	return i;
+}
+
+Client*
+drawnewclient(void)
+{
+	Client *cl, **cp;
+	int i;
+	
+	for (i = 0; i < sdraw.nclient; i++) {
+		cl = sdraw.client[i];
+		if (!cl)
+			break;
+	}
+	if (i == sdraw.nclient) {
+		cp = malloc((sdraw.nclient+1)*sizeof(Client*));
+		if (!cp)
+			return nil;
+		memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*));
+		free(sdraw.client);
+		sdraw.client = cp;
+		sdraw.nclient++;
+		cp[i] = 0;
+	}
+	cl = mallocz(sizeof(Client), 1);
+	if (!cl)
+		return nil;
+	cl->slot = i;
+	cl->clientid = ++sdraw.clientid;
+	cl->op = SoverD;
+	sdraw.client[i] = cl;
+	return cl;
+}
+
+Client*
+drawclient(Qid q)
+{
+	return drawclientofpath(q.path);
+}
+
+static void
+mkqid(Qid *qid, vlong path, int type, int vers)
+{
+	qid->path = path;
+	qid->vers = vers;
+	qid->type = type;
+}
+
+static void
+gendir(Dir *d, ulong q, int vers)
+{
+	char *s;
+	int type;
+	char buf[10];
+	Client *cl;
+	
+	switch (QIDTYPE(q)) {
+	case Qroot:
+	case Qdevdraw:
+	case Qclient:
+	case Q2nd:
+		type = QTDIR;
+		d->mode = 0666 | DMDIR;
+		break;
+	default:
+		type = QTFILE;
+		d->mode = 0666;
+	}
+	
+	d->name = nil;
+	switch (QIDTYPE(q)) {
+	case Qclient:
+		cl = drawclientofpath(q);
+		if (!cl)
+			return; // TODO: error: invalid client
+		snprint(buf, sizeof buf, "%d", cl->clientid);
+		d->name = estrdup9p(buf);
+	}
+	
+	mkqid(&d->qid, q, type, vers);
+
+	if (!d->name) {
+		s = qfiles[QIDTYPE(q)].name;
+		if (!s)
+			sysfatal("programming error! QIDTYPE=%uld", QIDTYPE(q));
+		d->name = estrdup9p(s);
+	}
+	
+	d->uid = estrdup9p(getuser());
+	d->gid = estrdup9p(d->uid);
+	d->muid = estrdup9p(d->uid);
+	d->atime = d->mtime = time(0);
+	d->length = 0;
+}
+
+static int
+rootgen(int n, Dir *dir, void*)
+{
+	switch (n) {
+	case 0:
+		gendir(dir, Qwinname, 0);
+		return 0;
+	case 1:
+		gendir(dir, Qdevdraw, 0);
+		return 0;
+	}
+	return -1;
+}
+
+static int
+drawgen(int n, Dir *dir, void*)
+{
+	Client *cl;
+	
+	if (n == 0) {
+		gendir(dir, Qnew, 0);
+		return 0;
+	}
+	
+	/* n is index for client, starting at 1 */
+	if (n <= sdraw.nclient) {
+		cl = sdraw.client[n-1];
+		if (!cl)
+			return -1;
+		gendir(dir, (n<<QSHIFT)|Qclient, 0);
+		return 0;
+	}
+	
+	return -1;
+}
+
+static int
+clgen(int n, Dir *dir, void *aux)
+{
+	Qid *q = aux;
+	
+	n += Qcolormap; /* first entry in enum */
+	if (n > Qrefresh) /* last entry in enum */
+		return -1;
+	
+	gendir(dir, q->path|n, q->vers);
+	return 0;
+}
+
+static void
+fsopen(Req *r)
+{
+	Client *cl;
+	DName *dn;
+	DImage *di;
+	
+	dlock();
+	
+	cl = nil;
+	if (QID(r->fid->qid) == Qnew) {
+		cl = drawnewclient();
+		if (!cl) {
+			respond(r, Enodev);
+			goto Out;
+		}
+		r->ofcall.qid.path = Qctl|((cl->slot+1)<<QSHIFT);
+		r->fid->qid.path = r->ofcall.qid.path;
+	}
+	
+	switch (QID(r->ofcall.qid)) {
+	case Qwinname:
+	case Qnew:
+		break;
+	case Qctl:
+		if (!cl)
+			cl = drawclient(r->fid->qid);
+		if (cl->busy) {
+			respond(r, Einuse);
+			goto Out;
+		}
+		cl->busy = 1;
+		flushrect = Rect(10000, 10000, -10000, -10000);
+		dn = drawlookupname(strlen(screenname), screenname);
+		if (!dn) {
+			respond(r, "draw: cannot happen 2");
+			goto Out;
+		}
+		if (drawinstall(cl, 0, dn->dimage->image, 0) == 0) {
+			respond(r, Edrawmem);
+			goto Out;
+		}
+		di = drawlookup(cl, 0, 0);
+		if (!di) {
+			respond(r, "draw: cannot happen 1");
+			goto Out;
+		}
+		di->vers = dn->vers;
+		di->name = strdup(screenname);
+		di->fromname = dn->dimage;
+		di->fromname->ref++;
+		incref(&cl->r);
+		break;
+	case Qcolormap:
+	case Qdata:
+	case Qrefresh:
+		cl = drawclient(r->fid->qid);
+		if (!cl) {
+			fprint(2, "client not valid: %uld\n", CLIENT(r->fid->qid));
+		}
+		incref(&cl->r);
+		break;
+	}
+	
+Out:
+	dunlock();
+	if (!r->responded)
+		respond(r, nil);
+}
+
+static void
+fsread(Req *r)
+{
+	Qid q;
+	Client *cl;
+	Memimage *i;
+	DImage *di;
+	Refresh *rf;
+	int index, m;
+	long n;
+	uchar *p;
+	ulong red, green, blue;
+	char obuf[12*12+2];
+	char buf[16];
+	
+	switch (QID(r->fid->qid)) {
+	case Qroot:
+		dirread9p(r, rootgen, nil);
+		respond(r, nil);
+		break;
+	case Qwinname:
+		readstr(r, screenname);
+		respond(r, nil);
+		break;
+	case Qdevdraw:
+		dirread9p(r, drawgen, nil);
+		respond(r, nil);
+		break;
+	case Qclient:
+		q.path = r->fid->qid.path&~((1<<QSHIFT)-1); /* slot component */
+		q.vers = r->fid->qid.vers;
+		q.type = QTFILE;
+		dirread9p(r, clgen, &q);
+		respond(r, nil);
+		break;
+	}
+	
+	cl = drawclientofpath(r->fid->qid.path);
+	dlock();
+	
+	switch (QID(r->fid->qid)) {
+	case Qctl:
+		if (r->ifcall.count < 12*12) {
+			respond(r, Eshortread);
+			goto Out;
+		}
+		if (cl->infoid < 0) {
+			respond(r, Enodrawimage);
+			goto Out;
+		}
+		if (cl->infoid == 0) {
+			i = screenimage;
+			if (!i) {
+				respond(r, Enodrawimage);
+				goto Out;
+			}
+		} else {
+			di = drawlookup(cl, cl->infoid, 1);
+			if (!di) {
+				respond(r, Enodrawimage);
+				goto Out;
+			}
+			i = di->image;
+		}
+		sprint(obuf, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
+			cl->clientid,
+			cl->infoid,
+			chantostr(buf, i->chan),
+			(i->flags&Frepl) == Frepl,
+			i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
+			i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
+		cl->infoid = -1;
+		readstr(r, obuf);
+		//r->ofcall.count--;
+		respond(r, nil);
+		dunlock();
+		return;
+	
+	case Qcolormap:
+		p = malloc(4*12*256+1);
+		if (!p) {
+			dunlock();
+			respond(r, Enomem);
+			return;
+		}
+		m = 0;
+		for (index = 0; index < 256; index++) {
+			getcolor(index, &red, &green, &blue);
+			m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n",
+				index, red>>24, green>>24, blue>>24);
+		}
+		readstr(r, (char*)p);
+		free(p);
+		break;
+	
+	case Qdata:
+		if (!cl->readdata) {
+			respond(r, "no draw data");
+			goto Out;
+		}
+		if (r->ifcall.count < cl->nreaddata) {
+			respond(r, Eshortread);
+			goto Out;
+		}
+		readbuf(r, cl->readdata, cl->nreaddata);
+		free(cl->readdata);
+		cl->readdata = nil;
+		break;
+	
+	case Qrefresh:
+		if (r->ifcall.count < 5*4) {
+			respond(r, Ebadarg);
+			goto Out;
+		}
+		for (;;) {
+			if (cl->refreshme || cl->refresh)
+				break;
+			dunlock();
+			qlock(&cl->refq);
+			// TODO:
+			// implement sleep(9) function: /sys/src/9/port/proc.c:/^sleep
+			qunlock(&cl->refq);
+			dlock();
+		}
+		p = (uchar*)r->ifcall.data;
+		n = r->ifcall.count;
+		while (cl->refresh && n >= 5*4) {
+			rf = cl->refresh;
+			BPLONG(p+0*4, rf->dimage->id);
+			BPLONG(p+1*4, rf->r.min.x);
+			BPLONG(p+2*4, rf->r.min.y);
+			BPLONG(p+3*4, rf->r.max.x);
+			BPLONG(p+4*4, rf->r.max.y);
+			cl->refresh = rf->next;
+			free(rf);
+			p += 5*4;
+			n -= 5*4;
+		}
+		cl->refreshme = 0;
+		break;
+	}
+Out:
+	if (!r->responded)
+		respond(r, nil);
+	dunlock();
+}
+
+static char*
+fswalk(Fid *fid, char *name, Qid *qid)
+{
+	Client *cl;
+	int clslot;
+	
+	if (strcmp(name, "..") == 0) {
+		switch (QID(fid->qid)) {
+		case Qroot:
+			*qid = fid->qid;
+			return nil;
+		case Qdevdraw:
+			mkqid(qid, Qroot, QTDIR, 0);
+			fid->qid = *qid;
+			return nil;
+		case Qclient:
+			mkqid(qid, Qdevdraw, QTDIR, 0);
+			fid->qid = *qid;
+			return nil;
+		}
+		return nil;
+	}
+	
+	switch (QID(fid->qid)) {
+	case Qroot:
+		for (int q = Qwinname; q <= Qdevdraw; q++) {
+			if (qfiles[q].name && strcmp(qfiles[q].name, name) == 0) {
+				mkqid(qid, q, qfiles[q].type, 0);
+				fid->qid = *qid;
+				return nil;
+			}
+		}
+		return "root file not found";
+	case Qdevdraw:
+		if (qfiles[Qnew].name && strcmp(qfiles[Qnew].name, name) == 0) {
+			mkqid(qid, Qnew, qfiles[Qnew].type, 0);
+			fid->qid = *qid;
+			return nil;
+		}
+		clslot = atoi(name);
+		cl = drawclientofslot(clslot);
+		if (!cl)
+			return "client not found";
+		mkqid(qid, Qclient|((cl->slot+1)<<QSHIFT), qfiles[Qclient].type, 0);
+		fid->qid = *qid;
+		return nil;
+	case Qclient:
+		cl = drawclient(fid->qid);
+		if (!cl)
+			return "no client for this file";
+		for (int q = Qcolormap; q <= Qrefresh; q++) {
+			if (qfiles[q].name && strcmp(qfiles[q].name, name) == 0) {
+				mkqid(qid, q|((cl->slot+1)<<QSHIFT), qfiles[q].type, 0);
+				fid->qid = *qid;
+				return nil;
+			}
+		}
+		return "client file not found";
+	}
+
+	return "file does not exist (walk)";
+}
+
+static void
+fsstat(Req *r)
+{
+	int q = QID(r->fid->qid);
+	
+	if (q >= 0 && q < Qmax) {
+		gendir(&r->d, r->fid->qid.path, r->fid->qid.vers);
+		respond(r, nil);
+		return;
+	}
+	respond(r, "not found");
+}
+
+static void
+fsattach(Req *r)
+{
+	mkqid(&r->fid->qid, Qroot, QTDIR, 0);
+	r->ofcall.qid = r->fid->qid;
+	respond(r, nil);
+}
+
+Srv fs = {
+	.attach = fsattach,
+	.walk1 = fswalk,
+	.stat = fsstat,
+	.open = fsopen,
+	.read = fsread,
+};
+
+static int
+initscreenimage(void)
+{
+	if (screenimage)
+		return 1;
+	
+	screendimage = makescreenimage();
+	if (!screendimage)
+		return 0;
+	screenimage = screendimage->image;
+	//TODO: mouseresize();
+	return 1;
+}
+
+void
+initfs(char *srvname)
+{
+	dlock();
+	if (!initscreenimage()) {
+		dunlock();
+		sysfatal("initscreenimage error");
+	}
+	dunlock();
+	
+	postsrv(&fs, srvname);
+}
--- /dev/null
+++ b/drawfs/main.c
@@ -1,0 +1,64 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "dat.h"
+#include "fns.h"
+
+extern int chatty9p;
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s -n name\n", argv0);
+	exits("usage");
+}
+
+int fscmdfd;
+int fsdispfd;
+
+void
+main(int argc, char **argv)
+{
+	char file[256];
+	char *name = nil;
+	int p[2];
+	int fd;
+	
+	ARGBEGIN{
+	case 'n':
+		name = EARGF(usage());
+		break;
+	case 'D':
+		chatty9p++;
+		break;
+	default:
+		usage();
+		break;
+	}ARGEND;
+	
+	if (!name || !name[0]) {
+		name = smprint("%d", getpid());
+	}
+	
+	snprint(file, sizeof file, "drawfs.%s", name);
+	initfs(file);
+	
+	exits(0);
+	
+	pipe(p);
+	snprint(file, sizeof file, "/srv/drawfs.%s.cmd", name);
+	fd = create(file, OWRITE|ORCLOSE, 0666);
+	fprint(fd, "%d", p[0]);
+	fscmdfd = p[1];
+	close(fd);
+	close(p[0]);
+	
+	pipe(p);
+	snprint(file, sizeof file, "/srv/drawfs.%s.display", name);
+	fd = create(file, OWRITE|ORCLOSE, 0666);
+	fprint(fd, "%d", p[0]);
+	fsdispfd = p[1];
+	close(fd);
+	close(p[0]);
+}
--- /dev/null
+++ b/drawfs/mkfile
@@ -1,0 +1,13 @@
+</$objtype/mkfile
+
+TARG=drawfs
+OFILES=\
+	main.$O\
+	fs.$O\
+	draw.$O\
+
+HFILES=\
+	fns.h\
+	dat.h\
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/drawmgr/drawmgr.c
@@ -1,0 +1,76 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s\n", argv0);
+	exits("usage");
+}
+
+int drawdispfd;
+int drawcmdfd;
+
+void
+eresized(int new)
+{
+	Image *tmpimg;
+	Point size;
+	uchar buf[1+4+4];
+	
+	if (new && getwindow(display, Refnone) < 0)
+		sysfatal("can't reattach to window: %r");
+	
+	size.x = Dx(screen->r);
+	size.y = Dy(screen->r);
+	
+	buf[0] = 'r';
+	BPLONG(buf+1, size.x);
+	BPLONG(buf+5, size.y);
+	write(drawcmdfd, buf, 1+4+4);
+	
+	seek(drawdispfd, 0, 0);
+	tmpimg = readimage(display, drawdispfd, 0);
+	if (!tmpimg)
+		sysfatal("%r");
+	draw(screen, screen->r, tmpimg, nil, ZP);
+	freeimage(tmpimg);
+}
+
+void
+main(int argc, char **argv)
+{
+	char file[256];
+	char *name = nil;
+	
+	ARGBEGIN{
+	case 'n':
+		name = EARGF(usage());
+		break;
+	default:
+		usage();
+		break;
+	}ARGEND;
+	
+	if (!name || !name[0])
+		usage();
+	
+	if (strlen(name) + strlen("/srv/drawfs..display") + 2 > 256)
+		sysfatal("error: name too long: %s\n", name);
+	
+	snprint(file, sizeof file, "/srv/drawfs.%s.display", name);
+	drawdispfd = open(file, OREAD);
+	if (drawdispfd < 0)
+		sysfatal("%r");
+	
+	snprint(file, sizeof file, "/srv/drawfs.%s.cmd", name);
+	drawcmdfd = open(file, ORDWR);
+	if (drawcmdfd < 0)
+		sysfatal("%r");
+	
+	if (initdraw(nil, nil, "drawmgr") < 0)
+		sysfatal("initdraw: %r");
+	
+	eresized(0);
+}
--- /dev/null
+++ b/drawmgr/mkfile
@@ -1,0 +1,6 @@
+</$objtype/mkfile
+
+TARG=drawmgr
+OFILES=drawmgr.$O
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,7 @@
+all:V:
+	@{ cd drawfs && mk $MKFLAGS }
+	@{ cd drawmgr && mk $MKFLAGS }
+
+install:V:
+	@{ cd drawfs && mk $MKFLAGS install }
+	@{ cd drawmgr && mk $MKFLAGS install }
--- /dev/null
+++ b/words
@@ -1,0 +1,41 @@
+FUNCTIONALITY
+
+always (in drawfs):
+- draw to internal image data (like memdraw)
+
+offline:
+- NOP
+- ignore cons+mouse
+
+connect (drawmgr):
+- open /dev/draw/new to create new connection
+- initial draw internal display to /dev/draw/n
+- open /dev/cons+mouse stuff
+
+online (drawmgr):
+- after each internal draw/flush, draw internal display to /dev/draw/n
+- forward cons+mouse
+
+disconnect (drawmgr):
+- close /dev/draw/n stuff
+- close /dev/cons+mouse stuff
+
+FILESYSTEM
+
+- default /dev/draw layout (basically copy devdraw.c code)
+- (additional cons+mouse stuff)
+- post fs to /srv/drawfs.NAME
+
+THREADS
+
+- filesystem thread (/srv/drawfs.NAME+mount)
+- display image (/srv/drawfs.NAME.display)
+- console access (/srv/drawfs.NAME.cmd)
+  - note when display changes (refresh)
+  - resize events
+  - how to syncronize pipe? when to read/write? poll?
+
+RESIZE
+
+mgr -----> fs.cmd
+r x[4] y[4]   (resize x, y)