Commit Diff


commit - e21fee604e3b53fd8c57ac3817e6d829f62d6ee3
commit + 1aa4e9c82e91806e2ec09b0c05951a5fc299a812
blob - 6c12648eefa249c62aeec1f9bc4409eb0aecd542
blob + 18bb409102b97935430c114f6f7f06b7128482af
--- include/thread.h
+++ include/thread.h
@@ -82,6 +82,7 @@ int		procrfork(void (*f)(void *arg), void *arg, unsign
 void**		procdata(void);
 void		threadexec(Channel *, int[3], char *, char *[]);
 void		threadexecl(Channel *, int[3], char *, ...);
+int		threadspawn(int[3], char*, char*[]);
 int		recv(Channel *c, void *v);
 void*		recvp(Channel *c);
 unsigned long		recvul(Channel *c);
blob - 5a5391d8a4ede7a75f0ddd15ba3f307aa928932c
blob + caf7a66c0a167f33336ddd469a7487d1d8b91f84
--- src/cmd/mkfile
+++ src/cmd/mkfile
@@ -12,13 +12,6 @@ DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "^($BUGGERE
 
 dir-all dir-install: $PLAN9/bin/9yacc
 
-XLIB=draw bio 9
-$O.clock: clock.$O ${XLIB:%=$PLAN9/lib/lib%.a}
-	$LD -o $target $prereq -L$X11/lib -lX11
-
-$O.tweak: tweak.$O ${XLIB:%=$PLAN9/lib/lib%.a}
-	$LD -o $target $prereq -L$X11/lib -lX11
-
 %.tab.h %.tab.c: %.y
 	$YACC $YFLAGS -s $stem $prereq
 
blob - c7e12878cce18e8a736332c17b51e17cbc7a9975 (mode 644)
blob + /dev/null
--- src/cmd/tweak.c
+++ /dev/null
@@ -1,2058 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <cursor.h>
-#include <event.h>
-#include <bio.h>
-
-typedef struct	Thing	Thing;
-
-struct Thing
-{
-	Image	*b;
-	Subfont 	*s;
-	char		*name;	/* file name */
-	int		face;		/* is 48x48 face file or cursor file*/
-	Rectangle r;		/* drawing region */
-	Rectangle tr;		/* text region */
-	Rectangle er;		/* entire region */
-	long		c;		/* character number in subfont */
-	int		mod;	/* modified */
-	int		mag;		/* magnification */
-	Rune		off;		/* offset for subfont indices */
-	Thing	*parent;	/* thing of which i'm an edit */
-	Thing	*next;
-};
-
-enum
-{
-	Border	= 1,
-	Up		= 1,
-	Down	= 0,
-	Mag		= 4,
-	Maxmag	= 10,
-};
-
-enum
-{
-	NORMAL	=0,
-	FACE	=1,
-	CURSOR	=2
-};
-
-enum
-{
-	Mopen,
-	Mread,
-	Mwrite,
-	Mcopy,
-	Mchar,
-	Mpixels,
-	Mclose,
-	Mexit,
-};
-
-enum
-{
-	Blue	= 54,
-};
-
-char	*menu3str[] = {
-	"open",
-	"read",
-	"write",
-	"copy",
-	"char",
-	"pixels",
-	"close",
-	"exit",
-	0,
-};
-
-Menu	menu3 = {
-	menu3str
-};
-
-Cursor sweep0 = {
-	{-7, -7},
-	{0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
-	 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
-	 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0,
-	 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0},
-	{0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
-	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE,
-	 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
-	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00}
-};
-
-Cursor box = {
-	{-7, -7},
-	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
-	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
-	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
-	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
-	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
-	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
-	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
-};
-
-Cursor sight = {
-	{-7, -7},
-	{0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
-	 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
-	 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
-	 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
-	{0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
-	 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
-	 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
-	 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
-};
-
-Cursor pixel = {
-	{-7, -7},
-	{0x1f, 0xf8, 0x3f, 0xfc,  0x7f, 0xfe,  0xf8, 0x1f,
-	0xf0, 0x0f,  0xe0, 0x07, 0xe0, 0x07, 0xfe, 0x7f, 
-	0xfe, 0x7f, 0xe0, 0x07, 0xe0, 0x07, 0xf0, 0x0f, 
-	0x78, 0x1f, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0xf8, },
-	{0x00, 0x00, 0x0f, 0xf0, 0x31, 0x8c, 0x21, 0x84, 
-	0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x40, 0x02, 
-	0x40, 0x02, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 
-	0x21, 0x84, 0x31, 0x8c, 0x0f, 0xf0, 0x00, 0x00, }
-};
-
-Cursor busy = {
-	{-7, -7},
-	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-	 0x00, 0x00, 0x00, 0x0c, 0x00, 0x8e, 0x1d, 0xc7,
-	 0xff, 0xe3, 0xff, 0xf3, 0xff, 0xff, 0x7f, 0xfe, 
-	 0x3f, 0xf8, 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00,},
-	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-	 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82,
-	 0x04, 0x41, 0xff, 0xe1, 0x5f, 0xf1, 0x3f, 0xfe, 
-	 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00,}
-};
-
-Cursor skull = {
-	{-7,-7},
-	{0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7, 
-	 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8, 
-	 0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, 
-	 0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,},
-	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03,
-	 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0,
-	 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27,
-	 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
-};
-
-Rectangle	cntlr;		/* control region */
-Rectangle	editr;		/* editing region */
-Rectangle	textr;		/* text region */
-Thing		*thing;
-Mouse		mouse;
-char		hex[] = "0123456789abcdefABCDEF";
-jmp_buf		err;
-char		*file;
-int		mag;
-int		but1val = 0;
-int		but2val = 255;
-int		invert = 0;
-Image		*values[256];
-Image		*greyvalues[256];
-uchar		data[8192];
-
-Thing*	tget(char*);
-void	mesg(char*, ...);
-void	drawthing(Thing*, int);
-void	xselect(void);
-void	menu(void);
-void	error(Display*, char*);
-void	buttons(int);
-void	drawall(void);
-void	tclose1(Thing*);
-
-void
-main(int argc, char *argv[])
-{
-	int i;
-	Event e;
-	Thing *t;
-
-	mag = Mag;
-	if(initdraw(error, 0, "tweak") < 0){
-		fprint(2, "tweak: initdraw failed: %r\n");
-		exits("initdraw");
-	}
-	for(i=0; i<256; i++){
-		values[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, cmap2rgba(i));
-		greyvalues[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (i<<24)|(i<<16)|(i<<8)|0xFF);
-		if(values[i] == 0 || greyvalues[i] == 0)
-			drawerror(display, "can't allocate image");
-	}
-	einit(Emouse|Ekeyboard);
-	eresized(0);
-	i = 1;
-	setjmp(err);
-	for(; i<argc; i++){
-		file = argv[i];
-		t = tget(argv[i]);
-		if(t)
-			drawthing(t, 1);
-		flushimage(display, 1);
-	}
-	file = 0;
-	setjmp(err);
-	for(;;)
-		switch(event(&e)){
-		case Ekeyboard:
-			break;
-		case Emouse:
-			mouse = e.mouse;
-			if(mouse.buttons & 3){
-				xselect();
-				break;
-			}
-			if(mouse.buttons & 4)
-				menu();
-		}
-}
-
-int
-xlog2(int n)
-{
-	int i;
-
-	for(i=0; (1<<i) <= n; i++)
-		if((1<<i) == n)
-			return i;
-	fprint(2, "log2 %d = 0\n", n);
-	return 0;
-}
-
-void
-error(Display *d, char *s)
-{
-	USED(d);
-
-	if(file)
-		mesg("can't read %s: %s: %r", file, s);
-	else
-		mesg("/dev/bitblt error: %s", s);
-	if(err[0])
-		longjmp(err, 1);
-	exits(s);
-}
-
-void
-redraw(Thing *t)
-{
-	Thing *nt;
-	Point p;
-
-	if(thing==0 || thing==t)
-		draw(screen, editr, display->white, nil, ZP);
-	if(thing == 0)
-		return;
-	if(thing != t){
-		for(nt=thing; nt->next!=t; nt=nt->next)
-			;
-		draw(screen, Rect(screen->r.min.x, nt->er.max.y, editr.max.x, editr.max.y),
-			display->white, nil, ZP);
-	}
-	for(nt=t; nt; nt=nt->next){
-		drawthing(nt, 0);
-		if(nt->next == 0){
-			p = Pt(editr.min.x, nt->er.max.y);
-			draw(screen, Rpt(p, editr.max), display->white, nil, ZP);
-		}
-	}
-	mesg("");
-}
-
-void
-eresized(int new)
-{
-	if(new && getwindow(display, Refnone) < 0)
-		error(display, "can't reattach to window");
-	cntlr = insetrect(screen->clipr, 1);
-	editr = cntlr;
-	textr = editr;
-	textr.min.y = textr.max.y - font->height;
-	cntlr.max.y = cntlr.min.y + font->height;
-	editr.min.y = cntlr.max.y+1;
-	editr.max.y = textr.min.y-1;
-	draw(screen, screen->clipr, display->white, nil, ZP);
-	draw(screen, Rect(editr.min.x, editr.max.y, editr.max.x+1, editr.max.y+1), display->black, nil, ZP);
-	replclipr(screen, 0, editr);
-	drawall();
-}
-
-void
-mesgstr(Point p, int line, char *s)
-{
-	Rectangle c, r;
-
-	r.min = p;
-	r.min.y += line*font->height;
-	r.max.y = r.min.y+font->height;
-	r.max.x = editr.max.x;
-	c = screen->clipr;
-	replclipr(screen, 0, r);
-	draw(screen, r, values[0xDD], nil, ZP);
-	r.min.x++;
-	string(screen, r.min, display->black, ZP, font, s);
-	replclipr(screen, 0, c);
-	flushimage(display, 1);
-}
-
-void
-mesg(char *fmt, ...)
-{
-	char buf[1024];
-	va_list arg;
-
-	va_start(arg, fmt);
-	vseprint(buf, buf+sizeof(buf), fmt, arg);
-	va_end(arg);
-	mesgstr(textr.min, 0, buf);
-}
-
-void
-tmesg(Thing *t, int line, char *fmt, ...)
-{
-	char buf[1024];
-	va_list arg;
-
-	va_start(arg, fmt);
-	vseprint(buf, buf+sizeof(buf), fmt, arg);
-	va_end(arg);
-	mesgstr(t->tr.min, line, buf);
-}
-
-
-void
-scntl(char *l)
-{
-	sprint(l, "mag: %d  but1: %d  but2: %d  invert-on-copy: %c", mag, but1val, but2val, "ny"[invert]);
-}
-
-void
-cntl(void)
-{
-	char buf[256];
-
-	scntl(buf);
-	mesgstr(cntlr.min, 0, buf);
-}
-
-void
-stext(Thing *t, char *l0, char *l1)
-{
-	Fontchar *fc;
-	char buf[256];
-
-	l1[0] = 0;
-	sprint(buf, "depth:%d r:%d %d  %d %d ", 
-		t->b->depth, t->b->r.min.x, t->b->r.min.y,
-		t->b->r.max.x, t->b->r.max.y);
-	if(t->parent)
-		sprint(buf+strlen(buf), "mag: %d ", t->mag);
-	sprint(l0, "%s file: %s", buf, t->name);
-	if(t->c >= 0){
-		fc = &t->parent->s->info[t->c];
-		sprint(l1, "c(hex): %x c(char): %C x: %d "
-			   "top: %d bottom: %d left: %d width: %d iwidth: %d",
-			(int)(t->c+t->parent->off), (int)(t->c+t->parent->off),
-			fc->x, fc->top, fc->bottom, fc->left,
-			fc->width, Dx(t->b->r));
-	}else if(t->s)
-		sprint(l1, "offset(hex): %ux n:%d  height:%d  ascent:%d",
-			t->off, t->s->n, t->s->height, t->s->ascent);
-}
-
-void
-text(Thing *t)
-{
-	char l0[256], l1[256];
-
-	stext(t, l0, l1);
-	tmesg(t, 0, l0);
-	if(l1[0])
-		tmesg(t, 1, l1);
-}
-
-void
-drawall(void)
-{
-	Thing *t;
-
-	cntl();
-	for(t=thing; t; t=t->next)
-		drawthing(t, 0);
-}
-
-int
-value(Image *b, int x)
-{
-	int v, l, w;
-	uchar mask;
-
-	w = b->depth;
-	if(w > 8){
-		mesg("ldepth too large");
-		return 0;
-	}
-	l = xlog2(w);
-	mask = (1<<w)-1;		/* ones at right end of word */
-	x -= b->r.min.x&~(7>>l);	/* adjust x relative to first pixel */
-	v = data[x>>(3-l)];
-	v >>= ((7>>l)<<l) - ((x&(7>>l))<<l);	/* pixel at right end of word */
-	v &= mask;			/* pixel at right end of word */
-	return v;
-}
-
-int
-bvalue(int v, int d)
-{
-	v &= (1<<d)-1;
-	if(d > screen->depth)
-		v >>= d - screen->depth;
-	else
-		while(d < screen->depth && d < 8){
-			v |= v << d;
-			d <<= 1;
-		}
-	if(v<0 || v>255){
-		mesg("internal error: bad color");
-		return Blue;
-	}
-	return v;
-}
-
-void
-drawthing(Thing *nt, int link)
-{
-	int n, nl, nf, i, x, y, sx, sy, fdx, dx, dy, v;
-	Thing *t;
-	Subfont *s;
-	Image *b, *col;
-	Point p, p1, p2;
-
-	if(link){
-		nt->next = 0;
-		if(thing == 0){
-			thing = nt;
-			y = editr.min.y;
-		}else{
-			for(t=thing; t->next; t=t->next)
-				;
-			t->next = nt;
-			y = t->er.max.y;
-		}
-	}else{
-		if(thing == nt)
-			y = editr.min.y;
-		else{
-			for(t=thing; t->next!=nt; t=t->next)
-				;
-			y = t->er.max.y;
-		}
-	}
-	s = nt->s;
-	b = nt->b;
-	nl = font->height;
-	if(s || nt->c>=0)
-		nl += font->height;
-	fdx = Dx(editr) - 2*Border;
-	dx = Dx(b->r);
-	dy = Dy(b->r);
-	if(nt->mag > 1){
-		dx *= nt->mag;
-		dy *= nt->mag;
-		fdx -= fdx%nt->mag;
-	}
-	nf = 1 + dx/fdx;
-	nt->er.min.y = y;
-	nt->er.min.x = editr.min.x;
-	nt->er.max.x = nt->er.min.x + Border + dx + Border;
-	if(nt->er.max.x > editr.max.x)
-		nt->er.max.x = editr.max.x;
-	nt->er.max.y = nt->er.min.y + Border + nf*(dy+Border);
-	nt->r = insetrect(nt->er, Border);
-	nt->er.max.x = editr.max.x;
-	draw(screen, nt->er, display->white, nil, ZP);
-	for(i=0; i<nf; i++){
-		p1 = Pt(nt->r.min.x-1, nt->r.min.y+i*(Border+dy));
-		/* draw portion of bitmap */
-		p = Pt(p1.x+1, p1.y);
-		if(nt->mag == 1)
-			draw(screen, Rect(p.x, p.y, p.x+fdx+Dx(b->r), p.y+Dy(b->r)),
-				b, nil, Pt(b->r.min.x+i*fdx, b->r.min.y));
-		else{
-			for(y=b->r.min.y; y<b->r.max.y; y++){
-				sy = p.y+(y-b->r.min.y)*nt->mag;
-				if((n=unloadimage(b, Rect(b->r.min.x, y, b->r.max.x, y+1), data, sizeof data)) < 0)
-					fprint(2, "unloadimage: %r\n");
-				for(x=b->r.min.x+i*(fdx/nt->mag); x<b->r.max.x; x++){
-					sx = p.x+(x-i*(fdx/nt->mag)-b->r.min.x)*nt->mag;
-					if(sx >= nt->r.max.x)
-						break;
-					v = bvalue(value(b, x), b->depth);
-					if(v == 255)
-						continue;
-					if(b->chan == GREY8)
-						draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
-							greyvalues[v], nil, ZP);
-					else
-						draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
-							values[v], nil, ZP);
-				}
-
-			}
-		}
-		/* line down left */
-		if(i == 0)
-			col = display->black;
-		else
-			col = display->white;
-		draw(screen, Rect(p1.x, p1.y, p1.x+1, p1.y+dy+Border), col, nil, ZP);
-		/* line across top */
-		draw(screen, Rect(p1.x, p1.y-1, nt->r.max.x+Border, p1.y), display->black, nil, ZP);
-		p2 = p1;
-		if(i == nf-1){
-			p2.x += 1 + dx%fdx;
-			col = display->black;
-		}else{
-			p2.x = nt->r.max.x;
-			col = display->white;
-		}
-		/* line down right */
-		draw(screen, Rect(p2.x, p2.y, p2.x+1, p2.y+dy+Border), col, nil, ZP);
-		/* line across bottom */
-		if(i == nf-1){
-			p1.y += Border+dy;
-			draw(screen, Rect(p1.x, p1.y-1, p2.x,p1.y), display->black, nil, ZP);
-		}
-	}
-	nt->tr.min.x = editr.min.x;
-	nt->tr.max.x = editr.max.x;
-	nt->tr.min.y = nt->er.max.y + Border;
-	nt->tr.max.y = nt->tr.min.y + nl;
-	nt->er.max.y = nt->tr.max.y + Border;
-	text(nt);
-}
-
-int
-tohex(int c)
-{
-	if('0'<=c && c<='9')
-		return c - '0';
-	if('a'<=c && c<='f')
-		return 10 + (c - 'a');
-	if('A'<=c && c<='F')
-		return 10 + (c - 'A');
-	return 0;
-}
-
-Thing*
-tget(char *file)
-{
-	int i, j, fd, face, x, y, c, chan;
-	Image *b;
-	Subfont *s;
-	Thing *t;
-	Dir *d;
-	jmp_buf oerr;
-	uchar buf[256];
-	char *data;
-
-	buf[0] = '\0';
-	errstr((char*)buf, sizeof buf);	/* flush pending error message */
-	memmove(oerr, err, sizeof err);
-	d = nil;
-	if(setjmp(err)){
-   Err:
-		free(d);
-		memmove(err, oerr, sizeof err);
-		return 0;
-	}
-	fd = open(file, OREAD);
-	if(fd < 0){
-		mesg("can't open %s: %r", file);
-		goto Err;
-	}
-	d = dirfstat(fd);
-	if(d == nil){
-		mesg("can't stat bitmap file %s: %r", file);
-		close(fd);
-		goto Err;
-	}
-	if(read(fd, buf, 11) != 11){
-		mesg("can't read %s: %r", file);
-		close(fd);
-		goto Err;
-	}
-	seek(fd, 0, 0);
-	data = (char*)buf;
-	if(*data == '{')
-		data++;
-	if(memcmp(data, "0x", 2)==0 && data[4]==','){
-		/*
-		 * cursor file
-		 */
-		face = CURSOR;
-		s = 0;
-		data = malloc(d->length+1);
-		if(data == 0){
-			mesg("can't malloc buffer: %r");
-			close(fd);
-			goto Err;
-		}
-		data[d->length] = 0;
-		if(read(fd, data, d->length) != d->length){
-			mesg("can't read cursor file %s: %r", file);
-			close(fd);
-			goto Err;
-		}
-		b = allocimage(display, Rect(0, 0, 16, 32), GREY1, 0, DNofill);
-		if(b == 0){
-			mesg("image alloc failed file %s: %r", file);
-			free(data);
-			close(fd);
-			goto Err;
-		}
-		i = 0;
-		for(x=0;x<64; ){
-			if((c=data[i]) == '\0')
-				goto ill;
-			if(c=='0' && data[i+1] == 'x'){
-				i += 2;
-				continue;
-			}
-			if(strchr(hex, c)){
-				buf[x++] = (tohex(c)<<4) | tohex(data[i+1]);
-				i += 2;
-				continue;
-			}
-			i++;
-		}
-		loadimage(b, Rect(0, 0, 16, 32), buf, sizeof buf);
-		free(data);
-	}else if(memcmp(buf, "0x", 2)==0){
-		/*
-		 * face file
-		 */
-		face = FACE;
-		s = 0;
-		data = malloc(d->length+1);
-		if(data == 0){
-			mesg("can't malloc buffer: %r");
-			close(fd);
-			goto Err;
-		}
-		data[d->length] = 0;
-		if(read(fd, data, d->length) != d->length){
-			mesg("can't read bitmap file %s: %r", file);
-			close(fd);
-			goto Err;
-		}
-		for(y=0,i=0; i<d->length; i++)
-			if(data[i] == '\n')
-				y++;
-		if(y == 0){
-	ill:
-			mesg("ill-formed face file %s", file);
-			close(fd);
-			free(data);
-			goto Err;
-		}
-		for(x=0,i=0; (c=data[i])!='\n'; ){
-			if(c==',' || c==' ' || c=='\t'){
-				i++;
-				continue;
-			}
-			if(c=='0' && data[i+1] == 'x'){
-				i += 2;
-				continue;
-			}
-			if(strchr(hex, c)){
-				x += 4;
-				i++;
-				continue;
-			}
-			goto ill;
-		}
-		if(x % y)
-			goto ill;
-		switch(x / y){
-		default:
-			goto ill;
-		case 1:
-			chan = GREY1;
-			break;
-		case 2:
-			chan = GREY2;
-			break;
-		case 4:
-			chan = GREY4;
-			break;
-		case 8:
-			chan = CMAP8;
-			break;
-		}
-		b = allocimage(display, Rect(0, 0, y, y), chan, 0, -1);
-		if(b == 0){
-			mesg("image alloc failed file %s: %r", file);
-			free(data);
-			close(fd);
-			goto Err;
-		}
-		i = 0;
-		for(j=0; j<y; j++){
-			for(x=0; (c=data[i])!='\n'; ){
-				if(c=='0' && data[i+1] == 'x'){
-					i += 2;
-					continue;
-				}
-				if(strchr(hex, c)){
-					buf[x++] = ~((tohex(c)<<4) | tohex(data[i+1]));
-					i += 2;
-					continue;
-				}
-				i++;
-			}
-			i++;
-			loadimage(b, Rect(0, j, y, j+1), buf, sizeof buf);
-		}
-		free(data);
-	}else{
-		face = NORMAL;
-		s = 0;
-		b = readimage(display, fd, 0);
-		if(b == 0){
-			mesg("can't read bitmap file %s: %r", file);
-			close(fd);
-			goto Err;
-		}
-		if(seek(fd, 0, 1) < d->length)
-			s = readsubfonti(display, file, fd, b, 0);
-	}
-	close(fd);
-	t = malloc(sizeof(Thing));
-	if(t == 0){
-   nomem:
-		mesg("malloc failed: %r");
-		if(s)
-			freesubfont(s);
-		else
-			freeimage(b);
-		goto Err;
-	}
-	t->name = strdup(file);
-	if(t->name == 0){
-		free(t);
-		goto nomem;
-	}
-	t->b = b;
-	t->s = s;
-	t->face = face;
-	t->mod = 0;
-	t->parent = 0;
-	t->c = -1;
-	t->mag = 1;
-	t->off = 0;
-	memmove(err, oerr, sizeof err);
-	return t;
-}
-
-int
-atline(int x, Point p, char *line, char *buf)
-{
-	char *s, *c, *word, *hit;
-	int w, wasblank;
-	Rune r;
-
-	wasblank = 1;
-	hit = 0;
-	word = 0;
-	for(s=line; *s; s+=w){
-		w = chartorune(&r, s);
-		x += runestringnwidth(font, &r, 1);
-		if(wasblank && r!=' ')
-			word = s;
-		wasblank = 0;
-		if(r == ' '){
-			if(x >= p.x)
-				break;
-			wasblank = 1;
-		}
-		if(r == ':')
-			hit = word;
-	}
-	if(x < p.x)
-		return 0;
-	c = utfrune(hit, ':');
-	strncpy(buf, hit, c-hit);
-	buf[c-hit] = 0;
-	return 1;
-}
-
-int
-attext(Thing *t, Point p, char *buf)
-{
-	char l0[256], l1[256];
-
-	if(!ptinrect(p, t->tr))
-		return 0;
-	stext(t, l0, l1);
-	if(p.y < t->tr.min.y+font->height)
-		return atline(t->r.min.x, p, l0, buf);
-	else
-		return atline(t->r.min.x, p, l1, buf);
-}
-
-int
-type(char *buf, char *tag)
-{
-	Rune r;
-	char *p;
-
-	esetcursor(&busy);
-	p = buf;
-	for(;;){
-		*p = 0;
-		mesg("%s: %s", tag, buf);
-		r = ekbd();
-		switch(r){
-		case '\n':
-			mesg("");
-			esetcursor(0);
-			return p-buf;
-		case 0x15:	/* control-U */
-			p = buf;
-			break;
-		case '\b':
-			if(p > buf)
-				--p;
-			break;
-		default:
-			p += runetochar(p, &r);
-		}
-	}
-	return 0;	/* shut up compiler */
-}
-
-void
-textedit(Thing *t, char *tag)
-{
-	char buf[256];
-	char *s;
-	Image *b;
-	Subfont *f;
-	Fontchar *fc, *nfc;
-	Rectangle r;
-	ulong chan;
-	int i, ld, d, w, c, doredraw, fdx, x;
-	Thing *nt;
-
-	buttons(Up);
-	if(type(buf, tag) == 0)
-		return;
-	if(strcmp(tag, "file") == 0){
-		for(s=buf; *s; s++)
-			if(*s <= ' '){
-				mesg("illegal file name");
-				return;
-			}
-		if(strcmp(t->name, buf) != 0){
-			if(t->parent)
-				t->parent->mod = 1;
-			else
-				t->mod = 1;
-		}
-		for(nt=thing; nt; nt=nt->next)
-			if(t==nt || t->parent==nt || nt->parent==t){
-				free(nt->name);
-				nt->name = strdup(buf);
-				if(nt->name == 0){
-					mesg("malloc failed: %r");
-					return;
-				}
-				text(nt);
-			}
-		return;
-	}
-	if(strcmp(tag, "depth") == 0){
-		if(buf[0]<'0' || '9'<buf[0] || (d=atoi(buf))<0 || d>8 || xlog2(d)<0){
-			mesg("illegal ldepth");
-			return;
-		}
-		if(d == t->b->depth)
-			return;
-		if(t->parent)
-			t->parent->mod = 1;
-		else
-			t->mod = 1;
-		if(d == 8)
-			chan = CMAP8;
-		else
-			chan = CHAN1(CGrey, d);
-		for(nt=thing; nt; nt=nt->next){
-			if(nt!=t && nt!=t->parent && nt->parent!=t)
-				continue;
-			b = allocimage(display, nt->b->r, chan, 0, 0);
-			if(b == 0){
-	nobmem:
-				mesg("image alloc failed: %r");
-				return;
-			}
-			draw(b, b->r, nt->b, nil, nt->b->r.min);
-			freeimage(nt->b);
-			nt->b = b;
-			if(nt->s){
-				b = allocimage(display, nt->b->r, chan, 0, -1);
-				if(b == 0)
-					goto nobmem;
-				draw(b, b->r, nt->b, nil, nt->b->r.min);
-				f = allocsubfont(t->name, nt->s->n, nt->s->height, nt->s->ascent, nt->s->info, b);
-				if(f == 0){
-	nofmem:
-					freeimage(b);
-					mesg("can't make subfont: %r");
-					return;
-				}
-				nt->s->info = 0;	/* prevent it being freed */
-				nt->s->bits = 0;
-				freesubfont(nt->s);
-				nt->s = f;
-			}
-			drawthing(nt, 0);
-		}
-		return;
-	}
-	if(strcmp(tag, "mag") == 0){
-		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<=0 || ld>Maxmag){
-			mesg("illegal magnification");
-			return;
-		}
-		if(t->mag == ld)
-			return;
-		t->mag = ld;
-		redraw(t);
-		return;
-	}
-	if(strcmp(tag, "r") == 0){
-		if(t->s){
-			mesg("can't change rectangle of subfont\n");
-			return;
-		}
-		s = buf;
-		r.min.x = strtoul(s, &s, 0);
-		r.min.y = strtoul(s, &s, 0);
-		r.max.x = strtoul(s, &s, 0);
-		r.max.y = strtoul(s, &s, 0);
-		if(Dx(r)<=0 || Dy(r)<=0){
-			mesg("illegal rectangle");
-			return;
-		}
-		if(t->parent)
-			t = t->parent;
-		for(nt=thing; nt; nt=nt->next){
-			if(nt->parent==t && !rectinrect(nt->b->r, r))
-				tclose1(nt);
-		}
-		b = allocimage(display, r, t->b->chan, 0, 0);
-		if(b == 0)
-			goto nobmem;
-		draw(b, r, t->b, nil, r.min);
-		freeimage(t->b);
-		t->b = b;
-		b = allocimage(display, r, t->b->chan, 0, 0);
-		if(b == 0)
-			goto nobmem;
-		redraw(t);
-		t->mod = 1;
-		return;
-	}
-	if(strcmp(tag, "ascent") == 0){
-		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>t->s->height){
-			mesg("illegal ascent");
-			return;
-		}
-		if(t->s->ascent == ld)
-			return;
-		t->s->ascent = ld;
-		text(t);
-		t->mod = 1;
-		return;
-	}
-	if(strcmp(tag, "height") == 0){
-		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
-			mesg("illegal height");
-			return;
-		}
-		if(t->s->height == ld)
-			return;
-		t->s->height = ld;
-		text(t);
-		t->mod = 1;
-		return;
-	}
-	if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){
-		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
-			mesg("illegal value");
-			return;
-		}
-		fc = &t->parent->s->info[t->c];
-		if(strcmp(tag, "left")==0){
-			if(fc->left == ld)
-				return;
-			fc->left = ld;
-		}else{
-			if(fc->width == ld)
-				return;
-			fc->width = ld;
-		}
-		text(t);
-		t->parent->mod = 1;
-		return;
-	}
-	if(strcmp(tag, "offset(hex)") == 0){
-		if(!strchr(hex, buf[0])){
-	illoff:
-			mesg("illegal offset");
-			return;
-		}
-		s = 0;
-		ld = strtoul(buf, &s, 16);
-		if(*s)
-			goto illoff;
-		t->off = ld;
-		text(t);
-		for(nt=thing; nt; nt=nt->next)
-			if(nt->parent == t)
-				text(nt);
-		return;
-	}
-	if(strcmp(tag, "n") == 0){
-		if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){
-			mesg("illegal n");
-			return;
-		}
-		f = t->s;
-		if(w == f->n)
-			return;
-		doredraw = 0;
-	again:
-		for(nt=thing; nt; nt=nt->next)
-			if(nt->parent == t){
-				doredraw = 1;
-				tclose1(nt);
-				goto again;
-			}
-		r = t->b->r;
-		if(w < f->n)
-			r.max.x = f->info[w].x;
-		b = allocimage(display, r, t->b->chan, 0, 0);
-		if(b == 0)
-			goto nobmem;
-		draw(b, b->r, t->b, nil, r.min);
-		fdx = Dx(editr) - 2*Border;
-		if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
-			doredraw = 1;
-		freeimage(t->b);
-		t->b = b;
-		b = allocimage(display, r, t->b->chan, 0, 0);
-		if(b == 0)
-			goto nobmem;
-		draw(b, b->r, t->b, nil, r.min);
-		nfc = malloc((w+1)*sizeof(Fontchar));
-		if(nfc == 0){
-			mesg("malloc failed");
-			freeimage(b);
-			return;
-		}
-		fc = f->info;
-		for(i=0; i<=w && i<=f->n; i++)
-			nfc[i] = fc[i];
-		if(w+1 < i)
-			memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar));
-		x = fc[f->n].x;
-		for(; i<=w; i++)
-			nfc[i].x = x;
-		f = allocsubfont(t->name, w, f->height, f->ascent, nfc, b);
-		if(f == 0)
-			goto nofmem;
-		t->s->bits = nil;	/* don't free it */
-		freesubfont(t->s);
-		f->info = nfc;
-		t->s = f;
-		if(doredraw)
-			redraw(thing);
-		else
-			drawthing(t, 0);
-		t->mod = 1;
-		return;
-	}
-	if(strcmp(tag, "iwidth") == 0){
-		if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){
-			mesg("illegal iwidth");
-			return;
-		}
-		w -= Dx(t->b->r);
-		if(w == 0)
-			return;
-		r = t->parent->b->r;
-		r.max.x += w;
-		c = t->c;
-		t = t->parent;
-		f = t->s;
-		b = allocimage(display, r, t->b->chan, 0, 0);
-		if(b == 0)
-			goto nobmem;
-		fc = &f->info[c];
-		draw(b, Rect(b->r.min.x, b->r.min.y,
-				b->r.min.x+(fc[1].x-t->b->r.min.x), b->r.min.y+Dy(t->b->r)),
-				t->b, nil, t->b->r.min);
-		draw(b, Rect(fc[1].x+w, b->r.min.y, w+t->b->r.max.x, b->r.min.y+Dy(t->b->r)),
-			t->b, nil, Pt(fc[1].x, t->b->r.min.y));
-		fdx = Dx(editr) - 2*Border;
-		doredraw = 0;
-		if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
-			doredraw = 1;
-		freeimage(t->b);
-		t->b = b;
-		b = allocimage(display, r, t->b->chan, 0, 0);
-		if(b == 0)
-			goto nobmem;
-		draw(b, b->r, t->b, nil, t->b->r.min);
-		fc = &f->info[c+1];
-		for(i=c+1; i<=f->n; i++, fc++)
-			fc->x += w;
-		f = allocsubfont(t->name, f->n, f->height, f->ascent,
-			f->info, b);
-		if(f == 0)
-			goto nofmem;
-		/* t->s and f share info; free carefully */
-		fc = f->info;
-		t->s->bits = nil;
-		t->s->info = 0;
-		freesubfont(t->s);
-		f->info = fc;
-		t->s = f;
-		if(doredraw)
-			redraw(t);
-		else
-			drawthing(t, 0);
-		/* redraw all affected chars */
-		for(nt=thing; nt; nt=nt->next){
-			if(nt->parent!=t || nt->c<c)
-				continue;
-			fc = &f->info[nt->c];
-			r.min.x = fc[0].x;
-			r.min.y = nt->b->r.min.y;
-			r.max.x = fc[1].x;
-			r.max.y = nt->b->r.max.y;
-			b = allocimage(display, r, nt->b->chan, 0, 0);
-			if(b == 0)
-				goto nobmem;
-			draw(b, r, t->b, nil, r.min);
-			doredraw = 0;
-			if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx)
-				doredraw = 1;
-			freeimage(nt->b);
-			nt->b = b;
-			if(c != nt->c)
-				text(nt);
-			else{
-				if(doredraw)
-					redraw(nt);
-				else
-					drawthing(nt, 0);
-			}
-		}
-		t->mod = 1;
-		return;
-	}
-	mesg("cannot edit %s in file %s", tag, t->name);
-}
-
-void
-cntledit(char *tag)
-{
-	char buf[256];
-	ulong l;
-
-	buttons(Up);
-	if(type(buf, tag) == 0)
-		return;
-	if(strcmp(tag, "mag") == 0){
-		if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<=0 || l>Maxmag){
-			mesg("illegal magnification");
-			return;
-		}
-		mag = l;
-		cntl();
-		return;
-	}
-	if(strcmp(tag, "but1")==0
-	|| strcmp(tag, "but2")==0){
-		if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<0 || l>255){
-			mesg("illegal value");
-			return;
-		}
-		if(strcmp(tag, "but1") == 0)
-			but1val = l;
-		else if(strcmp(tag, "but2") == 0)
-			but2val = l;
-		cntl();
-		return;
-	}
-	if(strcmp(tag, "invert-on-copy")==0){
-		if(buf[0]=='y' || buf[0]=='1')
-			invert = 1;
-		else if(buf[0]=='n' || buf[0]=='0')
-			invert = 0;
-		else{
-			mesg("illegal value");
-			return;
-		}
-		cntl();
-		return;
-	}
-	mesg("cannot edit %s", tag);
-}
-
-void
-buttons(int ud)
-{
-	while((mouse.buttons==0) != ud)
-		mouse = emouse();
-}
-
-Point
-screenpt(Thing *t, Point realp)
-{
-	int fdx, n;
-	Point p;
-
-	fdx = Dx(editr)-2*Border;
-	if(t->mag > 1)
-		fdx -= fdx%t->mag;
-	p = mulpt(subpt(realp, t->b->r.min), t->mag);
-	if(fdx < Dx(t->b->r)*t->mag){
-		n = p.x/fdx;
-		p.y += n * (Dy(t->b->r)*t->mag+Border);
-		p.x -= n * fdx;
-	}
-	p = addpt(p, t->r.min);
-	return p;
-}
-
-Point
-realpt(Thing *t, Point screenp)
-{
-	int fdx, n, dy;
-	Point p;
-
-	fdx = (Dx(editr)-2*Border);
-	if(t->mag > 1)
-		fdx -= fdx%t->mag;
-	p.y = screenp.y-t->r.min.y;
-	p.x = 0;
-	if(fdx < Dx(t->b->r)*t->mag){
-		dy = Dy(t->b->r)*t->mag+Border;
-		n = (p.y/dy);
-		p.x = n * fdx;
-		p.y -= n * dy;
-	}
-	p.x += screenp.x-t->r.min.x;
-	p = addpt(divpt(p, t->mag), t->b->r.min);
-	return p;
-}
-
-int
-sweep(int but, Rectangle *r)
-{
-	Thing *t;
-	Point p, q, lastq;
-
-	esetcursor(&sweep0);
-	buttons(Down);
-	if(mouse.buttons != (1<<(but-1))){
-		buttons(Up);
-		esetcursor(0);
-		return 0;
-	}
-	p = mouse.xy;
-	for(t=thing; t; t=t->next)
-		if(ptinrect(p, t->r))
-			break;
-	if(t)
-		p = screenpt(t, realpt(t, p));
-	r->min = p;
-	r->max = p;
-	esetcursor(&box);
-	lastq = ZP;
-	while(mouse.buttons == (1<<(but-1))){
-		edrawgetrect(insetrect(*r, -Borderwidth), 1);
-		mouse = emouse();
-		edrawgetrect(insetrect(*r, -Borderwidth), 0);
-		q = mouse.xy;
-		if(t)
-			q = screenpt(t, realpt(t, q));
-		if(eqpt(q, lastq))
-			continue;
-		*r = canonrect(Rpt(p, q));
-		lastq = q;
-	}
-	esetcursor(0);
-	if(mouse.buttons){
-		buttons(Up);
-		return 0;
-	}
-	return 1;
-}
-
-void
-openedit(Thing *t, Point pt, int c)
-{
-	int x, y;
-	Point p;
-	Rectangle r;
-	Rectangle br;
-	Fontchar *fc;
-	Thing *nt;
-
-	if(t->b->depth > 8){
-		mesg("image has depth %d; can't handle >8", t->b->depth);
-		return;
-	}
-	br = t->b->r;
-	if(t->s == 0){
-		c = -1; 
-		/* if big enough to bother, sweep box */
-		if(Dx(br)<=16 && Dy(br)<=16)
-			r = br;
-		else{
-			if(!sweep(1, &r))
-				return;
-			r = rectaddpt(r, subpt(br.min, t->r.min));
-			if(!rectclip(&r, br))
-				return;
-			if(Dx(br) <= 8){
-				r.min.x = br.min.x;
-				r.max.x = br.max.x;
-			}else if(Dx(r) < 4){
-	    toosmall:
-				mesg("rectangle too small");
-				return;
-			}
-			if(Dy(br) <= 8){
-				r.min.y = br.min.y;
-				r.max.y = br.max.y;
-			}else if(Dy(r) < 4)
-				goto toosmall;
-		}
-	}else if(c >= 0){
-		fc = &t->s->info[c];
-		r.min.x = fc[0].x;
-		r.min.y = br.min.y;
-		r.max.x = fc[1].x;
-		r.max.y = br.min.y + Dy(br);
-	}else{
-		/* just point at character */
-		fc = t->s->info;
-		p = addpt(pt, subpt(br.min, t->r.min));
-		x = br.min.x;
-		y = br.min.y;
-		for(c=0; c<t->s->n; c++,fc++){
-	    again:
-			r.min.x = x;
-			r.min.y = y;
-			r.max.x = x + fc[1].x - fc[0].x;
-			r.max.y = y + Dy(br);
-			if(ptinrect(p, r))
-				goto found;
-			if(r.max.x >= br.min.x+Dx(t->r)){
-				x -= Dx(t->r);
-				y += t->s->height;
-				if(fc[1].x > fc[0].x)
-					goto again;
-			}
-			x += fc[1].x - fc[0].x;
-		}
-		return;
-	   found:
-		r = br;
-		r.min.x = fc[0].x;
-		r.max.x = fc[1].x;
-	}
-	nt = malloc(sizeof(Thing));
-	if(nt == 0){
-   nomem:
-		mesg("can't allocate: %r");
-		return;
-	}
-	memset(nt, 0, sizeof(Thing));
-	nt->c = c;
-	nt->b = allocimage(display, r, t->b->chan, 0, DNofill);
-	if(nt->b == 0){
-		free(nt);
-		goto nomem;
-	}
-	draw(nt->b, r, t->b, nil, r.min);
-	nt->name = strdup(t->name);
-	if(nt->name == 0){
-		freeimage(nt->b);
-		free(nt);
-		goto nomem;
-	}
-	nt->parent = t;
-	nt->mag = mag;
-	drawthing(nt, 1);
-}
-
-void
-ckinfo(Thing *t, Rectangle mod)
-{
-	int i, j, k, top, bot, n, zero;
-	Fontchar *fc;
-	Rectangle r;
-	Image *b;
-	Thing *nt;
-
-	if(t->parent)
-		t = t->parent;
-	if(t->s==0 || Dy(t->b->r)==0)
-		return;
-	b = 0;
-	/* check bounding boxes */
-	fc = &t->s->info[0];
-	r.min.y = t->b->r.min.y;
-	r.max.y = t->b->r.max.y;
-	for(i=0; i<t->s->n; i++, fc++){
-		r.min.x = fc[0].x;
-		r.max.x = fc[1].x;
-		if(!rectXrect(mod, r))
-			continue;
-		if(b==0 || Dx(b->r)<Dx(r)){
-			if(b)
-				freeimage(b);
-			b = allocimage(display, rectsubpt(r, r.min), t->b->chan, 0, 0);
-			if(b == 0){
-				mesg("can't alloc image");
-				break;
-			}
-		}
-		draw(b, b->r, display->white, nil, ZP);
-		draw(b, b->r, t->b, nil, r.min);
-		top = 100000;
-		bot = 0;
-		n = 2+((Dx(r)/8)*t->b->depth);
-		for(j=0; j<b->r.max.y; j++){
-			memset(data, 0, n);
-			unloadimage(b, Rect(b->r.min.x, j, b->r.max.x, j+1), data, sizeof data);
-			zero = 1;
-			for(k=0; k<n; k++)
-				if(data[k]){
-					zero = 0;
-					break;
-				}
-			if(!zero){
-				if(top > j)
-					top = j;
-				bot = j+1;
-			}
-		}
-		if(top > j)
-			top = 0;
-		if(top!=fc->top || bot!=fc->bottom){
-			fc->top = top;
-			fc->bottom = bot;
-			for(nt=thing; nt; nt=nt->next)
-				if(nt->parent==t && nt->c==i)
-					text(nt);
-		}
-	}
-	if(b)
-		freeimage(b);
-}
-
-void
-twidpix(Thing *t, Point p, int set)
-{
-	Image *b, *v;
-	int c;
-
-	b = t->b;
-	if(!ptinrect(p, b->r))
-		return;
-	if(set)
-		c = but1val;
-	else
-		c = but2val;
-	if(b->chan == GREY8)
-		v = greyvalues[c];
-	else
-		v = values[c];
-	draw(b, Rect(p.x, p.y, p.x+1, p.y+1), v, nil, ZP);
-	p = screenpt(t, p);
-	draw(screen, Rect(p.x, p.y, p.x+t->mag, p.y+t->mag), v, nil, ZP);
-}
-
-void
-twiddle(Thing *t)
-{
-	int set;
-	Point p, lastp;
-	Image *b;
-	Thing *nt;
-	Rectangle mod;
-
-	if(mouse.buttons!=1 && mouse.buttons!=2){
-		buttons(Up);
-		return;
-	}
-	set = mouse.buttons==1;
-	b = t->b;
-	lastp = addpt(b->r.min, Pt(-1, -1));
-	mod = Rpt(addpt(b->r.max, Pt(1, 1)), lastp);
-	while(mouse.buttons){
-		p = realpt(t, mouse.xy);
-		if(!eqpt(p, lastp)){
-			lastp = p;
-			if(ptinrect(p, b->r)){
-				for(nt=thing; nt; nt=nt->next)
-					if(nt->parent==t->parent || nt==t->parent)
-						twidpix(nt, p, set);
-				if(t->parent)
-					t->parent->mod = 1;
-				else
-					t->mod = 1;
-				if(p.x < mod.min.x)
-					mod.min.x = p.x;
-				if(p.y < mod.min.y)
-					mod.min.y = p.y;
-				if(p.x >= mod.max.x)
-					mod.max.x = p.x+1;
-				if(p.y >= mod.max.y)
-					mod.max.y = p.y+1;
-			}
-		}
-		mouse = emouse();
-	}
-	ckinfo(t, mod);
-}
-
-void
-xselect(void)
-{
-	Thing *t;
-	char line[128], buf[128];
-	Point p;
-
-	if(ptinrect(mouse.xy, cntlr)){
-		scntl(line);
-		if(atline(cntlr.min.x, mouse.xy, line, buf)){
-			if(mouse.buttons == 1)
-				cntledit(buf);
-			else
-				buttons(Up);
-			return;
-		}
-		return;
-	}
-	for(t=thing; t; t=t->next){
-		if(attext(t, mouse.xy, buf)){
-			if(mouse.buttons == 1)
-				textedit(t, buf);
-			else
-				buttons(Up);
-			return;
-		}
-		if(ptinrect(mouse.xy, t->r)){
-			if(t->parent == 0){
-				if(mouse.buttons == 1){
-					p = mouse.xy;
-					buttons(Up);
-					openedit(t, p, -1);
-				}else
-					buttons(Up);
-				return;
-			}
-			twiddle(t);
-			return;
-		}
-	}
-}
-
-void
-twrite(Thing *t)
-{
-	int i, j, x, y, fd, ws, ld;
-	Biobuf buf;
-	Rectangle r;
-
-	if(t->parent)
-		t = t->parent;
-	esetcursor(&busy);
-	fd = create(t->name, OWRITE, 0666);
-	if(fd < 0){
-		mesg("can't write %s: %r", t->name);
-		return;
-	}
-	if(t->face && t->b->depth <= 4){
-		r = t->b->r;
-		ld = xlog2(t->b->depth);
-		/* This heuristic reflects peculiarly different formats */
-		ws = 4;
-		if(t->face == 2)	/* cursor file */
-			ws = 1;
-		else if(Dx(r)<32 || ld==0)
-			ws = 2;
-		Binit(&buf, fd, OWRITE);
-		if(t->face == CURSOR)
-			Bprint(&buf, "{");
-		for(y=r.min.y; y<r.max.y; y++){
-			unloadimage(t->b, Rect(r.min.x, y, r.max.x, y+1), data, sizeof data);
-			j = 0;
-			for(x=r.min.x; x<r.max.x; j+=ws,x+=ws*8>>ld){
-				Bprint(&buf, "0x");
-				for(i=0; i<ws; i++)
-					Bprint(&buf, "%.2x", data[i+j]);
-				Bprint(&buf, ", ");
-			}
-			if(t->face == CURSOR){
-				switch(y){
-				case 3: case 7: case 11: case 19: case 23: case 27:
-					Bprint(&buf, "\n ");
-					break;
-				case 15:
-					Bprint(&buf, "},\n{");
-					break;
-				case 31:
-					Bprint(&buf, "}\n");
-					break;
-				}
-			}else
-				Bprint(&buf, "\n");
-		}
-		Bterm(&buf);
-	}else
-		if(writeimage(fd, t->b, 0)<0 || (t->s && writesubfont(fd, t->s)<0)){
-			close(fd);
-			mesg("can't write %s: %r", t->name);
-		}
-	t->mod = 0;
-	close(fd);
-	mesg("wrote %s", t->name);
-}
-
-void
-tpixels(void)
-{
-	Thing *t;
-	Point p, lastp;
-
-	esetcursor(&pixel);
-	for(;;){
-		buttons(Down);
-		if(mouse.buttons != 4)
-			break;
-		for(t=thing; t; t=t->next){
-			lastp = Pt(-1, -1);
-			if(ptinrect(mouse.xy, t->r)){
-				while(ptinrect(mouse.xy, t->r) && mouse.buttons==4){
-					p = realpt(t, mouse.xy);
-					if(!eqpt(p, lastp)){
-						if(p.y != lastp.y)
-							unloadimage(t->b, Rect(t->b->r.min.x, p.y, t->b->r.max.x, p.y+1), data, sizeof data);
-						mesg("[%d,%d] = %d=0x%ux", p.x, p.y, value(t->b, p.x), value(t->b, p.x));
-						lastp = p;
-					}
-					mouse = emouse();
-				}
-				goto Continue;
-			}
-		}
-		mouse = emouse();
-    Continue:;
-	}
-	buttons(Up);
-	esetcursor(0);
-}
-
-void
-tclose1(Thing *t)
-{
-	Thing *nt;
-
-	if(t == thing)
-		thing = t->next;
-	else{
-		for(nt=thing; nt->next!=t; nt=nt->next)
-			;
-		nt->next = t->next;
-	}
-	do
-		for(nt=thing; nt; nt=nt->next)
-			if(nt->parent == t){
-				tclose1(nt);
-				break;
-			}
-	while(nt);
-	if(t->s)
-		freesubfont(t->s);
-	else
-		freeimage(t->b);
-	free(t->name);
-	free(t);
-}
-
-void
-tclose(Thing *t)
-{
-	Thing *ct;
-
-	if(t->mod){
-		mesg("%s modified", t->name);
-		t->mod = 0;
-		return;
-	}
-	/* fiddle to save redrawing unmoved things */
-	if(t == thing)
-		ct = 0;
-	else
-		for(ct=thing; ct; ct=ct->next)
-			if(ct->next==t || ct->next->parent==t)
-				break;
-	tclose1(t);
-	if(ct)
-		ct = ct->next;
-	else
-		ct = thing;
-	redraw(ct);
-}
-
-void
-tread(Thing *t)
-{
-	Thing *nt, *new;
-	Fontchar *i;
-	Rectangle r;
-	int nclosed;
-
-	if(t->parent)
-		t = t->parent;
-	new = tget(t->name);
-	if(new == 0)
-		return;
-	nclosed = 0;
-    again:
-	for(nt=thing; nt; nt=nt->next)
-		if(nt->parent == t){
-			if(!rectinrect(nt->b->r, new->b->r)
-			|| new->b->depth!=nt->b->depth){
-    closeit:
-				nclosed++;
-				nt->parent = 0;
-				tclose1(nt);
-				goto again;
-			}
-			if((t->s==0) != (new->s==0))
-				goto closeit;
-			if((t->face==0) != (new->face==0))
-				goto closeit;
-			if(t->s){	/* check same char */
-				if(nt->c >= new->s->n)
-					goto closeit;
-				i = &new->s->info[nt->c];
-				r.min.x = i[0].x;
-				r.max.x = i[1].x;
-				r.min.y = new->b->r.min.y;
-				r.max.y = new->b->r.max.y;
-				if(!eqrect(r, nt->b->r))
-					goto closeit;
-			}
-			nt->parent = new;
-			draw(nt->b, nt->b->r, new->b, nil, nt->b->r.min);
-		}
-	new->next = t->next;
-	if(t == thing)
-		thing = new;
-	else{
-		for(nt=thing; nt->next!=t; nt=nt->next)
-			;
-		nt->next = new;
-	}
-	if(t->s)
-		freesubfont(t->s);
-	else
-		freeimage(t->b);
-	free(t->name);
-	free(t);
-	for(nt=thing; nt; nt=nt->next)
-		if(nt==new || nt->parent==new)
-			if(nclosed == 0)
-				drawthing(nt, 0);	/* can draw in place */
-			else{
-				redraw(nt);	/* must redraw all below */
-				break;
-			}
-}
-
-void
-tchar(Thing *t)
-{
-	char buf[256], *p;
-	Rune r;
-	ulong c, d;
-
-	if(t->s == 0){
-		t = t->parent;
-		if(t==0 || t->s==0){
-			mesg("not a subfont");
-			return;
-		}
-	}
-	if(type(buf, "char (hex or character or hex-hex)") == 0)
-		return;
-	if(utflen(buf) == 1){
-		chartorune(&r, buf);
-		c = r;
-		d = r;
-	}else{
-		if(!strchr(hex, buf[0])){
-			mesg("illegal hex character");
-			return;
-		}
-		c = strtoul(buf, 0, 16);
-		d = c;
-		p = utfrune(buf, '-');
-		if(p){
-			d = strtoul(p+1, 0, 16);
-			if(d < c){
-				mesg("invalid range");
-				return;
-			}
-		}
-	}
-	c -= t->off;
-	d -= t->off;
-	while(c <= d){
-		if(c<0 || c>=t->s->n){
-			mesg("0x%lux not in font %s", c+t->off, t->name);
-			return;
-		}
-		openedit(t, Pt(0, 0), c);
-		c++;
-	}
-}
-
-void
-apply(void (*f)(Thing*))
-{
-	Thing *t;
-
-	esetcursor(&sight);
-	buttons(Down);
-	if(mouse.buttons == 4)
-		for(t=thing; t; t=t->next)
-			if(ptinrect(mouse.xy, t->er)){
-				buttons(Up);
-				f(t);
-				break;
-			}
-	buttons(Up);
-	esetcursor(0);
-}
-
-int
-complement(Image *t)
-{
-	int i, n;
-	uchar *buf;
-
-	n = Dy(t->r)*bytesperline(t->r, t->depth);
-	buf = malloc(n);
-	if(buf == 0)
-		return 0;
-	unloadimage(t, t->r, buf, n);
-	for(i=0; i<n; i++)
-		buf[i] = ~buf[i];
-	loadimage(t, t->r, buf, n);
-	free(buf);
-	return 1;
-}
-
-void
-copy(void)
-{
-	Thing *st, *dt, *nt;
-	Rectangle sr, dr, fr;
-	Image *tmp;
-	Point p1, p2;
-	int but, up;
-
-	if(!sweep(3, &sr))
-		return;
-	for(st=thing; st; st=st->next)
-		if(rectXrect(sr, st->r))
-			break;
-	if(st == 0)
-		return;
-	/* click gives full rectangle */
-	if(Dx(sr)<4 && Dy(sr)<4)
-		sr = st->r;
-	rectclip(&sr, st->r);
-	p1 = realpt(st, sr.min);
-	p2 = realpt(st, Pt(sr.min.x, sr.max.y));
-	up = 0;
-	if(p1.x != p2.x){	/* swept across a fold */
-   onafold:
-		mesg("sweep spans a fold");
-		goto Return;
-	}
-	p2 = realpt(st, sr.max);
-	sr.min = p1;
-	sr.max = p2;
-	fr.min = screenpt(st, sr.min);
-	fr.max = screenpt(st, sr.max);
-	p1 = subpt(p2, p1);	/* diagonal */
-	if(p1.x==0 || p1.y==0)
-		return;
-	border(screen, fr, -1, values[Blue], ZP);
-	esetcursor(&box);
-	for(; mouse.buttons==0; mouse=emouse()){
-		for(dt=thing; dt; dt=dt->next)
-			if(ptinrect(mouse.xy, dt->er))
-				break;
-		if(up)
-			edrawgetrect(insetrect(dr, -Borderwidth), 0);
-		up = 0;
-		if(dt == 0)
-			continue;
-		dr.max = screenpt(dt, realpt(dt, mouse.xy));
-		dr.min = subpt(dr.max, mulpt(p1, dt->mag));
-		if(!rectXrect(dr, dt->r))
-			continue;
-		edrawgetrect(insetrect(dr, -Borderwidth), 1);
-		up = 1;
-	}
-	/* if up==1, we had a hit */
-	esetcursor(0);
-	if(up)
-		edrawgetrect(insetrect(dr, -Borderwidth), 0);
-	but = mouse.buttons;
-	buttons(Up);
-	if(!up || but!=4)
-		goto Return;
-	dt = 0;
-	for(nt=thing; nt; nt=nt->next)
-		if(rectXrect(dr, nt->r)){
-			if(dt){
-				mesg("ambiguous sweep");
-				return;
-			}
-			dt = nt;
-		}
-	if(dt == 0)
-		goto Return;
-	p1 = realpt(dt, dr.min);
-	p2 = realpt(dt, Pt(dr.min.x, dr.max.y));
-	if(p1.x != p2.x)
-		goto onafold;
-	p2 = realpt(dt, dr.max);
-	dr.min = p1;
-	dr.max = p2;
-
-	if(invert){
-		tmp = allocimage(display, dr, dt->b->chan, 0, 255);
-		if(tmp == 0){
-    nomem:
-			mesg("can't allocate temporary");
-			goto Return;
-		}
-		draw(tmp, dr, st->b, nil, sr.min);
-		if(!complement(tmp))
-			goto nomem;
-		draw(dt->b, dr, tmp, nil, dr.min);
-		freeimage(tmp);
-	}else
-		draw(dt->b, dr, st->b, nil, sr.min);
-	if(dt->parent){
-		draw(dt->parent->b, dr, dt->b, nil, dr.min);
-		dt = dt->parent;
-	}
-	drawthing(dt, 0);
-	for(nt=thing; nt; nt=nt->next)
-		if(nt->parent==dt && rectXrect(dr, nt->b->r)){
-			draw(nt->b, dr, dt->b, nil, dr.min);
-			drawthing(nt, 0);
-		}
-	ckinfo(dt, dr);
-	dt->mod = 1;
-
-Return:
-	/* clear blue box */
-	drawthing(st, 0);
-}
-
-void
-menu(void)
-{
-	Thing *t;
-	char *mod;
-	int sel;
-	char buf[256];
-
-	sel = emenuhit(3, &mouse, &menu3);
-	switch(sel){
-	case Mopen:
-		if(type(buf, "file")){
-			t = tget(buf);
-			if(t)
-				drawthing(t, 1);
-		}
-		break;
-	case Mwrite:
-		apply(twrite);
-		break;
-	case Mread:
-		apply(tread);
-		break;
-	case Mchar:
-		apply(tchar);
-		break;
-	case Mcopy:
-		copy();
-		break;
-	case Mpixels:
-		tpixels();
-		break;
-	case Mclose:
-		apply(tclose);
-		break;
-	case Mexit:
-		mod = 0;
-		for(t=thing; t; t=t->next)
-			if(t->mod){
-				mod = t->name;
-				t->mod = 0;
-			}
-		if(mod){
-			mesg("%s modified", mod);
-			break;
-		}
-		esetcursor(&skull);
-		buttons(Down);
-		if(mouse.buttons == 4){
-			buttons(Up);
-			exits(0);
-		}
-		buttons(Up);
-		esetcursor(0);
-		break;
-	}
-}
blob - d6a3db201ce8463affb740883a51039f7bdcf07e
blob + fcecb26369c38d7e5f46e59762e1a66e3346a830
--- src/libdraw/x11-itrans.c
+++ src/libdraw/x11-itrans.c
@@ -96,7 +96,9 @@ __xtoplan9kbd(XEvent *e)
 			k = '\n';
 			break;
 		case XK_Alt_L:
+		case XK_Meta_L:	/* Shift Alt on PCs */
 		case XK_Alt_R:
+		case XK_Meta_R:	/* Shift Alt on PCs */
 			k = Kalt;
 			break;
 		default:		/* not ISO-1 or tty control */
@@ -117,7 +119,6 @@ __xtoplan9kbd(XEvent *e)
 		return -1;
 	}
 
-	/* BUG: could/should do Alt translation here! */
 	return k+0;
 }
 
blob - 372923c118504766129d3ed9f1ce93bbf1a3de13
blob + a4a90d8b12408f9f3ee564295e8e04c6c1890c64
--- src/libthread/exec-unix.c
+++ src/libthread/exec-unix.c
@@ -4,7 +4,7 @@
 #include "threadimpl.h"
 
 static void efork(int[3], int[2], char*, char**);
-static void
+static int
 _threadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs)
 {
 	int pfd[2];
@@ -63,20 +63,28 @@ _threadexec(Channel *pidc, int fd[3], char *prog, char
 		sendul(pidc, pid);
 
 	_threaddebug(DBGEXEC, "threadexec schedexecwait");
-	threadexits(0);
+	return pid;
 
 Bad:
 	_threaddebug(DBGEXEC, "threadexec bad %r");
 	if(pidc)
 		sendul(pidc, ~0);
+	return -1;
 }
 
 void
 threadexec(Channel *pidc, int fd[3], char *prog, char *args[])
 {
-	_threadexec(pidc, fd, prog, args, 0);
+	if(_threadexec(pidc, fd, prog, args, 0) >= 0)
+		threadexits(nil);
 }
 
+int
+threadspawn(int fd[3], char *prog, char *args[])
+{
+	return _threadexec(nil, fd, prog, args, 0);
+}
+
 /*
  * The &f+1 trick doesn't work on SunOS, so we might 
  * as well bite the bullet and do this correctly.