1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <cursor.h>
5 #include <event.h>
6 #include <bio.h>
7 #include "proof.h"
8
9 int res;
10 int hpos;
11 int vpos;
12 int DIV = 11;
13
14 Point offset;
15 Point xyoffset = { 0,0 };
16
17 Rectangle view[MAXVIEW];
18 Rectangle bound[MAXVIEW]; /* extreme points */
19 int nview = 1;
20
21 int lastp; /* last page number we were on */
22
23 #define NPAGENUMS 200
24 struct pagenum {
25 int num;
26 long adr;
27 } pagenums[NPAGENUMS];
28 int npagenums;
29
30 int curfont, cursize;
31
32 char *getcmdstr(void);
33
34 static void initpage(void);
35 static void view_setup(int);
36 static Point scale(Point);
37 static void clearview(Rectangle);
38 static int addpage(int);
39 static void spline(Image *, int, Point *);
40 static int skipto(int, int);
41 static void wiggly(int);
42 static void devcntrl(void);
43 static void eatline(void);
44 static int getn(void);
45 static int botpage(int);
46 static void getstr(char *);
47 static void getutf(char *);
48
49 #define Do screen->r.min
50 #define Dc screen->r.max
51
52 /* declarations and definitions of font stuff are in font.c and main.c */
53
54 static void
initpage(void)55 initpage(void)
56 {
57 int i;
58
59 view_setup(nview);
60 for (i = 0; i < nview-1; i++)
61 draw(screen, view[i], screen, nil, view[i+1].min);
62 clearview(view[nview-1]);
63 offset = view[nview-1].min;
64 vpos = 0;
65 }
66
67 static void
view_setup(int n)68 view_setup(int n)
69 {
70 int i, j, v, dx, dy, r, c;
71
72 switch (n) {
73 case 1: r = 1; c = 1; break;
74 case 2: r = 1; c = 2; break;
75 case 3: r = 1; c = 3; break;
76 case 4: r = 2; c = 2; break;
77 case 5: case 6: r = 2; c = 3; break;
78 case 7: case 8: case 9: r = 3; c = 3; break;
79 default: r = (n+2)/3; c = 3; break; /* finking out */
80 }
81 dx = (Dc.x - Do.x) / c;
82 dy = (Dc.y - Do.y) / r;
83 v = 0;
84 for (i = 0; i < r && v < n; i++)
85 for (j = 0; j < c && v < n; j++) {
86 view[v] = screen->r;
87 view[v].min.x = Do.x + j * dx;
88 view[v].max.x = Do.x + (j+1) * dx;
89 view[v].min.y = Do.y + i * dy;
90 view[v].max.y = Do.y + (i+1) * dy;
91 v++;
92 }
93 }
94
95 static void
clearview(Rectangle r)96 clearview(Rectangle r)
97 {
98 draw(screen, r, display->white, nil, r.min);
99 }
100
101 int resized;
eresized(int new)102 void eresized(int new)
103 {
104 /* this is called if we are resized */
105 if(new && getwindow(display, Refnone) < 0)
106 drawerror(display, "can't reattach to window");
107 initpage();
108 resized = 1;
109 }
110
111 static Point
scale(Point p)112 scale(Point p)
113 {
114 p.x /= DIV;
115 p.y /= DIV;
116 return addpt(xyoffset, addpt(offset,p));
117 }
118
119 static int
addpage(int n)120 addpage(int n)
121 {
122 int i;
123
124 for (i = 0; i < npagenums; i++)
125 if (n == pagenums[i].num)
126 return i;
127 if (npagenums < NPAGENUMS-1) {
128 pagenums[npagenums].num = n;
129 pagenums[npagenums].adr = offsetc();
130 npagenums++;
131 }
132 return npagenums;
133 }
134
135 void
readpage(void)136 readpage(void)
137 {
138 int c, i, a, alpha, phi;
139 static int first = 0;
140 int m, n, gonow = 1;
141 Rune r[32], t;
142 Point p,q,qq;
143
144 offset = screen->clipr.min;
145 esetcursor(&deadmouse);
146 while (gonow)
147 {
148 c = getc();
149 switch (c)
150 {
151 case -1:
152 esetcursor(0);
153 if (botpage(lastp+1)) {
154 initpage();
155 break;
156 }
157 exits(0);
158 case 'p': /* new page */
159 lastp = getn();
160 addpage(lastp);
161 if (first++ > 0) {
162 esetcursor(0);
163 botpage(lastp);
164 esetcursor(&deadmouse);
165 }
166 initpage();
167 break;
168 case '\n': /* when input is text */
169 case ' ':
170 case 0: /* occasional noise creeps in */
171 break;
172 case '0': case '1': case '2': case '3': case '4':
173 case '5': case '6': case '7': case '8': case '9':
174 /* two motion digits plus a character */
175 hpos += (c-'0')*10 + getc()-'0';
176
177 /* FALLS THROUGH */
178 case 'c': /* single ascii character */
179 r[0] = getrune();
180 r[1] = 0;
181 dochar(r);
182 break;
183
184 case 'C':
185 for(i=0; ; i++){
186 t = getrune();
187 if(isspace(t))
188 break;
189 r[i] = t;
190 }
191 r[i] = 0;
192 dochar(r);
193 break;
194
195 case 'N':
196 r[0] = getn();
197 r[1] = 0;
198 dochar(r);
199 break;
200
201 case 'D': /* draw function */
202 switch (getc())
203 {
204 case 'l': /* draw a line */
205 n = getn();
206 m = getn();
207 p = Pt(hpos,vpos);
208 q = addpt(p, Pt(n,m));
209 hpos += n;
210 vpos += m;
211 line(screen, scale(p), scale(q), 0, 0, 0, display->black, ZP);
212 break;
213 case 'c': /* circle */
214 /*nop*/
215 m = getn()/2;
216 p = Pt(hpos+m,vpos);
217 hpos += 2*m;
218 ellipse(screen, scale(p), m/DIV, m/DIV, 0, display->black, ZP);
219 /* p=currentpt; p.x+=dmap(m/2);circle bp,p,a,ONES,Mode*/
220 break;
221 case 'e': /* ellipse */
222 /*nop*/
223 m = getn()/2;
224 n = getn()/2;
225 p = Pt(hpos+m,vpos);
226 hpos += 2*m;
227 ellipse(screen, scale(p), m/DIV, n/DIV, 0, display->black, ZP);
228 break;
229 case 'a': /* arc */
230 p = scale(Pt(hpos,vpos));
231 n = getn();
232 m = getn();
233 hpos += n;
234 vpos += m;
235 q = scale(Pt(hpos,vpos));
236 n = getn();
237 m = getn();
238 hpos += n;
239 vpos += m;
240 qq = scale(Pt(hpos,vpos));
241 /*
242 * tricky: convert from 3-point clockwise to
243 * center, angle1, delta-angle counterclockwise.
244 */
245 a = hypot(qq.x-q.x, qq.y-q.y);
246 phi = atan2(q.y-p.y, p.x-q.x)*180./PI;
247 alpha = atan2(q.y-qq.y, qq.x-q.x)*180./PI - phi;
248 if(alpha < 0)
249 alpha += 360;
250 arc(screen, q, a, a, 0, display->black, ZP, phi, alpha);
251 break;
252 case '~': /* wiggly line */
253 wiggly(0);
254 break;
255 default:
256 break;
257 }
258 eatline();
259 break;
260 case 's':
261 n = getn(); /* ignore fractional sizes */
262 if (cursize == n)
263 break;
264 cursize = n;
265 if (cursize >= NFONT)
266 cursize = NFONT-1;
267 break;
268 case 'f':
269 curfont = getn();
270 break;
271 case 'H': /* absolute horizontal motion */
272 hpos = getn();
273 break;
274 case 'h': /* relative horizontal motion */
275 hpos += getn();
276 break;
277 case 'w': /* word space */
278 break;
279 case 'V':
280 vpos = getn();
281 break;
282 case 'v':
283 vpos += getn();
284 break;
285 case '#': /* comment */
286 case 'n': /* end of line */
287 eatline();
288 break;
289 case 'x': /* device control */
290 devcntrl();
291 break;
292 default:
293 fprint(2, "unknown input character %o %c at offset %lud\n", c, c, offsetc());
294 exits("bad char");
295 }
296 }
297 esetcursor(0);
298 }
299
300 static void
spline(Image * b,int n,Point * pp)301 spline(Image *b, int n, Point *pp)
302 {
303 long w, t1, t2, t3, fac=1000;
304 int i, j, steps=10;
305 Point p, q;
306
307 for (i = n; i > 0; i--)
308 pp[i] = pp[i-1];
309 pp[n+1] = pp[n];
310 n += 2;
311 p = pp[0];
312 for(i = 0; i < n-2; i++)
313 {
314 for(j = 0; j < steps; j++)
315 {
316 w = fac * j / steps;
317 t1 = w * w / (2 * fac);
318 w = w - fac/2;
319 t2 = 3*fac/4 - w * w / fac;
320 w = w - fac/2;
321 t3 = w * w / (2*fac);
322 q.x = (t1*pp[i+2].x + t2*pp[i+1].x +
323 t3*pp[i].x + fac/2) / fac;
324 q.y = (t1*pp[i+2].y + t2*pp[i+1].y +
325 t3*pp[i].y + fac/2) / fac;
326 line(b, p, q, 0, 0, 0, display->black, ZP);
327 p = q;
328 }
329 }
330 }
331
332 /* Have to parse skipped pages, to find out what fonts are loaded. */
333 static int
skipto(int gotop,int curp)334 skipto(int gotop, int curp)
335 {
336 char *p;
337 int i;
338
339 if (gotop == curp)
340 return 1;
341 for (i = 0; i < npagenums; i++)
342 if (pagenums[i].num == gotop) {
343 if (seekc(pagenums[i].adr) == Beof) {
344 fprint(2, "can't rewind input\n");
345 return 0;
346 }
347 return 1;
348 }
349 if (gotop <= curp) {
350 restart:
351 if (seekc(0) == Beof) {
352 fprint(2, "can't rewind input\n");
353 return 0;
354 }
355 }
356 for(;;){
357 p = rdlinec();
358 if (p == 0) {
359 if(gotop>curp){
360 gotop = curp;
361 goto restart;
362 }
363 return 0;
364 } else if (*p == 'p') {
365 lastp = curp = atoi(p+1);
366 addpage(lastp); /* maybe 1 too high */
367 if (curp>=gotop)
368 return 1;
369 }
370 }
371 }
372
373 static void
wiggly(int skip)374 wiggly(int skip)
375 {
376 Point p[300];
377 int c,i,n;
378 for (n = 1; (c = getc()) != '\n' && c>=0; n++) {
379 ungetc();
380 p[n].x = getn();
381 p[n].y = getn();
382 }
383 p[0] = Pt(hpos, vpos);
384 for (i = 1; i < n; i++)
385 p[i] = addpt(p[i],p[i-1]);
386 hpos = p[n-1].x;
387 vpos = p[n-1].y;
388 for (i = 0; i < n; i++)
389 p[i] = scale(p[i]);
390 if (!skip)
391 spline(screen,n,p);
392 }
393
394 static void
devcntrl(void)395 devcntrl(void) /* interpret device control functions */
396 {
397 char str[80];
398 int n;
399
400 getstr(str);
401 switch (str[0]) { /* crude for now */
402 case 'i': /* initialize */
403 break;
404 case 'T': /* device name */
405 getstr(devname);
406 break;
407 case 't': /* trailer */
408 break;
409 case 'p': /* pause -- can restart */
410 break;
411 case 's': /* stop */
412 break;
413 case 'r': /* resolution assumed when prepared */
414 res=getn();
415 DIV = floor(.5 + res/(100.0*mag));
416 if (DIV < 1)
417 DIV = 1;
418 mag = res/(100.0*DIV); /* adjust mag according to DIV coarseness */
419 break;
420 case 'f': /* font used */
421 n = getn();
422 getstr(str);
423 loadfontname(n, str);
424 break;
425 /* these don't belong here... */
426 case 'H': /* char height */
427 break;
428 case 'S': /* slant */
429 break;
430 case 'X':
431 break;
432 }
433 eatline();
434 }
435
436 int
isspace(int c)437 isspace(int c)
438 {
439 return c==' ' || c=='\t' || c=='\n';
440 }
441
442 static void
getstr(char * is)443 getstr(char *is)
444 {
445 uchar *s = (uchar *) is;
446
447 for (*s = getc(); isspace(*s); *s = getc())
448 ;
449 for (; !isspace(*s); *++s = getc())
450 ;
451 ungetc();
452 *s = 0;
453 }
454
455 static void
getutf(char * s)456 getutf(char *s) /* get next utf char, as bytes */
457 {
458 int c, i;
459
460 for (i=0;;) {
461 c = getc();
462 if (c < 0)
463 return;
464 s[i++] = c;
465
466 if (fullrune(s, i)) {
467 s[i] = 0;
468 return;
469 }
470 }
471 }
472
473 static void
eatline(void)474 eatline(void)
475 {
476 int c;
477
478 while ((c=getc()) != '\n' && c >= 0)
479 ;
480 }
481
482 static int
getn(void)483 getn(void)
484 {
485 int n, c, sign;
486
487 while (c = getc())
488 if (!isspace(c))
489 break;
490 if(c == '-'){
491 sign = -1;
492 c = getc();
493 }else
494 sign = 1;
495 for (n = 0; '0'<=c && c<='9'; c = getc())
496 n = n*10 + c - '0';
497 while (c == ' ')
498 c = getc();
499 ungetc();
500 return(n*sign);
501 }
502
503 static int
botpage(int np)504 botpage(int np) /* called at bottom of page np-1 == top of page np */
505 {
506 char *p;
507 int n;
508
509 while (p = getcmdstr()) {
510 if (*p == '\0')
511 return 0;
512 if (*p == 'q')
513 exits(p);
514 if (*p == 'c') /* nop */
515 continue;
516 if (*p == 'm') {
517 mag = atof(p+1);
518 if (mag <= .1 || mag >= 10)
519 mag = DEFMAG;
520 allfree(); /* zap fonts */
521 DIV = floor(.5 + res/(100.0*mag));
522 if (DIV < 1)
523 DIV = 1;
524 mag = res/(100.0*DIV);
525 return skipto(np-1, np); /* reprint the page */
526 }
527 if (*p == 'x') {
528 xyoffset.x += atoi(p+1)*100;
529 skipto(np-1, np);
530 return 1;
531 }
532 if (*p == 'y') {
533 xyoffset.y += atoi(p+1)*100;
534 skipto(np-1, np);
535 return 1;
536 }
537 if (*p == '/') { /* divide into n pieces */
538 nview = atoi(p+1);
539 if (nview < 1)
540 nview = 1;
541 else if (nview > MAXVIEW)
542 nview = MAXVIEW;
543 return skipto(np-1, np);
544 }
545 if (*p == 'p') {
546 if (p[1] == '\0'){ /* bare 'p' */
547 if(skipto(np-1, np))
548 return 1;
549 continue;
550 }
551 p++;
552 }
553 if ('0'<=*p && *p<='9') {
554 n = atoi(p);
555 if(skipto(n, np))
556 return 1;
557 continue;
558 }
559 if (*p == '-' || *p == '+') {
560 n = atoi(p);
561 if (n == 0)
562 n = *p == '-' ? -1 : 1;
563 if(skipto(np - 1 + n, np))
564 return 1;
565 continue;
566 }
567 if (*p == 'd') {
568 dbg = 1 - dbg;
569 continue;
570 }
571
572 fprint(2, "illegal; try q, 17, +2, -1, p, m.7, /2, x1, y-.5 or return\n");
573 }
574 return 0;
575 }
576