#ifndef lint static char sccsid[] = "@(#)subr.c 4.2 (Berkeley) 8/11/83"; #endif /* * subr.c: general subroutines for fed. */ #include "fed.h" /* * initialize: various one time initializations. */ initialize() { register int i, j; register char *cp; /* Initialize random variables */ curwind = -1; pencolor = 1; penweight = 0; /* * Initialize value of sqrtmat. This is a constant table * so we don't have to redo all these square roots when the pen * changes every time. */ for (i=0; i<10; i++) { for (j=0; j<10; j++) { sqrtmat[i][j] = sqrt((float) i*i + j*j); } } /* Initialize base locations on screen. These remain fixed. */ for (i=0; i"); minc = inchar(); sprintf(msgbuf, "Show font from %s to ", rdchar(minc)); message(msgbuf); maxc = inchar(); clearg(); zermat(tmpbuf, GLROW, GLCOL); cr = SCRHI-GLROW; cc = 3; for (i=minc; i<=maxc; i++) { if (disptable[i].nbytes) { /* * We really should try to find out how far to the * left the glyph goes so we don't run off the left * end of the screen, but this is hard, so we fake it. * Usually glyphs don't run past the left so it's OK. */ if (cc - disptable[i].left < 0) cc = disptable[i].left; nc = cc + disptable[i].width; if (nc >= SCRWID) { cc = 0; nc = disptable[i].width; cr -= 85; /* Should be GLROW but 4*100>360 */ if (cr < 0) break; /* Screen full. Just stop. */ } dispmsg(rdchar(i), cc, cr, 2); placechar(i, cr+BASELINE, cc, tmpbuf); cc = nc; } } for (;;) { nextcmd = inchar(); if (nextcmd != 'p') break; printg(); } if (nextcmd != 'Q' && nextcmd != 'E' && nextcmd != 'N') redraw(); else clearg(); ungetc(nextcmd, stdin); } /* * typein: Like showfont but takes a line of text from the user * and "typesets" it on the screen. */ typein() { register int i, cr, cc, nc; char *p; int roff, coff; char maxc, minc; char nextcmd; char tmpbuf[WINDSIZE]; char msgtype[100]; zoomout(); readline("Input line to be typeset: ", msgtype, sizeof msgtype); clearg(); zermat(tmpbuf, GLROW, GLCOL); cr = SCRHI-GLROW; cc = 3; for (p=msgtype; *p; p++) { i = *p; if (disptable[i].nbytes) { if (cc - disptable[i].left < 0) cc = disptable[i].left; nc = cc + disptable[i].width; if (nc >= SCRWID) { cc = 0; nc = disptable[i].width; cr -= 85; /* Should be GLROW but 4*100>360 */ if (cr < 0) break; /* Screen full. Just stop. */ } dispmsg(rdchar(i), cc, cr, 2); placechar(i, cr+BASELINE, cc, tmpbuf); cc = nc; } } for (;;) { nextcmd = inchar(); if (nextcmd != 'p') break; printg(); } if (nextcmd != 'Q' && nextcmd != 'E' && nextcmd != 'N') redraw(); else clearg(); ungetc(nextcmd, stdin); } /* * placechar: draw the character ch at position (llr, llc) on the screen. * Position means the logical center of the character. zero is a GLROW x GLCOL * matrix of zeros which is needed for comparison, that is, we assume that * the spot on the screen where this is going is blank, so the chars better * not overlap. */ placechar(ch, llr, llc, zero) int ch; int llr, llc; bitmat zero; { bitmat glbuf; int roff, coff; glbuf = findbits(ch, GLROW, GLCOL, 0, 0, &roff, &coff); if (glbuf == NULL) return; if (trace) fprintf(trace, "placechar('%s'), roff=%d, coff=%d, llr=%d, llc=%d, down=%d, left=%d, r=%d, c=%d\n", rdchar(ch), roff, coff, llr, llc, disptable[ch].down, disptable[ch].left, llr-disptable[ch].down, llc-disptable[ch].left); update(zero, glbuf, GLROW, GLCOL, llr-GLROW+roff, llc-coff); if (trace) fprintf(trace, "placechar, free %x\n", glbuf); free(glbuf); } /* * redraw: The screen has gotten screwed up somehow. * Assume nothing but make it look right. */ redraw() { register int i; zoomout(); clearg(); turnofrb(); for (i=0; i= 0) drawbox(base[curwind].r-1, base[curwind].c-1, 1, GLROW+2, GLCOL+2); } /* * findbits: find the data bits of glyph c, wherever they are, and make * nr x nc bitmat and put them in it, shifted by horoff and vertoff. */ bitmat findbits(c, nr, nc, horoff, vertoff, rcenter, ccenter) int c; int nr, nc; /* the size of the dest */ int horoff, vertoff; int *rcenter, *ccenter; { register int i, j; register int r1, r2, c1, c2; bitmat retval, source; int tr, tc; /* the size of source */ char tmp[WINDSIZE]; if (trace) fprintf(trace, "findbits(c=%s, nr=%d, nc=%d, horoff=%d, vertoff=%d\n", rdchar(c), nr, nc, horoff, vertoff); if (disptable[c].nbytes == 0) return (NULL); switch (cht[c].wherewind) { case -2: if (trace) fprintf(trace, "case -2, saved from prev place\n"); /* Saved from previous place */ source = cht[c].whereat; /* Ignore horoff/vertoff assuming they are already right */ *rcenter = cht[c].rcent; *ccenter = cht[c].ccent; /* * Small but important optimization: if the desired result is * a whole window and the source happens to be in a whole * window, just return the source pointer. This saves * lots of memory copies and happens quite often. */ if (nr == GLROW && nc == GLCOL) return (source); tr = GLROW; tc = GLCOL; break; case -1: if (trace) fprintf(trace, "case -1: first time\n"); /* First time for this glyph: get it from font file */ fseek(fontdes, (long) fbase+disptable[c].addr, 0); tr = cht[c].nrow; tc = cht[c].ncol; if (tr > GLROW || tc > GLCOL || disptable[c].nbytes > WINDSIZE) error("glyph too large for window"); *rcenter = vertoff + disptable[c].up; *ccenter = horoff + disptable[c].left; source = tmp; fread(source, disptable[c].nbytes, 1, fontdes); break; default: if (trace) fprintf(trace, "case default, in window %d", cht[c].wherewind); source = wind[cht[c].wherewind].val; tr = GLROW; tc = GLCOL; *rcenter = vertoff + cht[c].rcent; *ccenter = horoff + cht[c].ccent; break; } if (trace) fprintf(trace, "curchar=%c=%d, tr=%d, tc=%d\n", curchar, curchar, tr, tc); dumpmat("before copy, source", source, tr, tc); /* Copy in the bits into a bitmat of the right size */ retval = newmat(nr, nc); r1 = max(0, -vertoff); r2 = min(GLROW-vertoff-1, GLROW-1); r2 = min(r2, tr-1); c1 = max(0, -horoff); c2 = min(GLCOL-horoff-1, GLCOL-1); c2 = min(c2, tc-1); if (trace) fprintf(trace, "findbits copy: r1=%d, r2=%d, c1=%d, c2=%d, horoff=%d, vertoff=%d\n", r1, r2, c1, c2, horoff, vertoff); for (i=r1; i<=r2; i++) { for (j=c1; j<=c2; j++) setmat(retval, nr, nc, i+vertoff, j+horoff, mat(source, tr, tc, i, j, 6)); } dumpmat("result of copy", retval, nr, nc); return (retval); } /* * bufmod: called just before a buffer modifying command. * Makes a backup copy of the glyph so we can undo later. */ bufmod() { changes++; if (curwind < 0) return; if (wind[curwind].undval == NULL) wind[curwind].undval = newmat(GLROW, GLCOL); bitcopy(wind[curwind].undval, wind[curwind].val, GLROW, GLCOL); und_p_r = pen_r; und_p_c = pen_c; und_c_r = curs_r; und_c_c = curs_c; } /* * undo: restore the backup copy. We just swap pointers, which is * the same as interchanging the two matrices. This way, undo is * its own inverse. */ undo() { register bitmat tmp; if (wind[curwind].undval == NULL) { error("Nothing to undo"); } tmp = wind[curwind].val; wind[curwind].val = wind[curwind].undval; wind[curwind].undval = tmp; pen_r = und_p_r; pen_c = und_p_c; move(base[curwind].c+pen_c, base[curwind].r+GLROW-pen_r); curs_r = und_c_r; curs_c = und_c_c; syncwind(curwind); changes++; } /* * drawline: draw a line of current flavor between the named two points. * All points are relative to current window. * * The algorithm is that of a simple DDA. This is similar to what the * hardware of the HP 2648 does but the placing of the points will be * different (because of thick pens and erasers). */ drawline(from_r, from_c, to_r, to_c) { int length, i; float x, y, xinc, yinc; if (trace) fprintf(trace, "drawline from (%d, %d) to (%d, %d)\n", from_r, from_c, to_r, to_c); length = max(abs(to_r-from_r), abs(to_c-from_c)); if (length <= 0) { /* * The actual value doesn't matter, we're just avoiding * division by zero here. */ xinc = yinc = 1.0; } else { xinc = ((float) (to_r-from_r))/length; yinc = ((float) (to_c-from_c))/length; } drawpoint(from_r, from_c); x = from_r + 0.5; y = from_c + 0.5; for (i=0; i . */ setcmd() { char what, where; message("set "); what = inchar(); switch (what) { case 'p': /* set pen */ message("set pen "); where = inchar(); switch (where) { case 'f': /* set pen fine */ case 'l': /* set pen light */ message("set pen fine"); penweight = 0; break; case 'h': /* set pen heavy */ case 'b': /* set pen bold */ message("set pen heavy"); penweight = 1; break; default: error("Illegal kind of pen weight"); } break; case 's': /* set size of heavy pen */ message("set pen size to "); where = inchar() - '0'; sprintf(msgbuf, "set pen size to %d", where); message(msgbuf); if (where > 0 && where < 10) { setpen(where); } else error("Illegal size"); break; case 'd': message("set draw"); pencolor = 1; break; case 'e': message("set erase"); pencolor = 0; break; default: error("Illegal set"); } } /* * setpen: set the heavy pen size to s. * Main work here is defining template of pen. */ setpen(s) int s; { register int i, j; register float radius; if (s < 1) s = 1; hpensize = s; radius = hpensize; radius /= 2; for (i=0; i<10; i++) { for (j=0; j<10; j++) { penmat[i][j] = (radius >= sqrtmat[abs(i-4)][abs(j-4)]); } } /* * Kludge to make a 2-wide pen possible by specifying 1. */ if (hpensize == 1) penmat[4][5] = 1; if (trace) for (i=0; i<10; i++) { for (j=0; j<10; j++) { fprintf(trace, "%c", penmat[i][j] ? 'P' : '.'); } fprintf(trace, "\n"); } } /* * error: print the given error message and return for another command. */ error(msg) char *msg; { message(msg); longjmp(env); } /* * copymove: do a move or copy command. * cmd is C or M, the command. */ copymove(cmd) char cmd; { char *action; char src, dest; bitmat cpy; char lochr[5]; if (cmd == 'C') action = "copy"; else action = "move"; sprintf(msgbuf, "%s ", action); message(msgbuf); src = inchar(); sprintf(msgbuf, "%s %s to ", action, rdchar(src)); message(msgbuf); dest = inchar(); strcpy(lochr, rdchar(src)); sprintf(msgbuf, "%s %s to %s", action, lochr, rdchar(dest)); message(msgbuf); /* Do the copy */ disptable[dest] = disptable[src]; cht[dest] = cht[src]; if (cht[dest].wherewind >= 0) wind[cht[dest].wherewind].used = dest; if (cmd == 'C') { if (cht[dest].wherewind != -1) { /* * Make copies of the window so changing * one won't change the other. * The old copy gets the window on the screen, if any, * relegating the new copy to the background. */ cpy = newmat(GLROW, GLCOL); if (cht[dest].wherewind >= 0) bitcopy(cpy, wind[cht[src].wherewind].val, GLROW, GLCOL); else bitcopy(cpy, cht[src].whereat, GLROW, GLCOL); if (cht[dest].wherewind == curwind) curwind = -1; cht[dest].wherewind = -2; cht[dest].whereat = cpy; } } else { /* * Move. Delete the old entries. */ disptable[src].addr = disptable[src].nbytes = 0; cht[src].wherewind = -1; } changes++; } /* * cch: make sure there is a current character. */ cch() { if (curwind < 0) error("No current glyph"); } /* * confirm: if there have been changes, ask user if he is sure. */ confirm() { char ch; if (changes == 0) return; message("Changes since last write -- Are you sure?"); ch = inchar(); if (isupper(ch)) ch = tolower(ch); switch (ch) { case 'y': case 'q': case 'e': return; case 'n': default: error("Not sure - aborted"); } } /* * delchar: the D command. Delete a character from the buffer. */ delchar() { register char c, c1, c2; register int w; char buf[5]; message("delete "); c1 = inchar(); sprintf(msgbuf, "delete %s through ", rdchar(c1)); message(msgbuf); c2 = inchar(); strcpy(buf, rdchar(c1)); sprintf(msgbuf, "delete %s through %s", buf, rdchar(c2)); message(msgbuf); changes++; for (c=c1; c<=c2; c++) { if ((w = cht[c].wherewind) >= 0) { zermat(wind[w].val, GLROW, GLCOL); syncwind(w); } cht[c].wherewind = -1; disptable[c].addr = 0; disptable[c].nbytes = 0; disptable[c].up = 0; disptable[c].down = 0; disptable[c].left = 0; disptable[c].right = 0; disptable[c].width = 0; } } /* * zoom out to full screen so the screen doean't go nuts when we * print off the current zoom window. Save old value of zoom in * oldzoom so space can put us back. */ zoomout() { if (curzoom != 1) zoomn(curzoom = 1); } /* * newglyph: the n command. */ newglyph() { register int i, j; int windno; int vertoff, horoff; char *tmp; message("new glyph "); curchar = inchar(); sprintf(msgbuf, "new glyph %s", rdchar(curchar)); message(msgbuf); if (trace) fprintf(trace, "\n\nnewglyph(%s)\n", rdchar(curchar)); if (disptable[curchar].nbytes != 0) { if (trace) fprintf(trace, "char exists: %s\n", rdchar(curchar)); sprintf(msgbuf, "char exists: %s", rdchar(curchar)); error(msgbuf); } turnofcurs(); /* * Not on screen. First find a suitable window, * using round robin. */ windno = nextwind; if (trace) fprintf(trace, "chose window %d\n", windno); if (++nextwind >= NWIND) nextwind = 0; #ifdef notdef if (nextwind >= 3) nextwind = 0; #endif wind[windno].used = curchar; /* Put a box around the current window */ if (windno != curwind) { drawbox(base[curwind].r-1, base[curwind].c-1, 0, GLROW+2, GLCOL+2); drawbox(base[windno].r-1, base[windno].c-1, 1, GLROW+2, GLCOL+2); } /* Print the char at the lower left of the window */ sprintf(msgbuf, "%s", rdchar(curchar)); dispmsg(msgbuf, base[windno].c, base[windno].r-11, 2); /* Now make room in the window */ if (wind[windno].onscreen == NULL) { /* Brand new window, have to allocate space */ wind[windno].onscreen = newmat(GLROW, GLCOL); } else { /* Save prev glyph for later */ cht[wind[curchar].used].whereat = wind[windno].val; cht[wind[curchar].used].wherewind = -2; } if (wind[windno].undval != NULL) { if (trace) fprintf(trace, "newglyph frees undo: %x\n", wind[windno].undval); free(wind[windno].undval); } wind[windno].undval = NULL; /* * Vertical & horizontal offsets. Line up the baseline * of the char at BASELINE from bottom, but center * horizontally. */ wind[windno].val = newmat(GLROW, GLCOL); curwind = windno; cht[curchar].wherewind = windno; cht[curchar].rcent = curs_r = GLROW - BASELINE; cht[curchar].ccent = curs_c = GLCOL / 2; #ifdef notdef dumpmat("wind[windno].onscreen", wind[windno].onscreen, GLROW, GLCOL); #endif syncwind(windno); /* * Mung the zoom out to 1 and back. This is needed to * re-center the glyph on the screen if zoomed in, otherwise * if you move by one window it puts the cursor way over at * the right with only half the window visible. */ if ((i = curzoom) > 1) { zoomn(1); zoomn(i); } } /* * numedit: change one of the numerical parameters. */ numedit() { short * sp = 0; char * cp = 0; char c, f; char *fld; short ovalue, nvalue; char numb[20]; message("number of "); c = inchar(); sprintf(msgbuf, "number of %s ", rdchar(c)); message(msgbuf); f = inchar(); switch (f) { case 'a': sp = (short *) &disptable[c].addr; fld = "addr"; break; case 'n': sp = &disptable[c].nbytes; fld = "nbytes"; break; case 'u': cp = &disptable[c].up; fld = "up"; break; case 'd': cp = &disptable[c].down; fld = "down"; break; case 'l': cp = &disptable[c].left; fld = "left"; break; case 'r': cp = &disptable[c].right; fld = "right"; break; case 'w': sp = &disptable[c].width; fld = "width"; break; case 's': sp = (short *) &disptable[c].nbytes; fld = "size"; break; default: error("No such field"); } ovalue = sp ? *sp : *cp; sprintf(msgbuf, "number of %s %s (old value %d) is ", rdchar(c), fld, ovalue); readline(msgbuf, numb, sizeof numb); nvalue = atoi(numb); if (cp) *cp = nvalue; else *sp = nvalue; changes++; } /* * These routines turn the cursor and rubber band line on and off, * remembering its state for the o and r commands. */ turnoncurs() { curon(); curcurs = 1; } turnofcurs() { curoff(); curcurs = 0; } turnonrb() { rbon(); currb = 1; } turnofrb() { rboff(); currb = 0; } synccurs() { register int x, y; x = base[curwind].c + curs_c; y = base[curwind].r + GLROW - curs_r - 1; movecurs(x, y); } inchar() { sync(); synccurs(); return (rawchar()); } /* * fillin - fill in with 1's all the spots that are in the enclosed * area that (x, y) is in. */ fillin(x, y) int x, y; { if (x<0 || x>=GLROW || y<0 || y>=GLCOL || mat(wind[curwind].val, GLROW, GLCOL, x, y)) return; setmat(wind[curwind].val, GLROW, GLCOL, x, y, 1); fillin(x-1, y); fillin(x+1, y); fillin(x, y-1); fillin(x, y+1); } /* * syncwind: make sure that window #n shows on the screen what it's * supposed to after an arbitrary change. */ syncwind(n) int n; { if (trace) fprintf(trace, "syncwind(%d)\n", n); update(wind[n].onscreen, wind[n].val, GLROW, GLCOL, base[n].r, base[n].c); bitcopy(wind[n].onscreen, wind[n].val, GLROW, GLCOL); } /* * Embolden artificially emboldens the glyphs in the font by smearing * them to the right by the current heavy pen size. Or else italicize it. */ artificial() { int low, high, cur; int oldps, newps; char lowch[10]; #define ITAL 0 #define BOLD 1 #define RESIZE 2 #define SMOOTH 3 int kind; char *strbold; sprintf(msgbuf, "Artificially "); message(msgbuf); cur = inchar(); switch(cur) { case 'i': case 'I': kind = ITAL; strbold = "italicize"; break; case 'e': case 'E': kind = BOLD; strbold = "embolden"; break; case 'r': case 'R': kind = RESIZE; strbold = "resize"; break; case 's': case 'S': kind = SMOOTH; strbold = "smooth"; break; default: error("No such artificial operation"); } sprintf(msgbuf, "Artificially %s glyphs from ", strbold); message(msgbuf); low = inchar(); strcpy(lowch, rdchar(low)); sprintf(msgbuf, "Artificially %s glyphs from %s to ", strbold, lowch); message(msgbuf); high = inchar(); if (kind == RESIZE) { sprintf(msgbuf, "Artificially %s glyphs from %s to %s from ", strbold, lowch, rdchar(high)); oldps = readnum(msgbuf); sprintf(msgbuf, "Artificially %s glyphs from %s to %s from %dP to P", strbold, lowch, rdchar(high), oldps); newps = readnum(msgbuf); sprintf(msgbuf, "Artificially %s glyphs from %s to %s from %dP to %dP", strbold, lowch, rdchar(high), oldps, newps); message(msgbuf); if (oldps <= 0 || oldps > 36 || newps <= 0 || newps > 36 || oldps == newps) error("Bad point sizes"); } else { sprintf(msgbuf, "Artificially %s glyphs from %s to %s", strbold, lowch, rdchar(high)); message(msgbuf); } for (cur=low; cur<=high; cur++) { getglyph(cur); if (curchar == cur) { /* e.g. if the getglyph succeeded */ fflush(stdout); switch (kind) { case BOLD: boldglyph(); break; case ITAL: italglyph(); break; case RESIZE: if (oldps > newps) shrinkglyph(oldps, newps); else blowupglyph(oldps, newps); break; case SMOOTH: smoothglyph(); break; } syncwind(curwind); } } message("Done"); } /* * Artificially embolden the current glyph. */ boldglyph() { register int r, c, i; int smear = hpensize < 2 ? 2 : hpensize; for (r=0; r=smear; c--) for (i=1; i<=smear; i++) if (mat(wind[curwind].val, GLROW, GLCOL, r, c-i)) setmat(wind[curwind].val, GLROW, GLCOL, r, c, 1); } /* * Artificially italicize the current glyph. */ italglyph() { register int r, c, i, off; int baser = cht[curchar].rcent; /* GLROW - BASELINE; */ for (r=0; r=off; c--) { setmat(wind[curwind].val, GLROW, GLCOL, r, c, mat(wind[curwind].val, GLROW, GLCOL, r, c-off)); } for (c=off-1; c>=0; c--) setmat(wind[curwind].val, GLROW, GLCOL, r, c, 0); } for (r=baser; r=0; c--) setmat(wind[curwind].val, GLROW, GLCOL, r, c, 0); } } /* * Blow up or shrink a glyph from oldps points to newps points. * The basic idea is that for each on point in the old glyph we * find the corresponding point in the new glyph and copy the value. */ shrinkglyph(oldps, newps) int oldps, newps; { float ratio; register int or, oc, nr, nc; int n; bitmat tmp, curw; int baser = cht[curchar].rcent; int basec = cht[curchar].ccent; ratio = (float) newps / (float) oldps; tmp = newmat(GLROW, GLCOL); curw = wind[curwind].val; bitcopy(tmp, curw, GLROW, GLCOL); zermat(curw, GLROW, GLCOL); for (or=0; or= GLROW || nc < 0 || nc >= GLCOL) n = 0; else n = mat(tmp, GLROW, GLCOL, or, oc); if (n) setmat(curw, GLROW, GLCOL, nr, nc, n); } } disptable[curchar].width = disptable[curchar].width * ratio + 0.5; free(tmp); } /* * blow up a glyph. Otherwise like shrinkglyph. */ blowupglyph(oldps, newps) int oldps, newps; { float ratio; register int or, oc, nr, nc; int n; bitmat tmp, curw; int baser = cht[curchar].rcent; int basec = cht[curchar].ccent; ratio = (float) oldps / (float) newps; tmp = newmat(GLROW, GLCOL); curw = wind[curwind].val; bitcopy(tmp, curw, GLROW, GLCOL); zermat(curw, GLROW, GLCOL); for (nr=0; nr= GLROW || oc < 0 || oc >= GLCOL) n = 0; else n = mat(tmp, GLROW, GLCOL, or, oc); if (n) setmat(curw, GLROW, GLCOL, nr, nc, n); } } disptable[curchar].width = disptable[curchar].width / ratio + 0.5; free(tmp); } /* * Smooth a glyph. We look for corners and trim the point. Corners of * both blanks and dots in all 4 orientations are looked for. */ smoothglyph() { bitmat tmp, curw; register int r, c; register int c3; int a3, b2, b3, b4, c1, c2, c4, c5, d2, d3, d4, e3; tmp = newmat(GLROW, GLCOL); curw = wind[curwind].val; bitcopy(tmp, curw, GLROW, GLCOL); for (r=2; r"); kind = inchar(); switch (kind) { case 'h': case 'H': message("Invert horizontally"); for (r=0; r %d (%d)\n", c, tmp1, GLCOL-1-c, tmp2); setmat(curw, GLROW, GLCOL, r, c, tmp2); setmat(curw, GLROW, GLCOL, r, GLCOL-1-c, tmp1); } } break; case 'v': case 'V': message("Invert vertically"); for (c=0; c