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
addchild(Page * p,Page * c)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
loadchilds(Page * p,Kidinfo * k)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 *
getfilter(Rune * r,int x,int y)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
freecimage(Cimage * ci)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
closeimages(Page * p)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 *
loadimg(Rune * src,int x,int y)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 *
findimg(Rune * s)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
loadimages(Page * p)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
pageloadproc(void * v)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
pageload1(Page * p,Url * u,int dohist)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
pageload(Page * p,Url * u,int dohist)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
pageget(Page * p,Runestr * src,Runestr * post,int m,int dohist)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
pageclose(Page * p)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
pageabort(Page * p)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
tmpresize(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
renderchilds(Page * p)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
pagerender1(Page * p)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
pagerender(Page * p)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
pageredraw(Page * p)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
pageselect1(Page * p)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
pagedoubleclick(Page * p)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
pageselect(Page * p)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 *
pagewhich(Page * p,Point xy)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
pagemouse(Page * p,Point xy,int but)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
pagetype(Page * p,Rune r,Point xy)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
pagesnarf(Page * p)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
pagesetrefresh(Page * p)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