Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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;
}

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;
                }
        }
}

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;
}