#include #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" static void pageload1(Page *, Url *, int); static void addchild(Page *p, Page *c) { Page *t; c->parent = p; c->w = p->w; c->b = p->b; c->col = p->col; c->row = p->row; if(p->child == nil) p->child = c; else{ for(t=p->child; t->next!=nil; t=t->next) ; t->next = c; } } static void loadchilds(Page *p, Kidinfo *k) { Runestr rs; Kidinfo *t; Page *c; addrefresh(p, "loading frames..."); p->kidinfo = k; for(t=k->kidinfos; t!=nil; t=t->next){ c = emalloc(sizeof(Page)); addchild(p, c); if(t->isframeset){ c->url = urldup(p->url); loadchilds(c, t); }else{ c->kidinfo = t; /* this check shouldn't be necessary, but... */ if(t->src){ rs.r = urlcombine(p->url->act.r, t->src); rs.nr = runestrlen(rs.r); pageload1(c, urlalloc(&rs, nil, HGet), FALSE); closerunestr(&rs); } } } } static struct { char *mime; char *filter; }filtertab[] = { "image/gif", "gif -t9", "image/jpeg", "jpg -t9", "image/jpg", "jpg -t9", "image/pjpeg", "jpg -t9", "image/png", "png -t9", "image/ppm", "ppm -t9", nil, nil, }; char * getfilter(Rune *r, int x, int y) { char buf[128]; int i; snprint(buf, sizeof(buf), "%S", r); for(i=0; filtertab[i].mime!=nil; i++) if(cistrncmp(buf, filtertab[i].mime, strlen(filtertab[i].mime)) == 0) break; if(filtertab[i].filter == nil) return nil; if(x==0 && y==0) return smprint("%s", filtertab[i].filter); if(x!=0 && y!=0) return smprint("%s | resample -x %d -y %d", filtertab[i].filter, x, y); if(x != 0) return smprint("%s | resample -x %d", filtertab[i].filter, x); /* y != 0 */ return smprint("%s | resample -y %d", filtertab[i].filter, y); } static Cimage *cimages = nil; static QLock cimagelock; static void freecimage(Cimage *ci) { Cimage *ci1; qlock(&cimagelock); if(decref(ci) == 0){ if(ci->i) freeimage(ci->i); else if(ci->mi) freememimage(ci->mi); urlfree(ci->url); ci1 = cimages; if(ci1 == ci) cimages = ci->next; else{ while(ci1){ if(ci1->next == ci){ ci1->next = ci->next; break; } ci1 = ci1->next; } } free(ci); } qunlock(&cimagelock); } static void closeimages(Page *p) { int i; for(i=0; incimage; i++) freecimage(p->cimage[i]); free(p->cimage); p->cimage =nil; p->ncimage = 0; } static Cimage * loadimg(Rune *src, int x , int y) { Channel *sync; Cimage *ci; Runestr rs; Exec *e; char *filter; int fd, p[2], q[2]; ci = emalloc(sizeof(Cimage)); rs. r = src; rs.nr = runestrlen(rs.r); ci->url = urlalloc(&rs, nil, HGet); fd = urlopen(ci->url); if(fd < 0){ Err1: return ci; } filter = getfilter(ci->url->ctype.r, x, y); if(filter == nil){ werrstr("%S unsupported: %S", ci->url->ctype.r, ci->url->act.r); Err2: close(fd); goto Err1; } if(pipe(p)<0 || pipe(q)<0) error("can't create pipe"); close(p[0]); p[0] = fd; sync = chancreate(sizeof(ulong), 0); if(sync == nil) error("can't create channel"); e = emalloc(sizeof(Exec)); e->p[0] = p[0]; e->p[1] = p[1]; e->q[0] = q[0]; e->q[1] = q[1]; e->cmd = filter; e->sync = sync; proccreate(execproc, e, STACK); recvul(sync); chanfree(sync); close(p[0]); close(p[1]); close(q[1]); ci->mi = readmemimage(q[0]); close(q[0]); if(ci->mi == nil){ werrstr("can't read image"); goto Err2; } free(filter); return ci; } static Cimage * findimg(Rune *s) { Cimage *ci; qlock(&cimagelock); for(ci=cimages; ci!=nil; ci=ci->next) if(runestrcmp(ci->url->src.r, s) == 0) break; qunlock(&cimagelock); return ci; } void loadimages(Page *p) { Cimage *ci; Iimage *i; Rune *src; addrefresh(p, "loading images..."); reverseimages(&p->doc->images); for(i=p->doc->images; i!=nil; i=i->nextimage){ if(p->aborting) break; src = urlcombine(getbase(p), i->imsrc); ci = findimg(src); if(ci == nil){ ci = loadimg(src, i->imwidth, i->imheight); qlock(&cimagelock); ci->next = cimages; cimages = ci; qunlock(&cimagelock); } free(src); incref(ci); i->aux = ci; p->cimage = erealloc(p->cimage, ++p->ncimage*sizeof(Cimage *)); p->cimage[p->ncimage-1] = ci; p->changed = TRUE; addrefresh(p, ""); } } static char *mimetab[] = { "text/html", "application/xhtml", nil, }; static void pageloadproc(void *v) { Page *p; char buf[BUFSIZE], *s; long n, l; int fd, i, ctype; threadsetname("pageloadproc"); rfork(RFFDG); p = v; addrefresh(p, "opening: %S...", p->url->src.r); fd = urlopen(p->url); if(fd < 0){ addrefresh(p, "%S: %r", p->url->src.r); Err: p->loading = FALSE; return; } if(runestrlen(p->url->ctype.r) == 0) /* assume .html when headers don't say anyting */ goto Html; snprint(buf, sizeof(buf), "%S", p->url->ctype.r); for(i=0; mimetab[i]!=nil; i++) if(cistrncmp(buf, mimetab[i], strlen(mimetab[i])) == 0) break; if(mimetab[i]){ Html: ctype = TextHtml; }else if(cistrncmp(buf, "text/", 5) == 0) ctype = TextPlain; else{ close(fd); addrefresh(p, "%S: unsupported mime type: '%S'", p->url->act.r, p->url->ctype.r); goto Err; } addrefresh(p, "loading: %S...", p->url->src.r); s = nil; l = 0; while((n=read(fd, buf, sizeof(buf))) > 0){ if(p->aborting){ if(s){ free(s); s = nil; } break; } s = erealloc(s, l+n+1); memmove(s+l, buf, n); l += n; s[l] = '\0'; } close(fd); n = l; if(s){ s = convert(p->url->ctype, s, &n); p->items = parsehtml((uchar *)s, n, p->url->act.r, ctype, UTF_8, &p->doc); free(s); fixtext(p); if(ctype==TextHtml && p->aborting==FALSE){ p->changed = TRUE; addrefresh(p, ""); if(p->doc->doctitle){ p->title.r = erunestrdup(p->doc->doctitle); p->title.nr = runestrlen(p->title.r); } p->loading = XXX; if(p->doc->kidinfo) loadchilds(p, p->doc->kidinfo); else if(p->doc->images) loadimages(p); } } p->changed = TRUE; p->loading = FALSE; addrefresh(p, ""); } static void pageload1(Page *p, Url *u, int dohist) { pageclose(p); p->loading = TRUE; p->url = u; if(dohist) winaddhist(p->w, p->url); proccreate(pageloadproc, p, STACK); } void pageload(Page *p, Url *u, int dohist) { if(p->parent == nil) textset(&p->w->url, u->src.r, u->src.nr); draw(p->b, p->all, display->white, nil, ZP); pageload1(p, u, dohist); } void pageget(Page *p, Runestr *src, Runestr *post, int m, int dohist) { pageload(p, urlalloc(src, post, m), dohist); } void pageclose(Page *p) { Page *c, *nc; if(p == selpage) selpage = nil; pageabort(p); closeimages(p); urlfree(p->url); p->url = nil; if(p->doc){ freedocinfo(p->doc); p->doc = nil; } layfree(p->lay); p->lay = nil; freeitems(p->items); p->items = nil; for(c=p->child; c!=nil; c=nc){ nc = c->next; pageclose(c); free(c); } p->child = nil; closerunestr(&p->title); closerunestr(&p->refresh.rs); p->refresh.t = 0; p->pos = ZP; p->top = ZP; p->bot = ZP; p->loading = p->aborting = FALSE; } int pageabort(Page *p) { Page *c; for(c=p->child; c!=nil; c=c->next) pageabort(c); p->aborting = TRUE; while(p->loading) sleep(100); p->aborting = FALSE; return TRUE; } static Image *tmp; void tmpresize(void) { if(tmp) freeimage(tmp); tmp = eallocimage(display, Rect(0,0,Dx(screen->r),Dy(screen->r)), screen->chan, 0, -1); } static void renderchilds(Page *p) { Rectangle r; Kidinfo *k; Page *c; int i, j, x, y, *w, *h; draw(p->b, p->all, display->white, nil, ZP); r = p->all; y = r.min.y; c = p->child; k = p->kidinfo; frdims(k->rows, k->nrows, Dy(r), &h); frdims(k->cols, k->ncols, Dx(r), &w); for(i=0; inrows; i++){ x = r.min.x; for(j=0; jncols; j++){ if(c->aborting) return; c->b = p->b; c->all = Rect(x,y,x+w[j],y+h[i]); c->w = p->w; pagerender(c); c = c->next; x += w[j]; } y += h[i]; } free(w); free(h); } static void pagerender1(Page *p) { Rectangle r; r = p->all; p->hscrollr = r; p->hscrollr.min.y = r.max.y-Scrollsize; p->vscrollr = r; p->vscrollr.max.x = r.min.x+Scrollsize; r.max.y -= Scrollsize; r.min.x += Scrollsize; p->r = r; p->vscrollr.max.y = r.max.y; p->hscrollr.min.x = r.min.x; laypage(p); pageredraw(p); } void pagerender(Page *p) { if(p->child && p->loading==FALSE) renderchilds(p); else if(p->doc) pagerender1(p); } void pageredraw(Page *p) { Rectangle r; r = p->lay->r; if(Dx(r)==0 || Dy(r)==0){ draw(p->b, p->r, display->white, nil, ZP); return; } if(tmp == nil) tmpresize(); p->selecting = FALSE; draw(tmp, tmp->r, getbg(p), nil, ZP); laydraw(p, tmp, p->lay); draw(p->b, p->r, tmp, nil, tmp->r.min); r = p->vscrollr; r.min.y = r.max.y; r.max.y += Scrollsize; draw(p->b, r, tagcols[HIGH], nil, ZP); draw(p->b, insetrect(r, 1), tagcols[BACK], nil, ZP); pagescrldraw(p); } static void pageselect1(Page *p) /* when called, button 1 is down */ { Point mp, npos, opos; int b, scrled, x, y; b = mouse->buttons; mp = mousectl->xy; opos = getpt(p, mp); do{ x = y = 0; if(mp.x < p->r.min.x) x -= p->r.min.x-mp.x; else if(mp.x > p->r.max.x) x += mp.x-p->r.max.x; if(mp.y < p->r.min.y) y -= (p->r.min.y-mp.y)*Panspeed; else if(mp.y > p->r.max.y) y += (mp.y-p->r.max.y)*Panspeed; scrled = pagescrollxy(p, x, y); npos = getpt(p, mp); if(opos.y < npos.y){ p->top = opos; p->bot = npos; }else{ p->top = npos; p->bot = opos; } pageredraw(p); if(scrled == TRUE) scrsleep(100); else readmouse(mousectl); mp = mousectl->xy; }while(mousectl->buttons == b); } static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 }; static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 }; static Rune left2[] = { L'\'', L'"', L'`', 0 }; static Rune *left[] = { left1, left2, nil }; static Rune *right[] = { right1, left2, nil }; void pagedoubleclick(Page *p) { Point xy; Line *l; Box *b; xy = getpt(p, mouse->xy); l = linewhich(p->lay, xy); if(l==nil || l->hastext==FALSE) return; if(xy.xboxes->r.min.x && hasbrk(l->state)){ /* beginning of line? */ p->top = l->boxes->r.min; if(l->next && !hasbrk(l->next->state)){ for(l=l->next; l->next!=nil; l=l->next) if(hasbrk(l->next->state)) break; } p->bot = l->lastbox->r.max;; }else if(xy.x>l->lastbox->r.max.x && hasbrk(l->next->state)){ /* end of line? */ p->bot = l->lastbox->r.max; if(!hasbrk(l->state) && l->prev!=nil){ for(l=l->prev; l->prev!=nil; l=l->prev) if(hasbrk(l->state)) break; } p->top = l->boxes->r.min; }else{ b = pttobox(l, xy); if(b!=nil && b->i->tag==Itexttag){ p->top = b->r.min; p->bot = b->r.max; } } p->top.y += 2; p->bot.y -= 2; pageredraw(p); } static uint clickmsec; void pageselect(Page *p) { int b, x, y; selpage = p; /* * To have double-clicking and chording, we double-click * immediately if it might make sense. */ b = mouse->buttons; if(mouse->msec-clickmsec<500){ pagedoubleclick(p); x = mouse->xy.x; y = mouse->xy.y; /* stay here until something interesting happens */ do readmouse(mousectl); while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3); mouse->xy.x = x; /* in case we're calling pageselect1 */ mouse->xy.y = y; } if(mousectl->buttons == b) pageselect1(p); if(eqpt(p->top, p->bot)){ if(mouse->msec-clickmsec<500) pagedoubleclick(p); else clickmsec = mouse->msec; } while(mouse->buttons){ mouse->msec = 0; b = mouse->buttons; if(b & 2) /* snarf only */ cut(nil, nil, TRUE, FALSE, nil, 0); while(mouse->buttons == b) readmouse(mousectl); } } Page * pagewhich(Page *p, Point xy) { Page *c; if(p->child == nil) return p; for(c=p->child; c!=nil; c=c->next) if(ptinrect(xy, c->all)) return pagewhich(c, xy); return nil; } void pagemouse(Page *p, Point xy, int but) { Box *b; p = pagewhich(p, xy); if(p == nil) return; if(pagerefresh(p)) return; if(p->lay == nil) return; if(ptinrect(xy, p->vscrollr)){ pagescroll(p, but, FALSE); return; } if(ptinrect(xy, p->hscrollr)){ pagescroll(p, but, TRUE); return; } xy = getpt(p, xy); b = boxwhich(p->lay, xy); if(b && b->mouse) b->mouse(b, p, but); else if(but == 1) pageselect(p); } void pagetype(Page *p, Rune r, Point xy) { Box *b; int x, y; p = pagewhich(p, xy); if(p == nil) return; if(pagerefresh(p)) return; if(p->lay == nil) return; /* text field? */ xy = getpt(p, xy); b = boxwhich(p->lay, xy); if(b && b->key){ b->key(b, p, r); return; } /* ^H: same as 'Back' */ if(r == 0x08){ wingohist(p->w, FALSE); return; } x = 0; y = 0; switch(r){ case Kleft: x -= Dx(p->r)/2; break; case Kright: x += Dx(p->r)/2; break; case Kdown: case Kscrollonedown: y += Dy(p->r)/2; break; case Kpgdown: y += Dy(p->r); break; case Kup: case Kscrolloneup: y -= Dy(p->r)/2; break; case Kpgup: y -= Dy(p->r); break; case Khome: y -= Dy(p->lay->r); /* force p->pos.y = 0 */ break; case Kend: y = Dy(p->lay->r) - Dy(p->r); break; default: return; } if(pagescrollxy(p, x, y)) pageredraw(p); } void pagesnarf(Page *p) { Runestr rs; memset(&rs, 0, sizeof(Runestr)); laysnarf(p, p->lay, &rs); putsnarf(&rs); closerunestr(&rs); } void pagesetrefresh(Page *p) { Runestr rs; Rune *s, *q, *t; char *v; int n; if(!p->doc || !p->doc->refresh) return; s = p->doc->refresh; q = runestrchr(s, L'='); if(q == nil) return; q++; if(!q) return; n = runestrlen(q); if(*q == L'''){ q++; n -= 2; } if(n <= 0) return; t = runesmprint("%.*S", n, q); rs.r = urlcombine(getbase(p), t); rs.nr = runestrlen(rs.r); copyrunestr(&p->refresh.rs, &rs); closerunestr(&rs); free(t); /* now the time */ q = runestrchr(s, L';'); if(q){ v = smprint("%.*S", (int)(q-s), s); p->refresh.t = atoi(v); free(v); }else p->refresh.t = 1; p->refresh.t += time(0); } int pagerefresh(Page *p) { int t; if(!p->refresh.t) return 0; t = p->refresh.t - time(0); if(t > 0) return 0; pageget(p, &p->refresh.rs, nil, HGet, FALSE); return 1; }