| #include	<u.h> | 
 | #include	<libc.h> | 
 | #include	<draw.h> | 
 | #include	<cursor.h> | 
 | #include	<event.h> | 
 | #include	<bio.h> | 
 | #include	"proof.h" | 
 |  | 
 | int	res; | 
 | int	hpos; | 
 | int	vpos; | 
 | int	DIV = 11; | 
 |  | 
 | Point offset; | 
 | Point xyoffset = { 0,0 }; | 
 |  | 
 | Rectangle	view[MAXVIEW]; | 
 | Rectangle	bound[MAXVIEW];		/* extreme points */ | 
 | int	nview = 1; | 
 |  | 
 | int	lastp;	/* last page number we were on */ | 
 |  | 
 | #define	NPAGENUMS	200 | 
 | struct pagenum { | 
 | 	int	num; | 
 | 	long	adr; | 
 | } pagenums[NPAGENUMS]; | 
 | int	npagenums; | 
 |  | 
 | int	curfont, cursize; | 
 |  | 
 | char	*getcmdstr(void); | 
 |  | 
 | static void	initpage(void); | 
 | static void	view_setup(int); | 
 | static Point	scale(Point); | 
 | static void	clearview(Rectangle); | 
 | static int	addpage(int); | 
 | static void	spline(Image *, int, Point *); | 
 | static int	skipto(int, int); | 
 | static void	wiggly(int); | 
 | static void	devcntrl(void); | 
 | static void	eatline(void); | 
 | static int	getn(void); | 
 | static int	botpage(int); | 
 | static void	getstr(char *); | 
 | /* | 
 | static void	getutf(char *); | 
 | */ | 
 |  | 
 | #define Do screen->r.min | 
 | #define Dc screen->r.max | 
 |  | 
 | /* declarations and definitions of font stuff are in font.c and main.c */ | 
 |  | 
 | static void | 
 | initpage(void) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	view_setup(nview); | 
 | 	for (i = 0; i < nview-1; i++) | 
 | 		draw(screen, view[i], screen, nil, view[i+1].min); | 
 | 	clearview(view[nview-1]); | 
 | 	offset = view[nview-1].min; | 
 | 	vpos = 0; | 
 | } | 
 |  | 
 | static void | 
 | view_setup(int n) | 
 | { | 
 | 	int i, j, v, dx, dy, r, c; | 
 |  | 
 | 	switch (n) { | 
 | 	case 1: r = 1; c = 1; break; | 
 | 	case 2: r = 1; c = 2; break; | 
 | 	case 3: r = 1; c = 3; break; | 
 | 	case 4: r = 2; c = 2; break; | 
 | 	case 5: case 6: r = 2; c = 3; break; | 
 | 	case 7: case 8: case 9: r = 3; c = 3; break; | 
 | 	default: r = (n+2)/3; c = 3; break; /* finking out */ | 
 | 	} | 
 | 	dx = (Dc.x - Do.x) / c; | 
 | 	dy = (Dc.y - Do.y) / r; | 
 | 	v = 0; | 
 | 	for (i = 0; i < r && v < n; i++) | 
 | 		for (j = 0; j < c && v < n; j++) { | 
 | 			view[v] = screen->r; | 
 | 			view[v].min.x = Do.x + j * dx; | 
 | 			view[v].max.x = Do.x + (j+1) * dx; | 
 | 			view[v].min.y = Do.y + i * dy; | 
 | 			view[v].max.y = Do.y + (i+1) * dy; | 
 | 			v++; | 
 | 		} | 
 | } | 
 |  | 
 | static void | 
 | clearview(Rectangle r) | 
 | { | 
 | 	draw(screen, r, display->white, nil, r.min); | 
 | } | 
 |  | 
 | int resized; | 
 | void eresized(int new) | 
 | { | 
 | 	/* this is called if we are resized */ | 
 | 	if(new && getwindow(display, Refnone) < 0) | 
 | 		drawerror(display, "can't reattach to window"); | 
 | 	initpage(); | 
 | 	resized = 1; | 
 | } | 
 |  | 
 | static Point | 
 | scale(Point p) | 
 | { | 
 | 	p.x /= DIV; | 
 | 	p.y /= DIV; | 
 | 	return addpt(xyoffset, addpt(offset,p)); | 
 | } | 
 |  | 
 | static int | 
 | addpage(int n) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < npagenums; i++) | 
 | 		if (n == pagenums[i].num) | 
 | 			return i; | 
 | 	if (npagenums < NPAGENUMS-1) { | 
 | 		pagenums[npagenums].num = n; | 
 | 		pagenums[npagenums].adr = offsetc(); | 
 | 		npagenums++; | 
 | 	} | 
 | 	return npagenums; | 
 | } | 
 |  | 
 | void | 
 | readpage(void) | 
 | { | 
 | 	int c, i, a, alpha, phi; | 
 | 	static int first = 0; | 
 | 	int m, n, gonow = 1; | 
 | 	Rune r[32], t; | 
 | 	Point p,q,qq; | 
 |  | 
 | 	offset = screen->clipr.min; | 
 | 	esetcursor(&deadmouse); | 
 | 	while (gonow) | 
 | 	{ | 
 | 		c = getc(); | 
 | 		switch (c) | 
 | 		{ | 
 | 		case -1: | 
 | 			esetcursor(0); | 
 | 			if (botpage(lastp+1)) { | 
 | 				initpage(); | 
 | 				break; | 
 | 			} | 
 | 			exits(0); | 
 | 		case 'p':	/* new page */ | 
 | 			lastp = getn(); | 
 | 			addpage(lastp); | 
 | 			if (first++ > 0) { | 
 | 				esetcursor(0); | 
 | 				botpage(lastp); | 
 | 				esetcursor(&deadmouse); | 
 | 			} | 
 | 			initpage(); | 
 | 			break; | 
 | 		case '\n':	/* when input is text */ | 
 | 		case ' ': | 
 | 		case 0:		/* occasional noise creeps in */ | 
 | 			break; | 
 | 		case '0': case '1': case '2': case '3': case '4': | 
 | 		case '5': case '6': case '7': case '8': case '9': | 
 | 			/* two motion digits plus a character */ | 
 | 			hpos += (c-'0')*10 + getc()-'0'; | 
 |  | 
 | 		/* FALLS THROUGH */ | 
 | 		case 'c':	/* single ascii character */ | 
 | 			r[0] = getrune(); | 
 | 			r[1] = 0; | 
 | 			dochar(r); | 
 | 			break; | 
 |  | 
 | 		case 'C': | 
 | 			for(i=0; ; i++){ | 
 | 				t = getrune(); | 
 | 				if(isspace(t)) | 
 | 					break; | 
 | 				r[i] = t; | 
 | 			} | 
 | 			r[i] = 0; | 
 | 			dochar(r); | 
 | 			break; | 
 |  | 
 | 		case 'N': | 
 | 			r[0] = getn(); | 
 | 			r[1] = 0; | 
 | 			dochar(r); | 
 | 			break; | 
 |  | 
 | 		case 'D':	/* draw function */ | 
 | 			switch (getc()) | 
 | 			{ | 
 | 			case 'l':	/* draw a line */ | 
 | 				n = getn(); | 
 | 				m = getn(); | 
 | 				p = Pt(hpos,vpos); | 
 | 				q = addpt(p, Pt(n,m)); | 
 | 				hpos += n; | 
 | 				vpos += m; | 
 | 				line(screen, scale(p), scale(q), 0, 0, 0, display->black, ZP); | 
 | 				break; | 
 | 			case 'c':	/* circle */ | 
 | 				/*nop*/ | 
 | 				m = getn()/2; | 
 | 				p = Pt(hpos+m,vpos); | 
 | 				hpos += 2*m; | 
 | 				ellipse(screen, scale(p), m/DIV, m/DIV, 0, display->black, ZP); | 
 | 				/* p=currentpt; p.x+=dmap(m/2);circle bp,p,a,ONES,Mode*/ | 
 | 				break; | 
 | 			case 'e':	/* ellipse */ | 
 | 				/*nop*/ | 
 | 				m = getn()/2; | 
 | 				n = getn()/2; | 
 | 				p = Pt(hpos+m,vpos); | 
 | 				hpos += 2*m; | 
 | 				ellipse(screen, scale(p), m/DIV, n/DIV, 0, display->black, ZP); | 
 | 				break; | 
 | 			case 'a':	/* arc */ | 
 | 				p = scale(Pt(hpos,vpos)); | 
 | 				n = getn(); | 
 | 				m = getn(); | 
 | 				hpos += n; | 
 | 				vpos += m; | 
 | 				q = scale(Pt(hpos,vpos)); | 
 | 				n = getn(); | 
 | 				m = getn(); | 
 | 				hpos += n; | 
 | 				vpos += m; | 
 | 				qq = scale(Pt(hpos,vpos)); | 
 | 				/* | 
 | 				  * tricky: convert from 3-point clockwise to | 
 | 				  * center, angle1, delta-angle counterclockwise. | 
 | 				 */ | 
 | 				a = hypot(qq.x-q.x, qq.y-q.y); | 
 | 				phi = atan2(q.y-p.y, p.x-q.x)*180./PI; | 
 | 				alpha = atan2(q.y-qq.y, qq.x-q.x)*180./PI - phi; | 
 | 				if(alpha < 0) | 
 | 					alpha += 360; | 
 | 				arc(screen, q, a, a, 0, display->black, ZP, phi, alpha); | 
 | 				break; | 
 | 			case '~':	/* wiggly line */ | 
 | 				wiggly(0); | 
 | 				break; | 
 | 			default: | 
 | 				break; | 
 | 			} | 
 | 			eatline(); | 
 | 			break; | 
 | 		case 's': | 
 | 			n = getn();	/* ignore fractional sizes */ | 
 | 			if (cursize == n) | 
 | 				break; | 
 | 			cursize = n; | 
 | 			if (cursize >= NFONT) | 
 | 				cursize = NFONT-1; | 
 | 			break; | 
 | 		case 'f': | 
 | 			curfont = getn(); | 
 | 			break; | 
 | 		case 'H':	/* absolute horizontal motion */ | 
 | 			hpos = getn(); | 
 | 			break; | 
 | 		case 'h':	/* relative horizontal motion */ | 
 | 			hpos += getn(); | 
 | 			break; | 
 | 		case 'w':	/* word space */ | 
 | 			break; | 
 | 		case 'V': | 
 | 			vpos = getn(); | 
 | 			break; | 
 | 		case 'v': | 
 | 			vpos += getn(); | 
 | 			break; | 
 | 		case '#':	/* comment */ | 
 | 		case 'n':	/* end of line */ | 
 | 			eatline(); | 
 | 			break; | 
 | 		case 'x':	/* device control */ | 
 | 			devcntrl(); | 
 | 			break; | 
 | 		default: | 
 | 			fprint(2, "unknown input character %o %c at offset %lud\n", c, c, offsetc()); | 
 | 			exits("bad char"); | 
 | 		} | 
 | 	} | 
 | 	esetcursor(0); | 
 | } | 
 |  | 
 | static void | 
 | spline(Image *b, int n, Point *pp) | 
 | { | 
 | 	long w, t1, t2, t3, fac=1000;  | 
 | 	int i, j, steps=10;  | 
 | 	Point p, q; | 
 |  | 
 | 	for (i = n; i > 0; i--) | 
 | 		pp[i] = pp[i-1]; | 
 | 	pp[n+1] = pp[n]; | 
 | 	n += 2; | 
 | 	p = pp[0]; | 
 | 	for(i = 0; i < n-2; i++) | 
 | 	{ | 
 | 		for(j = 0; j < steps; j++) | 
 | 		{ | 
 | 			w = fac * j / steps; | 
 | 			t1 = w * w / (2 * fac); | 
 | 			w = w - fac/2; | 
 | 			t2 = 3*fac/4 - w * w / fac; | 
 | 			w = w - fac/2; | 
 | 			t3 = w * w / (2*fac); | 
 | 			q.x = (t1*pp[i+2].x + t2*pp[i+1].x +  | 
 | 				t3*pp[i].x + fac/2) / fac; | 
 | 			q.y = (t1*pp[i+2].y + t2*pp[i+1].y +  | 
 | 				t3*pp[i].y + fac/2) / fac; | 
 | 			line(b, p, q, 0, 0, 0, display->black, ZP); | 
 | 			p = q; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | /* Have to parse skipped pages, to find out what fonts are loaded. */ | 
 | static int | 
 | skipto(int gotop, int curp) | 
 | { | 
 | 	char *p; | 
 | 	int i; | 
 |  | 
 | 	if (gotop == curp) | 
 | 		return 1; | 
 | 	for (i = 0; i < npagenums; i++) | 
 | 		if (pagenums[i].num == gotop) { | 
 | 			if (seekc(pagenums[i].adr) == Beof) { | 
 | 				fprint(2, "can't rewind input\n"); | 
 | 				return 0; | 
 | 			} | 
 | 			return 1; | 
 | 		} | 
 | 	if (gotop <= curp) { | 
 | 	    restart: | 
 | 		if (seekc(0) == Beof) { | 
 | 			fprint(2, "can't rewind input\n"); | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	for(;;){ | 
 | 		p = rdlinec(); | 
 | 		if (p == 0) { | 
 | 			if(gotop>curp){ | 
 | 				gotop = curp; | 
 | 				goto restart; | 
 | 			} | 
 | 			return 0; | 
 | 		} else if (*p == 'p') { | 
 | 			lastp = curp = atoi(p+1); | 
 | 			addpage(lastp);	/* maybe 1 too high */ | 
 | 			if (curp>=gotop) | 
 | 				return 1; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | wiggly(int skip) | 
 | { | 
 | 	Point p[300]; | 
 | 	int c,i,n; | 
 | 	for (n = 1; (c = getc()) != '\n' && c>=0; n++) { | 
 | 		ungetc(); | 
 | 		p[n].x = getn(); | 
 | 		p[n].y = getn(); | 
 | 	} | 
 | 	p[0] = Pt(hpos, vpos); | 
 | 	for (i = 1; i < n; i++) | 
 | 		p[i] = addpt(p[i],p[i-1]); | 
 | 	hpos = p[n-1].x; | 
 | 	vpos = p[n-1].y; | 
 | 	for (i = 0; i < n; i++) | 
 | 		p[i] = scale(p[i]); | 
 | 	if (!skip) | 
 | 		spline(screen,n,p); | 
 | } | 
 |  | 
 | static void | 
 | devcntrl(void)	/* interpret device control functions */ | 
 | { | 
 |         char str[80]; | 
 | 	int n; | 
 |  | 
 | 	getstr(str); | 
 | 	switch (str[0]) {	/* crude for now */ | 
 | 	case 'i':	/* initialize */ | 
 | 		break; | 
 | 	case 'T':	/* device name */ | 
 | 		getstr(devname); | 
 | 		break; | 
 | 	case 't':	/* trailer */ | 
 | 		break; | 
 | 	case 'p':	/* pause -- can restart */ | 
 | 		break; | 
 | 	case 's':	/* stop */ | 
 | 		break; | 
 | 	case 'r':	/* resolution assumed when prepared */ | 
 | 		res=getn(); | 
 | 		DIV = floor(.5 + res/(100.0*mag)); | 
 | 		if (DIV < 1) | 
 | 			DIV = 1; | 
 | 		mag = res/(100.0*DIV); /* adjust mag according to DIV coarseness */ | 
 | 		break; | 
 | 	case 'f':	/* font used */ | 
 | 		n = getn(); | 
 | 		getstr(str); | 
 | 		loadfontname(n, str); | 
 | 		break; | 
 | 	/* these don't belong here... */ | 
 | 	case 'H':	/* char height */ | 
 | 		break; | 
 | 	case 'S':	/* slant */ | 
 | 		break; | 
 | 	case 'X': | 
 | 		break; | 
 | 	} | 
 | 	eatline(); | 
 | } | 
 |  | 
 | int | 
 | isspace(int c) | 
 | { | 
 | 	return c==' ' || c=='\t' || c=='\n'; | 
 | } | 
 |  | 
 | static void | 
 | getstr(char *is) | 
 | { | 
 | 	uchar *s = (uchar *) is; | 
 |  | 
 | 	for (*s = getc(); isspace(*s); *s = getc()) | 
 | 		; | 
 | 	for (; !isspace(*s); *++s = getc()) | 
 | 		; | 
 | 	ungetc(); | 
 | 	*s = 0; | 
 | } | 
 |  | 
 | #if 0 | 
 | static void | 
 | getutf(char *s)		/* get next utf char, as bytes */ | 
 | { | 
 | 	int c, i; | 
 |  | 
 | 	for (i=0;;) { | 
 | 		c = getc(); | 
 | 		if (c < 0) | 
 | 			return; | 
 | 		s[i++] = c; | 
 |  | 
 | 		if (fullrune(s, i)) { | 
 | 			s[i] = 0; | 
 | 			return; | 
 | 		} | 
 | 	} | 
 | } | 
 | #endif | 
 |  | 
 | static void | 
 | eatline(void) | 
 | { | 
 | 	int c; | 
 |  | 
 | 	while ((c=getc()) != '\n' && c >= 0) | 
 | 		; | 
 | } | 
 |  | 
 | static int | 
 | getn(void) | 
 | { | 
 | 	int n, c, sign; | 
 |  | 
 | 	while (c = getc()) | 
 | 		if (!isspace(c)) | 
 | 			break; | 
 | 	if(c == '-'){ | 
 | 		sign = -1; | 
 | 		c = getc(); | 
 | 	}else | 
 | 		sign = 1; | 
 | 	for (n = 0; '0'<=c && c<='9'; c = getc()) | 
 | 		n = n*10 + c - '0'; | 
 | 	while (c == ' ') | 
 | 		c = getc(); | 
 | 	ungetc(); | 
 | 	return(n*sign); | 
 | } | 
 |  | 
 | static int | 
 | botpage(int np)	/* called at bottom of page np-1 == top of page np */ | 
 | { | 
 | 	char *p; | 
 | 	int n; | 
 |  | 
 | 	while (p = getcmdstr()) { | 
 | 		if (*p == '\0') | 
 | 			return 0; | 
 | 		if (*p == 'q') | 
 | 			exits(p); | 
 | 		if (*p == 'c')		/* nop */ | 
 | 			continue; | 
 | 		if (*p == 'm') { | 
 | 			mag = atof(p+1); | 
 | 			if (mag <= .1 || mag >= 10) | 
 | 				mag = DEFMAG; | 
 | 			allfree();	/* zap fonts */ | 
 | 			DIV = floor(.5 + res/(100.0*mag)); | 
 | 			if (DIV < 1) | 
 | 				DIV = 1; | 
 | 			mag = res/(100.0*DIV); | 
 | 			return skipto(np-1, np);	/* reprint the page */ | 
 | 		} | 
 | 		if (*p == 'x') { | 
 | 			xyoffset.x += atoi(p+1)*100; | 
 | 			skipto(np-1, np); | 
 | 			return 1; | 
 | 		} | 
 | 		if (*p == 'y') { | 
 | 			xyoffset.y += atoi(p+1)*100; | 
 | 			skipto(np-1, np); | 
 | 			return 1; | 
 | 		} | 
 | 		if (*p == '/') {	/* divide into n pieces */ | 
 | 			nview = atoi(p+1); | 
 | 			if (nview < 1) | 
 | 				nview = 1; | 
 | 			else if (nview > MAXVIEW) | 
 | 				nview = MAXVIEW; | 
 | 			return skipto(np-1, np); | 
 | 		} | 
 | 		if (*p == 'p') { | 
 | 			if (p[1] == '\0'){	/* bare 'p' */ | 
 | 				if(skipto(np-1, np)) | 
 | 					return 1; | 
 | 				continue; | 
 | 			} | 
 | 			p++; | 
 | 		} | 
 | 		if ('0'<=*p && *p<='9') { | 
 | 			n = atoi(p); | 
 | 			if(skipto(n, np)) | 
 | 				return 1; | 
 | 			continue; | 
 | 		} | 
 | 		if (*p == '-' || *p == '+') { | 
 | 			n = atoi(p); | 
 | 			if (n == 0) | 
 | 				n = *p == '-' ? -1 : 1; | 
 | 			if(skipto(np - 1 + n, np)) | 
 | 				return 1; | 
 | 			continue; | 
 | 		} | 
 | 		if (*p == 'd') { | 
 | 			dbg = 1 - dbg; | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		fprint(2, "illegal;  try q, 17, +2, -1, p, m.7, /2, x1, y-.5 or return\n"); | 
 | 	} | 
 | 	return 0; | 
 | } |