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 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 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 96 clearview(Rectangle r) 97 { 98 draw(screen, r, display->white, nil, r.min); 99 } 100 101 int resized; 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 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 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 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 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 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 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 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 437 isspace(int c) 438 { 439 return c==' ' || c=='\t' || c=='\n'; 440 } 441 442 static void 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 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 474 eatline(void) 475 { 476 int c; 477 478 while ((c=getc()) != '\n' && c >= 0) 479 ; 480 } 481 482 static int 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 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