richterm

"terminal emulator" with support for text fonts and images for plan9
git clone git://nsmpr.xyz/richterm.git
Log | Files | Refs | README

richterm.c (18666B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <plumb.h>
      4 #include <fcall.h>
      5 #include <thread.h>
      6 #include <draw.h>
      7 #include <keyboard.h>
      8 #include <mouse.h>
      9 #include <cursor.h>
     10 #include <9p.h>
     11 
     12 #include "array.h"
     13 #include "richterm.h"
     14 
     15 int getelem(Point);
     16 void insertfromcons(Array *);
     17 void mouse(Mousectl *, Mouse, int *);
     18 void resize(void);
     19 void runcmd(void *);
     20 void scroll(int, Rich *);
     21 void send_interrupt(void);
     22 void shutdown(void);
     23 void text2runes(char *str, Array *elems);
     24 
     25 Array *elems;
     26 Array *fonts;
     27 Array *richdata;
     28 Channel *pidchan, *redrawc, *insertc;
     29 Image *Iscrollbar, *Ilink, *Inormbg, *Iselbg, *Itext;
     30 Keyboardctl *kctl;
     31 Mousectl *mctl;
     32 Rich rich;
     33 char *cmd = "/bin/richrc";
     34 char *srvname;
     35 int hostpid = -1;
     36 Array *drawcache;
     37 
     38 void mfollow(void);
     39 void mpaste(void);
     40 void mplumb(void);
     41 void msnarf(void);
     42 
     43 char *mitems[] = {"follow", "paste", "snarf", "plumb", nil};
     44 void (*mfunc[])(void) = {mfollow, mpaste, msnarf, mplumb, nil};
     45 
     46 Menu mmenu = {
     47 	.item = mitems,
     48 	.gen = nil,
     49 	.lasthit = 0,
     50 };
     51 
     52 void rfollow(void *);
     53 void rsnarf(void *);
     54 void rplumb(void *);
     55 
     56 char *ritems[] = {"Follow", "Snarf", "Plumb", nil};
     57 void (*rfunc[])(void *) = {rfollow, rsnarf, rplumb, nil};
     58 int rsize = sizeof(ritems) / sizeof(*ritems);
     59 char * rgen(int);
     60 
     61 Menu rmenu = {
     62 	.item = nil,
     63 	.gen = rgen,
     64 	.lasthit = 0,
     65 };
     66 
     67 char * rusergen(int);
     68 void ruseract(int);
     69 
     70 Menu rusermenu = {
     71 	.item = nil,
     72 	.gen = rusergen,
     73 	.lasthit = 0,
     74 };
     75 
     76 enum {
     77 	MM_NONE,
     78 	MM_SCROLLBAR,
     79 	MM_TEXT,
     80 	MM_SELECT,
     81 };
     82 
     83 void
     84 usage(void)
     85 {
     86 	fprint(2, "usage: %s [-D] [-c command] [-s srvname] [args]\n", argv0);
     87 	exits("usage");
     88 }
     89 
     90 void
     91 threadmain(int argc, char **argv)
     92 {
     93 	int rv[2], mmode;
     94 	Mouse mv;
     95 	Rune kv;
     96 	Array *av;
     97 
     98 	ARGBEGIN{
     99 	case 'D':
    100 		chatty9p++;
    101 		break;
    102 	case 'c':
    103 		cmd = EARGF(usage());
    104 		break;
    105 	case 's':
    106 		srvname = EARGF(usage());
    107 		break;
    108 	default:
    109 		usage();
    110 	} ARGEND
    111 
    112 	av = nil;
    113 	mmode = MM_NONE;
    114 
    115 	if (initdraw(0, 0, argv0) < 0)
    116 		sysfatal("%s: %r", argv0);
    117 	if ((mctl = initmouse(nil, screen)) == nil)
    118 		sysfatal("%s: %r", argv0);
    119 	if ((kctl = initkeyboard(nil)) == nil)
    120 		sysfatal("%s: %r", argv0);
    121 
    122 	display->locking = 1;
    123 	unlockdisplay(display);
    124 
    125 	Iscrollbar = allocimage(
    126 	  display, Rect(0,0,1,1), screen->chan, 1, 0x999999FF);
    127 	Inormbg = allocimage(
    128 	  display, Rect(0,0,1,1), screen->chan, 1, DWhite);
    129 	Iselbg = allocimage(
    130 	  display, Rect(0,0,1,1), screen->chan, 1, 0xBBBBBBFF);
    131 	Ilink = allocimage(
    132 	  display, Rect(0,0,1,1), screen->chan, 1, DBlue);
    133 
    134 	Itext = display->black;
    135 
    136 	fonts = arraycreate(sizeof(Font *), 2, nil);
    137 	arraygrow(fonts, 1, &font);
    138 
    139 	rich.l = mallocz(sizeof(QLock), 1);
    140 
    141 	qlock(rich.l);
    142 
    143 	rich.selmin = 0;
    144 	rich.selmax = 0;
    145 
    146 	qunlock(rich.l);
    147 
    148 	redrawc = chancreate(sizeof(void *), 8);
    149 	insertc = chancreate(sizeof(Array *), 8);
    150 
    151 	elems = arraycreate(sizeof(Elem *), 128, nil);
    152 	richdata = arraycreate(sizeof(char), 1024, nil);
    153 	drawcache = arraycreate(sizeof(DrawState), 1024, nil);
    154 
    155 	rich.selmin = 0;
    156 	rich.selmax = 0;
    157 
    158 	resize();
    159 	draw(screen, screen->r, Inormbg, nil, ZP);
    160 	drawscrollbar();
    161 	flushimage(display, 1);
    162 
    163 	if(rfork(RFENVG) < 0)
    164 		sysfatal("rfork: %r");
    165 	quotefmtinstall();
    166 	atexit(shutdown);
    167 
    168 	pidchan = chancreate(sizeof(int), 0);
    169 	proccreate(runcmd, argv, 16 * 1024);
    170 	hostpid = recvul(pidchan);
    171 
    172 	threadsetname("main");
    173 
    174 	void *ov;
    175 
    176 	enum {MOUSE, RESIZE, REDRAW, INSERT, KBD, AEND};
    177 	Alt alts[AEND + 1] = {
    178 		{mctl->c, &mv, CHANRCV},
    179 		{mctl->resizec, rv, CHANRCV},
    180 		{redrawc, &ov, CHANRCV},
    181 		{insertc, &av, CHANRCV},
    182 		{kctl->c, &kv, CHANRCV},
    183 		{nil, nil, CHANEND},
    184 	};
    185 
    186 	for (;;) {
    187 		switch(alt(alts)) {
    188 		case MOUSE:
    189 			mouse(mctl, mv, &mmode);
    190 			break;
    191 		case RESIZE:
    192 			if (getwindow(display, Refnone) < 0)
    193 				sysfatal("resize failed: %r");
    194 			resize();
    195 			nbsend(redrawc, nil);
    196 			break;
    197 		case REDRAW:
    198 
    199 			while (nbrecv(redrawc, &ov) != 0);
    200 
    201 			lockdisplay(display);
    202 			draw(screen, rich.r, Inormbg, nil, ZP);
    203 			drawelems(0, elems->count);
    204 			drawscrollbar();
    205 			flushimage(display, 1);
    206 			unlockdisplay(display);
    207 			break;
    208 		case INSERT:
    209 			insertfromcons(av);
    210 			nbsend(redrawc, nil);
    211 			break;
    212 		case KBD:
    213 			if (kv == 0x7f) shutdown(); /* delete */
    214 			if (kv == 0xf00e) { /* d-pad up */
    215 				scroll(
    216 				  rich.scroll - Dy(screen->r) / 8,
    217 				  &rich);
    218 				break;
    219 			}
    220 			if (kv == 0xf800) { /* d-pad down */
    221 				scroll(
    222 				  rich.scroll + Dy(screen->r) / 8,
    223 				  &rich);
    224 				break;
    225 			}
    226 			if (kv == 0xf00f) { /* page up */
    227 				scroll(
    228 				  rich.scroll - Dy(screen->r) / 4,
    229 				  &rich);
    230 				break;
    231 			}
    232 			if (kv == 0xf013) { /* page down */
    233 				scroll(
    234 				  rich.scroll + Dy(screen->r) / 4,
    235 				  &rich);
    236 				break;
    237 			}
    238 			if (elems->count > 0) {
    239 				if (kv == 0x08) { /* backspace */
    240 					if (rich.input < elems->count) {
    241 						Elem *edisc;
    242 						arrayget(elems, elems->count - 1, &edisc);
    243 						if (edisc == nil) break;
    244 						freeelem(edisc);
    245 						free(edisc);
    246 						arraydel(elems, elems->count - 1, 1);
    247 					}
    248 				} else {
    249 					Elem *enew = mallocz(sizeof(Elem), 1);
    250 					enew->type = TRune;
    251 					enew->r = kv;
    252 					arraygrow(elems, 1, &enew);
    253 				}
    254 				if (kv == '\n') {
    255 					Rune *r;
    256 					char *s;
    257 					r = getrunes(rich.input, elems->count);
    258 					s = smprint("%S", r);
    259 
    260 					arraygrow(richdata, 1, ".");
    261 					arraygrow(richdata, strlen(s), s);
    262 					arraygrow(richdata, 2, "n\n");
    263 
    264 					rich.input = elems->count;
    265 
    266 					Array *msg = arraycreate(sizeof(char), strlen(s), nil);
    267 					arraygrow(msg, strlen(s), s);
    268 					nbsend(consc, &msg);
    269 					free(r);
    270 					free(s);
    271 				}
    272 				nbsend(redrawc, nil);
    273 			}
    274 			break;
    275 		case AEND:
    276 			break;
    277 		}
    278 	}
    279 }
    280 
    281 void
    282 mouse(Mousectl *mc, Mouse mv, int *mmode)
    283 {
    284 	static long selstart, selend;
    285 	long rstart, rend;
    286 	if (mv.buttons == 0) {
    287 		*mmode = MM_NONE;
    288 		return;
    289 	}
    290 	if (mv.buttons == 8) {
    291 		scroll(
    292 		  rich.scroll - (mv.xy.y - rich.r.min.y),
    293 		  &rich);
    294 		return;
    295 	}
    296 	if (mv.buttons == 16) {
    297 		scroll(
    298 		  rich.scroll + (mv.xy.y - rich.r.min.y),
    299 		  &rich);
    300 		return;
    301 	}
    302 
    303 	if (*mmode == MM_NONE) {
    304 		if (ptinrect(mv.xy, rich.rs) != 0)
    305 			*mmode = MM_SCROLLBAR;
    306 		if (ptinrect(mv.xy, rich.r) != 0)
    307 			*mmode = MM_TEXT;
    308 	}
    309 
    310 	switch (*mmode) {
    311 	case MM_SCROLLBAR:
    312 		if (mv.buttons == 1) {
    313 			scroll(
    314 			  rich.scroll - (mv.xy.y - rich.r.min.y),
    315 			  &rich);
    316 		} else if (mv.buttons == 2) {
    317 			scroll(
    318 			  (mv.xy.y - rich.r.min.y) *
    319 		  	    ((double)rich.max / Dy(rich.r)),
    320 		 	  &rich);
    321 		} else if (mv.buttons == 4) {
    322 			scroll(
    323 			  rich.scroll + (mv.xy.y - rich.r.min.y),
    324 			  &rich);
    325 		}
    326 		break;
    327 	case MM_TEXT:
    328 		if (mv.buttons == 1) {
    329 
    330 			rstart = rich.selmin;
    331 			rend = rich.selmax;
    332 
    333 			selstart = getelem(mv.xy);
    334 			selend = selstart;
    335 
    336 			rich.selmin = selstart;
    337 			rich.selmax = selstart;
    338 
    339 			lockdisplay(display);
    340 			drawelems(rstart, rend);
    341 			flushimage(display, 1);
    342 			unlockdisplay(display);
    343 
    344 			*mmode = MM_SELECT;
    345 		} else if (mv.buttons == 2) {
    346 			int f;
    347 			f = menuhit(2, mc, &mmenu, nil);
    348 			if (f >= 0) mfunc[f]();
    349 			*mmode = MM_NONE;
    350 		} else if (mv.buttons == 4) {
    351 			int f;
    352 			int n;
    353 			char *link;
    354 			n = getelem(mv.xy);
    355 			link =  getlink(n);
    356 			if (link != nil) {
    357 				f = menuhit(3, mc, &rmenu, nil);
    358 				if (f >= 0) {
    359 					if (f >= rsize - 1) ruseract(f - rsize + 1);
    360 					else rfunc[f](link);
    361 				}
    362 			} else if (menubuf->count > 0) {
    363 				f = menuhit(3, mc, &rusermenu, nil);
    364 				if (f >= 0) ruseract(f);
    365 			}
    366 			*mmode = MM_NONE;
    367 		}
    368 		break;
    369 
    370 	case MM_SELECT:
    371 		rend = selend;
    372 		if (mv.buttons == (1|2)) {
    373 			break;
    374 		}
    375 		if (mv.buttons == (1|4)) {
    376 			break;
    377 		}
    378 		selend = getelem(mv.xy);
    379 		if (selend == -1 ) selend = selstart;
    380 
    381 		if (selstart < selend) {
    382 			rich.selmin = selstart;
    383 			rich.selmax = selend;
    384 		} else {
    385 			rich.selmin = selend;
    386 			rich.selmax = selstart;
    387 		}
    388 
    389 		lockdisplay(display);
    390 		if (selend < rend) drawelems(selend, rend);
    391 		else drawelems(rend, selend);
    392 		flushimage(display, 1);
    393 		unlockdisplay(display);
    394 
    395 	}
    396 }
    397 
    398 Font *
    399 getfont(Array *fonts, char *name)
    400 {
    401 	int i;
    402 	Font *newfont, *fnt;
    403 
    404 	if (name == nil) return font;
    405 	if (name[0] == '\0') return font;
    406 
    407 	for (i = 0; i < fonts->count; i++){
    408 		arrayget(fonts, i, &fnt);
    409 		if (strcmp(fnt->name, name) == 0) return fnt;
    410 	}
    411 	if ((newfont = openfont(display, name)) == nil) {
    412 		fprint(2, "%r\n");
    413 		newfont = font;
    414 	} else {
    415 		arraygrow(fonts, 1, &newfont);
    416 	}
    417 	return newfont;
    418 }
    419 
    420 void
    421 scroll(int y, Rich *r)
    422 {
    423 	int i, delta;
    424 	if (y < 0) y = 0;
    425 	if (y > r->max) y = r->max;
    426 
    427 	delta = r->scroll - y;
    428 	r->scroll = y;
    429 
    430 	for (i = 0; i < drawcache->count; i++) {
    431 		DrawState *ds;
    432 		if ((ds = arrayget(drawcache, i, nil)) == nil) {
    433 			sysfatal("scroll: drawcache failure.");
    434 		}
    435 		ds->pos.y += delta;
    436 		ds->nlpos.y += delta;
    437 	}
    438 
    439 	nbsend(redrawc, nil);
    440 }
    441 
    442 void
    443 drawscrollbar(void)
    444 {
    445 	double D;
    446 	Rectangle rs;
    447 	D =  (double)rich.max / (double)Dy(rich.r);
    448 	if (D != 0) {
    449 		rs = rectaddpt(Rect(
    450 		  0,  rich.scroll / D,
    451 		  11, (rich.scroll + Dy(rich.r)) / D
    452 		), rich.rs.min);
    453 	} else {
    454 		rs = rich.rs;
    455 		rs.max.x--;
    456 	};
    457 
    458 	draw(screen, rich.rs, Iscrollbar, nil, ZP);
    459 	draw(screen, rs, Inormbg, nil, ZP);
    460 }
    461 
    462 void
    463 runcmd(void *args)
    464 {
    465 	char **argv = args;
    466 	char *_cmd, *syslib, *cputype;
    467 	
    468 	rfork(RFNAMEG);
    469 
    470 	if ((initfs(srvname)) != 0)
    471 		sysfatal("initfs failed: %r");
    472 
    473 	bind("/mnt/richterm", "/dev/", MBEFORE);
    474 
    475 	bind("/sys/lib/richterm/bin/rc/", "/bin", MAFTER);
    476 	cputype = getenv("cputype");
    477 	syslib = smprint("/sys/lib/richterm/bin/%s/", cputype);
    478 	bind(syslib, "/bin", MAFTER);
    479 	free(cputype);
    480 	free(syslib);
    481 
    482 	rfork(RFFDG);
    483 	close(0);
    484 	open("/dev/cons", OREAD);
    485 	close(1);
    486 	open("/dev/cons", OWRITE);
    487 	dup(1, 2);
    488 	
    489 	_cmd = strdup(cmd);
    490 	while (*argv != nil) {
    491 		char *old = _cmd;
    492 		_cmd = smprint("%s %q", _cmd, *argv);
    493 		free(old);
    494 		argv++;
    495 	}
    496 
    497 	procexecl(pidchan, "/bin/rc", "rcX", "-c", _cmd, nil);
    498 
    499 	sysfatal("%r");
    500 }
    501 
    502 void
    503 shutdown(void)
    504 {
    505 	send_interrupt();
    506 	threadexitsall(nil);
    507 }
    508 
    509 void
    510 send_interrupt(void)
    511 {
    512 	if(hostpid > 0)
    513 		postnote(PNGROUP, hostpid, "interrupt");
    514 }
    515 
    516 void
    517 resize(void)
    518 {
    519 	drawcache->count = 0;
    520 	rich.rs = Rpt(
    521 	  addpt(Pt(1,1), screen->r.min),
    522 	  Pt(screen->r.min.x + 13, screen->r.max.y - 1)
    523 	);
    524 	rich.r = Rpt(
    525 	  addpt(screen->r.min, Pt(17, 0)),
    526 	  screen->r.max
    527 	);
    528 }
    529 
    530 void
    531 mfollow(void)
    532 {
    533 	Rune *r;
    534 	r = getrunes(rich.selmin, rich.selmax);
    535 	
    536 	char *link;
    537 	link = smprint("%S", r);
    538 
    539 	Array *a;
    540 	a = arraycreate(sizeof(char), 1024, nil);
    541 	if (a == nil) sysfatal("rfollow: arraycreate failed: %r");
    542 	arraygrow(a, 5, "link ");
    543 	arraygrow(a, strlen(link), link);
    544 	arraygrow(a, 1, "\n");
    545 
    546 	nbsend(ctlc, &a);
    547 
    548 	free(r);
    549 	free(link);
    550 }
    551 
    552 void
    553 mpaste(void)
    554 {
    555 	int fd;
    556 	long n;
    557 	char buf[4096];
    558 	if ((fd = open("/dev/snarf", OREAD)) > 0) {
    559 		while((n = read(fd, buf, sizeof(buf))) > 0) {
    560 			buf[n] = '\0';
    561 			text2runes(buf, elems);
    562 		}
    563 		if (n < 0) fprint(2, "mpaste: %r\n");
    564 		close(fd);
    565 
    566 		nbsend(redrawc, nil);
    567 	}
    568 }
    569 
    570 void
    571 msnarf(void)
    572 {
    573 	int fd;
    574 	long n;
    575 	Rune *r;
    576 	char *s;
    577 	if ((rich.selmin < rich.selmax) &&
    578 	  ((fd = open("/dev/snarf", OWRITE)) > 0)) {
    579 		r = getrunes(rich.selmin, rich.selmax);
    580 		s = smprint("%S", r);
    581 		n = write(fd, s, strlen(s));
    582 		if (n < strlen(s)) fprint(2, "msnarf: %r\n");
    583 		close(fd);
    584 		free(r);
    585 		free(s);
    586 	}
    587 }
    588 
    589 void
    590 mplumb(void)
    591 {
    592 	char buf[1024];
    593 	int pd;
    594 	Plumbmsg m;
    595 	Rune *r;
    596 	char *s;
    597 	if ((pd = plumbopen("send", OWRITE)) > 0) {
    598 		r = getrunes(rich.selmin, rich.selmax);
    599 		s = smprint("%S", r);
    600 		m = (Plumbmsg) {
    601 			"richterm",
    602 			nil,
    603 			getwd(buf, sizeof(buf)),
    604 			"text",
    605 			nil,
    606 			strlen(s),
    607 			s
    608 		};
    609 		plumbsend(pd, &m);
    610 		close(pd);
    611 		free(r);
    612 		free(s);
    613 	}
    614 }
    615 
    616 
    617 char *
    618 rgen(int n)
    619 {
    620 	if (n <  rsize - 1) return ritems[n];
    621 	else return rusergen(n - rsize + 1);
    622 }
    623 
    624 void
    625 rfollow(void *v)
    626 {
    627 	char *link = (char *)v;
    628 
    629 	Array *a;
    630 	a = arraycreate(sizeof(char), 1024, nil);
    631 	if (a == nil) sysfatal("rfollow: arraycreate failed: %r");
    632 	arraygrow(a, 5, "link ");
    633 	arraygrow(a, strlen(link), link);
    634 	arraygrow(a, 1, "\n");
    635 
    636 	nbsend(ctlc, &a);
    637 }
    638 
    639 void
    640 rsnarf(void *v)
    641 {
    642 	char *link = (char *)v;
    643 
    644 	int fd;
    645 	long n;
    646 	if ((fd = open("/dev/snarf", OWRITE)) > 0) {
    647 		n = write(fd, link, strlen(link));
    648 		if (n < strlen(link)) fprint(2, "rsnarf: %r\n");
    649 		close(fd);
    650 	}
    651 }
    652 
    653 void
    654 rplumb(void *v)
    655 {
    656 	char *link = (char *)v;
    657 
    658 	char buf[1024];
    659 	int pd;
    660 	Plumbmsg m;
    661 	if ((pd = plumbopen("send", OWRITE)) > 0) {
    662 		m = (Plumbmsg) {
    663 			"richterm",
    664 			nil,
    665 			getwd(buf, sizeof(buf)),
    666 			"text",
    667 			nil,
    668 			strlen(link),
    669 			link
    670 		};
    671 		plumbsend(pd, &m);
    672 		close(pd);
    673 	}
    674 }
    675 
    676 void
    677 ruseract(int f)
    678 {
    679 	Array *a;
    680 	char *s;
    681 	s = rusergen(f);
    682 	if (s == nil) {
    683 		return;
    684 	}
    685 	a = arraycreate(sizeof(char), 2048, nil);
    686 	arraygrow(a, 5, "menu ");
    687 	arraygrow(a, strlen(s), s);
    688 	arraygrow(a, 1, "\n");
    689 	nbsend(ctlc, &a);
    690 }
    691 
    692 char *
    693 rusergen(int f)
    694 {
    695 	static char genbuf[1024];
    696 	int i, k;
    697 	char *ps, *pe;
    698 	memset(genbuf, 0, sizeof(genbuf));
    699 	ps = menubuf->p;
    700 	for (k = 0, i = 0; (k != f) && (i < menubuf->count); i++) {
    701 		if (menubuf->p[i] == '\n') {
    702 			ps = menubuf->p + i + 1;
    703 			k++;
    704 		}
    705 	}
    706 	if (k != f) return nil;
    707 	i++;
    708 	for (pe = ps; i < menubuf->count; i++, pe++) {
    709 		if (*pe == '\n') break;
    710 	}
    711 	if (pe == '\0') return nil;
    712 	if (ps == pe) return nil;
    713 	memcpy(genbuf, ps, pe - ps);
    714 	return genbuf;
    715 }
    716 
    717 void
    718 text2runes(char *str, Array *elems)
    719 {
    720 	if (str == nil) return;
    721 	if (*str == '\0') return;
    722 
    723 	Rune *r, *rp;
    724 	r = runesmprint("%s", str);
    725 
    726 	for (rp = r; *rp != L'\0'; rp++) {
    727 		Elem *e = mallocz(sizeof(Elem), 1);
    728 		e->type = TRune;
    729 		e->r = *rp;
    730 		arraygrow(elems, 1, &e);
    731 	}
    732 	free(r);
    733 }
    734 
    735 void
    736 parsedata(Array *data, Array *elems)
    737 {
    738 	Elem *e;
    739 	long i;
    740 	char *dp;
    741 	Array *buf = arraycreate(sizeof(char), 80, nil);
    742 
    743 	qlock(data->l);
    744 
    745 	dp = data->p;
    746 
    747 	for (i = 0; i < data->count; i++) {
    748 		if (dp[i] != '\n') arraygrow(buf, 1, dp + i);
    749 		else {
    750 			arraygrow(buf, 1, "\0");
    751 			e = nil;
    752 			switch(buf->p[0]) {
    753 			case E_TEXT:
    754 				text2runes(buf->p + 1, elems);
    755 				break;
    756 			case E_NL:
    757 				e = malloc(sizeof(Elem));
    758 				e->type = TRune;
    759 				e->r = L'\n';
    760 				break;
    761 			case E_TAB:
    762 				e = malloc(sizeof(Elem));
    763 				e->type = TRune;
    764 				e->r = L'\t';
    765 				break;
    766 			case E_SPACE:
    767 				e = malloc(sizeof(Elem));
    768 				e->type = TRune;
    769 				e->r = L' ';
    770 				break;
    771 			case E_LINK:
    772 				e = malloc(sizeof(Elem));
    773 				e->type = TLink;
    774 				e->str = (buf->p[1] != '\0') ? 
    775 					strdup(buf->p + 1) : nil;
    776 				break;
    777 			case E_FONT:
    778 				e = malloc(sizeof(Elem));
    779 				e->type = TFont;
    780 				e->str = (buf->p[1] != '\0') ? 
    781 					strdup(buf->p + 1) : nil;
    782 				break;
    783 			}
    784 
    785 			if (e != nil) arraygrow(elems, 1, &e);
    786 			buf->count = 0;
    787 		}
    788 	}
    789 
    790 	qunlock(data->l);
    791 
    792 	arrayfree(buf);
    793 }
    794 
    795 void
    796 drawelems(long start, long end)
    797 {
    798 	DrawState ds;
    799 	Elem *e = nil;
    800 
    801 	for (; drawcache->count > 0; drawcache->count--) {
    802 		if (arrayget(drawcache, drawcache->count - 1, &ds) == nil) {
    803 			sysfatal("drawelems: drawcache failure.");
    804 		}
    805 		if (ds.n <= start) break;
    806 	}
    807 
    808 	if (drawcache->count == 0) {
    809 		ds.pos = Pt(rich.r.min.x, rich.r.min.y - rich.scroll);
    810 		ds.nlpos = ds.pos;
    811 		ds.font = font;
    812 		ds.link = nil;
    813 		ds.n = 0;
    814 		arraygrow(drawcache, 1, &ds);
    815 	} else {
    816 		arrayget(drawcache, 0, &ds);
    817 		drawcache->count = 1;
    818 	}
    819 
    820 	if (end > elems->count) end = elems->count;
    821 
    822 	for (; ds.n < end; ds.n++) {
    823 		if (arrayget(elems, ds.n, &e) == nil)
    824 			sysfatal("drawelems: failed to get elem");
    825 		
    826 		switch (e->type) {
    827 		case TLink:
    828 			ds.link = e->str;
    829 			break;
    830 		case TFont:
    831 			ds.font = getfont(fonts, e->str);
    832 			break;
    833 		case TRune:
    834 			drawrune(&ds, e);
    835 			break;
    836 		}
    837 	}
    838 	if (e != nil) rich.max = ds.nlpos.y + rich.scroll - rich.r.min.y;
    839 	else rich.max = 0;
    840 }
    841 
    842 Point
    843 drawrune(DrawState *ds, Elem *e)
    844 {
    845 	Rectangle r = elemrect(ds, e);
    846 
    847 	Image *fg, *bg;
    848 
    849 	Rune R[2];
    850 	R[0] = e->r;
    851 	R[1] = L'\0';
    852 
    853 	if ((e->r == L'\t') || (e->r == L'\n')) R[0] = L' ';
    854 
    855 	fg = (ds->link != nil) ? Ilink : Itext;
    856 	bg = ((ds->n >= rich.selmin) &&
    857 	  (ds->n < rich.selmax)) ? Iselbg : Inormbg;
    858 
    859 	if (r.max.x > rich.r.max.x) {
    860 		if (rectXrect(r, rich.r) != 0) draw(screen, r, bg, nil, ZP);
    861 		ds->pos = ds->nlpos;
    862 		r = elemrect(ds, e);
    863 		arraygrow(drawcache, 1, ds);
    864 	}
    865 
    866 	if (rectXrect(r, rich.r) != 0) {
    867 		draw(screen, r, bg, nil, ZP);
    868 		runestringn(screen, ds->pos, fg, ZP, ds->font, R, 1);
    869 	}
    870 
    871 	ds->pos.x = r.max.x;
    872 	if (ds->nlpos.y < r.max.y) ds->nlpos.y = r.max.y;
    873 	if (ds->pos.x >= rich.r.max.x) ds->pos = ds->nlpos;
    874 
    875 	return ds->pos;
    876 }
    877 
    878 void
    879 insertfromcons(Array *a)
    880 {
    881 	int i;
    882 
    883 	if (a->count == 0) return;
    884 
    885 	qlock(a->l);
    886 
    887 	arraygrow(richdata, 1, ".");
    888 
    889 	for (i = 0; i < a->count; i++) {
    890 		switch (a->p[i]) {
    891 		case '\n':
    892 			arraygrow(richdata, 4, "\nn\n.");
    893 			break;
    894 		default:
    895 			arraygrow(richdata, 1, a->p + i);
    896 		}
    897 	}
    898 
    899 	arraygrow(richdata, 1, "\n");
    900 
    901 	qunlock(a->l);
    902 
    903 	arrayfree(a);
    904 
    905 	Rune *r = getrunes(rich.input, elems->count);
    906 
    907 	clearelems();
    908 	drawcache->count = 0;
    909 
    910 	parsedata(richdata, elems);
    911 	rich.input = elems->count;
    912 	for (i = 0; r[i] != L'\0'; i++) {
    913 		Elem *e = mallocz(sizeof(Elem), 1);
    914 		e->type = TRune;
    915 		e->r = r[i];
    916 		arraygrow(elems, 1, &e);
    917 	}
    918 	free(r);
    919 }
    920 
    921 void
    922 freeelem(Elem *e)
    923 {
    924 	if (e == nil) sysfatal("freeelem: elem is nil!");
    925 	switch(e->type) {
    926 	case TLink:
    927 	case TFont:
    928 		if (e->str != nil) free(e->str);
    929 		break;
    930 	}
    931 }
    932 
    933 void
    934 clearelems(void)
    935 {
    936 	int i;
    937 	for (i = 0; i < elems->count; i++) {
    938 		Elem *e;
    939 		arrayget(elems, i, &e);
    940 		freeelem(e);
    941 		free(e);
    942 	};
    943 	elems->count = 0;
    944 }
    945 
    946 int
    947 getelem(Point xy)
    948 {
    949 	long i;
    950 
    951 	DrawState ds;
    952 
    953 	for (i = drawcache->count - 1; i >= 0; i--) {
    954 		if (arrayget(drawcache, 0, &ds) == nil) {
    955 			sysfatal("getelem: drawcache failure");
    956 		}
    957 		if (ds.pos.y <= xy.y) break;
    958 	}
    959 
    960 	for (i = 0; i < elems->count; i++) {
    961 
    962 		if (ds.pos.y > rich.r.max.y) {
    963 			return -1;
    964 		}
    965 		if (ds.pos.y > xy.y) {
    966 			return i - 1;
    967 		}
    968 
    969 		Elem *e;
    970 		arrayget(elems, i, &e);
    971 
    972 		if (e->type == TFont) {
    973 			ds.font = getfont(fonts, e->str);
    974 			continue;
    975 		}
    976 		if (e->type == TLink) continue;
    977 
    978 		Rectangle r = elemrect(&ds, e);
    979 		if (ptinrect(xy, r) != 0) {
    980 			return i;
    981 		}
    982 
    983 		ds.pos.x = r.max.x;
    984 		if (ds.nlpos.y < r.max.y) ds.nlpos.y = r.max.y;
    985 		if (ds.pos.x >= rich.r.max.x) ds.pos = ds.nlpos;
    986 
    987 	}
    988 	return -1;
    989 }
    990 
    991 Rune *
    992 getrunes(long start, long end)
    993 {
    994 	Rune *r = malloc(sizeof(Rune) * (end - start + 1));
    995 	long i, n;
    996 	Elem *e;
    997 	for (n = 0, i = start; i < end; i++) {
    998 		arrayget(elems, i, &e);
    999 		if (e->type ==TRune) {
   1000 			r[n] = e->r;
   1001 			n++;
   1002 		}
   1003 	}
   1004 	r[n] = L'\0';
   1005 	return r;
   1006 }
   1007 
   1008 char *
   1009 getlink(long n)
   1010 {
   1011 	for (;n >= 0; n--) {
   1012 		Elem *e;
   1013 		arrayget(elems, n, &e);
   1014 		if (e->type == TLink) return e->str;
   1015 	}
   1016 	return nil;
   1017 }
   1018 
   1019 Rectangle
   1020 elemrect(DrawState *ds, Elem *e)
   1021 {
   1022 	Rectangle r;
   1023 	int tabw;
   1024 	Rune rbuf[2];
   1025 
   1026 	if (e->type != TRune) return Rpt(ds->pos, ds->pos);
   1027 
   1028 	rbuf[0] = e->r;
   1029 	rbuf[1] = L'\0';
   1030 
   1031 	switch(rbuf[0]) {
   1032 	case L'\n':
   1033 		r.max.x = rich.r.max.x;
   1034 		break;
   1035 	case L'\t':
   1036 		tabw = stringnwidth(font, "0", 1) * 4;
   1037 		r.max.x = rich.r.min.x + ((ds->pos.x - rich.r.min.x) / tabw + 1) * tabw;
   1038 		break;
   1039 	default:
   1040 		r.max.x = ds->pos.x + runestringnwidth(ds->font, rbuf, 1);
   1041 	}
   1042 
   1043 	r.min = ds->pos;
   1044 	r.max.y = ds->pos.y + ds->font->height;
   1045 
   1046 	return r;
   1047 }