1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <memdraw.h> 5 #include <thread.h> 6 #include <cursor.h> 7 #include <mouse.h> 8 #include <keyboard.h> 9 #include <frame.h> 10 #include <plumb.h> 11 #include <html.h> 12 #include "dat.h" 13 #include "fns.h" 14 15 static void pageload1(Page *, Url *, int); 16 17 static 18 void 19 addchild(Page *p, Page *c) 20 { 21 Page *t; 22 23 c->parent = p; 24 c->w = p->w; 25 c->b = p->b; 26 c->col = p->col; 27 c->row = p->row; 28 if(p->child == nil) 29 p->child = c; 30 else{ 31 for(t=p->child; t->next!=nil; t=t->next) 32 ; 33 t->next = c; 34 } 35 } 36 37 static 38 void 39 loadchilds(Page *p, Kidinfo *k) 40 { 41 Runestr rs; 42 Kidinfo *t; 43 Page *c; 44 45 addrefresh(p, "loading frames..."); 46 p->kidinfo = k; 47 for(t=k->kidinfos; t!=nil; t=t->next){ 48 c = emalloc(sizeof(Page)); 49 addchild(p, c); 50 if(t->isframeset){ 51 c->url = urldup(p->url); 52 loadchilds(c, t); 53 }else{ 54 c->kidinfo = t; 55 /* this check shouldn't be necessary, but... */ 56 if(t->src){ 57 rs.r = urlcombine(p->url->act.r, t->src); 58 rs.nr = runestrlen(rs.r); 59 pageload1(c, urlalloc(&rs, nil, HGet), FALSE); 60 closerunestr(&rs); 61 } 62 } 63 } 64 } 65 66 static struct { 67 char *mime; 68 char *filter; 69 }filtertab[] = { 70 "image/gif", "gif -t9", 71 "image/jpeg", "jpg -t9", 72 "image/jpg", "jpg -t9", 73 "image/pjpeg", "jpg -t9", 74 "image/png", "png -t9", 75 "image/ppm", "ppm -t9", 76 nil, nil, 77 }; 78 79 char * 80 getfilter(Rune *r, int x, int y) 81 { 82 char buf[128]; 83 int i; 84 85 snprint(buf, sizeof(buf), "%S", r); 86 for(i=0; filtertab[i].mime!=nil; i++) 87 if(cistrncmp(buf, filtertab[i].mime, strlen(filtertab[i].mime)) == 0) 88 break; 89 90 if(filtertab[i].filter == nil) 91 return nil; 92 93 if(x==0 && y==0) 94 return smprint("%s", filtertab[i].filter); 95 if(x!=0 && y!=0) 96 return smprint("%s | resample -x %d -y %d", filtertab[i].filter, x, y); 97 if(x != 0) 98 return smprint("%s | resample -x %d", filtertab[i].filter, x); 99 /* y != 0 */ 100 return smprint("%s | resample -y %d", filtertab[i].filter, y); 101 } 102 103 static Cimage *cimages = nil; 104 static QLock cimagelock; 105 106 static 107 void 108 freecimage(Cimage *ci) 109 { 110 Cimage *ci1; 111 112 qlock(&cimagelock); 113 if(decref(ci) == 0){ 114 if(ci->i) 115 freeimage(ci->i); 116 else if(ci->mi) 117 freememimage(ci->mi); 118 urlfree(ci->url); 119 ci1 = cimages; 120 if(ci1 == ci) 121 cimages = ci->next; 122 else{ 123 while(ci1){ 124 if(ci1->next == ci){ 125 ci1->next = ci->next; 126 break; 127 } 128 ci1 = ci1->next; 129 } 130 } 131 free(ci); 132 } 133 qunlock(&cimagelock); 134 } 135 136 static 137 void 138 closeimages(Page *p) 139 { 140 int i; 141 142 for(i=0; i<p->ncimage; i++) 143 freecimage(p->cimage[i]); 144 free(p->cimage); 145 p->cimage =nil; 146 p->ncimage = 0; 147 } 148 149 static 150 Cimage * 151 loadimg(Rune *src, int x , int y) 152 { 153 Channel *sync; 154 Cimage *ci; 155 Runestr rs; 156 Exec *e; 157 char *filter; 158 int fd, p[2], q[2]; 159 160 ci = emalloc(sizeof(Cimage)); 161 rs. r = src; 162 rs.nr = runestrlen(rs.r); 163 ci->url = urlalloc(&rs, nil, HGet); 164 fd = urlopen(ci->url); 165 if(fd < 0){ 166 Err1: 167 return ci; 168 } 169 filter = getfilter(ci->url->ctype.r, x, y); 170 if(filter == nil){ 171 werrstr("%S unsupported: %S", ci->url->ctype.r, ci->url->act.r); 172 Err2: 173 close(fd); 174 goto Err1; 175 } 176 177 if(pipe(p)<0 || pipe(q)<0) 178 error("can't create pipe"); 179 close(p[0]); 180 p[0] = fd; 181 sync = chancreate(sizeof(ulong), 0); 182 if(sync == nil) 183 error("can't create channel"); 184 e = emalloc(sizeof(Exec)); 185 e->p[0] = p[0]; 186 e->p[1] = p[1]; 187 e->q[0] = q[0]; 188 e->q[1] = q[1]; 189 e->cmd = filter; 190 e->sync = sync; 191 proccreate(execproc, e, STACK); 192 recvul(sync); 193 chanfree(sync); 194 close(p[0]); 195 close(p[1]); 196 close(q[1]); 197 198 ci->mi = readmemimage(q[0]); 199 close(q[0]); 200 if(ci->mi == nil){ 201 werrstr("can't read image"); 202 goto Err2; 203 } 204 free(filter); 205 return ci; 206 } 207 208 static 209 Cimage * 210 findimg(Rune *s) 211 { 212 Cimage *ci; 213 214 qlock(&cimagelock); 215 for(ci=cimages; ci!=nil; ci=ci->next) 216 if(runestrcmp(ci->url->src.r, s) == 0) 217 break; 218 219 qunlock(&cimagelock); 220 return ci; 221 } 222 223 void 224 loadimages(Page *p) 225 { 226 Cimage *ci; 227 Iimage *i; 228 Rune *src; 229 230 addrefresh(p, "loading images..."); 231 reverseimages(&p->doc->images); 232 for(i=p->doc->images; i!=nil; i=i->nextimage){ 233 if(p->aborting) 234 break; 235 src = urlcombine(getbase(p), i->imsrc); 236 ci = findimg(src); 237 if(ci == nil){ 238 ci = loadimg(src, i->imwidth, i->imheight); 239 qlock(&cimagelock); 240 ci->next = cimages; 241 cimages = ci; 242 qunlock(&cimagelock); 243 } 244 free(src); 245 incref(ci); 246 i->aux = ci; 247 p->cimage = erealloc(p->cimage, ++p->ncimage*sizeof(Cimage *)); 248 p->cimage[p->ncimage-1] = ci; 249 p->changed = TRUE; 250 addrefresh(p, ""); 251 } 252 } 253 254 static char *mimetab[] = { 255 "text/html", 256 "application/xhtml", 257 nil, 258 }; 259 260 static 261 void 262 pageloadproc(void *v) 263 { 264 Page *p; 265 char buf[BUFSIZE], *s; 266 long n, l; 267 int fd, i, ctype; 268 269 threadsetname("pageloadproc"); 270 rfork(RFFDG); 271 272 p = v; 273 addrefresh(p, "opening: %S...", p->url->src.r); 274 fd = urlopen(p->url); 275 if(fd < 0){ 276 addrefresh(p, "%S: %r", p->url->src.r); 277 Err: 278 p->loading = FALSE; 279 return; 280 } 281 if(runestrlen(p->url->ctype.r) == 0) /* assume .html when headers don't say anyting */ 282 goto Html; 283 284 snprint(buf, sizeof(buf), "%S", p->url->ctype.r); 285 for(i=0; mimetab[i]!=nil; i++) 286 if(cistrncmp(buf, mimetab[i], strlen(mimetab[i])) == 0) 287 break; 288 289 if(mimetab[i]){ 290 Html: 291 ctype = TextHtml; 292 }else if(cistrncmp(buf, "text/", 5) == 0) 293 ctype = TextPlain; 294 else{ 295 close(fd); 296 addrefresh(p, "%S: unsupported mime type: '%S'", p->url->act.r, p->url->ctype.r); 297 goto Err; 298 } 299 addrefresh(p, "loading: %S...", p->url->src.r); 300 s = nil; 301 l = 0; 302 while((n=read(fd, buf, sizeof(buf))) > 0){ 303 if(p->aborting){ 304 if(s){ 305 free(s); 306 s = nil; 307 } 308 break; 309 } 310 s = erealloc(s, l+n+1); 311 memmove(s+l, buf, n); 312 l += n; 313 s[l] = '\0'; 314 } 315 close(fd); 316 n = l; 317 if(s){ 318 s = convert(p->url->ctype, s, &n); 319 p->items = parsehtml((uchar *)s, n, p->url->act.r, ctype, UTF_8, &p->doc); 320 free(s); 321 fixtext(p); 322 if(ctype==TextHtml && p->aborting==FALSE){ 323 p->changed = TRUE; 324 addrefresh(p, ""); 325 if(p->doc->doctitle){ 326 p->title.r = erunestrdup(p->doc->doctitle); 327 p->title.nr = runestrlen(p->title.r); 328 } 329 p->loading = XXX; 330 if(p->doc->kidinfo) 331 loadchilds(p, p->doc->kidinfo); 332 else if(p->doc->images) 333 loadimages(p); 334 } 335 } 336 p->changed = TRUE; 337 p->loading = FALSE; 338 addrefresh(p, ""); 339 } 340 341 static 342 void 343 pageload1(Page *p, Url *u, int dohist) 344 { 345 pageclose(p); 346 p->loading = TRUE; 347 p->url = u; 348 if(dohist) 349 winaddhist(p->w, p->url); 350 proccreate(pageloadproc, p, STACK); 351 } 352 353 void 354 pageload(Page *p, Url *u, int dohist) 355 { 356 if(p->parent == nil) 357 textset(&p->w->url, u->src.r, u->src.nr); 358 draw(p->b, p->all, display->white, nil, ZP); 359 pageload1(p, u, dohist); 360 } 361 362 void 363 pageget(Page *p, Runestr *src, Runestr *post, int m, int dohist) 364 { 365 pageload(p, urlalloc(src, post, m), dohist); 366 } 367 368 void 369 pageclose(Page *p) 370 { 371 Page *c, *nc; 372 373 if(p == selpage) 374 selpage = nil; 375 pageabort(p); 376 closeimages(p); 377 urlfree(p->url); 378 p->url = nil; 379 if(p->doc){ 380 freedocinfo(p->doc); 381 p->doc = nil; 382 } 383 layfree(p->lay); 384 p->lay = nil; 385 freeitems(p->items); 386 p->items = nil; 387 for(c=p->child; c!=nil; c=nc){ 388 nc = c->next; 389 pageclose(c); 390 free(c); 391 } 392 p->child = nil; 393 closerunestr(&p->title); 394 closerunestr(&p->refresh.rs); 395 p->refresh.t = 0; 396 p->pos = ZP; 397 p->top = ZP; 398 p->bot = ZP; 399 p->loading = p->aborting = FALSE; 400 } 401 402 int 403 pageabort(Page *p) 404 { 405 Page *c; 406 407 for(c=p->child; c!=nil; c=c->next) 408 pageabort(c); 409 410 p->aborting = TRUE; 411 while(p->loading) 412 sleep(100); 413 414 p->aborting = FALSE; 415 return TRUE; 416 } 417 418 419 static Image *tmp; 420 421 void 422 tmpresize(void) 423 { 424 if(tmp) 425 freeimage(tmp); 426 427 tmp = eallocimage(display, Rect(0,0,Dx(screen->r),Dy(screen->r)), screen->chan, 0, -1); 428 } 429 430 static 431 void 432 renderchilds(Page *p) 433 { 434 Rectangle r; 435 Kidinfo *k; 436 Page *c; 437 int i, j, x, y, *w, *h; 438 439 draw(p->b, p->all, display->white, nil, ZP); 440 r = p->all; 441 y = r.min.y; 442 c = p->child; 443 k = p->kidinfo; 444 frdims(k->rows, k->nrows, Dy(r), &h); 445 frdims(k->cols, k->ncols, Dx(r), &w); 446 for(i=0; i<k->nrows; i++){ 447 x = r.min.x; 448 for(j=0; j<k->ncols; j++){ 449 if(c->aborting) 450 return; 451 c->b = p->b; 452 c->all = Rect(x,y,x+w[j],y+h[i]); 453 c->w = p->w; 454 pagerender(c); 455 c = c->next; 456 x += w[j]; 457 } 458 y += h[i]; 459 } 460 free(w); 461 free(h); 462 } 463 464 static 465 void 466 pagerender1(Page *p) 467 { 468 Rectangle r; 469 470 r = p->all; 471 p->hscrollr = r; 472 p->hscrollr.min.y = r.max.y-Scrollsize; 473 p->vscrollr = r; 474 p->vscrollr.max.x = r.min.x+Scrollsize; 475 r.max.y -= Scrollsize; 476 r.min.x += Scrollsize; 477 p->r = r; 478 p->vscrollr.max.y = r.max.y; 479 p->hscrollr.min.x = r.min.x; 480 laypage(p); 481 pageredraw(p); 482 } 483 484 void 485 pagerender(Page *p) 486 { 487 if(p->child && p->loading==FALSE) 488 renderchilds(p); 489 else if(p->doc) 490 pagerender1(p); 491 } 492 493 void 494 pageredraw(Page *p) 495 { 496 Rectangle r; 497 498 r = p->lay->r; 499 if(Dx(r)==0 || Dy(r)==0){ 500 draw(p->b, p->r, display->white, nil, ZP); 501 return; 502 } 503 if(tmp == nil) 504 tmpresize(); 505 506 p->selecting = FALSE; 507 draw(tmp, tmp->r, getbg(p), nil, ZP); 508 laydraw(p, tmp, p->lay); 509 draw(p->b, p->r, tmp, nil, tmp->r.min); 510 r = p->vscrollr; 511 r.min.y = r.max.y; 512 r.max.y += Scrollsize; 513 draw(p->b, r, tagcols[HIGH], nil, ZP); 514 draw(p->b, insetrect(r, 1), tagcols[BACK], nil, ZP); 515 pagescrldraw(p); 516 } 517 518 static 519 void 520 pageselect1(Page *p) /* when called, button 1 is down */ 521 { 522 Point mp, npos, opos; 523 int b, scrled, x, y; 524 525 b = mouse->buttons; 526 mp = mousectl->xy; 527 opos = getpt(p, mp); 528 do{ 529 x = y = 0; 530 if(mp.x < p->r.min.x) 531 x -= p->r.min.x-mp.x; 532 else if(mp.x > p->r.max.x) 533 x += mp.x-p->r.max.x; 534 if(mp.y < p->r.min.y) 535 y -= (p->r.min.y-mp.y)*Panspeed; 536 else if(mp.y > p->r.max.y) 537 y += (mp.y-p->r.max.y)*Panspeed; 538 539 scrled = pagescrollxy(p, x, y); 540 npos = getpt(p, mp); 541 if(opos.y < npos.y){ 542 p->top = opos; 543 p->bot = npos; 544 }else{ 545 p->top = npos; 546 p->bot = opos; 547 } 548 pageredraw(p); 549 if(scrled == TRUE) 550 scrsleep(100); 551 else 552 readmouse(mousectl); 553 554 mp = mousectl->xy; 555 }while(mousectl->buttons == b); 556 } 557 558 static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 }; 559 static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 }; 560 static Rune left2[] = { L'\'', L'"', L'`', 0 }; 561 562 static 563 Rune *left[] = { 564 left1, 565 left2, 566 nil 567 }; 568 static 569 Rune *right[] = { 570 right1, 571 left2, 572 nil 573 }; 574 575 void 576 pagedoubleclick(Page *p) 577 { 578 Point xy; 579 Line *l; 580 Box *b; 581 582 xy = getpt(p, mouse->xy); 583 l = linewhich(p->lay, xy); 584 if(l==nil || l->hastext==FALSE) 585 return; 586 587 if(xy.x<l->boxes->r.min.x && hasbrk(l->state)){ /* beginning of line? */ 588 p->top = l->boxes->r.min; 589 if(l->next && !hasbrk(l->next->state)){ 590 for(l=l->next; l->next!=nil; l=l->next) 591 if(hasbrk(l->next->state)) 592 break; 593 } 594 p->bot = l->lastbox->r.max;; 595 }else if(xy.x>l->lastbox->r.max.x && hasbrk(l->next->state)){ /* end of line? */ 596 p->bot = l->lastbox->r.max; 597 if(!hasbrk(l->state) && l->prev!=nil){ 598 for(l=l->prev; l->prev!=nil; l=l->prev) 599 if(hasbrk(l->state)) 600 break; 601 } 602 p->top = l->boxes->r.min; 603 }else{ 604 b = pttobox(l, xy); 605 if(b!=nil && b->i->tag==Itexttag){ 606 p->top = b->r.min; 607 p->bot = b->r.max; 608 } 609 } 610 p->top.y += 2; 611 p->bot.y -= 2; 612 pageredraw(p); 613 } 614 615 static uint clickmsec; 616 617 void 618 pageselect(Page *p) 619 { 620 int b, x, y; 621 622 623 selpage = p; 624 /* 625 * To have double-clicking and chording, we double-click 626 * immediately if it might make sense. 627 */ 628 b = mouse->buttons; 629 if(mouse->msec-clickmsec<500){ 630 pagedoubleclick(p); 631 x = mouse->xy.x; 632 y = mouse->xy.y; 633 /* stay here until something interesting happens */ 634 do 635 readmouse(mousectl); 636 while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3); 637 mouse->xy.x = x; /* in case we're calling pageselect1 */ 638 mouse->xy.y = y; 639 } 640 if(mousectl->buttons == b) 641 pageselect1(p); 642 643 if(eqpt(p->top, p->bot)){ 644 if(mouse->msec-clickmsec<500) 645 pagedoubleclick(p); 646 else 647 clickmsec = mouse->msec; 648 } 649 while(mouse->buttons){ 650 mouse->msec = 0; 651 b = mouse->buttons; 652 if(b & 2) /* snarf only */ 653 cut(nil, nil, TRUE, FALSE, nil, 0); 654 while(mouse->buttons == b) 655 readmouse(mousectl); 656 } 657 } 658 659 Page * 660 pagewhich(Page *p, Point xy) 661 { 662 Page *c; 663 664 if(p->child == nil) 665 return p; 666 667 for(c=p->child; c!=nil; c=c->next) 668 if(ptinrect(xy, c->all)) 669 return pagewhich(c, xy); 670 671 return nil; 672 } 673 674 void 675 pagemouse(Page *p, Point xy, int but) 676 { 677 Box *b; 678 679 p = pagewhich(p, xy); 680 if(p == nil) 681 return; 682 683 if(pagerefresh(p)) 684 return; 685 686 if(p->lay == nil) 687 return; 688 689 if(ptinrect(xy, p->vscrollr)){ 690 pagescroll(p, but, FALSE); 691 return; 692 } 693 if(ptinrect(xy, p->hscrollr)){ 694 pagescroll(p, but, TRUE); 695 return; 696 } 697 xy = getpt(p, xy); 698 b = boxwhich(p->lay, xy); 699 if(b && b->mouse) 700 b->mouse(b, p, but); 701 else if(but == 1) 702 pageselect(p); 703 } 704 705 void 706 pagetype(Page *p, Rune r, Point xy) 707 { 708 Box *b; 709 int x, y; 710 711 p = pagewhich(p, xy); 712 if(p == nil) 713 return; 714 715 if(pagerefresh(p)) 716 return; 717 718 if(p->lay == nil) 719 return; 720 721 /* text field? */ 722 xy = getpt(p, xy); 723 b = boxwhich(p->lay, xy); 724 if(b && b->key){ 725 b->key(b, p, r); 726 return; 727 } 728 /* ^H: same as 'Back' */ 729 if(r == 0x08){ 730 wingohist(p->w, FALSE); 731 return; 732 } 733 734 x = 0; 735 y = 0; 736 switch(r){ 737 case Kleft: 738 x -= Dx(p->r)/2; 739 break; 740 case Kright: 741 x += Dx(p->r)/2; 742 break; 743 case Kdown: 744 case Kscrollonedown: 745 y += Dy(p->r)/2; 746 break; 747 case Kpgdown: 748 y += Dy(p->r); 749 break; 750 case Kup: 751 case Kscrolloneup: 752 y -= Dy(p->r)/2; 753 break; 754 case Kpgup: 755 y -= Dy(p->r); 756 break; 757 case Khome: 758 y -= Dy(p->lay->r); /* force p->pos.y = 0 */ 759 break; 760 case Kend: 761 y = Dy(p->lay->r) - Dy(p->r); 762 break; 763 default: 764 return; 765 } 766 if(pagescrollxy(p, x, y)) 767 pageredraw(p); 768 } 769 770 void 771 pagesnarf(Page *p) 772 { 773 Runestr rs; 774 775 memset(&rs, 0, sizeof(Runestr)); 776 laysnarf(p, p->lay, &rs); 777 putsnarf(&rs); 778 closerunestr(&rs); 779 } 780 781 void 782 pagesetrefresh(Page *p) 783 { 784 Runestr rs; 785 Rune *s, *q, *t; 786 char *v; 787 int n; 788 789 if(!p->doc || !p->doc->refresh) 790 return; 791 792 s = p->doc->refresh; 793 q = runestrchr(s, L'='); 794 if(q == nil) 795 return; 796 q++; 797 if(!q) 798 return; 799 n = runestrlen(q); 800 if(*q == L'''){ 801 q++; 802 n -= 2; 803 } 804 if(n <= 0) 805 return; 806 t = runesmprint("%.*S", n, q); 807 rs.r = urlcombine(getbase(p), t); 808 rs.nr = runestrlen(rs.r); 809 copyrunestr(&p->refresh.rs, &rs); 810 closerunestr(&rs); 811 free(t); 812 813 /* now the time */ 814 q = runestrchr(s, L';'); 815 if(q){ 816 v = smprint("%.*S", (int)(q-s), s); 817 p->refresh.t = atoi(v); 818 free(v); 819 }else 820 p->refresh.t = 1; 821 822 p->refresh.t += time(0); 823 } 824 825 int 826 pagerefresh(Page *p) 827 { 828 int t; 829 830 if(!p->refresh.t) 831 return 0; 832 833 t = p->refresh.t - time(0); 834 if(t > 0) 835 return 0; 836 837 pageget(p, &p->refresh.rs, nil, HGet, FALSE); 838 return 1; 839 } 840