#include #include #include #include #define _LIBG_EXTENSION #include #define ONES (~0UL) #define xtbinit(a,b,c,d) binit(a,b, "anim") typedef unsigned char uchar; #define INPUTSCALE 10000 /* inherited from fdevelop */ int inputscale = INPUTSCALE; #define NZOOM 20 Rectangle zoomr[NZOOM]; /* for zooming in and out */ int nzoom = 0; /* current one */ Rectangle origscreen; FILE *inf; /* options for various commands */ #define Tcenter 10 #define Tljust 20 #define Trjust 30 #define Tabove 40 #define Tbelow 50 #define Tsmall 1 #define Tmedium 2 #define Tbig 3 #define Tbigbig 4 #define Lsolid 10 #define Lfat 20 #define Lfatfat 30 #define Ldotted 40 #define Ldashed 50 #define Lline 1 #define Larrow1 2 #define Larrow2 3 #define Larrow3 4 #define Bnofill 1 #define Bfill 2 #define Cnofill 1 #define Cfill 2 #define eq(s,t) (strcmp((char *) s, (char *) t) == 0) #define muldiv(a,b,c) (((a) * (b)) / (c)) /* ought to be inline */ /* holds data for all input objects */ unsigned memsize; /* bytes */ uchar *inbuf; /* input collected here */ uchar *input; /* leave a null at front */ uchar *nextinp; /* next free slot in input */ int nobj = 0; /* number of objects in input */ int overflow = 0; /* 1 => too much input */ long slot[2000]; /* slots */ int slotnum; uchar *savechar(int); uchar *draw_obj(uchar *, int, int); uchar *step_obj(uchar *, int, int); uchar *prev_obj(uchar *); uchar *next_obj(uchar *); uchar *refresh(uchar *); int zoomin(void); Rectangle square(Rectangle); Point fetchpt(int, uchar *); Point scalept(int, Point); void putstring(char *); int reshaped = 0; /* set to 1 in ereshaped */ /* this ought to include Expose events in X... */ Mouse mouse; int buttondown(void); Cursor skull, deadmouse; char kbdline [100]; int do_kbd(void); #define MAXVIEW 20 char *viewname[MAXVIEW]; Rectangle viewpt[MAXVIEW]; int curview = 0; int nview = 0; #define INSET 4 /* picture inset from frame */ #define AW 8 /* arrowhead width and height */ #define AH 10 #define MARGINPCT 5 int margin = MARGINPCT; /* percent margin around edges */ #define MAXCLICK 20 char *clickname[MAXCLICK]; /* click names */ int clickval[MAXCLICK]; /* 1 => click on this */ int clicking = 0; /* number of active clicks */ int nclick = 0; int boxops[] ={ 'n', Bnofill, 'f', Bfill, 0 }; int circops[] ={ 'n', Cnofill, 'f', Cfill, 0 }; int textops[] ={ 'c', Tcenter, 'l', Tljust, 'r', Trjust, 's', Tsmall, 'm', Tmedium, 'b', Tbig, 'B', Tbigbig, 0 }; int lineops[] ={ 's', Lsolid, 'f', Lfat, 'F', Lfatfat, 'o', Ldotted, 'a', Ldashed, '-', Lline, '>', Larrow1, '<', Larrow2, 'x', Larrow3, 0 }; enum { Fwd = 1, Back = 0 }; enum { F_XOR = DxorS, F_CLR = Zero, F_STORE = S, F_OR = DorS }; int delay = 1; /* how long to delay between things */ int singstep = 0; /* single step if 1, cycle if 2 */ int dir = Fwd; /* 1 = fwd, 0 = backward */ int fatness = 0; /* n => draw with 2n+1 lines */ int xormode = F_XOR; /* otherwise OR/CLR */ enum Menuitems { Again = 0, /* Menu items -- must be 0, other in order */ Faster, Slower, Step, Forward, Fatter, Thinner, Zoomin, Zoomout, Xor, Newfile, Quit, Backward , Proceed, Hit, Cycle, }; char *m3[] = { "again", "faster", "slower", "1 step", "backward", "fatter", "thinner", "zoom in", "zoom out", "or mode", "new file", "Quit?", 0 }; char *stepmenu[] = { "1 step", "cycle", "run" }; char *dirmenu[] = { "forward", "backward" }; char *modemenu[] = { "or mode", "xor mode" }; char *m3gen(int); char *m2gen(int); Menu mbut3 = { (char **) 0, m3gen, 0 }; Menu mbut2 = { (char **) 0, m2gen, 0 }; int last_hit; int last_but; int Etimer; extern int do_rcv(FILE *); extern int skipline(FILE *); extern int skipbl(FILE *); extern int badfile(FILE *); extern int savect(int); extern int saveint(int); extern int savelong(long); extern int savepair(int, int); extern int sendopt(int[], char *); extern int erase(uchar *); extern int fatline(Point, Point, int, int); extern int fatcircle(Point, int, int, int); extern int arrow(Point, Point, int, int, int); extern FILE *show(FILE *); extern int clear(void); extern int view_setup(int); extern int init_params(void); extern int checkmouse(void); extern int domouse(void); main(int argc, char *argv[]) { FILE *fp = stdin; xtbinit(0, 0, &argc, argv); einit(Emouse|Ekeyboard); Etimer = etimer(0, 75); setbuf(stderr, NULL); origscreen = screen.r; memsize = 1000000; while (argc > 1 && argv[1][0] == '-') { switch (argv[1][1]) { case 'm': memsize = atoi(&argv[1][2]); break; } argv++; argc--; } if (inbuf == NULL && (inbuf = (uchar *) malloc(memsize)) == NULL) { fprintf(stderr, "can't allocate %u bytes", memsize); exit(1); } if ((fp = fopen(argv[1], "r")) == NULL) { fprintf(stderr, "can't open file %s\n", argv[1]); exit(1); } while ((fp = show(fp)) != 0) ; } FILE *show(FILE *fp) { uchar *ip; int i, n, c; Event tev; init_params(); input = inbuf+1; /* leave a null at front */ nextinp = inbuf+1; /* next free slot in input */ clear(); cursorswitch(&deadmouse); do_rcv(fp); cursorswitch(0); dir = Fwd; ip = nextinp; /* pointing at the end */ again: for (; ip; ) { checkmouse(); /* waits for a menu selection */ n = domouse(); if (n == Quit) return 0; if (n == Hit) continue; /* wait for another mouse hit */ if (n == Forward) { /* change from fwd to back -- fiddle ip */ ip = next_obj(ip); continue; } if (n == Backward) { ip = prev_obj(ip); continue; } if (n == Newfile) { char buf[100]; /* fprintf(stderr, "filename? "); */ putstring("filename? "); if (do_kbd() == 0) continue; fclose(fp); /* open up the new file, return its fp. ick */ if ((fp = fopen(kbdline, "r")) != NULL) return fp; sprintf(buf, "can't open %s", kbdline); fprintf(stderr, "%s\n", buf); putstring(buf); continue; } if (n == Again) { if (reshaped) { view_setup(nview); reshaped = 0; } clear(); dir = Fwd; ip = input; } if (n == Zoomin || n == Zoomout) { clear(); dir = Fwd; ip = refresh(ip); continue; } if (singstep == 1) { ip = step_obj(ip, xormode, dir); } else { /* free running */ cycle: while (ip) { ip = step_obj(ip, xormode, dir); if (buttondown()) { /* someone has touched the mouse */ singstep = 0; break; /* back round main loop */ } for(i = delay-1; i>0; i--) eread(Etimer, &tev); } if (singstep == 2) { clear(); dir = Fwd; ip = input; goto cycle; } } } if (ip == 0) ip = dir == Fwd ? nextinp : input; goto again; } uchar *refresh(uchar *cp) /* redraw the screen up to cp */ { uchar *ip; for (ip = input; ip != 0 && ip < cp; ) ip = step_obj(ip, xormode, Fwd); return ip; } init_params(void) { int i; for (i = 0; i < MAXCLICK; i++) if (clickname[i]) { free(clickname[i]); clickname[i] = 0; clickval[i] = 0; } for (i = 0; i < MAXVIEW; i++) if (viewname[i]) { free(viewname[i]); viewname[i] = 0; } nview = nclick = curview = nobj = overflow = slotnum = 0; screen.r = inset(screen.r, INSET); zoomr[0] = screen.r = square(screen.r); nzoom = 0; } Rectangle shrink(Rectangle r, int pct) /* shrink rectangle by 2*pct */ { int dx = (r.max.x-r.min.x) * pct / 100; int dy = (r.max.y-r.min.y) * pct / 100; r.min = add(r.min, Pt(dx,dy)); r.max = sub(r.max, Pt(dx,dy)); return r; } Rectangle square(Rectangle r) /* return largest square within r */ { double ar; ar = (double) Dy(r)/Dx(r); /* y/x aspect ratio */ if (ar < 0.9 || ar > 1.1) /* leave it alone */ return r; if (ar <= 1) /* x is longer than y */ r.max.x = r.min.x + Dy(r); else /* y is longer */ r.max.y = r.min.y + Dx(r); return r; } view_setup(int n) { int i, j, v, dx, dy, r, c; Rectangle sr; sr = square(screen.r); switch (n) { case 1: r = 1; c = 1; break; case 2: r = 2; c = 1; break; case 3: case 4: r = 2; c = 2; break; case 5: case 6: r = 3; c = 2; break; case 7: case 8: case 9: r = 3; c = 3; break; default: r = (n+2)/3; c = 3; break; /* finking out */ } dx = Dx(sr) / c; dy = Dy(sr) / r; v = 0; for (i = 0; i < r && v < n; i++) for (j = 0; j < c && v < n; j++) { viewpt[v] = sr; viewpt[v].min.x = sr.min.x + j * dx; viewpt[v].max.x = sr.min.x + (j+1) * dx; viewpt[v].min.y = sr.min.y + i * dy; viewpt[v].max.y = sr.min.y + (i+1) * dy; v++; } for (i = 0; i < n; i++) { viewpt[i] = shrink(viewpt[i], margin); } zoomr[nzoom] = screen.r; } void ereshaped(Rectangle r) { reshaped = 1; origscreen = r; screen.r = inset(r, INSET); zoomr[0] = screen.r = square(screen.r); nzoom = 0; } drawrect(Rectangle r, int mode) { segment(&screen, r.min, Pt(r.min.x,r.max.y), ONES, mode); segment(&screen, r.min, Pt(r.max.x,r.min.y), ONES, mode); segment(&screen, r.max, Pt(r.min.x,r.max.y), ONES, mode); segment(&screen, r.max, Pt(r.max.x,r.min.y), ONES, mode); } domouse(void) { int i, n; Rectangle r; if (last_but == 1) return Proceed; if (last_but == 3) { switch (last_hit) { case Again: return Again; case Faster: if (delay > 1) delay /= 2; return Hit; case Slower: delay *= 2; return Hit; case Step: singstep = (singstep+1) % 3; return Hit; case Forward: dir = 1 - dir; if (xormode == F_OR) xormode = F_CLR; else if (xormode == F_CLR) xormode = F_OR; return dir == Fwd ? Forward : Backward; case Fatter: fatness++; return Hit; case Thinner: if (fatness > 0) fatness--; return Hit; case Zoomin: return zoomin(); case Zoomout: if (nzoom > 0) nzoom--; return Zoomout; case Xor: if (xormode == F_OR || xormode == F_CLR) xormode = F_XOR; else if (dir == Fwd) xormode = F_OR; else xormode = F_CLR; return Hit; case Newfile: return Newfile; case Quit: return Quit; default: return Hit; } } else if (last_but == 2) { Rectangle r; if (last_hit == -1) return Hit; else if (last_hit < nview) { r = getrect(2, &mouse); /* really ought to be 2,3 */ if (r.min.x == 0 && r.max.x == 0) /* bailed out */ return Hit; if (Dx(r) < 10 || Dy(r) < 10) /* too small */ return Hit; if (eqpt(r.min, r.max)) r = inset(origscreen, INSET); drawrect(inset(viewpt[last_hit], -(INSET+fatness)), F_CLR); drawrect(r, F_OR); viewpt[last_hit] = r = inset(r, INSET+fatness); return Hit; } else { /* a click */ if (clickval[last_hit-nview]) { /* was on, so turn off */ clickval[last_hit-nview] = 0; clicking--; } else { clickval[last_hit-nview] = 1; clicking++; } return Hit; } } } int zoomin(void) { Rectangle r; r = getrect(3, &mouse); /* fprintf(stderr, "into zoomin %d, r %d %d %d %d\n", */ /* nzoom, r.min.x, r.min.y, r.max.x, r.max.y); */ if (r.min.x == 0) /* no selection */ return Hit; if (Dx(r) < 10 || Dy(r) < 10) { /* very small */ if (Dx(zoomr[nzoom+1]) < 10) /* no previous one */ return Hit; r = zoomr[nzoom+1]; /* use previous one */ } else { /* preserve aspect ratio if near square */ double ar = (double) Dy(r) / Dx(r); if (ar > 0.9 && ar < 1.1) /* force ratio */ r = square(r); } /* either new one or zoom back in */ if (nzoom < NZOOM-1) { nzoom++; zoomr[nzoom] = r; } return Zoomin; } do_rcv(FILE *fp) { int c, n, b, m, i, v, x1, x2, y1, y2; char opts[100]; char text[100]; char *p; uchar *ip, *oip; while ((b = c = getc(fp)) != EOF) { switch (c) { case ' ': case '\t': case '\n': break; case '#': /* comments */ skipline(fp); break; case 'b': /* blank. ignore for now */ skipline(fp); break; case 'd': /* definition of some sort */ switch (c = skipbl(fp)) { case 'v': /* view */ case 'c': /* click */ if (fscanf(fp, "%d %s", &i, text) != 2) return badfile(fp); if (c == 'c') { clickname[i] = malloc(strlen(text)+1); strcpy(clickname[i], text); nclick++; } else { /* c == 'v' */ viewname[i] = malloc(strlen(text)+1); strcpy(viewname[i], text); nview++; } skipline(fp); /* might be a title there */ break; case 'p': /* only pragma is 'e' for end */ skipline(fp); view_setup(nview); break; default: return badfile(fp); } break; case 'c': /* click */ if (fscanf(fp, "%d", &i) != 1) return badfile(fp); oip = savechar(c); savechar(i); savect(nextinp-oip); skipline(fp); break; case 'e': /* erase */ if (fscanf(fp, "%d", &i) != 1) return badfile(fp); oip = savechar(c); savelong(slot[i]); savect(nextinp-oip); skipline(fp); break; case 'g': /* geom: draw line, box, circle, ... */ if (fscanf(fp, "%d", &slotnum) != 1) return badfile(fp); switch (c = skipbl(fp)) { case 'l': case 'b': if (fscanf(fp, "%d %s %d %d %d %d", &v, opts, &x1, &y1, &x2, &y2) != 6) return badfile(fp); slot[slotnum] = nextinp-input; oip = savechar(c); savechar(v); savechar(sendopt(c=='b' ? boxops : lineops, opts)); /* options */ savepair(x1, y1); savepair(x2, y2); savect(nextinp-oip); break; case 'c': /* circle */ if (fscanf(fp, "%d %s %d %d %d", &v, opts, &x1, &y1, &x2) != 5) return badfile(fp);; slot[slotnum] = nextinp-input; oip = savechar('o'); /* 'o' is for circle */ savechar(v); savechar(sendopt(circops, opts)); savepair(x1, y1); saveint(x2); savect(nextinp-oip); break; case 't': /* text */ if (fscanf(fp, "%d %s %d %d", &v, opts, &x1, &y1) != 4) return badfile(fp); slot[slotnum] = nextinp-input; oip = savechar('t'); savechar(v); savechar(sendopt(textops, opts)); savepair(x1, y1); getc(fp); /* skip 1 separator; no quotes */ for (p = text; (c = getc(fp)) != '\n'; ) *p++ = c; *p = 0; ungetc('\n', fp); /* slow... */ p = (char *) nextinp + 1; if (eq(text, "bullet")) strcpy(p, "*"); else if (eq(text, "dot")) strcpy(p, "."); else if (eq(text, "circle")) strcpy(p, "o"); else if (eq(text, "times")) strcpy(p, "x"); else strcpy(p, text); *nextinp = n = strlen(p); /* insert count before string */ nextinp += n + 2; /* +2 = count before + \0 on end */ savect(nextinp-oip); break; default: return badfile(fp); } break; } if (b == 'c' || b == 'e' || b == 'g') /* graphical objs */ draw_obj(oip, F_XOR, Fwd); *nextinp = 0; } } badfile(FILE *fp) { fprintf(stderr, "input file is not in .i format"); while (getc(fp) != EOF) ; fclose(fp); } sendopt(int optvals[], char *opts) { int i, n; n = 0; for (i = 0; optvals[i] && opts; i += 2) if (*opts == optvals[i]) { n += optvals[i+1]; opts++; } return n; } clear(void) /* screen */ { Rectangle r; screen.r = origscreen; r = inset(screen.r, INSET); bitblt(&screen, r.min, &screen, r, 0); } uchar *savechar(int c) { *nextinp++ = c; return nextinp-1; } savect(int n) { if (n > 255) { fprintf(stderr, "text string too long"); n = 255; } *nextinp++ = n; } saveint(int n) { *nextinp++ = n >> 8; *nextinp++ = n & 0377; } savelong(long n) { *nextinp++ = n >> 24; *nextinp++ = n >> 16; *nextinp++ = n >> 8; *nextinp++ = n; } savepair(int x, int y) { saveint(x); saveint(y); } getpoint(uchar *ip) { return *ip << 8 | *(ip+1); } long getlong(uchar *ip) { return *ip << 24 | *(ip+1) << 16 | *(ip+2) << 8 | *(ip+3); } Point scalept(int v, Point p) { p.x = muldiv(p.x, Dx(viewpt[v]), inputscale); p.y = Dy(viewpt[v]) - muldiv(p.y, Dy(viewpt[v]), inputscale); return p; } scalex(int v, int x) { int i; int nx = muldiv(x, Dx(viewpt[v]), inputscale); for (i = 1; i <= nzoom; i++) nx = nx * ((double)Dx(screen.r) / Dx(zoomr[i])); return nx; } Point fetchpt(int v, uchar *ip) { Point pt; int i; pt.x = *ip << 8 | *(ip+1); pt.y = *(ip+2) << 8 | *(ip+3); pt = scalept(v, pt); pt = add(pt, viewpt[v].min); for (i = 1; i <= nzoom; i++) { pt.x = (pt.x-zoomr[i].min.x) * (double) Dx(zoomr[0]) / Dx(zoomr[i]); pt.y = (pt.y-zoomr[i].min.y) * (double) Dy(zoomr[0]) / Dy(zoomr[i]); } /* pt.x += screen.r.min.x; /* pt.y += screen.r.min.y; */ return pt; } /* Encoding: type, view#, opts, coords, chars, etc., # = length of group bvoxxyyxxyy# lvoxxyyxxyy# ovoxxyyrr# tvoxxyynccc0# ennnn# cn# */ uchar *prev_obj(uchar *ip) { if (ip <= input) return 0; return ip - ip[-1] - 1; } uchar *next_obj(uchar *ip) { if (ip < input || ip >= nextinp) return 0; switch (*ip) { case 0: return 0; case 'b': case 'l': return ip + 12; case 'o': return ip + 10; case 't': return ip + ip[7] + 10; case 'c': return ip + 3; case 'e': return ip + 6; default: return 0; } } uchar *step_obj(uchar *ip, int mode, int dir) /* draw objs until one that changes something */ { int c; uchar *oip; if (clicking) { for (;;) { oip = ip; ip = draw_obj(ip, mode, dir); if (ip == 0 || (oip && *oip == 'c' && clickval[oip[1]])) return ip; } } else { /* stepping */ while (ip) { c = *ip; ip = draw_obj(ip, mode, dir); if (c == 'b' || c == 'l' || c == 't' || c == 'e' || c == 'o') return ip; } return ip; } } uchar *draw_obj(uchar *ip, int mode, int dir) /* draw obj from coords at ip */ { int c, r, thick, n, shift, head; Point p0, p1, p2; if (ip < input || ip >= nextinp) return 0; switch (c = *ip++) { case 'b': p0 = fetchpt(*ip, ip+2); p1 = fetchpt(*ip, ip+6); if (ip[1] == Bfill) { if (p0.y < p1.y) /* rectf(&screen, Rpt(p0, p1), mode); */ bitblt(&screen, p0, &screen, Rpt(p0, p1), mode); else /* rectf(&screen, Rect(p0.x,p1.y,p1.x,p0.y), mode); */ bitblt(&screen, p0, &screen, Rect(p0.x,p1.y,p1.x,p0.y), mode); } else { segment(&screen, p0, Pt(p0.x,p1.y), ONES, mode); segment(&screen, Pt(p0.x,p1.y), p1, ONES, mode); segment(&screen, p1, Pt(p1.x,p0.y), ONES, mode); segment(&screen, Pt(p1.x,p0.y), p0, ONES, mode); } if (dir == Fwd) ip += 1+9+1; else ip -= (*(ip-2) + 2); break; case 'l': p0 = fetchpt(*ip, ip+2); p1 = fetchpt(*ip, ip+6); thick = ip[1]/10; /* ought to be a macro! */ if (thick == Ldotted/10 || thick == Ldashed/10) thick = 1; thick = 2 * thick - 1; /* 1,3,5 */ fatline(p0, p1, mode, thick); head = ip[1]%10; /* ditto */ if (head == Larrow1 || head == Larrow3) arrow(p0, p1, AW, AH, mode); if (head == Larrow2 || head == Larrow3) arrow(p1, p0, AW, AH, mode); if (dir == Fwd) ip += 1+9+1; else ip -= (*(ip-2) + 2); break; case 'o': p0 = fetchpt(*ip, ip+2); r = scalex(*ip, getpoint(ip+6)); /* fprintf(stderr, "draw circle %d at %d,%d\n", r, p0.x, p0.y); */ if (ip[1] == Cnofill) fatcircle(p0, r, mode, 1); else disc(&screen, p0, r + fatness, ONES, mode); if (dir == Fwd) ip += 1+7+1; else ip -= (*(ip-2) + 2); break; case 't': p0 = fetchpt(*ip, ip+2); /* fprintf(stderr, "draw text %s at %d,%d\n", ip+7, p0.x, p0.y); */ n = ip[6]; shift = (ip[1]/10) * 10; /* ought to be a macro! */ if (shift == Tljust) shift = 0; else if (shift == Tcenter) shift = (9 * n) / 2; /* 9 = char width */ else shift = 9 * n; string(&screen, sub(p0, Pt(shift,6)), font, (char *) ip+7, mode); if (dir == Fwd) ip += 1+5 + *(ip+6)+2 + 1; else ip -= (*(ip-2) + 2); break; case 'e': erase(ip-1); if (dir == Fwd) ip += 5; else ip -= (*(ip-2) + 2); break; case 'c': if (dir == Fwd) ip += 2; else ip -= (*(ip-2) + 2); break; default: ip = 0; break; } return ip; } erase(uchar *ip) { long target = getlong(ip+1); /* target label index */ int mode = F_XOR; if (xormode == F_OR || xormode == F_CLR) mode = dir == Fwd ? F_CLR : F_OR; draw_obj(input+target, mode, Fwd); } #define abs(x) ((x) >= 0 ? (x) : -(x)) fatline(Point p0, Point p1, int mode, int thick) { int i, fat, beg, nl; fat = thick * (2 * fatness + 1); beg = fat / 2; if (abs(p1.x-p0.x) >= abs(p1.y-p0.y)) { /* horizontal */ for (nl = 0, i = -beg; nl < fat; nl++, i++) segment(&screen, add(p0, Pt(0,i)), add(p1, Pt(0,i)), ONES, mode); } else { for (nl = 0, i = -beg; nl < fat; nl++, i++) segment(&screen, add(p0, Pt(i,0)), add(p1, Pt(i,0)), ONES, mode); } } fatcircle(Point p0, int r, int mode, int thick) { int i, fat, beg, nl; fat = thick * (2 * fatness + 1); beg = fat / 2; for (nl = 0, i = -beg; nl < fat; nl++, i++) circle(&screen, p0, r+i, ONES, mode); } arrow(Point p1, Point p2, int w, int h, int c) /* draw arrow of height,width (h,w) at p2 of segment p1,p2 */ { Point d; int norm, qx, qy, lx, ly; d = sub(p2, p1); norm = sqrt((long)d.x*d.x + (long)d.y*d.y); if (norm == 0) /* shouldn't happen, but ... */ return; qx = p2.x - muldiv(h, d.x, norm); qy = p2.y - muldiv(h, d.y, norm); lx = muldiv(w/2, -d.y, norm); ly = muldiv(w/2, d.x, norm); /* segment(&screen, p1, p2, c); */ segment(&screen, Pt(qx+lx, qy+ly), p2, ONES, c); segment(&screen, Pt(qx-lx, qy-ly), p2, ONES, c); } skipbl(FILE *fp) { int c; while ((c = getc(fp)) == ' ' || c == '\t') ; return c; } skipline(FILE *fp) { int c; while ((c = getc(fp)) != '\n') ; ungetc('\n', fp); } char *m3gen(int n) { static char buf[50]; if (n < 0 || n > Quit) return 0; else if (n == Faster) { sprintf(buf, "faster %d", delay); return buf; } else if (n == Slower) { sprintf(buf, "slower %d", delay); return buf; } else if (n == Step) { return stepmenu[singstep]; } else if (n == Forward) { return dirmenu[dir]; } else if (n == Fatter) { sprintf(buf, "fatter %d", fatness+1); return buf; } else if (n == Thinner) { sprintf(buf, "thinner %d", fatness+1); return buf; } else if (n == Xor) { return xormode == F_XOR ? modemenu[0] : modemenu[1]; } else return m3[n]; } char *m2gen(int n) { static char buf[50]; if (n < 0 || n >= nview+nclick) return 0; else if (n < nview) { sprintf(buf, "view %s", viewname[n]); return buf; } else { sprintf(buf, "click %s%s", clickname[n-nview], clickval[n-nview] ? "*" : ""); return buf; } } #define button3(b) ((b) & 4) #define button2(b) ((b) & 2) #define button1(b) ((b) & 1) #define button23(b) ((b) & 6) #define button123(b) ((b) & 7) int buttondown(void) /* report state of buttons, if any */ { if (!ecanmouse()) /* no event pending */ return 0; mouse = emouse(); /* something, but it could be motion */ return mouse.buttons & 7; } int waitdown(void) /* wait until some button is down */ { while (!(mouse.buttons & 7)) mouse = emouse(); return mouse.buttons & 7; } int waitup(void) { while (mouse.buttons & 7) mouse = emouse(); return mouse.buttons & 7; } checkmouse(void) /* return button touched if any */ { int c, b; char *p = NULL; extern int confirm(int); b = waitdown(); last_but = 0; last_hit = -1; c = 0; if (button3(b)) { last_hit = menuhit(3, &mouse, &mbut3); last_but = 3; } else if (button2(b)) { last_hit = menuhit(2, &mouse, &mbut2); last_but = 2; } else { /* button1() */ last_but = 1; } waitup(); if (last_but == 3 && last_hit >= 0) { p = m3[last_hit]; c = p[strlen(p) - 1]; } if (c == '?' && !confirm(last_but)) last_hit = -1; return last_but; } confirm(int but) /* ask for confirmation if menu item ends with '?' */ { int c; static int but_cvt[8] = { 0, 1, 2, 0, 3, 0, 0, 0 }; cursorswitch(&skull); c = waitdown(); waitup(); cursorswitch(0); return but == but_cvt[c]; } Cursor deadmouse = { { 0, 0}, /* offset */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x82, 0x04, 0x41, 0xFF, 0xE1, 0x5F, 0xF1, 0x3F, 0xFE, 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x82, 0x04, 0x41, 0xFF, 0xE1, 0x5F, 0xF1, 0x3F, 0xFE, 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } }; Cursor skull ={ { 0, 0 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0, 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0, 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } }; do_kbd(void) /* read a line from keyboard */ { char *p; for (p = kbdline; (*p = ekbd()) != '\n' && *p != '\r'; p++) { /* fprintf(stderr, "%c", *p); */ if (*p == '\b') p -= 2; if (p < kbdline) p = kbdline; p[1] = 0; putstring(kbdline); } *p = 0; /* fprintf(stderr, "\n"); */ /* fprintf(stderr, "\nfilename is [%s]\n", kbdline); */ return strlen(kbdline); } void putstring(char *buf) { Point p; static int jmax = 0, l; p = add(screen.r.min, Pt(20,20)); bitblt(&screen, p, &screen, Rect(p.x, p.y, p.x+jmax, p.y+font->height), F_CLR); string(&screen, p, font, buf, F_OR); if ((l = strwidth(font, buf)) > jmax) jmax = l; }