xref: /plan9/sys/src/cmd/page/view.c (revision ad6ca847b1a6a504acb0003cd6c5c6d92687369b)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * the actual viewer that handles screen stuff
37dd7cddfSDavid du Colombier  */
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier #include <u.h>
67dd7cddfSDavid du Colombier #include <libc.h>
77dd7cddfSDavid du Colombier #include <draw.h>
87dd7cddfSDavid du Colombier #include <cursor.h>
97dd7cddfSDavid du Colombier #include <event.h>
107dd7cddfSDavid du Colombier #include <bio.h>
117dd7cddfSDavid du Colombier #include <plumb.h>
127dd7cddfSDavid du Colombier #include <ctype.h>
137dd7cddfSDavid du Colombier #include <keyboard.h>
147dd7cddfSDavid du Colombier #include "page.h"
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier Document *doc;
177dd7cddfSDavid du Colombier Image *im;
18493edcedSDavid du Colombier Image *tofree;
197dd7cddfSDavid du Colombier int page;
20a7529a1dSDavid du Colombier int angle = 0;
2159cc4ca5SDavid du Colombier int showbottom = 0;		/* on the next showpage, move the image so the bottom is visible. */
227dd7cddfSDavid du Colombier 
237dd7cddfSDavid du Colombier Rectangle ulrange;	/* the upper left corner of the image must be in this rectangle */
247dd7cddfSDavid du Colombier Point ul;			/* the upper left corner of the image is at this point on the screen */
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier Point pclip(Point, Rectangle);
277dd7cddfSDavid du Colombier Rectangle mkrange(Rectangle screenr, Rectangle imr);
287dd7cddfSDavid du Colombier void redraw(Image*);
297dd7cddfSDavid du Colombier 
307dd7cddfSDavid du Colombier Cursor reading={
317dd7cddfSDavid du Colombier 	{-1, -1},
327dd7cddfSDavid du Colombier 	{0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
337dd7cddfSDavid du Colombier 	 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
347dd7cddfSDavid du Colombier 	 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
357dd7cddfSDavid du Colombier 	 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
367dd7cddfSDavid du Colombier 	{0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
377dd7cddfSDavid du Colombier 	 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
387dd7cddfSDavid du Colombier 	 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
397dd7cddfSDavid du Colombier 	 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
407dd7cddfSDavid du Colombier };
417dd7cddfSDavid du Colombier 
42223a736eSDavid du Colombier Cursor query = {
43223a736eSDavid du Colombier 	{-7,-7},
44223a736eSDavid du Colombier 	{0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
45223a736eSDavid du Colombier 	 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8,
46223a736eSDavid du Colombier 	 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0,
47223a736eSDavid du Colombier 	 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
48223a736eSDavid du Colombier 	{0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c,
49223a736eSDavid du Colombier 	 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0,
50223a736eSDavid du Colombier 	 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80,
51223a736eSDavid du Colombier 	 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
52223a736eSDavid du Colombier };
53223a736eSDavid du Colombier 
547dd7cddfSDavid du Colombier enum {
557dd7cddfSDavid du Colombier 	Left = 1,
567dd7cddfSDavid du Colombier 	Middle = 2,
577dd7cddfSDavid du Colombier 	Right = 4,
587dd7cddfSDavid du Colombier 
597dd7cddfSDavid du Colombier 	RMenu = 3,
607dd7cddfSDavid du Colombier };
617dd7cddfSDavid du Colombier 
62493edcedSDavid du Colombier static void
delayfreeimage(Image * m)63493edcedSDavid du Colombier delayfreeimage(Image *m)
64493edcedSDavid du Colombier {
65493edcedSDavid du Colombier 	if(m == tofree)
66493edcedSDavid du Colombier 		return;
67493edcedSDavid du Colombier 	if(tofree)
68493edcedSDavid du Colombier 		freeimage(tofree);
69493edcedSDavid du Colombier 	tofree = m;
70493edcedSDavid du Colombier }
71493edcedSDavid du Colombier 
729a747e4fSDavid du Colombier void
unhide(void)739a747e4fSDavid du Colombier unhide(void)
749a747e4fSDavid du Colombier {
759a747e4fSDavid du Colombier 	static int wctl = -1;
769a747e4fSDavid du Colombier 
779a747e4fSDavid du Colombier 	if(wctl < 0)
789a747e4fSDavid du Colombier 		wctl = open("/dev/wctl", OWRITE);
799a747e4fSDavid du Colombier 	if(wctl < 0)
809a747e4fSDavid du Colombier 		return;
819a747e4fSDavid du Colombier 
829a747e4fSDavid du Colombier 	write(wctl, "unhide", 6);
839a747e4fSDavid du Colombier }
849a747e4fSDavid du Colombier 
857dd7cddfSDavid du Colombier int
max(int a,int b)867dd7cddfSDavid du Colombier max(int a, int b)
877dd7cddfSDavid du Colombier {
887dd7cddfSDavid du Colombier 	return a > b ? a : b;
897dd7cddfSDavid du Colombier }
907dd7cddfSDavid du Colombier 
917dd7cddfSDavid du Colombier int
min(int a,int b)927dd7cddfSDavid du Colombier min(int a, int b)
937dd7cddfSDavid du Colombier {
947dd7cddfSDavid du Colombier 	return a < b ? a : b;
957dd7cddfSDavid du Colombier }
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier char*
menugen(int n)997dd7cddfSDavid du Colombier menugen(int n)
1007dd7cddfSDavid du Colombier {
1017dd7cddfSDavid du Colombier 	static char menustr[32];
1027dd7cddfSDavid du Colombier 	char *p;
1037dd7cddfSDavid du Colombier 	int len;
1047dd7cddfSDavid du Colombier 
1057dd7cddfSDavid du Colombier 	if(n == doc->npage)
1067dd7cddfSDavid du Colombier 		return "exit";
1077dd7cddfSDavid du Colombier 	if(n > doc->npage)
1087dd7cddfSDavid du Colombier 		return nil;
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier 	if(reverse)
1117dd7cddfSDavid du Colombier 		n = doc->npage-1-n;
1127dd7cddfSDavid du Colombier 
1137dd7cddfSDavid du Colombier 	p = doc->pagename(doc, n);
1147dd7cddfSDavid du Colombier 	len = (sizeof menustr)-2;
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier 	if(strlen(p) > len && strrchr(p, '/'))
1177dd7cddfSDavid du Colombier 		p = strrchr(p, '/')+1;
1187dd7cddfSDavid du Colombier 	if(strlen(p) > len)
1197dd7cddfSDavid du Colombier 		p = p+strlen(p)-len;
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier 	strcpy(menustr+1, p);
1227dd7cddfSDavid du Colombier 	if(page == n)
1237dd7cddfSDavid du Colombier 		menustr[0] = '>';
1247dd7cddfSDavid du Colombier 	else
1257dd7cddfSDavid du Colombier 		menustr[0] = ' ';
1267dd7cddfSDavid du Colombier 	return menustr;
1277dd7cddfSDavid du Colombier }
1287dd7cddfSDavid du Colombier 
1297dd7cddfSDavid du Colombier void
showpage(int page,Menu * m)1307dd7cddfSDavid du Colombier showpage(int page, Menu *m)
1317dd7cddfSDavid du Colombier {
1327dd7cddfSDavid du Colombier 	if(doc->fwdonly)
1337dd7cddfSDavid du Colombier 		m->lasthit = 0;	/* this page */
1347dd7cddfSDavid du Colombier 	else
1357dd7cddfSDavid du Colombier 		m->lasthit = reverse ? doc->npage-1-page : page;
1367dd7cddfSDavid du Colombier 
1377dd7cddfSDavid du Colombier 	esetcursor(&reading);
138493edcedSDavid du Colombier 	delayfreeimage(nil);
139493edcedSDavid du Colombier 	im = cachedpage(doc, angle, page);
140493edcedSDavid du Colombier 	if(im == nil)
1417dd7cddfSDavid du Colombier 		wexits(0);
142493edcedSDavid du Colombier 	if(resizing)
1437dd7cddfSDavid du Colombier 		resize(Dx(im->r), Dy(im->r));
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier 	esetcursor(nil);
14659cc4ca5SDavid du Colombier 	if(showbottom){
14759cc4ca5SDavid du Colombier 		ul.y = screen->r.max.y - Dy(im->r);
14859cc4ca5SDavid du Colombier 		showbottom = 0;
14959cc4ca5SDavid du Colombier 	}
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier 	redraw(screen);
1527dd7cddfSDavid du Colombier 	flushimage(display, 1);
1537dd7cddfSDavid du Colombier }
1547dd7cddfSDavid du Colombier 
1557dd7cddfSDavid du Colombier char*
writebitmap(void)1567dd7cddfSDavid du Colombier writebitmap(void)
1577dd7cddfSDavid du Colombier {
1587dd7cddfSDavid du Colombier 	char basename[64];
1597dd7cddfSDavid du Colombier 	char name[64+30];
1607dd7cddfSDavid du Colombier 	static char result[200];
1617dd7cddfSDavid du Colombier 	char *p, *q;
1627dd7cddfSDavid du Colombier 	int fd;
1637dd7cddfSDavid du Colombier 
1647dd7cddfSDavid du Colombier 	if(im == nil)
1657dd7cddfSDavid du Colombier 		return "no image";
1667dd7cddfSDavid du Colombier 
1677dd7cddfSDavid du Colombier 	memset(basename, 0, sizeof basename);
1687dd7cddfSDavid du Colombier 	if(doc->docname)
1697dd7cddfSDavid du Colombier 		strncpy(basename, doc->docname, sizeof(basename)-1);
1707dd7cddfSDavid du Colombier 	else if((p = menugen(page)) && p[0] != '\0')
1717dd7cddfSDavid du Colombier 		strncpy(basename, p+1, sizeof(basename)-1);
1727dd7cddfSDavid du Colombier 
1737dd7cddfSDavid du Colombier 	if(basename[0]) {
1747dd7cddfSDavid du Colombier 		if(q = strrchr(basename, '/'))
1757dd7cddfSDavid du Colombier 			q++;
1767dd7cddfSDavid du Colombier 		else
1777dd7cddfSDavid du Colombier 			q = basename;
1787dd7cddfSDavid du Colombier 		if(p = strchr(q, '.'))
1797dd7cddfSDavid du Colombier 			*p = 0;
1807dd7cddfSDavid du Colombier 
1817dd7cddfSDavid du Colombier 		memset(name, 0, sizeof name);
1827dd7cddfSDavid du Colombier 		snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
1837dd7cddfSDavid du Colombier 		if(access(name, 0) >= 0) {
1847dd7cddfSDavid du Colombier 			strcat(name, "XXXX");
1857dd7cddfSDavid du Colombier 			mktemp(name);
1867dd7cddfSDavid du Colombier 		}
1877dd7cddfSDavid du Colombier 		if(access(name, 0) >= 0)
1887dd7cddfSDavid du Colombier 			return "couldn't think of a name for bitmap";
1897dd7cddfSDavid du Colombier 	} else {
1907dd7cddfSDavid du Colombier 		strcpy(name, "bitXXXX");
1917dd7cddfSDavid du Colombier 		mktemp(name);
1927dd7cddfSDavid du Colombier 		if(access(name, 0) >= 0)
1937dd7cddfSDavid du Colombier 			return "couldn't think of a name for bitmap";
1947dd7cddfSDavid du Colombier 	}
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 	if((fd = create(name, OWRITE, 0666)) < 0) {
1977dd7cddfSDavid du Colombier 		snprint(result, sizeof result, "cannot create %s: %r", name);
1987dd7cddfSDavid du Colombier 		return result;
1997dd7cddfSDavid du Colombier 	}
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier 	if(writeimage(fd, im, 0) < 0) {
2027dd7cddfSDavid du Colombier 		snprint(result, sizeof result, "cannot writeimage: %r");
2037dd7cddfSDavid du Colombier 		close(fd);
2047dd7cddfSDavid du Colombier 		return result;
2057dd7cddfSDavid du Colombier 	}
2067dd7cddfSDavid du Colombier 	close(fd);
2077dd7cddfSDavid du Colombier 
2087dd7cddfSDavid du Colombier 	snprint(result, sizeof result, "wrote %s", name);
2097dd7cddfSDavid du Colombier 	return result;
2107dd7cddfSDavid du Colombier }
2117dd7cddfSDavid du Colombier 
2127dd7cddfSDavid du Colombier static void translate(Point);
2137dd7cddfSDavid du Colombier 
2147dd7cddfSDavid du Colombier static int
showdata(Plumbmsg * msg)2157dd7cddfSDavid du Colombier showdata(Plumbmsg *msg)
2167dd7cddfSDavid du Colombier {
2177dd7cddfSDavid du Colombier 	char *s;
2187dd7cddfSDavid du Colombier 
2197dd7cddfSDavid du Colombier 	s = plumblookup(msg->attr, "action");
2207dd7cddfSDavid du Colombier 	return s && strcmp(s, "showdata")==0;
2217dd7cddfSDavid du Colombier }
2227dd7cddfSDavid du Colombier 
2230ceffd3cSDavid du Colombier static int
plumbquit(Plumbmsg * msg)2240ceffd3cSDavid du Colombier plumbquit(Plumbmsg *msg)
2250ceffd3cSDavid du Colombier {
2260ceffd3cSDavid du Colombier 	char *s;
2270ceffd3cSDavid du Colombier 
2280ceffd3cSDavid du Colombier 	s = plumblookup(msg->attr, "action");
2290ceffd3cSDavid du Colombier 	return s && strcmp(s, "quit")==0;
2300ceffd3cSDavid du Colombier }
2310ceffd3cSDavid du Colombier 
23208fd2d13SDavid du Colombier /* correspond to entries in miditems[] below,
23308fd2d13SDavid du Colombier  * changing one means you need to change
23408fd2d13SDavid du Colombier  */
23508fd2d13SDavid du Colombier enum{
23608fd2d13SDavid du Colombier 	Restore = 0,
23708fd2d13SDavid du Colombier 	Zin,
23808fd2d13SDavid du Colombier 	Fit,
23908fd2d13SDavid du Colombier 	Rot,
24008fd2d13SDavid du Colombier 	Upside,
24108fd2d13SDavid du Colombier 	Empty1,
24208fd2d13SDavid du Colombier 	Next,
24308fd2d13SDavid du Colombier 	Prev,
2447def40e1SDavid du Colombier 	Zerox,
24508fd2d13SDavid du Colombier 	Empty2,
24608fd2d13SDavid du Colombier 	Reverse,
24708fd2d13SDavid du Colombier 	Del,
24808fd2d13SDavid du Colombier 	Write,
24908fd2d13SDavid du Colombier 	Empty3,
25008fd2d13SDavid du Colombier 	Exit,
25108fd2d13SDavid du Colombier };
25208fd2d13SDavid du Colombier 
2537dd7cddfSDavid du Colombier void
viewer(Document * dd)2547dd7cddfSDavid du Colombier viewer(Document *dd)
2557dd7cddfSDavid du Colombier {
2567dd7cddfSDavid du Colombier 	int i, fd, n, oldpage;
2577dd7cddfSDavid du Colombier 	int nxt;
25808fd2d13SDavid du Colombier 	Menu menu, midmenu;
2597dd7cddfSDavid du Colombier 	Mouse m;
2607dd7cddfSDavid du Colombier 	Event e;
2617dd7cddfSDavid du Colombier 	Point dxy, oxy, xy0;
2627dd7cddfSDavid du Colombier 	Rectangle r;
26308fd2d13SDavid du Colombier 	Image *tmp;
26408fd2d13SDavid du Colombier 	static char *fwditems[] = { "this page", "next page", "exit", 0 };
26508fd2d13SDavid du Colombier  	static char *miditems[] = {
26608fd2d13SDavid du Colombier  		"orig size",
26708fd2d13SDavid du Colombier  		"zoom in",
26808fd2d13SDavid du Colombier  		"fit window",
26908fd2d13SDavid du Colombier  		"rotate 90",
27008fd2d13SDavid du Colombier  		"upside down",
27108fd2d13SDavid du Colombier  		"",
27208fd2d13SDavid du Colombier  		"next",
27308fd2d13SDavid du Colombier  		"prev",
2747def40e1SDavid du Colombier 		"zerox",
27508fd2d13SDavid du Colombier  		"",
27608fd2d13SDavid du Colombier  		"reverse",
27708fd2d13SDavid du Colombier  		"discard",
27808fd2d13SDavid du Colombier  		"write",
27908fd2d13SDavid du Colombier  		"",
28008fd2d13SDavid du Colombier  		"quit",
28108fd2d13SDavid du Colombier  		0
28208fd2d13SDavid du Colombier  	};
2837dd7cddfSDavid du Colombier 	char *s;
2847dd7cddfSDavid du Colombier 	enum { Eplumb = 4 };
2857dd7cddfSDavid du Colombier 	Plumbmsg *pm;
2867dd7cddfSDavid du Colombier 
2877dd7cddfSDavid du Colombier 	doc = dd;    /* save global for menuhit */
2887dd7cddfSDavid du Colombier 	ul = screen->r.min;
2897dd7cddfSDavid du Colombier 	einit(Emouse|Ekeyboard);
2907dd7cddfSDavid du Colombier 	if(doc->addpage != nil)
2917dd7cddfSDavid du Colombier 		eplumb(Eplumb, "image");
2927dd7cddfSDavid du Colombier 
2937dd7cddfSDavid du Colombier 	esetcursor(&reading);
2947dd7cddfSDavid du Colombier 	r.min = ZP;
2957dd7cddfSDavid du Colombier 
2967dd7cddfSDavid du Colombier 	/*
2977dd7cddfSDavid du Colombier 	 * im is a global pointer to the current image.
2987dd7cddfSDavid du Colombier 	 * eventually, i think we will have a layer between
2997dd7cddfSDavid du Colombier 	 * the display routines and the ps/pdf/whatever routines
3007dd7cddfSDavid du Colombier 	 * to perhaps cache and handle images of different
3017dd7cddfSDavid du Colombier 	 * sizes, etc.
3027dd7cddfSDavid du Colombier 	 */
3037dd7cddfSDavid du Colombier 	im = 0;
3047dd7cddfSDavid du Colombier 	page = reverse ? doc->npage-1 : 0;
3057dd7cddfSDavid du Colombier 
3067dd7cddfSDavid du Colombier 	if(doc->fwdonly) {
3077dd7cddfSDavid du Colombier 		menu.item = fwditems;
3087dd7cddfSDavid du Colombier 		menu.gen = 0;
3097dd7cddfSDavid du Colombier 		menu.lasthit = 0;
3107dd7cddfSDavid du Colombier 	} else {
3117dd7cddfSDavid du Colombier 		menu.item = 0;
3127dd7cddfSDavid du Colombier 		menu.gen = menugen;
3137dd7cddfSDavid du Colombier 		menu.lasthit = 0;
3147dd7cddfSDavid du Colombier 	}
3157dd7cddfSDavid du Colombier 
31608fd2d13SDavid du Colombier 	midmenu.item = miditems;
31708fd2d13SDavid du Colombier 	midmenu.gen = 0;
31808fd2d13SDavid du Colombier 	midmenu.lasthit = Next;
31908fd2d13SDavid du Colombier 
320*ad6ca847SDavid du Colombier 	if(doc->docname != nil)
321*ad6ca847SDavid du Colombier 		setlabel(doc->docname);
3227dd7cddfSDavid du Colombier 	showpage(page, &menu);
3237dd7cddfSDavid du Colombier 	esetcursor(nil);
3247dd7cddfSDavid du Colombier 
3257dd7cddfSDavid du Colombier 	nxt = 0;
3267dd7cddfSDavid du Colombier 	for(;;) {
3277dd7cddfSDavid du Colombier 		/*
3287dd7cddfSDavid du Colombier 		 * throughout, if doc->fwdonly is set, we restrict the functionality
3297dd7cddfSDavid du Colombier 		 * a fair amount.  we don't care about doc->npage anymore, and
3307dd7cddfSDavid du Colombier 		 * all that can be done is select the next page.
3317dd7cddfSDavid du Colombier 		 */
332493edcedSDavid du Colombier 		unlockdisplay(display);
333493edcedSDavid du Colombier 		i = eread(Emouse|Ekeyboard|Eplumb, &e);
334493edcedSDavid du Colombier 		lockdisplay(display);
335493edcedSDavid du Colombier 		switch(i){
3367dd7cddfSDavid du Colombier 		case Ekeyboard:
3377dd7cddfSDavid du Colombier 			if(e.kbdc <= 0xFF && isdigit(e.kbdc)) {
3387dd7cddfSDavid du Colombier 				nxt = nxt*10+e.kbdc-'0';
3397dd7cddfSDavid du Colombier 				break;
3407dd7cddfSDavid du Colombier 			} else if(e.kbdc != '\n')
3417dd7cddfSDavid du Colombier 				nxt = 0;
3427dd7cddfSDavid du Colombier 			switch(e.kbdc) {
3437dd7cddfSDavid du Colombier 			case 'r':	/* reverse page order */
3447dd7cddfSDavid du Colombier 				if(doc->fwdonly)
3457dd7cddfSDavid du Colombier 					break;
3467dd7cddfSDavid du Colombier 				reverse = !reverse;
3477dd7cddfSDavid du Colombier 				menu.lasthit = doc->npage-1-menu.lasthit;
3487dd7cddfSDavid du Colombier 
3497dd7cddfSDavid du Colombier 				/*
3507dd7cddfSDavid du Colombier 				 * the theory is that if we are reversing the
3517dd7cddfSDavid du Colombier 				 * document order and are on the first or last
3527dd7cddfSDavid du Colombier 				 * page then we're just starting and really want
3537dd7cddfSDavid du Colombier 		 	 	 * to view the other end.  maybe the if
3547dd7cddfSDavid du Colombier 				 * should be dropped and this should happen always.
3557dd7cddfSDavid du Colombier 				 */
3567dd7cddfSDavid du Colombier 				if(page == 0 || page == doc->npage-1) {
3577dd7cddfSDavid du Colombier 					page = doc->npage-1-page;
3587dd7cddfSDavid du Colombier 					showpage(page, &menu);
3597dd7cddfSDavid du Colombier 				}
3607dd7cddfSDavid du Colombier 				break;
3617dd7cddfSDavid du Colombier 			case 'w':	/* write bitmap of current screen */
3627dd7cddfSDavid du Colombier 				esetcursor(&reading);
3637dd7cddfSDavid du Colombier 				s = writebitmap();
3647dd7cddfSDavid du Colombier 				if(s)
3657dd7cddfSDavid du Colombier 					string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
3667dd7cddfSDavid du Colombier 						display->defaultfont, s);
3677dd7cddfSDavid du Colombier 				esetcursor(nil);
3687dd7cddfSDavid du Colombier 				flushimage(display, 1);
3697dd7cddfSDavid du Colombier 				break;
3707dd7cddfSDavid du Colombier 			case 'd':	/* remove image from working set */
3717dd7cddfSDavid du Colombier 				if(doc->rmpage && page < doc->npage) {
3727dd7cddfSDavid du Colombier 					if(doc->rmpage(doc, page) >= 0) {
3737dd7cddfSDavid du Colombier 						if(doc->npage < 0)
3747dd7cddfSDavid du Colombier 							wexits(0);
3757dd7cddfSDavid du Colombier 						if(page >= doc->npage)
3767dd7cddfSDavid du Colombier 							page = doc->npage-1;
3777dd7cddfSDavid du Colombier 						showpage(page, &menu);
3787dd7cddfSDavid du Colombier 					}
3797dd7cddfSDavid du Colombier 				}
3807dd7cddfSDavid du Colombier 				break;
3817dd7cddfSDavid du Colombier 			case 'q':
3827dd7cddfSDavid du Colombier 			case 0x04: /* ctrl-d */
3837dd7cddfSDavid du Colombier 				wexits(0);
3847dd7cddfSDavid du Colombier 			case 'u':
3857dd7cddfSDavid du Colombier 				if(im==nil)
3867dd7cddfSDavid du Colombier 					break;
387a7529a1dSDavid du Colombier 				angle = (angle+180) % 360;
388493edcedSDavid du Colombier 				showpage(page, &menu);
3897dd7cddfSDavid du Colombier 				break;
3907dd7cddfSDavid du Colombier 			case '-':
3917dd7cddfSDavid du Colombier 			case '\b':
392223a736eSDavid du Colombier 			case Kleft:
3937dd7cddfSDavid du Colombier 				if(page > 0 && !doc->fwdonly) {
3947dd7cddfSDavid du Colombier 					--page;
3957dd7cddfSDavid du Colombier 					showpage(page, &menu);
3967dd7cddfSDavid du Colombier 				}
3977dd7cddfSDavid du Colombier 				break;
3987dd7cddfSDavid du Colombier 			case '\n':
3997dd7cddfSDavid du Colombier 				if(nxt) {
4007dd7cddfSDavid du Colombier 					nxt--;
4017dd7cddfSDavid du Colombier 					if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
4027dd7cddfSDavid du Colombier 						showpage(page=nxt, &menu);
4037dd7cddfSDavid du Colombier 					nxt = 0;
4047dd7cddfSDavid du Colombier 					break;
4057dd7cddfSDavid du Colombier 				}
40659cc4ca5SDavid du Colombier 				goto Gotonext;
407223a736eSDavid du Colombier 			case Kright:
408223a736eSDavid du Colombier 			case ' ':
40959cc4ca5SDavid du Colombier 			Gotonext:
4107dd7cddfSDavid du Colombier 				if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
4117dd7cddfSDavid du Colombier 					wexits(0);
4127dd7cddfSDavid du Colombier 				showpage(page, &menu);
4137dd7cddfSDavid du Colombier 				break;
41459cc4ca5SDavid du Colombier 
41559cc4ca5SDavid du Colombier 			/*
41659cc4ca5SDavid du Colombier 			 * The upper y coordinate of the image is at ul.y in screen->r.
41759cc4ca5SDavid du Colombier 			 * Panning up means moving the upper left corner down.  If the
41859cc4ca5SDavid du Colombier 			 * upper left corner is currently visible, we need to go back a page.
41959cc4ca5SDavid du Colombier 			 */
42059cc4ca5SDavid du Colombier 			case Kup:
42159cc4ca5SDavid du Colombier 				if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
42259cc4ca5SDavid du Colombier 					if(page > 0 && !doc->fwdonly){
42359cc4ca5SDavid du Colombier 						--page;
42459cc4ca5SDavid du Colombier 						showbottom = 1;
42559cc4ca5SDavid du Colombier 						showpage(page, &menu);
42659cc4ca5SDavid du Colombier 					}
42759cc4ca5SDavid du Colombier 				} else {
42859cc4ca5SDavid du Colombier 					i = Dy(screen->r)/2;
42959cc4ca5SDavid du Colombier 					if(i > 10)
43059cc4ca5SDavid du Colombier 						i -= 10;
43159cc4ca5SDavid du Colombier 					if(i+ul.y > screen->r.min.y)
43259cc4ca5SDavid du Colombier 						i = screen->r.min.y - ul.y;
43359cc4ca5SDavid du Colombier 					translate(Pt(0, i));
43459cc4ca5SDavid du Colombier 				}
43559cc4ca5SDavid du Colombier 				break;
43659cc4ca5SDavid du Colombier 
43759cc4ca5SDavid du Colombier 			/*
43859cc4ca5SDavid du Colombier 			 * If the lower y coordinate is on the screen, we go to the next page.
43959cc4ca5SDavid du Colombier 			 * The lower y coordinate is at ul.y + Dy(im->r).
44059cc4ca5SDavid du Colombier 			 */
44159cc4ca5SDavid du Colombier 			case Kdown:
44259cc4ca5SDavid du Colombier 				i = ul.y + Dy(im->r);
44359cc4ca5SDavid du Colombier 				if(screen->r.min.y <= i && i <= screen->r.max.y){
44459cc4ca5SDavid du Colombier 					ul.y = screen->r.min.y;
44559cc4ca5SDavid du Colombier 					goto Gotonext;
44659cc4ca5SDavid du Colombier 				} else {
44759cc4ca5SDavid du Colombier 					i = -Dy(screen->r)/2;
44859cc4ca5SDavid du Colombier 					if(i < -10)
44959cc4ca5SDavid du Colombier 						i += 10;
45059cc4ca5SDavid du Colombier 					if(i+ul.y+Dy(im->r) <= screen->r.max.y)
45159cc4ca5SDavid du Colombier 						i = screen->r.max.y - Dy(im->r) - ul.y - 1;
45259cc4ca5SDavid du Colombier 					translate(Pt(0, i));
45359cc4ca5SDavid du Colombier 				}
45459cc4ca5SDavid du Colombier 				break;
455223a736eSDavid du Colombier 			default:
456223a736eSDavid du Colombier 				esetcursor(&query);
457223a736eSDavid du Colombier 				sleep(1000);
458223a736eSDavid du Colombier 				esetcursor(nil);
459223a736eSDavid du Colombier 				break;
4607dd7cddfSDavid du Colombier 			}
4617dd7cddfSDavid du Colombier 			break;
4627dd7cddfSDavid du Colombier 
4637dd7cddfSDavid du Colombier 		case Emouse:
4647dd7cddfSDavid du Colombier 			m = e.mouse;
4657dd7cddfSDavid du Colombier 			switch(m.buttons){
4667dd7cddfSDavid du Colombier 			case Left:
4677dd7cddfSDavid du Colombier 				oxy = m.xy;
4687dd7cddfSDavid du Colombier 				xy0 = oxy;
4697dd7cddfSDavid du Colombier 				do {
4707dd7cddfSDavid du Colombier 					dxy = subpt(m.xy, oxy);
4717dd7cddfSDavid du Colombier 					oxy = m.xy;
4727dd7cddfSDavid du Colombier 					translate(dxy);
473493edcedSDavid du Colombier 					unlockdisplay(display);
4747dd7cddfSDavid du Colombier 					m = emouse();
475493edcedSDavid du Colombier 					lockdisplay(display);
4767dd7cddfSDavid du Colombier 				} while(m.buttons == Left);
4777dd7cddfSDavid du Colombier 				if(m.buttons) {
4787dd7cddfSDavid du Colombier 					dxy = subpt(xy0, oxy);
4797dd7cddfSDavid du Colombier 					translate(dxy);
4807dd7cddfSDavid du Colombier 				}
4817dd7cddfSDavid du Colombier 				break;
4827dd7cddfSDavid du Colombier 
4837dd7cddfSDavid du Colombier 			case Middle:
4847dd7cddfSDavid du Colombier 				if(doc->npage == 0)
4857dd7cddfSDavid du Colombier 					break;
4867dd7cddfSDavid du Colombier 
487493edcedSDavid du Colombier 				unlockdisplay(display);
48808fd2d13SDavid du Colombier 				n = emenuhit(Middle, &m, &midmenu);
489493edcedSDavid du Colombier 				lockdisplay(display);
49008fd2d13SDavid du Colombier 				if(n == -1)
49108fd2d13SDavid du Colombier 					break;
49208fd2d13SDavid du Colombier 				switch(n){
49308fd2d13SDavid du Colombier 				case Next: 	/* next */
4947dd7cddfSDavid du Colombier 					if(reverse)
4957dd7cddfSDavid du Colombier 						page--;
4967dd7cddfSDavid du Colombier 					else
4977dd7cddfSDavid du Colombier 						page++;
49808fd2d13SDavid du Colombier 					if(page < 0) {
49908fd2d13SDavid du Colombier 						if(reverse) return;
50008fd2d13SDavid du Colombier 						else page = 0;
50108fd2d13SDavid du Colombier 					}
5027dd7cddfSDavid du Colombier 
50308fd2d13SDavid du Colombier 					if((page >= doc->npage) && !doc->fwdonly)
5047dd7cddfSDavid du Colombier 						return;
5057dd7cddfSDavid du Colombier 
5067dd7cddfSDavid du Colombier 					showpage(page, &menu);
5077dd7cddfSDavid du Colombier 					nxt = 0;
5087dd7cddfSDavid du Colombier 					break;
50908fd2d13SDavid du Colombier 				case Prev:	/* prev */
51008fd2d13SDavid du Colombier 					if(reverse)
51108fd2d13SDavid du Colombier 						page++;
51208fd2d13SDavid du Colombier 					else
51308fd2d13SDavid du Colombier 						page--;
51408fd2d13SDavid du Colombier 					if(page < 0) {
51508fd2d13SDavid du Colombier 						if(reverse) return;
51608fd2d13SDavid du Colombier 						else page = 0;
51708fd2d13SDavid du Colombier 					}
51808fd2d13SDavid du Colombier 
51908fd2d13SDavid du Colombier 					if((page >= doc->npage) && !doc->fwdonly && !reverse)
52008fd2d13SDavid du Colombier 						return;
52108fd2d13SDavid du Colombier 
52208fd2d13SDavid du Colombier 					showpage(page, &menu);
52308fd2d13SDavid du Colombier 					nxt = 0;
52408fd2d13SDavid du Colombier 					break;
5257def40e1SDavid du Colombier 				case Zerox:	/* prev */
5267def40e1SDavid du Colombier 					zerox();
52708fd2d13SDavid du Colombier 					break;
52808fd2d13SDavid du Colombier 				case Zin:	/* zoom in */
52908fd2d13SDavid du Colombier 					{
53008fd2d13SDavid du Colombier 						double delta;
53108fd2d13SDavid du Colombier 						Rectangle r;
53208fd2d13SDavid du Colombier 
53308fd2d13SDavid du Colombier 						r = egetrect(Middle, &m);
53408fd2d13SDavid du Colombier 						if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
53508fd2d13SDavid du Colombier 							Dx(r) == 0 || Dy(r) == 0)
53608fd2d13SDavid du Colombier 							break;
53708fd2d13SDavid du Colombier 						/* use the smaller side to expand */
53808fd2d13SDavid du Colombier 						if(Dx(r) < Dy(r))
53908fd2d13SDavid du Colombier 							delta = (double)Dx(im->r)/(double)Dx(r);
54008fd2d13SDavid du Colombier 						else
54108fd2d13SDavid du Colombier 							delta = (double)Dy(im->r)/(double)Dy(r);
54208fd2d13SDavid du Colombier 
54308fd2d13SDavid du Colombier 						esetcursor(&reading);
5445316891fSDavid du Colombier 						tmp = xallocimage(display,
54508fd2d13SDavid du Colombier 								Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)),
54608fd2d13SDavid du Colombier 								im->chan, 0, DBlack);
5475316891fSDavid du Colombier 						if(tmp == nil) {
5485316891fSDavid du Colombier 							fprint(2, "out of memory during zoom: %r\n");
5495316891fSDavid du Colombier 							wexits("memory");
5505316891fSDavid du Colombier 						}
55108fd2d13SDavid du Colombier 						resample(im, tmp);
55208fd2d13SDavid du Colombier 						im = tmp;
553493edcedSDavid du Colombier 						delayfreeimage(tmp);
55408fd2d13SDavid du Colombier 						esetcursor(nil);
55508fd2d13SDavid du Colombier 						ul = screen->r.min;
55608fd2d13SDavid du Colombier 						redraw(screen);
55708fd2d13SDavid du Colombier 						flushimage(display, 1);
55808fd2d13SDavid du Colombier 						break;
55908fd2d13SDavid du Colombier 					}
56008fd2d13SDavid du Colombier 				case Fit:	/* fit */
56108fd2d13SDavid du Colombier 					{
56208fd2d13SDavid du Colombier 						double delta;
56308fd2d13SDavid du Colombier 						Rectangle r;
56408fd2d13SDavid du Colombier 
56508fd2d13SDavid du Colombier 						delta = (double)Dx(screen->r)/(double)Dx(im->r);
56608fd2d13SDavid du Colombier 						if((double)Dy(im->r)*delta > Dy(screen->r))
56708fd2d13SDavid du Colombier 							delta = (double)Dy(screen->r)/(double)Dy(im->r);
56808fd2d13SDavid du Colombier 
56908fd2d13SDavid du Colombier 						r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
57008fd2d13SDavid du Colombier 						esetcursor(&reading);
5715316891fSDavid du Colombier 						tmp = xallocimage(display, r, im->chan, 0, DBlack);
5725316891fSDavid du Colombier 						if(tmp == nil) {
5735316891fSDavid du Colombier 							fprint(2, "out of memory during fit: %r\n");
5745316891fSDavid du Colombier 							wexits("memory");
5755316891fSDavid du Colombier 						}
57608fd2d13SDavid du Colombier 						resample(im, tmp);
57708fd2d13SDavid du Colombier 						im = tmp;
578493edcedSDavid du Colombier 						delayfreeimage(tmp);
57908fd2d13SDavid du Colombier 						esetcursor(nil);
58008fd2d13SDavid du Colombier 						ul = screen->r.min;
58108fd2d13SDavid du Colombier 						redraw(screen);
58208fd2d13SDavid du Colombier 						flushimage(display, 1);
58308fd2d13SDavid du Colombier 						break;
58408fd2d13SDavid du Colombier 					}
58508fd2d13SDavid du Colombier 				case Rot:	/* rotate 90 */
586a7529a1dSDavid du Colombier 					angle = (angle+90) % 360;
587493edcedSDavid du Colombier 					showpage(page, &menu);
58808fd2d13SDavid du Colombier 					break;
58908fd2d13SDavid du Colombier 				case Upside: 	/* upside-down */
590a7529a1dSDavid du Colombier 					angle = (angle+180) % 360;
591493edcedSDavid du Colombier 					showpage(page, &menu);
59208fd2d13SDavid du Colombier 					break;
59308fd2d13SDavid du Colombier 				case Restore:	/* restore */
59408fd2d13SDavid du Colombier 					showpage(page, &menu);
59508fd2d13SDavid du Colombier 					break;
59608fd2d13SDavid du Colombier 				case Reverse:	/* reverse */
59708fd2d13SDavid du Colombier 					if(doc->fwdonly)
59808fd2d13SDavid du Colombier 						break;
59908fd2d13SDavid du Colombier 					reverse = !reverse;
60008fd2d13SDavid du Colombier 					menu.lasthit = doc->npage-1-menu.lasthit;
60108fd2d13SDavid du Colombier 
60208fd2d13SDavid du Colombier 					if(page == 0 || page == doc->npage-1) {
60308fd2d13SDavid du Colombier 						page = doc->npage-1-page;
60408fd2d13SDavid du Colombier 						showpage(page, &menu);
60508fd2d13SDavid du Colombier 					}
60608fd2d13SDavid du Colombier 					break;
60708fd2d13SDavid du Colombier 				case Write: /* write */
60808fd2d13SDavid du Colombier 					esetcursor(&reading);
60908fd2d13SDavid du Colombier 					s = writebitmap();
61008fd2d13SDavid du Colombier 					if(s)
61108fd2d13SDavid du Colombier 						string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
61208fd2d13SDavid du Colombier 							display->defaultfont, s);
61308fd2d13SDavid du Colombier 					esetcursor(nil);
61408fd2d13SDavid du Colombier 					flushimage(display, 1);
61508fd2d13SDavid du Colombier 					break;
61608fd2d13SDavid du Colombier 				case Del: /* delete */
61708fd2d13SDavid du Colombier 					if(doc->rmpage && page < doc->npage) {
61808fd2d13SDavid du Colombier 						if(doc->rmpage(doc, page) >= 0) {
61908fd2d13SDavid du Colombier 							if(doc->npage < 0)
62008fd2d13SDavid du Colombier 								wexits(0);
62108fd2d13SDavid du Colombier 							if(page >= doc->npage)
62208fd2d13SDavid du Colombier 								page = doc->npage-1;
62308fd2d13SDavid du Colombier 							showpage(page, &menu);
62408fd2d13SDavid du Colombier 						}
62508fd2d13SDavid du Colombier 					}
62608fd2d13SDavid du Colombier 					break;
62708fd2d13SDavid du Colombier 				case Exit:	/* exit */
62808fd2d13SDavid du Colombier 					return;
62908fd2d13SDavid du Colombier 				case Empty1:
63008fd2d13SDavid du Colombier 				case Empty2:
63108fd2d13SDavid du Colombier 				case Empty3:
63208fd2d13SDavid du Colombier 					break;
63308fd2d13SDavid du Colombier 
63408fd2d13SDavid du Colombier 				};
63508fd2d13SDavid du Colombier 
63608fd2d13SDavid du Colombier 
6377dd7cddfSDavid du Colombier 
6387dd7cddfSDavid du Colombier 			case Right:
6397dd7cddfSDavid du Colombier 				if(doc->npage == 0)
6407dd7cddfSDavid du Colombier 					break;
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier 				oldpage = page;
643493edcedSDavid du Colombier 				unlockdisplay(display);
6447dd7cddfSDavid du Colombier 				n = emenuhit(RMenu, &m, &menu);
645493edcedSDavid du Colombier 				lockdisplay(display);
6467dd7cddfSDavid du Colombier 				if(n == -1)
6477dd7cddfSDavid du Colombier 					break;
6487dd7cddfSDavid du Colombier 
6497dd7cddfSDavid du Colombier 				if(doc->fwdonly) {
6507dd7cddfSDavid du Colombier 					switch(n){
6517dd7cddfSDavid du Colombier 					case 0:	/* this page */
6527dd7cddfSDavid du Colombier 						break;
6537dd7cddfSDavid du Colombier 					case 1:	/* next page */
6547dd7cddfSDavid du Colombier 						showpage(++page, &menu);
6557dd7cddfSDavid du Colombier 						break;
6567dd7cddfSDavid du Colombier 					case 2:	/* exit */
6577dd7cddfSDavid du Colombier 						return;
6587dd7cddfSDavid du Colombier 					}
6597dd7cddfSDavid du Colombier 					break;
6607dd7cddfSDavid du Colombier 				}
6617dd7cddfSDavid du Colombier 
6627dd7cddfSDavid du Colombier 				if(n == doc->npage)
6637dd7cddfSDavid du Colombier 					return;
6647dd7cddfSDavid du Colombier 				else
6657dd7cddfSDavid du Colombier 					page = reverse ? doc->npage-1-n : n;
6667dd7cddfSDavid du Colombier 
6677dd7cddfSDavid du Colombier 				if(oldpage != page)
6687dd7cddfSDavid du Colombier 					showpage(page, &menu);
6697dd7cddfSDavid du Colombier 				nxt = 0;
6707dd7cddfSDavid du Colombier 				break;
6717dd7cddfSDavid du Colombier 			}
6727dd7cddfSDavid du Colombier 			break;
6737dd7cddfSDavid du Colombier 
6747dd7cddfSDavid du Colombier 		case Eplumb:
6757dd7cddfSDavid du Colombier 			pm = e.v;
6767dd7cddfSDavid du Colombier 			if(pm->ndata <= 0){
6777dd7cddfSDavid du Colombier 				plumbfree(pm);
6787dd7cddfSDavid du Colombier 				break;
6797dd7cddfSDavid du Colombier 			}
6800ceffd3cSDavid du Colombier 			if(plumbquit(pm))
6810ceffd3cSDavid du Colombier 				exits(nil);
6827dd7cddfSDavid du Colombier 			if(showdata(pm)) {
6837dd7cddfSDavid du Colombier 				s = estrdup("/tmp/pageplumbXXXXXXX");
6847dd7cddfSDavid du Colombier 				fd = opentemp(s);
6857dd7cddfSDavid du Colombier 				write(fd, pm->data, pm->ndata);
6867dd7cddfSDavid du Colombier 				/* lose fd reference on purpose; the file is open ORCLOSE */
6877dd7cddfSDavid du Colombier 			} else if(pm->data[0] == '/') {
6887dd7cddfSDavid du Colombier 				s = estrdup(pm->data);
6897dd7cddfSDavid du Colombier 			} else {
6907dd7cddfSDavid du Colombier 				s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
6917dd7cddfSDavid du Colombier 				sprint(s, "%s/%s", pm->wdir, pm->data);
6927dd7cddfSDavid du Colombier 				cleanname(s);
6937dd7cddfSDavid du Colombier 			}
6947dd7cddfSDavid du Colombier 			if((i = doc->addpage(doc, s)) >= 0) {
6957dd7cddfSDavid du Colombier 				page = i;
6969a747e4fSDavid du Colombier 				unhide();
6977dd7cddfSDavid du Colombier 				showpage(page, &menu);
6987dd7cddfSDavid du Colombier 			}
6997dd7cddfSDavid du Colombier 			free(s);
7007dd7cddfSDavid du Colombier 			plumbfree(pm);
7017dd7cddfSDavid du Colombier 			break;
7027dd7cddfSDavid du Colombier 		}
7037dd7cddfSDavid du Colombier 	}
7047dd7cddfSDavid du Colombier }
7057dd7cddfSDavid du Colombier 
7067dd7cddfSDavid du Colombier Image *gray;
7077dd7cddfSDavid du Colombier 
7087dd7cddfSDavid du Colombier /*
7097dd7cddfSDavid du Colombier  * A draw operation that touches only the area contained in bot but not in top.
7107dd7cddfSDavid du Colombier  * mp and sp get aligned with bot.min.
7117dd7cddfSDavid du Colombier  */
7127dd7cddfSDavid du Colombier static void
gendrawdiff(Image * dst,Rectangle bot,Rectangle top,Image * src,Point sp,Image * mask,Point mp,int op)7137dd7cddfSDavid du Colombier gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
7146b6b9ac8SDavid du Colombier 	Image *src, Point sp, Image *mask, Point mp, int op)
7157dd7cddfSDavid du Colombier {
7167dd7cddfSDavid du Colombier 	Rectangle r;
7177dd7cddfSDavid du Colombier 	Point origin;
7187dd7cddfSDavid du Colombier 	Point delta;
7197dd7cddfSDavid du Colombier 
72008fd2d13SDavid du Colombier 	USED(op);
72108fd2d13SDavid du Colombier 
7227dd7cddfSDavid du Colombier 	if(Dx(bot)*Dy(bot) == 0)
7237dd7cddfSDavid du Colombier 		return;
7247dd7cddfSDavid du Colombier 
7257dd7cddfSDavid du Colombier 	/* no points in bot - top */
7267dd7cddfSDavid du Colombier 	if(rectinrect(bot, top))
7277dd7cddfSDavid du Colombier 		return;
7287dd7cddfSDavid du Colombier 
7297dd7cddfSDavid du Colombier 	/* bot - top ≡ bot */
7307dd7cddfSDavid du Colombier 	if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
7316b6b9ac8SDavid du Colombier 		gendrawop(dst, bot, src, sp, mask, mp, op);
7327dd7cddfSDavid du Colombier 		return;
7337dd7cddfSDavid du Colombier 	}
7347dd7cddfSDavid du Colombier 
7357dd7cddfSDavid du Colombier 	origin = bot.min;
7367dd7cddfSDavid du Colombier 	/* split bot into rectangles that don't intersect top */
7377dd7cddfSDavid du Colombier 	/* left side */
7387dd7cddfSDavid du Colombier 	if(bot.min.x < top.min.x){
7397dd7cddfSDavid du Colombier 		r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
7407dd7cddfSDavid du Colombier 		delta = subpt(r.min, origin);
7416b6b9ac8SDavid du Colombier 		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
7427dd7cddfSDavid du Colombier 		bot.min.x = top.min.x;
7437dd7cddfSDavid du Colombier 	}
7447dd7cddfSDavid du Colombier 
7457dd7cddfSDavid du Colombier 	/* right side */
7467dd7cddfSDavid du Colombier 	if(bot.max.x > top.max.x){
7477dd7cddfSDavid du Colombier 		r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
7487dd7cddfSDavid du Colombier 		delta = subpt(r.min, origin);
7496b6b9ac8SDavid du Colombier 		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
7507dd7cddfSDavid du Colombier 		bot.max.x = top.max.x;
7517dd7cddfSDavid du Colombier 	}
7527dd7cddfSDavid du Colombier 
7537dd7cddfSDavid du Colombier 	/* top */
7547dd7cddfSDavid du Colombier 	if(bot.min.y < top.min.y){
7557dd7cddfSDavid du Colombier 		r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
7567dd7cddfSDavid du Colombier 		delta = subpt(r.min, origin);
7576b6b9ac8SDavid du Colombier 		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
7587dd7cddfSDavid du Colombier 		bot.min.y = top.min.y;
7597dd7cddfSDavid du Colombier 	}
7607dd7cddfSDavid du Colombier 
7617dd7cddfSDavid du Colombier 	/* bottom */
7627dd7cddfSDavid du Colombier 	if(bot.max.y > top.max.y){
7637dd7cddfSDavid du Colombier 		r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
7647dd7cddfSDavid du Colombier 		delta = subpt(r.min, origin);
7656b6b9ac8SDavid du Colombier 		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
7667dd7cddfSDavid du Colombier 		bot.max.y = top.max.y;
7677dd7cddfSDavid du Colombier 	}
7687dd7cddfSDavid du Colombier }
7697dd7cddfSDavid du Colombier 
7707dd7cddfSDavid du Colombier static void
drawdiff(Image * dst,Rectangle bot,Rectangle top,Image * src,Image * mask,Point p,int op)7716b6b9ac8SDavid du Colombier drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
7727dd7cddfSDavid du Colombier {
7736b6b9ac8SDavid du Colombier 	gendrawdiff(dst, bot, top, src, p, mask, p, op);
7747dd7cddfSDavid du Colombier }
7757dd7cddfSDavid du Colombier 
7767dd7cddfSDavid du Colombier /*
7777dd7cddfSDavid du Colombier  * Translate the image in the window by delta.
7787dd7cddfSDavid du Colombier  */
7797dd7cddfSDavid du Colombier static void
translate(Point delta)7807dd7cddfSDavid du Colombier translate(Point delta)
7817dd7cddfSDavid du Colombier {
7827dd7cddfSDavid du Colombier 	Point u;
7837dd7cddfSDavid du Colombier 	Rectangle r, or;
7847dd7cddfSDavid du Colombier 
7857dd7cddfSDavid du Colombier 	if(im == nil)
7867dd7cddfSDavid du Colombier 		return;
7877dd7cddfSDavid du Colombier 
7887dd7cddfSDavid du Colombier 	u = pclip(addpt(ul, delta), ulrange);
7897dd7cddfSDavid du Colombier 	delta = subpt(u, ul);
7907dd7cddfSDavid du Colombier 	if(delta.x == 0 && delta.y == 0)
7917dd7cddfSDavid du Colombier 		return;
7927dd7cddfSDavid du Colombier 
7937dd7cddfSDavid du Colombier 	/*
7947dd7cddfSDavid du Colombier 	 * The upper left corner of the image is currently at ul.
7957dd7cddfSDavid du Colombier 	 * We want to move it to u.
7967dd7cddfSDavid du Colombier 	 */
7977dd7cddfSDavid du Colombier 	or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
7987dd7cddfSDavid du Colombier 	r = rectaddpt(or, delta);
7997dd7cddfSDavid du Colombier 
8006b6b9ac8SDavid du Colombier 	drawop(screen, r, screen, nil, ul, S);
8017dd7cddfSDavid du Colombier 	ul = u;
8027dd7cddfSDavid du Colombier 
8037dd7cddfSDavid du Colombier 	/* fill in gray where image used to be but isn't. */
8046b6b9ac8SDavid du Colombier 	drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
8057dd7cddfSDavid du Colombier 
8067dd7cddfSDavid du Colombier 	/* fill in black border */
8076b6b9ac8SDavid du Colombier 	drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
8087dd7cddfSDavid du Colombier 
8097dd7cddfSDavid du Colombier 	/* fill in image where it used to be off the screen. */
8106b6b9ac8SDavid du Colombier 	if(rectclip(&or, screen->r))
8116b6b9ac8SDavid du Colombier 		drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
8126b6b9ac8SDavid du Colombier 	else
8136b6b9ac8SDavid du Colombier 		drawop(screen, r, im, nil, im->r.min, S);
8147dd7cddfSDavid du Colombier 	flushimage(display, 1);
8157dd7cddfSDavid du Colombier }
8167dd7cddfSDavid du Colombier 
8177dd7cddfSDavid du Colombier void
redraw(Image * screen)8187dd7cddfSDavid du Colombier redraw(Image *screen)
8197dd7cddfSDavid du Colombier {
8207dd7cddfSDavid du Colombier 	Rectangle r;
8217dd7cddfSDavid du Colombier 
8227dd7cddfSDavid du Colombier 	if(im == nil)
8237dd7cddfSDavid du Colombier 		return;
8247dd7cddfSDavid du Colombier 
8257dd7cddfSDavid du Colombier 	ulrange.max = screen->r.max;
8267dd7cddfSDavid du Colombier 	ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
8277dd7cddfSDavid du Colombier 
8287dd7cddfSDavid du Colombier 	ul = pclip(ul, ulrange);
8296b6b9ac8SDavid du Colombier 	drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
8307dd7cddfSDavid du Colombier 
8317dd7cddfSDavid du Colombier 	if(im->repl)
8327dd7cddfSDavid du Colombier 		return;
8337dd7cddfSDavid du Colombier 
8347dd7cddfSDavid du Colombier 	/* fill in any outer edges */
8357dd7cddfSDavid du Colombier 	/* black border */
8367dd7cddfSDavid du Colombier 	r = rectaddpt(im->r, subpt(ul, im->r.min));
8377dd7cddfSDavid du Colombier 	border(screen, r, -2, display->black, ZP);
8387dd7cddfSDavid du Colombier 	r.min = subpt(r.min, Pt(2,2));
8397dd7cddfSDavid du Colombier 	r.max = addpt(r.max, Pt(2,2));
8407dd7cddfSDavid du Colombier 
8417dd7cddfSDavid du Colombier 	/* gray for the rest */
8427dd7cddfSDavid du Colombier 	if(gray == nil) {
8437dd7cddfSDavid du Colombier 		gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
8447dd7cddfSDavid du Colombier 		if(gray == nil) {
8457dd7cddfSDavid du Colombier 			fprint(2, "g out of memory: %r\n");
8467dd7cddfSDavid du Colombier 			wexits("mem");
8477dd7cddfSDavid du Colombier 		}
8487dd7cddfSDavid du Colombier 	}
8497dd7cddfSDavid du Colombier 	border(screen, r, -4000, gray, ZP);
8507dd7cddfSDavid du Colombier //	flushimage(display, 0);
8517dd7cddfSDavid du Colombier }
8527dd7cddfSDavid du Colombier 
8537dd7cddfSDavid du Colombier void
eresized(int new)8547dd7cddfSDavid du Colombier eresized(int new)
8557dd7cddfSDavid du Colombier {
8567dd7cddfSDavid du Colombier 	Rectangle r;
8577dd7cddfSDavid du Colombier 	r = screen->r;
8587dd7cddfSDavid du Colombier 	if(new && getwindow(display, Refnone) < 0)
8597dd7cddfSDavid du Colombier 		fprint(2,"can't reattach to window");
8607dd7cddfSDavid du Colombier 	ul = addpt(ul, subpt(screen->r.min, r.min));
8617dd7cddfSDavid du Colombier 	redraw(screen);
8627dd7cddfSDavid du Colombier }
8637dd7cddfSDavid du Colombier 
8647dd7cddfSDavid du Colombier /* clip p to be in r */
8657dd7cddfSDavid du Colombier Point
pclip(Point p,Rectangle r)8667dd7cddfSDavid du Colombier pclip(Point p, Rectangle r)
8677dd7cddfSDavid du Colombier {
8687dd7cddfSDavid du Colombier 	if(p.x < r.min.x)
8697dd7cddfSDavid du Colombier 		p.x = r.min.x;
8707dd7cddfSDavid du Colombier 	else if(p.x >= r.max.x)
8717dd7cddfSDavid du Colombier 		p.x = r.max.x-1;
8727dd7cddfSDavid du Colombier 
8737dd7cddfSDavid du Colombier 	if(p.y < r.min.y)
8747dd7cddfSDavid du Colombier 		p.y = r.min.y;
8757dd7cddfSDavid du Colombier 	else if(p.y >= r.max.y)
8767dd7cddfSDavid du Colombier 		p.y = r.max.y-1;
8777dd7cddfSDavid du Colombier 
8787dd7cddfSDavid du Colombier 	return p;
8797dd7cddfSDavid du Colombier }
8807dd7cddfSDavid du Colombier 
8817dd7cddfSDavid du Colombier /*
8827dd7cddfSDavid du Colombier  * resize is perhaps a misnomer.
8837dd7cddfSDavid du Colombier  * this really just grows the window to be at least dx across
8847dd7cddfSDavid du Colombier  * and dy high.  if the window hits the bottom or right edge,
8857dd7cddfSDavid du Colombier  * it is backed up until it hits the top or left edge.
8867dd7cddfSDavid du Colombier  */
8877dd7cddfSDavid du Colombier void
resize(int dx,int dy)8887dd7cddfSDavid du Colombier resize(int dx, int dy)
8897dd7cddfSDavid du Colombier {
8907dd7cddfSDavid du Colombier 	static Rectangle sr;
8917dd7cddfSDavid du Colombier 	Rectangle r, or;
8927dd7cddfSDavid du Colombier 
8937dd7cddfSDavid du Colombier 	dx += 2*Borderwidth;
8947dd7cddfSDavid du Colombier 	dy += 2*Borderwidth;
8957dd7cddfSDavid du Colombier 	if(wctlfd < 0){
8967dd7cddfSDavid du Colombier 		wctlfd = open("/dev/wctl", OWRITE);
8977dd7cddfSDavid du Colombier 		if(wctlfd < 0)
8987dd7cddfSDavid du Colombier 			return;
8997dd7cddfSDavid du Colombier 	}
9007dd7cddfSDavid du Colombier 
9017dd7cddfSDavid du Colombier 	r = insetrect(screen->r, -Borderwidth);
9027dd7cddfSDavid du Colombier 	if(Dx(r) >= dx && Dy(r) >= dy)
9037dd7cddfSDavid du Colombier 		return;
9047dd7cddfSDavid du Colombier 
9057dd7cddfSDavid du Colombier 	if(Dx(sr)*Dy(sr) == 0)
9067dd7cddfSDavid du Colombier 		sr = screenrect();
9077dd7cddfSDavid du Colombier 
9087dd7cddfSDavid du Colombier 	or = r;
9097dd7cddfSDavid du Colombier 
9107dd7cddfSDavid du Colombier 	r.max.x = max(r.min.x+dx, r.max.x);
9117dd7cddfSDavid du Colombier 	r.max.y = max(r.min.y+dy, r.max.y);
9127dd7cddfSDavid du Colombier 	if(r.max.x > sr.max.x){
9137dd7cddfSDavid du Colombier 		if(Dx(r) > Dx(sr)){
9147dd7cddfSDavid du Colombier 			r.min.x = 0;
9157dd7cddfSDavid du Colombier 			r.max.x = sr.max.x;
9167dd7cddfSDavid du Colombier 		}else
9177dd7cddfSDavid du Colombier 			r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
9187dd7cddfSDavid du Colombier 	}
9197dd7cddfSDavid du Colombier 	if(r.max.y > sr.max.y){
9207dd7cddfSDavid du Colombier 		if(Dy(r) > Dy(sr)){
9217dd7cddfSDavid du Colombier 			r.min.y = 0;
9227dd7cddfSDavid du Colombier 			r.max.y = sr.max.y;
9237dd7cddfSDavid du Colombier 		}else
9247dd7cddfSDavid du Colombier 			r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
9257dd7cddfSDavid du Colombier 	}
9267dd7cddfSDavid du Colombier 
9277dd7cddfSDavid du Colombier 	/*
9287dd7cddfSDavid du Colombier 	 * Sometimes we can't actually grow the window big enough,
9297dd7cddfSDavid du Colombier 	 * and resizing it to the same shape makes it flash.
9307dd7cddfSDavid du Colombier 	 */
9317dd7cddfSDavid du Colombier 	if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
9327dd7cddfSDavid du Colombier 		return;
9337dd7cddfSDavid du Colombier 
9347dd7cddfSDavid du Colombier 	fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n",
9357dd7cddfSDavid du Colombier 		r.min.x, r.min.y, r.max.x, r.max.y);
9367dd7cddfSDavid du Colombier }
9377dd7cddfSDavid du Colombier 
9387dd7cddfSDavid du Colombier /*
9397dd7cddfSDavid du Colombier  * If we allocimage after a resize but before flushing the draw buffer,
9407dd7cddfSDavid du Colombier  * we won't have seen the reshape event, and we won't have called
9417dd7cddfSDavid du Colombier  * getwindow, and allocimage will fail.  So we flushimage before every alloc.
9427dd7cddfSDavid du Colombier  */
9437dd7cddfSDavid du Colombier Image*
xallocimage(Display * d,Rectangle r,ulong chan,int repl,ulong val)9447dd7cddfSDavid du Colombier xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
9457dd7cddfSDavid du Colombier {
9467dd7cddfSDavid du Colombier 	flushimage(display, 0);
9477dd7cddfSDavid du Colombier 	return allocimage(d, r, chan, repl, val);
9487dd7cddfSDavid du Colombier }
9497dd7cddfSDavid du Colombier 
9507dd7cddfSDavid du Colombier /* all code below this line should be in the library, but is stolen from colors instead */
9517dd7cddfSDavid du Colombier static char*
rdenv(char * name)9527dd7cddfSDavid du Colombier rdenv(char *name)
9537dd7cddfSDavid du Colombier {
9547dd7cddfSDavid du Colombier 	char *v;
9557dd7cddfSDavid du Colombier 	int fd, size;
9567dd7cddfSDavid du Colombier 
9577dd7cddfSDavid du Colombier 	fd = open(name, OREAD);
9587dd7cddfSDavid du Colombier 	if(fd < 0)
9597dd7cddfSDavid du Colombier 		return 0;
9607dd7cddfSDavid du Colombier 	size = seek(fd, 0, 2);
9617dd7cddfSDavid du Colombier 	v = malloc(size+1);
9627dd7cddfSDavid du Colombier 	if(v == 0){
9637dd7cddfSDavid du Colombier 		fprint(2, "page: can't malloc: %r\n");
9647dd7cddfSDavid du Colombier 		wexits("no mem");
9657dd7cddfSDavid du Colombier 	}
9667dd7cddfSDavid du Colombier 	seek(fd, 0, 0);
9677dd7cddfSDavid du Colombier 	read(fd, v, size);
9687dd7cddfSDavid du Colombier 	v[size] = 0;
9697dd7cddfSDavid du Colombier 	close(fd);
9707dd7cddfSDavid du Colombier 	return v;
9717dd7cddfSDavid du Colombier }
9727dd7cddfSDavid du Colombier 
9737dd7cddfSDavid du Colombier void
newwin(void)9747dd7cddfSDavid du Colombier newwin(void)
9757dd7cddfSDavid du Colombier {
9767dd7cddfSDavid du Colombier 	char *srv, *mntsrv;
9777dd7cddfSDavid du Colombier 	char spec[100];
9787dd7cddfSDavid du Colombier 	int srvfd, cons, pid;
9797dd7cddfSDavid du Colombier 
9807dd7cddfSDavid du Colombier 	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){
9817dd7cddfSDavid du Colombier 	case -1:
9827dd7cddfSDavid du Colombier 		fprint(2, "page: can't fork: %r\n");
9837dd7cddfSDavid du Colombier 		wexits("no fork");
9847dd7cddfSDavid du Colombier 	case 0:
9857dd7cddfSDavid du Colombier 		break;
9867dd7cddfSDavid du Colombier 	default:
9877dd7cddfSDavid du Colombier 		wexits(0);
9887dd7cddfSDavid du Colombier 	}
9897dd7cddfSDavid du Colombier 
9907dd7cddfSDavid du Colombier 	srv = rdenv("/env/wsys");
9917dd7cddfSDavid du Colombier 	if(srv == 0){
9927dd7cddfSDavid du Colombier 		mntsrv = rdenv("/mnt/term/env/wsys");
9937dd7cddfSDavid du Colombier 		if(mntsrv == 0){
9947dd7cddfSDavid du Colombier 			fprint(2, "page: can't find $wsys\n");
9957dd7cddfSDavid du Colombier 			wexits("srv");
9967dd7cddfSDavid du Colombier 		}
9977dd7cddfSDavid du Colombier 		srv = malloc(strlen(mntsrv)+10);
9987dd7cddfSDavid du Colombier 		sprint(srv, "/mnt/term%s", mntsrv);
9997dd7cddfSDavid du Colombier 		free(mntsrv);
10007dd7cddfSDavid du Colombier 		pid  = 0;			/* can't send notes to remote processes! */
10017dd7cddfSDavid du Colombier 	}else
10027dd7cddfSDavid du Colombier 		pid = getpid();
10037dd7cddfSDavid du Colombier 	srvfd = open(srv, ORDWR);
10047dd7cddfSDavid du Colombier 	if(srvfd == -1){
10057dd7cddfSDavid du Colombier 		fprint(2, "page: can't open %s: %r\n", srv);
10067dd7cddfSDavid du Colombier 		wexits("no srv");
10077dd7cddfSDavid du Colombier 	}
1008fae06aafSDavid du Colombier 	free(srv);
10097dd7cddfSDavid du Colombier 	sprint(spec, "new -pid %d", pid);
10109a747e4fSDavid du Colombier 	if(mount(srvfd, -1, "/mnt/wsys", 0, spec) == -1){
10117dd7cddfSDavid du Colombier 		fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec);
10127dd7cddfSDavid du Colombier 		wexits("no mount");
10137dd7cddfSDavid du Colombier 	}
10147dd7cddfSDavid du Colombier 	close(srvfd);
10157dd7cddfSDavid du Colombier 	unmount("/mnt/acme", "/dev");
10167dd7cddfSDavid du Colombier 	bind("/mnt/wsys", "/dev", MBEFORE);
10177dd7cddfSDavid du Colombier 	cons = open("/dev/cons", OREAD);
10187dd7cddfSDavid du Colombier 	if(cons==-1){
10197dd7cddfSDavid du Colombier 	NoCons:
10207dd7cddfSDavid du Colombier 		fprint(2, "page: can't open /dev/cons: %r");
10217dd7cddfSDavid du Colombier 		wexits("no cons");
10227dd7cddfSDavid du Colombier 	}
10237dd7cddfSDavid du Colombier 	dup(cons, 0);
10247dd7cddfSDavid du Colombier 	close(cons);
10257dd7cddfSDavid du Colombier 	cons = open("/dev/cons", OWRITE);
10267dd7cddfSDavid du Colombier 	if(cons==-1)
10277dd7cddfSDavid du Colombier 		goto NoCons;
10287dd7cddfSDavid du Colombier 	dup(cons, 1);
10297dd7cddfSDavid du Colombier 	dup(cons, 2);
10307dd7cddfSDavid du Colombier 	close(cons);
10317dd7cddfSDavid du Colombier //	wctlfd = open("/dev/wctl", OWRITE);
10327dd7cddfSDavid du Colombier }
10337dd7cddfSDavid du Colombier 
10347dd7cddfSDavid du Colombier Rectangle
screenrect(void)10357dd7cddfSDavid du Colombier screenrect(void)
10367dd7cddfSDavid du Colombier {
10377dd7cddfSDavid du Colombier 	int fd;
10387dd7cddfSDavid du Colombier 	char buf[12*5];
10397dd7cddfSDavid du Colombier 
10407dd7cddfSDavid du Colombier 	fd = open("/dev/screen", OREAD);
10417dd7cddfSDavid du Colombier 	if(fd == -1)
10427dd7cddfSDavid du Colombier 		fd=open("/mnt/term/dev/screen", OREAD);
10437dd7cddfSDavid du Colombier 	if(fd == -1){
10447dd7cddfSDavid du Colombier 		fprint(2, "page: can't open /dev/screen: %r\n");
10457dd7cddfSDavid du Colombier 		wexits("window read");
10467dd7cddfSDavid du Colombier 	}
10477dd7cddfSDavid du Colombier 	if(read(fd, buf, sizeof buf) != sizeof buf){
10487dd7cddfSDavid du Colombier 		fprint(2, "page: can't read /dev/screen: %r\n");
10497dd7cddfSDavid du Colombier 		wexits("screen read");
10507dd7cddfSDavid du Colombier 	}
10517dd7cddfSDavid du Colombier 	close(fd);
10527dd7cddfSDavid du Colombier 	return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48));
10537dd7cddfSDavid du Colombier }
10547dd7cddfSDavid du Colombier 
10557def40e1SDavid du Colombier void
zerox(void)10567def40e1SDavid du Colombier zerox(void)
10577def40e1SDavid du Colombier {
10587def40e1SDavid du Colombier 	int pfd[2];
10597def40e1SDavid du Colombier 
10607def40e1SDavid du Colombier 	pipe(pfd);
10617def40e1SDavid du Colombier 	switch(rfork(RFFDG|RFREND|RFPROC)) {
10627def40e1SDavid du Colombier 		case -1:
10637def40e1SDavid du Colombier 			wexits("cannot fork in zerox: %r");
10647def40e1SDavid du Colombier 		case 0:
10657def40e1SDavid du Colombier 			dup(pfd[1], 0);
10667def40e1SDavid du Colombier 			close(pfd[0]);
1067f19e7b74SDavid du Colombier 			execl("/bin/page", "page", "-w", nil);
10687def40e1SDavid du Colombier 			wexits("cannot exec in zerox: %r\n");
10697def40e1SDavid du Colombier 		default:
10707def40e1SDavid du Colombier 			close(pfd[1]);
10717def40e1SDavid du Colombier 			writeimage(pfd[0], im, 0);
10727def40e1SDavid du Colombier 			close(pfd[0]);
10737def40e1SDavid du Colombier 			break;
10747def40e1SDavid du Colombier 	}
10757def40e1SDavid du Colombier }
1076