xref: /plan9/sys/src/cmd/page/view.c (revision 0ceffd3c74a1de03d13dc3aa8fa06e39d209dbd4)
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
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
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
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
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*
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
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*
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
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 
223*0ceffd3cSDavid du Colombier static int
224*0ceffd3cSDavid du Colombier plumbquit(Plumbmsg *msg)
225*0ceffd3cSDavid du Colombier {
226*0ceffd3cSDavid du Colombier 	char *s;
227*0ceffd3cSDavid du Colombier 
228*0ceffd3cSDavid du Colombier 	s = plumblookup(msg->attr, "action");
229*0ceffd3cSDavid du Colombier 	return s && strcmp(s, "quit")==0;
230*0ceffd3cSDavid du Colombier }
231*0ceffd3cSDavid 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
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 
3207dd7cddfSDavid du Colombier 	showpage(page, &menu);
3217dd7cddfSDavid du Colombier 	esetcursor(nil);
3227dd7cddfSDavid du Colombier 
3237dd7cddfSDavid du Colombier 	nxt = 0;
3247dd7cddfSDavid du Colombier 	for(;;) {
3257dd7cddfSDavid du Colombier 		/*
3267dd7cddfSDavid du Colombier 		 * throughout, if doc->fwdonly is set, we restrict the functionality
3277dd7cddfSDavid du Colombier 		 * a fair amount.  we don't care about doc->npage anymore, and
3287dd7cddfSDavid du Colombier 		 * all that can be done is select the next page.
3297dd7cddfSDavid du Colombier 		 */
330493edcedSDavid du Colombier 		unlockdisplay(display);
331493edcedSDavid du Colombier 		i = eread(Emouse|Ekeyboard|Eplumb, &e);
332493edcedSDavid du Colombier 		lockdisplay(display);
333493edcedSDavid du Colombier 		switch(i){
3347dd7cddfSDavid du Colombier 		case Ekeyboard:
3357dd7cddfSDavid du Colombier 			if(e.kbdc <= 0xFF && isdigit(e.kbdc)) {
3367dd7cddfSDavid du Colombier 				nxt = nxt*10+e.kbdc-'0';
3377dd7cddfSDavid du Colombier 				break;
3387dd7cddfSDavid du Colombier 			} else if(e.kbdc != '\n')
3397dd7cddfSDavid du Colombier 				nxt = 0;
3407dd7cddfSDavid du Colombier 			switch(e.kbdc) {
3417dd7cddfSDavid du Colombier 			case 'r':	/* reverse page order */
3427dd7cddfSDavid du Colombier 				if(doc->fwdonly)
3437dd7cddfSDavid du Colombier 					break;
3447dd7cddfSDavid du Colombier 				reverse = !reverse;
3457dd7cddfSDavid du Colombier 				menu.lasthit = doc->npage-1-menu.lasthit;
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier 				/*
3487dd7cddfSDavid du Colombier 				 * the theory is that if we are reversing the
3497dd7cddfSDavid du Colombier 				 * document order and are on the first or last
3507dd7cddfSDavid du Colombier 				 * page then we're just starting and really want
3517dd7cddfSDavid du Colombier 		 	 	 * to view the other end.  maybe the if
3527dd7cddfSDavid du Colombier 				 * should be dropped and this should happen always.
3537dd7cddfSDavid du Colombier 				 */
3547dd7cddfSDavid du Colombier 				if(page == 0 || page == doc->npage-1) {
3557dd7cddfSDavid du Colombier 					page = doc->npage-1-page;
3567dd7cddfSDavid du Colombier 					showpage(page, &menu);
3577dd7cddfSDavid du Colombier 				}
3587dd7cddfSDavid du Colombier 				break;
3597dd7cddfSDavid du Colombier 			case 'w':	/* write bitmap of current screen */
3607dd7cddfSDavid du Colombier 				esetcursor(&reading);
3617dd7cddfSDavid du Colombier 				s = writebitmap();
3627dd7cddfSDavid du Colombier 				if(s)
3637dd7cddfSDavid du Colombier 					string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
3647dd7cddfSDavid du Colombier 						display->defaultfont, s);
3657dd7cddfSDavid du Colombier 				esetcursor(nil);
3667dd7cddfSDavid du Colombier 				flushimage(display, 1);
3677dd7cddfSDavid du Colombier 				break;
3687dd7cddfSDavid du Colombier 			case 'd':	/* remove image from working set */
3697dd7cddfSDavid du Colombier 				if(doc->rmpage && page < doc->npage) {
3707dd7cddfSDavid du Colombier 					if(doc->rmpage(doc, page) >= 0) {
3717dd7cddfSDavid du Colombier 						if(doc->npage < 0)
3727dd7cddfSDavid du Colombier 							wexits(0);
3737dd7cddfSDavid du Colombier 						if(page >= doc->npage)
3747dd7cddfSDavid du Colombier 							page = doc->npage-1;
3757dd7cddfSDavid du Colombier 						showpage(page, &menu);
3767dd7cddfSDavid du Colombier 					}
3777dd7cddfSDavid du Colombier 				}
3787dd7cddfSDavid du Colombier 				break;
3797dd7cddfSDavid du Colombier 			case 'q':
3807dd7cddfSDavid du Colombier 			case 0x04: /* ctrl-d */
3817dd7cddfSDavid du Colombier 				wexits(0);
3827dd7cddfSDavid du Colombier 			case 'u':
3837dd7cddfSDavid du Colombier 				if(im==nil)
3847dd7cddfSDavid du Colombier 					break;
385a7529a1dSDavid du Colombier 				angle = (angle+180) % 360;
386493edcedSDavid du Colombier 				showpage(page, &menu);
3877dd7cddfSDavid du Colombier 				break;
3887dd7cddfSDavid du Colombier 			case '-':
3897dd7cddfSDavid du Colombier 			case '\b':
390223a736eSDavid du Colombier 			case Kleft:
3917dd7cddfSDavid du Colombier 				if(page > 0 && !doc->fwdonly) {
3927dd7cddfSDavid du Colombier 					--page;
3937dd7cddfSDavid du Colombier 					showpage(page, &menu);
3947dd7cddfSDavid du Colombier 				}
3957dd7cddfSDavid du Colombier 				break;
3967dd7cddfSDavid du Colombier 			case '\n':
3977dd7cddfSDavid du Colombier 				if(nxt) {
3987dd7cddfSDavid du Colombier 					nxt--;
3997dd7cddfSDavid du Colombier 					if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
4007dd7cddfSDavid du Colombier 						showpage(page=nxt, &menu);
4017dd7cddfSDavid du Colombier 					nxt = 0;
4027dd7cddfSDavid du Colombier 					break;
4037dd7cddfSDavid du Colombier 				}
40459cc4ca5SDavid du Colombier 				goto Gotonext;
405223a736eSDavid du Colombier 			case Kright:
406223a736eSDavid du Colombier 			case ' ':
40759cc4ca5SDavid du Colombier 			Gotonext:
4087dd7cddfSDavid du Colombier 				if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
4097dd7cddfSDavid du Colombier 					wexits(0);
4107dd7cddfSDavid du Colombier 				showpage(page, &menu);
4117dd7cddfSDavid du Colombier 				break;
41259cc4ca5SDavid du Colombier 
41359cc4ca5SDavid du Colombier 			/*
41459cc4ca5SDavid du Colombier 			 * The upper y coordinate of the image is at ul.y in screen->r.
41559cc4ca5SDavid du Colombier 			 * Panning up means moving the upper left corner down.  If the
41659cc4ca5SDavid du Colombier 			 * upper left corner is currently visible, we need to go back a page.
41759cc4ca5SDavid du Colombier 			 */
41859cc4ca5SDavid du Colombier 			case Kup:
41959cc4ca5SDavid du Colombier 				if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
42059cc4ca5SDavid du Colombier 					if(page > 0 && !doc->fwdonly){
42159cc4ca5SDavid du Colombier 						--page;
42259cc4ca5SDavid du Colombier 						showbottom = 1;
42359cc4ca5SDavid du Colombier 						showpage(page, &menu);
42459cc4ca5SDavid du Colombier 					}
42559cc4ca5SDavid du Colombier 				} else {
42659cc4ca5SDavid du Colombier 					i = Dy(screen->r)/2;
42759cc4ca5SDavid du Colombier 					if(i > 10)
42859cc4ca5SDavid du Colombier 						i -= 10;
42959cc4ca5SDavid du Colombier 					if(i+ul.y > screen->r.min.y)
43059cc4ca5SDavid du Colombier 						i = screen->r.min.y - ul.y;
43159cc4ca5SDavid du Colombier 					translate(Pt(0, i));
43259cc4ca5SDavid du Colombier 				}
43359cc4ca5SDavid du Colombier 				break;
43459cc4ca5SDavid du Colombier 
43559cc4ca5SDavid du Colombier 			/*
43659cc4ca5SDavid du Colombier 			 * If the lower y coordinate is on the screen, we go to the next page.
43759cc4ca5SDavid du Colombier 			 * The lower y coordinate is at ul.y + Dy(im->r).
43859cc4ca5SDavid du Colombier 			 */
43959cc4ca5SDavid du Colombier 			case Kdown:
44059cc4ca5SDavid du Colombier 				i = ul.y + Dy(im->r);
44159cc4ca5SDavid du Colombier 				if(screen->r.min.y <= i && i <= screen->r.max.y){
44259cc4ca5SDavid du Colombier 					ul.y = screen->r.min.y;
44359cc4ca5SDavid du Colombier 					goto Gotonext;
44459cc4ca5SDavid du Colombier 				} else {
44559cc4ca5SDavid du Colombier 					i = -Dy(screen->r)/2;
44659cc4ca5SDavid du Colombier 					if(i < -10)
44759cc4ca5SDavid du Colombier 						i += 10;
44859cc4ca5SDavid du Colombier 					if(i+ul.y+Dy(im->r) <= screen->r.max.y)
44959cc4ca5SDavid du Colombier 						i = screen->r.max.y - Dy(im->r) - ul.y - 1;
45059cc4ca5SDavid du Colombier 					translate(Pt(0, i));
45159cc4ca5SDavid du Colombier 				}
45259cc4ca5SDavid du Colombier 				break;
453223a736eSDavid du Colombier 			default:
454223a736eSDavid du Colombier 				esetcursor(&query);
455223a736eSDavid du Colombier 				sleep(1000);
456223a736eSDavid du Colombier 				esetcursor(nil);
457223a736eSDavid du Colombier 				break;
4587dd7cddfSDavid du Colombier 			}
4597dd7cddfSDavid du Colombier 			break;
4607dd7cddfSDavid du Colombier 
4617dd7cddfSDavid du Colombier 		case Emouse:
4627dd7cddfSDavid du Colombier 			m = e.mouse;
4637dd7cddfSDavid du Colombier 			switch(m.buttons){
4647dd7cddfSDavid du Colombier 			case Left:
4657dd7cddfSDavid du Colombier 				oxy = m.xy;
4667dd7cddfSDavid du Colombier 				xy0 = oxy;
4677dd7cddfSDavid du Colombier 				do {
4687dd7cddfSDavid du Colombier 					dxy = subpt(m.xy, oxy);
4697dd7cddfSDavid du Colombier 					oxy = m.xy;
4707dd7cddfSDavid du Colombier 					translate(dxy);
471493edcedSDavid du Colombier 					unlockdisplay(display);
4727dd7cddfSDavid du Colombier 					m = emouse();
473493edcedSDavid du Colombier 					lockdisplay(display);
4747dd7cddfSDavid du Colombier 				} while(m.buttons == Left);
4757dd7cddfSDavid du Colombier 				if(m.buttons) {
4767dd7cddfSDavid du Colombier 					dxy = subpt(xy0, oxy);
4777dd7cddfSDavid du Colombier 					translate(dxy);
4787dd7cddfSDavid du Colombier 				}
4797dd7cddfSDavid du Colombier 				break;
4807dd7cddfSDavid du Colombier 
4817dd7cddfSDavid du Colombier 			case Middle:
4827dd7cddfSDavid du Colombier 				if(doc->npage == 0)
4837dd7cddfSDavid du Colombier 					break;
4847dd7cddfSDavid du Colombier 
485493edcedSDavid du Colombier 				unlockdisplay(display);
48608fd2d13SDavid du Colombier 				n = emenuhit(Middle, &m, &midmenu);
487493edcedSDavid du Colombier 				lockdisplay(display);
48808fd2d13SDavid du Colombier 				if(n == -1)
48908fd2d13SDavid du Colombier 					break;
49008fd2d13SDavid du Colombier 				switch(n){
49108fd2d13SDavid du Colombier 				case Next: 	/* next */
4927dd7cddfSDavid du Colombier 					if(reverse)
4937dd7cddfSDavid du Colombier 						page--;
4947dd7cddfSDavid du Colombier 					else
4957dd7cddfSDavid du Colombier 						page++;
49608fd2d13SDavid du Colombier 					if(page < 0) {
49708fd2d13SDavid du Colombier 						if(reverse) return;
49808fd2d13SDavid du Colombier 						else page = 0;
49908fd2d13SDavid du Colombier 					}
5007dd7cddfSDavid du Colombier 
50108fd2d13SDavid du Colombier 					if((page >= doc->npage) && !doc->fwdonly)
5027dd7cddfSDavid du Colombier 						return;
5037dd7cddfSDavid du Colombier 
5047dd7cddfSDavid du Colombier 					showpage(page, &menu);
5057dd7cddfSDavid du Colombier 					nxt = 0;
5067dd7cddfSDavid du Colombier 					break;
50708fd2d13SDavid du Colombier 				case Prev:	/* prev */
50808fd2d13SDavid du Colombier 					if(reverse)
50908fd2d13SDavid du Colombier 						page++;
51008fd2d13SDavid du Colombier 					else
51108fd2d13SDavid du Colombier 						page--;
51208fd2d13SDavid du Colombier 					if(page < 0) {
51308fd2d13SDavid du Colombier 						if(reverse) return;
51408fd2d13SDavid du Colombier 						else page = 0;
51508fd2d13SDavid du Colombier 					}
51608fd2d13SDavid du Colombier 
51708fd2d13SDavid du Colombier 					if((page >= doc->npage) && !doc->fwdonly && !reverse)
51808fd2d13SDavid du Colombier 						return;
51908fd2d13SDavid du Colombier 
52008fd2d13SDavid du Colombier 					showpage(page, &menu);
52108fd2d13SDavid du Colombier 					nxt = 0;
52208fd2d13SDavid du Colombier 					break;
5237def40e1SDavid du Colombier 				case Zerox:	/* prev */
5247def40e1SDavid du Colombier 					zerox();
52508fd2d13SDavid du Colombier 					break;
52608fd2d13SDavid du Colombier 				case Zin:	/* zoom in */
52708fd2d13SDavid du Colombier 					{
52808fd2d13SDavid du Colombier 						double delta;
52908fd2d13SDavid du Colombier 						Rectangle r;
53008fd2d13SDavid du Colombier 
53108fd2d13SDavid du Colombier 						r = egetrect(Middle, &m);
53208fd2d13SDavid du Colombier 						if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
53308fd2d13SDavid du Colombier 							Dx(r) == 0 || Dy(r) == 0)
53408fd2d13SDavid du Colombier 							break;
53508fd2d13SDavid du Colombier 						/* use the smaller side to expand */
53608fd2d13SDavid du Colombier 						if(Dx(r) < Dy(r))
53708fd2d13SDavid du Colombier 							delta = (double)Dx(im->r)/(double)Dx(r);
53808fd2d13SDavid du Colombier 						else
53908fd2d13SDavid du Colombier 							delta = (double)Dy(im->r)/(double)Dy(r);
54008fd2d13SDavid du Colombier 
54108fd2d13SDavid du Colombier 						esetcursor(&reading);
5425316891fSDavid du Colombier 						tmp = xallocimage(display,
54308fd2d13SDavid du Colombier 								Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)),
54408fd2d13SDavid du Colombier 								im->chan, 0, DBlack);
5455316891fSDavid du Colombier 						if(tmp == nil) {
5465316891fSDavid du Colombier 							fprint(2, "out of memory during zoom: %r\n");
5475316891fSDavid du Colombier 							wexits("memory");
5485316891fSDavid du Colombier 						}
54908fd2d13SDavid du Colombier 						resample(im, tmp);
55008fd2d13SDavid du Colombier 						im = tmp;
551493edcedSDavid du Colombier 						delayfreeimage(tmp);
55208fd2d13SDavid du Colombier 						esetcursor(nil);
55308fd2d13SDavid du Colombier 						ul = screen->r.min;
55408fd2d13SDavid du Colombier 						redraw(screen);
55508fd2d13SDavid du Colombier 						flushimage(display, 1);
55608fd2d13SDavid du Colombier 						break;
55708fd2d13SDavid du Colombier 					}
55808fd2d13SDavid du Colombier 				case Fit:	/* fit */
55908fd2d13SDavid du Colombier 					{
56008fd2d13SDavid du Colombier 						double delta;
56108fd2d13SDavid du Colombier 						Rectangle r;
56208fd2d13SDavid du Colombier 
56308fd2d13SDavid du Colombier 						delta = (double)Dx(screen->r)/(double)Dx(im->r);
56408fd2d13SDavid du Colombier 						if((double)Dy(im->r)*delta > Dy(screen->r))
56508fd2d13SDavid du Colombier 							delta = (double)Dy(screen->r)/(double)Dy(im->r);
56608fd2d13SDavid du Colombier 
56708fd2d13SDavid du Colombier 						r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
56808fd2d13SDavid du Colombier 						esetcursor(&reading);
5695316891fSDavid du Colombier 						tmp = xallocimage(display, r, im->chan, 0, DBlack);
5705316891fSDavid du Colombier 						if(tmp == nil) {
5715316891fSDavid du Colombier 							fprint(2, "out of memory during fit: %r\n");
5725316891fSDavid du Colombier 							wexits("memory");
5735316891fSDavid du Colombier 						}
57408fd2d13SDavid du Colombier 						resample(im, tmp);
57508fd2d13SDavid du Colombier 						im = tmp;
576493edcedSDavid du Colombier 						delayfreeimage(tmp);
57708fd2d13SDavid du Colombier 						esetcursor(nil);
57808fd2d13SDavid du Colombier 						ul = screen->r.min;
57908fd2d13SDavid du Colombier 						redraw(screen);
58008fd2d13SDavid du Colombier 						flushimage(display, 1);
58108fd2d13SDavid du Colombier 						break;
58208fd2d13SDavid du Colombier 					}
58308fd2d13SDavid du Colombier 				case Rot:	/* rotate 90 */
584a7529a1dSDavid du Colombier 					angle = (angle+90) % 360;
585493edcedSDavid du Colombier 					showpage(page, &menu);
58608fd2d13SDavid du Colombier 					break;
58708fd2d13SDavid du Colombier 				case Upside: 	/* upside-down */
588a7529a1dSDavid du Colombier 					angle = (angle+180) % 360;
589493edcedSDavid du Colombier 					showpage(page, &menu);
59008fd2d13SDavid du Colombier 					break;
59108fd2d13SDavid du Colombier 				case Restore:	/* restore */
59208fd2d13SDavid du Colombier 					showpage(page, &menu);
59308fd2d13SDavid du Colombier 					break;
59408fd2d13SDavid du Colombier 				case Reverse:	/* reverse */
59508fd2d13SDavid du Colombier 					if(doc->fwdonly)
59608fd2d13SDavid du Colombier 						break;
59708fd2d13SDavid du Colombier 					reverse = !reverse;
59808fd2d13SDavid du Colombier 					menu.lasthit = doc->npage-1-menu.lasthit;
59908fd2d13SDavid du Colombier 
60008fd2d13SDavid du Colombier 					if(page == 0 || page == doc->npage-1) {
60108fd2d13SDavid du Colombier 						page = doc->npage-1-page;
60208fd2d13SDavid du Colombier 						showpage(page, &menu);
60308fd2d13SDavid du Colombier 					}
60408fd2d13SDavid du Colombier 					break;
60508fd2d13SDavid du Colombier 				case Write: /* write */
60608fd2d13SDavid du Colombier 					esetcursor(&reading);
60708fd2d13SDavid du Colombier 					s = writebitmap();
60808fd2d13SDavid du Colombier 					if(s)
60908fd2d13SDavid du Colombier 						string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
61008fd2d13SDavid du Colombier 							display->defaultfont, s);
61108fd2d13SDavid du Colombier 					esetcursor(nil);
61208fd2d13SDavid du Colombier 					flushimage(display, 1);
61308fd2d13SDavid du Colombier 					break;
61408fd2d13SDavid du Colombier 				case Del: /* delete */
61508fd2d13SDavid du Colombier 					if(doc->rmpage && page < doc->npage) {
61608fd2d13SDavid du Colombier 						if(doc->rmpage(doc, page) >= 0) {
61708fd2d13SDavid du Colombier 							if(doc->npage < 0)
61808fd2d13SDavid du Colombier 								wexits(0);
61908fd2d13SDavid du Colombier 							if(page >= doc->npage)
62008fd2d13SDavid du Colombier 								page = doc->npage-1;
62108fd2d13SDavid du Colombier 							showpage(page, &menu);
62208fd2d13SDavid du Colombier 						}
62308fd2d13SDavid du Colombier 					}
62408fd2d13SDavid du Colombier 					break;
62508fd2d13SDavid du Colombier 				case Exit:	/* exit */
62608fd2d13SDavid du Colombier 					return;
62708fd2d13SDavid du Colombier 				case Empty1:
62808fd2d13SDavid du Colombier 				case Empty2:
62908fd2d13SDavid du Colombier 				case Empty3:
63008fd2d13SDavid du Colombier 					break;
63108fd2d13SDavid du Colombier 
63208fd2d13SDavid du Colombier 				};
63308fd2d13SDavid du Colombier 
63408fd2d13SDavid du Colombier 
6357dd7cddfSDavid du Colombier 
6367dd7cddfSDavid du Colombier 			case Right:
6377dd7cddfSDavid du Colombier 				if(doc->npage == 0)
6387dd7cddfSDavid du Colombier 					break;
6397dd7cddfSDavid du Colombier 
6407dd7cddfSDavid du Colombier 				oldpage = page;
641493edcedSDavid du Colombier 				unlockdisplay(display);
6427dd7cddfSDavid du Colombier 				n = emenuhit(RMenu, &m, &menu);
643493edcedSDavid du Colombier 				lockdisplay(display);
6447dd7cddfSDavid du Colombier 				if(n == -1)
6457dd7cddfSDavid du Colombier 					break;
6467dd7cddfSDavid du Colombier 
6477dd7cddfSDavid du Colombier 				if(doc->fwdonly) {
6487dd7cddfSDavid du Colombier 					switch(n){
6497dd7cddfSDavid du Colombier 					case 0:	/* this page */
6507dd7cddfSDavid du Colombier 						break;
6517dd7cddfSDavid du Colombier 					case 1:	/* next page */
6527dd7cddfSDavid du Colombier 						showpage(++page, &menu);
6537dd7cddfSDavid du Colombier 						break;
6547dd7cddfSDavid du Colombier 					case 2:	/* exit */
6557dd7cddfSDavid du Colombier 						return;
6567dd7cddfSDavid du Colombier 					}
6577dd7cddfSDavid du Colombier 					break;
6587dd7cddfSDavid du Colombier 				}
6597dd7cddfSDavid du Colombier 
6607dd7cddfSDavid du Colombier 				if(n == doc->npage)
6617dd7cddfSDavid du Colombier 					return;
6627dd7cddfSDavid du Colombier 				else
6637dd7cddfSDavid du Colombier 					page = reverse ? doc->npage-1-n : n;
6647dd7cddfSDavid du Colombier 
6657dd7cddfSDavid du Colombier 				if(oldpage != page)
6667dd7cddfSDavid du Colombier 					showpage(page, &menu);
6677dd7cddfSDavid du Colombier 				nxt = 0;
6687dd7cddfSDavid du Colombier 				break;
6697dd7cddfSDavid du Colombier 			}
6707dd7cddfSDavid du Colombier 			break;
6717dd7cddfSDavid du Colombier 
6727dd7cddfSDavid du Colombier 		case Eplumb:
6737dd7cddfSDavid du Colombier 			pm = e.v;
6747dd7cddfSDavid du Colombier 			if(pm->ndata <= 0){
6757dd7cddfSDavid du Colombier 				plumbfree(pm);
6767dd7cddfSDavid du Colombier 				break;
6777dd7cddfSDavid du Colombier 			}
678*0ceffd3cSDavid du Colombier 			if(plumbquit(pm))
679*0ceffd3cSDavid du Colombier 				exits(nil);
6807dd7cddfSDavid du Colombier 			if(showdata(pm)) {
6817dd7cddfSDavid du Colombier 				s = estrdup("/tmp/pageplumbXXXXXXX");
6827dd7cddfSDavid du Colombier 				fd = opentemp(s);
6837dd7cddfSDavid du Colombier 				write(fd, pm->data, pm->ndata);
6847dd7cddfSDavid du Colombier 				/* lose fd reference on purpose; the file is open ORCLOSE */
6857dd7cddfSDavid du Colombier 			} else if(pm->data[0] == '/') {
6867dd7cddfSDavid du Colombier 				s = estrdup(pm->data);
6877dd7cddfSDavid du Colombier 			} else {
6887dd7cddfSDavid du Colombier 				s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
6897dd7cddfSDavid du Colombier 				sprint(s, "%s/%s", pm->wdir, pm->data);
6907dd7cddfSDavid du Colombier 				cleanname(s);
6917dd7cddfSDavid du Colombier 			}
6927dd7cddfSDavid du Colombier 			if((i = doc->addpage(doc, s)) >= 0) {
6937dd7cddfSDavid du Colombier 				page = i;
6949a747e4fSDavid du Colombier 				unhide();
6957dd7cddfSDavid du Colombier 				showpage(page, &menu);
6967dd7cddfSDavid du Colombier 			}
6977dd7cddfSDavid du Colombier 			free(s);
6987dd7cddfSDavid du Colombier 			plumbfree(pm);
6997dd7cddfSDavid du Colombier 			break;
7007dd7cddfSDavid du Colombier 		}
7017dd7cddfSDavid du Colombier 	}
7027dd7cddfSDavid du Colombier }
7037dd7cddfSDavid du Colombier 
7047dd7cddfSDavid du Colombier Image *gray;
7057dd7cddfSDavid du Colombier 
7067dd7cddfSDavid du Colombier /*
7077dd7cddfSDavid du Colombier  * A draw operation that touches only the area contained in bot but not in top.
7087dd7cddfSDavid du Colombier  * mp and sp get aligned with bot.min.
7097dd7cddfSDavid du Colombier  */
7107dd7cddfSDavid du Colombier static void
7117dd7cddfSDavid du Colombier gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
7126b6b9ac8SDavid du Colombier 	Image *src, Point sp, Image *mask, Point mp, int op)
7137dd7cddfSDavid du Colombier {
7147dd7cddfSDavid du Colombier 	Rectangle r;
7157dd7cddfSDavid du Colombier 	Point origin;
7167dd7cddfSDavid du Colombier 	Point delta;
7177dd7cddfSDavid du Colombier 
71808fd2d13SDavid du Colombier 	USED(op);
71908fd2d13SDavid du Colombier 
7207dd7cddfSDavid du Colombier 	if(Dx(bot)*Dy(bot) == 0)
7217dd7cddfSDavid du Colombier 		return;
7227dd7cddfSDavid du Colombier 
7237dd7cddfSDavid du Colombier 	/* no points in bot - top */
7247dd7cddfSDavid du Colombier 	if(rectinrect(bot, top))
7257dd7cddfSDavid du Colombier 		return;
7267dd7cddfSDavid du Colombier 
7277dd7cddfSDavid du Colombier 	/* bot - top ≡ bot */
7287dd7cddfSDavid du Colombier 	if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
7296b6b9ac8SDavid du Colombier 		gendrawop(dst, bot, src, sp, mask, mp, op);
7307dd7cddfSDavid du Colombier 		return;
7317dd7cddfSDavid du Colombier 	}
7327dd7cddfSDavid du Colombier 
7337dd7cddfSDavid du Colombier 	origin = bot.min;
7347dd7cddfSDavid du Colombier 	/* split bot into rectangles that don't intersect top */
7357dd7cddfSDavid du Colombier 	/* left side */
7367dd7cddfSDavid du Colombier 	if(bot.min.x < top.min.x){
7377dd7cddfSDavid du Colombier 		r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
7387dd7cddfSDavid du Colombier 		delta = subpt(r.min, origin);
7396b6b9ac8SDavid du Colombier 		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
7407dd7cddfSDavid du Colombier 		bot.min.x = top.min.x;
7417dd7cddfSDavid du Colombier 	}
7427dd7cddfSDavid du Colombier 
7437dd7cddfSDavid du Colombier 	/* right side */
7447dd7cddfSDavid du Colombier 	if(bot.max.x > top.max.x){
7457dd7cddfSDavid du Colombier 		r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
7467dd7cddfSDavid du Colombier 		delta = subpt(r.min, origin);
7476b6b9ac8SDavid du Colombier 		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
7487dd7cddfSDavid du Colombier 		bot.max.x = top.max.x;
7497dd7cddfSDavid du Colombier 	}
7507dd7cddfSDavid du Colombier 
7517dd7cddfSDavid du Colombier 	/* top */
7527dd7cddfSDavid du Colombier 	if(bot.min.y < top.min.y){
7537dd7cddfSDavid du Colombier 		r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
7547dd7cddfSDavid du Colombier 		delta = subpt(r.min, origin);
7556b6b9ac8SDavid du Colombier 		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
7567dd7cddfSDavid du Colombier 		bot.min.y = top.min.y;
7577dd7cddfSDavid du Colombier 	}
7587dd7cddfSDavid du Colombier 
7597dd7cddfSDavid du Colombier 	/* bottom */
7607dd7cddfSDavid du Colombier 	if(bot.max.y > top.max.y){
7617dd7cddfSDavid du Colombier 		r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
7627dd7cddfSDavid du Colombier 		delta = subpt(r.min, origin);
7636b6b9ac8SDavid du Colombier 		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
7647dd7cddfSDavid du Colombier 		bot.max.y = top.max.y;
7657dd7cddfSDavid du Colombier 	}
7667dd7cddfSDavid du Colombier }
7677dd7cddfSDavid du Colombier 
7687dd7cddfSDavid du Colombier static void
7696b6b9ac8SDavid du Colombier drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
7707dd7cddfSDavid du Colombier {
7716b6b9ac8SDavid du Colombier 	gendrawdiff(dst, bot, top, src, p, mask, p, op);
7727dd7cddfSDavid du Colombier }
7737dd7cddfSDavid du Colombier 
7747dd7cddfSDavid du Colombier /*
7757dd7cddfSDavid du Colombier  * Translate the image in the window by delta.
7767dd7cddfSDavid du Colombier  */
7777dd7cddfSDavid du Colombier static void
7787dd7cddfSDavid du Colombier translate(Point delta)
7797dd7cddfSDavid du Colombier {
7807dd7cddfSDavid du Colombier 	Point u;
7817dd7cddfSDavid du Colombier 	Rectangle r, or;
7827dd7cddfSDavid du Colombier 
7837dd7cddfSDavid du Colombier 	if(im == nil)
7847dd7cddfSDavid du Colombier 		return;
7857dd7cddfSDavid du Colombier 
7867dd7cddfSDavid du Colombier 	u = pclip(addpt(ul, delta), ulrange);
7877dd7cddfSDavid du Colombier 	delta = subpt(u, ul);
7887dd7cddfSDavid du Colombier 	if(delta.x == 0 && delta.y == 0)
7897dd7cddfSDavid du Colombier 		return;
7907dd7cddfSDavid du Colombier 
7917dd7cddfSDavid du Colombier 	/*
7927dd7cddfSDavid du Colombier 	 * The upper left corner of the image is currently at ul.
7937dd7cddfSDavid du Colombier 	 * We want to move it to u.
7947dd7cddfSDavid du Colombier 	 */
7957dd7cddfSDavid du Colombier 	or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
7967dd7cddfSDavid du Colombier 	r = rectaddpt(or, delta);
7977dd7cddfSDavid du Colombier 
7986b6b9ac8SDavid du Colombier 	drawop(screen, r, screen, nil, ul, S);
7997dd7cddfSDavid du Colombier 	ul = u;
8007dd7cddfSDavid du Colombier 
8017dd7cddfSDavid du Colombier 	/* fill in gray where image used to be but isn't. */
8026b6b9ac8SDavid du Colombier 	drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
8037dd7cddfSDavid du Colombier 
8047dd7cddfSDavid du Colombier 	/* fill in black border */
8056b6b9ac8SDavid du Colombier 	drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
8067dd7cddfSDavid du Colombier 
8077dd7cddfSDavid du Colombier 	/* fill in image where it used to be off the screen. */
8086b6b9ac8SDavid du Colombier 	if(rectclip(&or, screen->r))
8096b6b9ac8SDavid du Colombier 		drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
8106b6b9ac8SDavid du Colombier 	else
8116b6b9ac8SDavid du Colombier 		drawop(screen, r, im, nil, im->r.min, S);
8127dd7cddfSDavid du Colombier 	flushimage(display, 1);
8137dd7cddfSDavid du Colombier }
8147dd7cddfSDavid du Colombier 
8157dd7cddfSDavid du Colombier void
8167dd7cddfSDavid du Colombier redraw(Image *screen)
8177dd7cddfSDavid du Colombier {
8187dd7cddfSDavid du Colombier 	Rectangle r;
8197dd7cddfSDavid du Colombier 
8207dd7cddfSDavid du Colombier 	if(im == nil)
8217dd7cddfSDavid du Colombier 		return;
8227dd7cddfSDavid du Colombier 
8237dd7cddfSDavid du Colombier 	ulrange.max = screen->r.max;
8247dd7cddfSDavid du Colombier 	ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
8257dd7cddfSDavid du Colombier 
8267dd7cddfSDavid du Colombier 	ul = pclip(ul, ulrange);
8276b6b9ac8SDavid du Colombier 	drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
8287dd7cddfSDavid du Colombier 
8297dd7cddfSDavid du Colombier 	if(im->repl)
8307dd7cddfSDavid du Colombier 		return;
8317dd7cddfSDavid du Colombier 
8327dd7cddfSDavid du Colombier 	/* fill in any outer edges */
8337dd7cddfSDavid du Colombier 	/* black border */
8347dd7cddfSDavid du Colombier 	r = rectaddpt(im->r, subpt(ul, im->r.min));
8357dd7cddfSDavid du Colombier 	border(screen, r, -2, display->black, ZP);
8367dd7cddfSDavid du Colombier 	r.min = subpt(r.min, Pt(2,2));
8377dd7cddfSDavid du Colombier 	r.max = addpt(r.max, Pt(2,2));
8387dd7cddfSDavid du Colombier 
8397dd7cddfSDavid du Colombier 	/* gray for the rest */
8407dd7cddfSDavid du Colombier 	if(gray == nil) {
8417dd7cddfSDavid du Colombier 		gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
8427dd7cddfSDavid du Colombier 		if(gray == nil) {
8437dd7cddfSDavid du Colombier 			fprint(2, "g out of memory: %r\n");
8447dd7cddfSDavid du Colombier 			wexits("mem");
8457dd7cddfSDavid du Colombier 		}
8467dd7cddfSDavid du Colombier 	}
8477dd7cddfSDavid du Colombier 	border(screen, r, -4000, gray, ZP);
8487dd7cddfSDavid du Colombier //	flushimage(display, 0);
8497dd7cddfSDavid du Colombier }
8507dd7cddfSDavid du Colombier 
8517dd7cddfSDavid du Colombier void
8527dd7cddfSDavid du Colombier eresized(int new)
8537dd7cddfSDavid du Colombier {
8547dd7cddfSDavid du Colombier 	Rectangle r;
8557dd7cddfSDavid du Colombier 	r = screen->r;
8567dd7cddfSDavid du Colombier 	if(new && getwindow(display, Refnone) < 0)
8577dd7cddfSDavid du Colombier 		fprint(2,"can't reattach to window");
8587dd7cddfSDavid du Colombier 	ul = addpt(ul, subpt(screen->r.min, r.min));
8597dd7cddfSDavid du Colombier 	redraw(screen);
8607dd7cddfSDavid du Colombier }
8617dd7cddfSDavid du Colombier 
8627dd7cddfSDavid du Colombier /* clip p to be in r */
8637dd7cddfSDavid du Colombier Point
8647dd7cddfSDavid du Colombier pclip(Point p, Rectangle r)
8657dd7cddfSDavid du Colombier {
8667dd7cddfSDavid du Colombier 	if(p.x < r.min.x)
8677dd7cddfSDavid du Colombier 		p.x = r.min.x;
8687dd7cddfSDavid du Colombier 	else if(p.x >= r.max.x)
8697dd7cddfSDavid du Colombier 		p.x = r.max.x-1;
8707dd7cddfSDavid du Colombier 
8717dd7cddfSDavid du Colombier 	if(p.y < r.min.y)
8727dd7cddfSDavid du Colombier 		p.y = r.min.y;
8737dd7cddfSDavid du Colombier 	else if(p.y >= r.max.y)
8747dd7cddfSDavid du Colombier 		p.y = r.max.y-1;
8757dd7cddfSDavid du Colombier 
8767dd7cddfSDavid du Colombier 	return p;
8777dd7cddfSDavid du Colombier }
8787dd7cddfSDavid du Colombier 
8797dd7cddfSDavid du Colombier /*
8807dd7cddfSDavid du Colombier  * resize is perhaps a misnomer.
8817dd7cddfSDavid du Colombier  * this really just grows the window to be at least dx across
8827dd7cddfSDavid du Colombier  * and dy high.  if the window hits the bottom or right edge,
8837dd7cddfSDavid du Colombier  * it is backed up until it hits the top or left edge.
8847dd7cddfSDavid du Colombier  */
8857dd7cddfSDavid du Colombier void
8867dd7cddfSDavid du Colombier resize(int dx, int dy)
8877dd7cddfSDavid du Colombier {
8887dd7cddfSDavid du Colombier 	static Rectangle sr;
8897dd7cddfSDavid du Colombier 	Rectangle r, or;
8907dd7cddfSDavid du Colombier 
8917dd7cddfSDavid du Colombier 	dx += 2*Borderwidth;
8927dd7cddfSDavid du Colombier 	dy += 2*Borderwidth;
8937dd7cddfSDavid du Colombier 	if(wctlfd < 0){
8947dd7cddfSDavid du Colombier 		wctlfd = open("/dev/wctl", OWRITE);
8957dd7cddfSDavid du Colombier 		if(wctlfd < 0)
8967dd7cddfSDavid du Colombier 			return;
8977dd7cddfSDavid du Colombier 	}
8987dd7cddfSDavid du Colombier 
8997dd7cddfSDavid du Colombier 	r = insetrect(screen->r, -Borderwidth);
9007dd7cddfSDavid du Colombier 	if(Dx(r) >= dx && Dy(r) >= dy)
9017dd7cddfSDavid du Colombier 		return;
9027dd7cddfSDavid du Colombier 
9037dd7cddfSDavid du Colombier 	if(Dx(sr)*Dy(sr) == 0)
9047dd7cddfSDavid du Colombier 		sr = screenrect();
9057dd7cddfSDavid du Colombier 
9067dd7cddfSDavid du Colombier 	or = r;
9077dd7cddfSDavid du Colombier 
9087dd7cddfSDavid du Colombier 	r.max.x = max(r.min.x+dx, r.max.x);
9097dd7cddfSDavid du Colombier 	r.max.y = max(r.min.y+dy, r.max.y);
9107dd7cddfSDavid du Colombier 	if(r.max.x > sr.max.x){
9117dd7cddfSDavid du Colombier 		if(Dx(r) > Dx(sr)){
9127dd7cddfSDavid du Colombier 			r.min.x = 0;
9137dd7cddfSDavid du Colombier 			r.max.x = sr.max.x;
9147dd7cddfSDavid du Colombier 		}else
9157dd7cddfSDavid du Colombier 			r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
9167dd7cddfSDavid du Colombier 	}
9177dd7cddfSDavid du Colombier 	if(r.max.y > sr.max.y){
9187dd7cddfSDavid du Colombier 		if(Dy(r) > Dy(sr)){
9197dd7cddfSDavid du Colombier 			r.min.y = 0;
9207dd7cddfSDavid du Colombier 			r.max.y = sr.max.y;
9217dd7cddfSDavid du Colombier 		}else
9227dd7cddfSDavid du Colombier 			r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
9237dd7cddfSDavid du Colombier 	}
9247dd7cddfSDavid du Colombier 
9257dd7cddfSDavid du Colombier 	/*
9267dd7cddfSDavid du Colombier 	 * Sometimes we can't actually grow the window big enough,
9277dd7cddfSDavid du Colombier 	 * and resizing it to the same shape makes it flash.
9287dd7cddfSDavid du Colombier 	 */
9297dd7cddfSDavid du Colombier 	if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
9307dd7cddfSDavid du Colombier 		return;
9317dd7cddfSDavid du Colombier 
9327dd7cddfSDavid du Colombier 	fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n",
9337dd7cddfSDavid du Colombier 		r.min.x, r.min.y, r.max.x, r.max.y);
9347dd7cddfSDavid du Colombier }
9357dd7cddfSDavid du Colombier 
9367dd7cddfSDavid du Colombier /*
9377dd7cddfSDavid du Colombier  * If we allocimage after a resize but before flushing the draw buffer,
9387dd7cddfSDavid du Colombier  * we won't have seen the reshape event, and we won't have called
9397dd7cddfSDavid du Colombier  * getwindow, and allocimage will fail.  So we flushimage before every alloc.
9407dd7cddfSDavid du Colombier  */
9417dd7cddfSDavid du Colombier Image*
9427dd7cddfSDavid du Colombier xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
9437dd7cddfSDavid du Colombier {
9447dd7cddfSDavid du Colombier 	flushimage(display, 0);
9457dd7cddfSDavid du Colombier 	return allocimage(d, r, chan, repl, val);
9467dd7cddfSDavid du Colombier }
9477dd7cddfSDavid du Colombier 
9487dd7cddfSDavid du Colombier /* all code below this line should be in the library, but is stolen from colors instead */
9497dd7cddfSDavid du Colombier static char*
9507dd7cddfSDavid du Colombier rdenv(char *name)
9517dd7cddfSDavid du Colombier {
9527dd7cddfSDavid du Colombier 	char *v;
9537dd7cddfSDavid du Colombier 	int fd, size;
9547dd7cddfSDavid du Colombier 
9557dd7cddfSDavid du Colombier 	fd = open(name, OREAD);
9567dd7cddfSDavid du Colombier 	if(fd < 0)
9577dd7cddfSDavid du Colombier 		return 0;
9587dd7cddfSDavid du Colombier 	size = seek(fd, 0, 2);
9597dd7cddfSDavid du Colombier 	v = malloc(size+1);
9607dd7cddfSDavid du Colombier 	if(v == 0){
9617dd7cddfSDavid du Colombier 		fprint(2, "page: can't malloc: %r\n");
9627dd7cddfSDavid du Colombier 		wexits("no mem");
9637dd7cddfSDavid du Colombier 	}
9647dd7cddfSDavid du Colombier 	seek(fd, 0, 0);
9657dd7cddfSDavid du Colombier 	read(fd, v, size);
9667dd7cddfSDavid du Colombier 	v[size] = 0;
9677dd7cddfSDavid du Colombier 	close(fd);
9687dd7cddfSDavid du Colombier 	return v;
9697dd7cddfSDavid du Colombier }
9707dd7cddfSDavid du Colombier 
9717dd7cddfSDavid du Colombier void
9727dd7cddfSDavid du Colombier newwin(void)
9737dd7cddfSDavid du Colombier {
9747dd7cddfSDavid du Colombier 	char *srv, *mntsrv;
9757dd7cddfSDavid du Colombier 	char spec[100];
9767dd7cddfSDavid du Colombier 	int srvfd, cons, pid;
9777dd7cddfSDavid du Colombier 
9787dd7cddfSDavid du Colombier 	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){
9797dd7cddfSDavid du Colombier 	case -1:
9807dd7cddfSDavid du Colombier 		fprint(2, "page: can't fork: %r\n");
9817dd7cddfSDavid du Colombier 		wexits("no fork");
9827dd7cddfSDavid du Colombier 	case 0:
9837dd7cddfSDavid du Colombier 		break;
9847dd7cddfSDavid du Colombier 	default:
9857dd7cddfSDavid du Colombier 		wexits(0);
9867dd7cddfSDavid du Colombier 	}
9877dd7cddfSDavid du Colombier 
9887dd7cddfSDavid du Colombier 	srv = rdenv("/env/wsys");
9897dd7cddfSDavid du Colombier 	if(srv == 0){
9907dd7cddfSDavid du Colombier 		mntsrv = rdenv("/mnt/term/env/wsys");
9917dd7cddfSDavid du Colombier 		if(mntsrv == 0){
9927dd7cddfSDavid du Colombier 			fprint(2, "page: can't find $wsys\n");
9937dd7cddfSDavid du Colombier 			wexits("srv");
9947dd7cddfSDavid du Colombier 		}
9957dd7cddfSDavid du Colombier 		srv = malloc(strlen(mntsrv)+10);
9967dd7cddfSDavid du Colombier 		sprint(srv, "/mnt/term%s", mntsrv);
9977dd7cddfSDavid du Colombier 		free(mntsrv);
9987dd7cddfSDavid du Colombier 		pid  = 0;			/* can't send notes to remote processes! */
9997dd7cddfSDavid du Colombier 	}else
10007dd7cddfSDavid du Colombier 		pid = getpid();
10017dd7cddfSDavid du Colombier 	srvfd = open(srv, ORDWR);
10027dd7cddfSDavid du Colombier 	if(srvfd == -1){
10037dd7cddfSDavid du Colombier 		fprint(2, "page: can't open %s: %r\n", srv);
10047dd7cddfSDavid du Colombier 		wexits("no srv");
10057dd7cddfSDavid du Colombier 	}
1006fae06aafSDavid du Colombier 	free(srv);
10077dd7cddfSDavid du Colombier 	sprint(spec, "new -pid %d", pid);
10089a747e4fSDavid du Colombier 	if(mount(srvfd, -1, "/mnt/wsys", 0, spec) == -1){
10097dd7cddfSDavid du Colombier 		fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec);
10107dd7cddfSDavid du Colombier 		wexits("no mount");
10117dd7cddfSDavid du Colombier 	}
10127dd7cddfSDavid du Colombier 	close(srvfd);
10137dd7cddfSDavid du Colombier 	unmount("/mnt/acme", "/dev");
10147dd7cddfSDavid du Colombier 	bind("/mnt/wsys", "/dev", MBEFORE);
10157dd7cddfSDavid du Colombier 	cons = open("/dev/cons", OREAD);
10167dd7cddfSDavid du Colombier 	if(cons==-1){
10177dd7cddfSDavid du Colombier 	NoCons:
10187dd7cddfSDavid du Colombier 		fprint(2, "page: can't open /dev/cons: %r");
10197dd7cddfSDavid du Colombier 		wexits("no cons");
10207dd7cddfSDavid du Colombier 	}
10217dd7cddfSDavid du Colombier 	dup(cons, 0);
10227dd7cddfSDavid du Colombier 	close(cons);
10237dd7cddfSDavid du Colombier 	cons = open("/dev/cons", OWRITE);
10247dd7cddfSDavid du Colombier 	if(cons==-1)
10257dd7cddfSDavid du Colombier 		goto NoCons;
10267dd7cddfSDavid du Colombier 	dup(cons, 1);
10277dd7cddfSDavid du Colombier 	dup(cons, 2);
10287dd7cddfSDavid du Colombier 	close(cons);
10297dd7cddfSDavid du Colombier //	wctlfd = open("/dev/wctl", OWRITE);
10307dd7cddfSDavid du Colombier }
10317dd7cddfSDavid du Colombier 
10327dd7cddfSDavid du Colombier Rectangle
10337dd7cddfSDavid du Colombier screenrect(void)
10347dd7cddfSDavid du Colombier {
10357dd7cddfSDavid du Colombier 	int fd;
10367dd7cddfSDavid du Colombier 	char buf[12*5];
10377dd7cddfSDavid du Colombier 
10387dd7cddfSDavid du Colombier 	fd = open("/dev/screen", OREAD);
10397dd7cddfSDavid du Colombier 	if(fd == -1)
10407dd7cddfSDavid du Colombier 		fd=open("/mnt/term/dev/screen", OREAD);
10417dd7cddfSDavid du Colombier 	if(fd == -1){
10427dd7cddfSDavid du Colombier 		fprint(2, "page: can't open /dev/screen: %r\n");
10437dd7cddfSDavid du Colombier 		wexits("window read");
10447dd7cddfSDavid du Colombier 	}
10457dd7cddfSDavid du Colombier 	if(read(fd, buf, sizeof buf) != sizeof buf){
10467dd7cddfSDavid du Colombier 		fprint(2, "page: can't read /dev/screen: %r\n");
10477dd7cddfSDavid du Colombier 		wexits("screen read");
10487dd7cddfSDavid du Colombier 	}
10497dd7cddfSDavid du Colombier 	close(fd);
10507dd7cddfSDavid du Colombier 	return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48));
10517dd7cddfSDavid du Colombier }
10527dd7cddfSDavid du Colombier 
10537def40e1SDavid du Colombier void
10547def40e1SDavid du Colombier zerox(void)
10557def40e1SDavid du Colombier {
10567def40e1SDavid du Colombier 	int pfd[2];
10577def40e1SDavid du Colombier 
10587def40e1SDavid du Colombier 	pipe(pfd);
10597def40e1SDavid du Colombier 	switch(rfork(RFFDG|RFREND|RFPROC)) {
10607def40e1SDavid du Colombier 		case -1:
10617def40e1SDavid du Colombier 			wexits("cannot fork in zerox: %r");
10627def40e1SDavid du Colombier 		case 0:
10637def40e1SDavid du Colombier 			dup(pfd[1], 0);
10647def40e1SDavid du Colombier 			close(pfd[0]);
1065f19e7b74SDavid du Colombier 			execl("/bin/page", "page", "-w", nil);
10667def40e1SDavid du Colombier 			wexits("cannot exec in zerox: %r\n");
10677def40e1SDavid du Colombier 		default:
10687def40e1SDavid du Colombier 			close(pfd[1]);
10697def40e1SDavid du Colombier 			writeimage(pfd[0], im, 0);
10707def40e1SDavid du Colombier 			close(pfd[0]);
10717def40e1SDavid du Colombier 			break;
10727def40e1SDavid du Colombier 	}
10737def40e1SDavid du Colombier }
1074