Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
}