xref: /plan9/sys/src/cmd/vt/main.c (revision 21abd8f25aed5cdf552ef4ed86cf7cccb8b56d7c)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5 #include <bio.h>
6 #include <keyboard.h>
7 #include "cons.h"
8 
9 enum{
10 	Ehost		= 4,
11 };
12 
13 char	*menutext2[] = {
14 	"backup",
15 	"forward",
16 	"reset",
17 	"clear",
18 	"send",
19 	"page",
20 	0
21 };
22 
23 char	*menutext3[] = {
24 	"24x80",
25 	"crnl",
26 	"nl",
27 	"raw",
28 	"exit",
29 	0
30 };
31 
32 /* variables associated with the screen */
33 
34 int	x, y;	/* character positions */
35 char	*backp;
36 int	backc;
37 int	atend;
38 int	nbacklines;
39 int	xmax, ymax;
40 int	blocked;
41 int	resize_flag;
42 int	pagemode;
43 int	olines;
44 int	peekc;
45 int	cursoron = 1;
46 Menu	menu2;
47 Menu	menu3;
48 char	*histp;
49 char	hist[HISTSIZ];
50 int	yscrmin, yscrmax;
51 int	attr, defattr;
52 int	wctlout;
53 
54 Image	*bordercol;
55 Image	*cursback;
56 Image	*colors[8];
57 Image	*hicolors[8];
58 Image	*red;
59 Image	*fgcolor;
60 Image	*bgcolor;
61 Image	*fgdefault;
62 Image	*bgdefault;
63 
64 uint rgbacolors[8] = {
65 	0x000000FF,	/* black */
66 	0xAA0000FF,	/* red */
67 	0x00AA00FF,	/* green */
68 	0xFF5500FF,	/* brown */
69 	0x0000FFFF,	/* blue */
70 	0xAA00AAFF,	/* purple */
71 	0x00AAAAFF,	/* cyan */
72 	0x7F7F7FFF,	/* white */
73 };
74 
75 ulong rgbahicolors[8] = {
76 	0x555555FF,	/* light black aka grey */
77 	0xFF5555FF,	/* light red */
78 	0x55FF55FF,	/* light green */
79 	0xFFFF55FF,	/* light brown aka yellow */
80 	0x5555FFFF,	/* light blue */
81 	0xFF55FFFF,	/* light purple */
82 	0x55FFFFFF,	/* light cyan */
83 	0xFFFFFFFF,	/* light grey aka white */
84 };
85 
86 /* terminal control */
87 struct ttystate ttystate[2] = { {0, 1}, {0, 1} };
88 
89 int	NS;
90 int	CW;
91 Consstate *cs;
92 Mouse	mouse;
93 
94 int	debug;
95 int	nocolor;
96 int	logfd = -1;
97 int	outfd = -1;
98 Biobuf	*snarffp = 0;
99 
100 char	*host_buf;
101 char	*hostp;				/* input from host */
102 int	host_bsize = 2*BSIZE;
103 int	hostlength;			/* amount of input from host */
104 char	echo_input[BSIZE];
105 char	*echop = echo_input;		/* characters to echo, after canon */
106 char	sendbuf[BSIZE];	/* hope you can't type ahead more than BSIZE chars */
107 char	*sendp = sendbuf;
108 
109 char *term;
110 struct funckey *fk;
111 
112 /* functions */
113 void	initialize(int, char **);
114 void	ebegin(int);
115 int	waitchar(void);
116 int	rcvchar(void);
117 void	set_input(char *);
118 void	set_host(Event *);
119 void	bigscroll(void);
120 void	readmenu(void);
121 void	eresized(int);
122 void	resize(void);
123 void	send_interrupt(void);
124 int	alnum(int);
125 void	escapedump(int,uchar *,int);
126 
127 void
main(int argc,char ** argv)128 main(int argc, char **argv)
129 {
130 	initialize(argc, argv);
131 	emulate();
132 }
133 
134 void
usage(void)135 usage(void)
136 {
137 	fprint(2, "usage: %s [-2abcx] [-f font] [-l logfile]\n", argv0);
138 	exits("usage");
139 }
140 
141 void
initialize(int argc,char ** argv)142 initialize(int argc, char **argv)
143 {
144 	int i, blkbg;
145 	char *fontname, *p;
146 
147 	rfork(RFNAMEG|RFNOTEG);
148 
149 	fontname = nil;
150 	term = "vt100";
151 	fk = vt100fk;
152 	blkbg = nocolor = 0;
153 	ARGBEGIN{
154 	case '2':
155 		term = "vt220";
156 		fk = vt220fk;
157 		break;
158 	case 'a':
159 		term = "ansi";
160 		fk = ansifk;
161 		break;
162 	case 'b':
163 		blkbg = 1;		/* e.g., for linux colored output */
164 		break;
165 	case 'c':
166 		nocolor = 1;
167 		break;
168 	case 'f':
169 		fontname = EARGF(usage());
170 		break;
171 	case 'l':
172 		p = EARGF(usage());
173 		logfd = create(p, OWRITE, 0666);
174 		if(logfd < 0)
175 			sysfatal("could not create log file: %s: %r", p);
176 		break;
177 	case 'x':
178 		fk = xtermfk;
179 		term = "xterm";
180 		break;
181 	default:
182 		usage();
183 		break;
184 	}ARGEND;
185 
186 	host_buf = malloc(host_bsize);
187 	hostp = host_buf;
188 	hostlength = 0;
189 
190 	if(initdraw(0, fontname, term) < 0){
191 		fprint(2, "%s: initdraw failed: %r\n", term);
192 		exits("initdraw");
193 	}
194 	werrstr("");		/* clear spurious error messages */
195 	ebegin(Ehost);
196 
197 	histp = hist;
198 	menu2.item = menutext2;
199 	menu3.item = menutext3;
200 	pagemode = 0;
201 	blocked = 0;
202 	NS = font->height;
203 	CW = stringwidth(font, "m");
204 
205 	red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed);
206 	bordercol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCCC);
207 	cursback = allocimage(display, Rect(0, 0, CW+1, NS+1), screen->chan, 0, DNofill);
208 
209 	for(i=0; i<8; i++){
210 		colors[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,
211 			rgbacolors[i]);
212 		hicolors[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,
213 			rgbahicolors[i]);
214 	}
215 
216 	bgdefault = (blkbg? display->black: display->white);
217 	fgdefault = (blkbg? display->white: display->black);
218 	bgcolor = bgdefault;
219 	fgcolor = fgdefault;
220 
221 	resize();
222 
223 	if(argc > 0) {
224 		sendnchars(strlen(argv[0]),argv[0]);
225 		sendnchars(1,"\n");
226 	}
227 }
228 
229 void
clear(Rectangle r)230 clear(Rectangle r)
231 {
232 	draw(screen, r, bgcolor, nil, ZP);
233 }
234 
235 void
newline(void)236 newline(void)
237 {
238 	nbacklines--;
239 	if(y >= yscrmax) {
240 		y = yscrmax;
241 		if(pagemode && olines >= yscrmax) {
242 			blocked = 1;
243 			return;
244 		}
245 		scroll(yscrmin+1, yscrmax+1, yscrmin, yscrmax);
246 	} else
247 		y++;
248 	olines++;
249 }
250 
251 void
cursoff(void)252 cursoff(void)
253 {
254 	draw(screen, Rpt(pt(x, y), addpt(pt(x, y), Pt(CW,NS))),
255 		cursback, nil, cursback->r.min);
256 }
257 
258 void
curson(int bl)259 curson(int bl)
260 {
261 	Image *col;
262 
263 	if(!cursoron){
264 		cursoff();
265 		return;
266 	}
267 
268 	draw(cursback, cursback->r, screen, nil, pt(x, y));
269 	if(bl)
270 		col = red;
271 	else
272 		col = bordercol;
273 	border(screen, Rpt(pt(x, y), addpt(pt(x, y), Pt(CW,NS))), 2, col, ZP);
274 }
275 
276 int
get_next_char(void)277 get_next_char(void)
278 {
279 	int c = peekc;
280 	uchar buf[1];
281 	peekc = 0;
282 	if(c > 0)
283 		return(c);
284 	while(c <= 0) {
285 		if(backp) {
286 			c = *backp;
287 			if(c && nbacklines >= 0) {
288 				backp++;
289 				if(backp >= &hist[HISTSIZ])
290 					backp = hist;
291 				return(c);
292 			}
293 			backp = 0;
294 		}
295 		c = (uchar)waitchar();
296 		if(c > 0 && logfd >= 0) {
297 			buf[0] = c;
298 			write(logfd, buf, 1);
299 		}
300 	}
301 	*histp++ = c;
302 	if(histp >= &hist[HISTSIZ])
303 		histp = hist;
304 	*histp = '\0';
305 	return(c);
306 }
307 
308 int
canon(char * ep,int c)309 canon(char *ep, int c)
310 {
311 	if(c&0200)
312 		return(SCROLL);
313 	switch(c) {
314 		case '\b':
315 			if(sendp > sendbuf)
316 				sendp--;
317 			*ep++ = '\b';
318 			*ep++ = ' ';
319 			*ep++ = '\b';
320 			break;
321 		case 0x15:	/* ^U line kill */
322 			sendp = sendbuf;
323 			*ep++ = '^';
324 			*ep++ = 'U';
325 			*ep++ = '\n';
326 			break;
327 		case 0x17:	/* ^W word kill */
328 			while(sendp > sendbuf && !alnum(*sendp)) {
329 				*ep++ = '\b';
330 				*ep++ = ' ';
331 				*ep++ = '\b';
332 				sendp--;
333 			}
334 			while(sendp > sendbuf && alnum(*sendp)) {
335 				*ep++ = '\b';
336 				*ep++ = ' ';
337 				*ep++ = '\b';
338 				sendp--;
339 			}
340 			break;
341 		case '\177':	/* interrupt */
342 			sendp = sendbuf;
343 			send_interrupt();
344 			return(NEWLINE);
345 		case '\021':	/* quit */
346 		case '\r':
347 		case '\n':
348 			if(sendp < &sendbuf[512])
349 				*sendp++ = '\n';
350 			sendnchars((int)(sendp-sendbuf), sendbuf);
351 			sendp = sendbuf;
352 			if(c == '\n' || c == '\r') {
353 				*ep++ = '\n';
354 			}
355 			*ep = 0;
356 			return(NEWLINE);
357 		case '\004':	/* EOT */
358 			if(sendp == sendbuf) {
359 				sendnchars(0,sendbuf);
360 				*ep = 0;
361 				return(NEWLINE);
362 			}
363 			/* fall through */
364 		default:
365 			if(sendp < &sendbuf[512])
366 				*sendp++ = c;
367 			*ep++ = c;
368 			break;
369 
370 	}
371 	*ep = 0;
372 	return(OTHER);
373 }
374 
375 void
sendfk(char * name)376 sendfk(char *name)
377 {
378 	int i;
379 	static int fd;
380 
381 	for(i=0; fk[i].name; i++)
382 		if(strcmp(name, fk[i].name)==0){
383 			sendnchars2(strlen(fk[i].sequence), fk[i].sequence);
384 			return;
385 		}
386 }
387 
388 int
waitchar(void)389 waitchar(void)
390 {
391 	Event e;
392 	int c;
393 	char c2;
394 	int newmouse;
395 	int wasblocked;
396 	int kbdchar = -1;
397 	char echobuf[3*BSIZE];
398 	static int lastc = -1;
399 
400 
401 	for(;;) {
402 		if(resize_flag)
403 			resize();
404 		wasblocked = blocked;
405 		if(backp)
406 			return(0);
407 		if(ecanmouse() && (button2() || button3()))
408 			readmenu();
409 		if(snarffp) {
410 			if((c = Bgetc(snarffp)) < 0) {
411 				if(lastc != '\n')
412 					write(outfd,"\n",1);
413 				Bterm(snarffp);
414 				snarffp = 0;
415 				if(lastc != '\n') {
416 					lastc = -1;
417 					return('\n');
418 				}
419 				lastc = -1;
420 				continue;
421 			}
422 			lastc = c;
423 			c2 = c;
424 			write(outfd, &c2, 1);
425 			return(c);
426 		}
427 		if(!blocked && host_avail())
428 			return(rcvchar());
429 		if(kbdchar > 0) {
430 			if(blocked)
431 				resize();
432 			if(cs->raw) {
433 				switch(kbdchar){
434 				case Kup:
435 					sendfk("up key");
436 					break;
437 				case Kdown:
438 					sendfk("down key");
439 					break;
440 				case Kleft:
441 					sendfk("left key");
442 					break;
443 				case Kright:
444 					sendfk("right key");
445 					break;
446 				case Kpgup:
447 					sendfk("page up");
448 					break;
449 				case Kpgdown:
450 					sendfk("page down");
451 					break;
452 				case KF|1:
453 					sendfk("F1");
454 					break;
455 				case KF|2:
456 					sendfk("F2");
457 					break;
458 				case KF|3:
459 					sendfk("F3");
460 					break;
461 				case KF|4:
462 					sendfk("F4");
463 					break;
464 				case KF|5:
465 					sendfk("F5");
466 					break;
467 				case KF|6:
468 					sendfk("F6");
469 					break;
470 				case KF|7:
471 					sendfk("F7");
472 					break;
473 				case KF|8:
474 					sendfk("F8");
475 					break;
476 				case KF|9:
477 					sendfk("F9");
478 					break;
479 				case KF|10:
480 					sendfk("F10");
481 					break;
482 				case KF|11:
483 					sendfk("F11");
484 					break;
485 				case KF|12:
486 					sendfk("F12");
487 					break;
488 				case '\n':
489 					echobuf[0] = '\r';
490 					sendnchars(1, echobuf);
491 					break;
492 				case '\r':
493 					echobuf[0] = '\n';
494 					sendnchars(1, echobuf);
495 					break;
496 				default:
497 					echobuf[0] = kbdchar;
498 					sendnchars(1, echobuf);
499 					break;
500 				}
501 			} else if(canon(echobuf,kbdchar) == SCROLL) {
502 				if(!blocked)
503 					bigscroll();
504 			} else
505 				strcat(echo_input,echobuf);
506 			blocked = 0;
507 			kbdchar = -1;
508 			continue;
509 		}
510 		curson(wasblocked);	/* turn on cursor while we're waiting */
511 		do {
512 			newmouse = 0;
513 			switch(eread(blocked ? Emouse|Ekeyboard :
514 					       Emouse|Ekeyboard|Ehost, &e)) {
515 			case Emouse:
516 				mouse = e.mouse;
517 				if(button2() || button3())
518 					readmenu();
519 				else if(resize_flag == 0) {
520 					/* eresized() is triggered by special mouse event */
521 					newmouse = 1;
522 				}
523 				break;
524 			case Ekeyboard:
525 				kbdchar = e.kbdc;
526 				break;
527 			case Ehost:
528 				set_host(&e);
529 				break;
530 			default:
531 				perror("protocol violation");
532 				exits("protocol violation");
533 			}
534 		} while(newmouse == 1);
535 		cursoff();	/* turn cursor back off */
536 	}
537 }
538 
539 void
eresized(int new)540 eresized(int new)
541 {
542 	resize_flag = 1+new;
543 }
544 
545 void
putenvint(char * name,int x)546 putenvint(char *name, int x)
547 {
548 	char buf[20];
549 
550 	snprint(buf, sizeof buf, "%d", x);
551 	putenv(name, buf);
552 }
553 
554 void
exportsize(void)555 exportsize(void)
556 {
557 	putenvint("XPIXELS", Dx(screen->r)-2*XMARGIN);
558 	putenvint("YPIXELS", Dy(screen->r)-2*XMARGIN);
559 	putenvint("LINES", ymax+1);
560 	putenvint("COLS", xmax+1);
561 	putenv("TERM", term);
562 }
563 
564 void
resize(void)565 resize(void)
566 {
567 	if(resize_flag > 1 && getwindow(display, Refnone) < 0){
568 		fprint(2, "can't reattach to window: %r\n");
569 		exits("can't reattach to window");
570 	}
571 	xmax = (Dx(screen->r)-2*XMARGIN)/CW-1;
572 	ymax = (Dy(screen->r)-2*YMARGIN)/NS-1;
573 	if(xmax == 0 || ymax == 0)
574 		exits("window gone");
575 	x = 0;
576 	y = 0;
577 	yscrmin = 0;
578 	yscrmax = ymax;
579 	olines = 0;
580 	exportsize();
581 	clear(screen->r);
582 	resize_flag = 0;
583 	werrstr("");		/* clear spurious error messages */
584 }
585 
586 void
setdim(int ht,int wid)587 setdim(int ht, int wid)
588 {
589 	int fd;
590 	Rectangle r;
591 
592 	if(ht != -1)
593 		ymax = ht-1;
594 	if(wid != -1)
595 		xmax = wid-1;
596 
597 	r.min = screen->r.min;
598 	r.max = addpt(screen->r.min,
599 			Pt((xmax+1)*CW+2*XMARGIN+2*INSET,
600 				(ymax+1)*NS+2*YMARGIN+2*INSET));
601 	fd = open("/dev/wctl", OWRITE);
602 	if(fd < 0 || fprint(fd, "resize -dx %d -dy %d\n", Dx(r)+2*Borderwidth,
603 	    Dy(r)+2*Borderwidth) < 0){
604 		border(screen, r, INSET, bordercol, ZP);
605 		exportsize();
606 	}
607 	if(fd >= 0)
608 		close(fd);
609 }
610 
611 void
readmenu(void)612 readmenu(void)
613 {
614 	if(button3()) {
615 		menu3.item[1] = ttystate[cs->raw].crnl ? "cr" : "crnl";
616 		menu3.item[2] = ttystate[cs->raw].nlcr ? "nl" : "nlcr";
617 		menu3.item[3] = cs->raw ? "cooked" : "raw";
618 
619 		switch(emenuhit(3, &mouse, &menu3)) {
620 		case 0:		/* 24x80 */
621 			setdim(24, 80);
622 			return;
623 		case 1:		/* newline after cr? */
624 			ttystate[cs->raw].crnl = !ttystate[cs->raw].crnl;
625 			return;
626 		case 2:		/* cr after newline? */
627 			ttystate[cs->raw].nlcr = !ttystate[cs->raw].nlcr;
628 			return;
629 		case 3:		/* switch raw mode */
630 			cs->raw = !cs->raw;
631 			return;
632 		case 4:
633 			exits(0);
634 		}
635 		return;
636 	}
637 
638 	menu2.item[5] = pagemode? "scroll": "page";
639 
640 	switch(emenuhit(2, &mouse, &menu2)) {
641 
642 	case 0:		/* back up */
643 		if(atend == 0) {
644 			backc++;
645 			backup(backc);
646 		}
647 		return;
648 
649 	case 1:		/* move forward */
650 		backc--;
651 		if(backc >= 0)
652 			backup(backc);
653 		else
654 			backc = 0;
655 		return;
656 
657 	case 2:		/* reset */
658 		backc = 0;
659 		backup(0);
660 		return;
661 
662 	case 3:		/* clear screen */
663 		eresized(0);
664 		return;
665 
666 	case 4:		/* send the snarf buffer */
667 		snarffp = Bopen("/dev/snarf",OREAD);
668 		return;
669 
670 	case 5:		/* pause and clear at end of screen */
671 		pagemode = 1-pagemode;
672 		if(blocked && !pagemode) {
673 			eresized(0);
674 			blocked = 0;
675 		}
676 		return;
677 	}
678 }
679 
680 void
backup(int count)681 backup(int count)
682 {
683 	register n;
684 	register char *cp;
685 
686 	eresized(0);
687 	n = 3*(count+1)*ymax/4;
688 	cp = histp;
689 	atend = 0;
690 	while (n >= 0) {
691 		cp--;
692 		if(cp < hist)
693 			cp = &hist[HISTSIZ-1];
694 		if(*cp == '\0') {
695 			atend = 1;
696 			break;
697 		}
698 		if(*cp == '\n')
699 			n--;
700 	}
701 	cp++;
702 	if(cp >= &hist[HISTSIZ])
703 		cp = hist;
704 	backp = cp;
705 	nbacklines = ymax-2;
706 }
707 
708 Point
pt(int x,int y)709 pt(int x, int y)
710 {
711 	return addpt(screen->r.min, Pt(x*CW+XMARGIN,y*NS+YMARGIN));
712 }
713 
714 void
scroll(int sy,int ly,int dy,int cy)715 scroll(int sy, int ly, int dy, int cy)	/* source, limit, dest, which line to clear */
716 {
717 	draw(screen, Rpt(pt(0, dy), pt(xmax+1, dy+ly-sy)), screen, nil, pt(0, sy));
718 	clear(Rpt(pt(0, cy), pt(xmax+1, cy+1)));
719 	flushimage(display, 1);
720 }
721 
722 void
bigscroll(void)723 bigscroll(void)			/* scroll up half a page */
724 {
725 	int half = ymax/3;
726 
727 	if(x == 0 && y == 0)
728 		return;
729 	if(y < half) {
730 		clear(Rpt(pt(0,0),pt(xmax+1,ymax+1)));
731 		x = y = 0;
732 		return;
733 	}
734 	draw(screen, Rpt(pt(0, 0), pt(xmax+1, ymax+1)), screen, nil, pt(0, half));
735 	clear(Rpt(pt(0,y-half+1),pt(xmax+1,ymax+1)));
736 	y -= half;
737 	if(olines)
738 		olines -= half;
739 	flushimage(display, 1);
740 }
741 
742 int
number(char * p,int * got)743 number(char *p, int *got)
744 {
745 	int c, n = 0;
746 
747 	if(got)
748 		*got = 0;
749 	while ((c = get_next_char()) >= '0' && c <= '9'){
750 		if(got)
751 			*got = 1;
752 		n = n*10 + c - '0';
753 	}
754 	*p = c;
755 	return(n);
756 }
757 
758 /* stubs */
759 
760 void
sendnchars(int n,char * p)761 sendnchars(int n,char *p)
762 {
763 	sendnchars2(n, p);
764 	p[n+1] = 0;
765 }
766 
767 void
sendnchars2(int n,char * p)768 sendnchars2(int n,char *p)
769 {
770 	if(write(outfd,p,n) < 0) {
771 		close(outfd);
772 		close(0);
773 		close(1);
774 		close(2);
775 		exits("write");
776 	}
777 }
778 
779 int
host_avail(void)780 host_avail(void)
781 {
782 	return(*echop || ((hostp - host_buf) < hostlength));
783 }
784 
785 int
rcvchar(void)786 rcvchar(void)
787 {
788 	int c;
789 	if(*echop) {
790 		c = *echop++;
791 		if(!*echop) {
792 			echop = echo_input;
793 			*echop = 0;
794 		}
795 		return c;
796 	}
797 	return *hostp++;
798 }
799 
800 void
set_host(Event * e)801 set_host(Event *e)
802 {
803 	hostlength = e->n;
804 	if(hostlength > host_bsize) {
805 		host_bsize *= 2;
806 		host_buf = realloc(host_buf,host_bsize);
807 	}
808 	hostp = host_buf;
809 	memmove(host_buf,e->data,hostlength);
810 	host_buf[hostlength]=0;
811 }
812 
813 void
ringbell(void)814 ringbell(void){
815 }
816 
817 int
alnum(int c)818 alnum(int c)
819 {
820 	if(c >= 'a' && c <= 'z')
821 		return 1;
822 	if(c >= 'A' && c <= 'Z')
823 		return 1;
824 	if(c >= '0' && c <= '9')
825 		return 1;
826 	return 0;
827 }
828 
829 void
escapedump(int fd,uchar * str,int len)830 escapedump(int fd,uchar *str,int len)
831 {
832 	int i;
833 
834 	for(i = 0; i < len; i++) {
835 		if((str[i] < ' ' || str[i] > '\177') &&
836 			str[i] != '\n' && str[i] != '\t') fprint(fd,"^%c",str[i]+64);
837 		else if(str[i] == '\177') fprint(fd,"^$");
838 		else if(str[i] == '\n') fprint(fd,"^J\n");
839 		else fprint(fd,"%c",str[i]);
840 	}
841 }
842 
843 void
funckey(int key)844 funckey(int key)
845 {
846 	if(key >= NKEYS)
847 		return;
848 	if(fk[key].name == 0)
849 		return;
850 	sendnchars2(strlen(fk[key].sequence), fk[key].sequence);
851 }
852 
853 
854 void
drawstring(Point p,char * str,int attr)855 drawstring(Point p, char *str, int attr)
856 {
857 	int i;
858 	Image *txt, *bg, *tmp;
859 
860 	txt = fgcolor;
861 	bg = bgcolor;
862 	if(attr & TReverse){
863 		tmp = txt;
864 		txt = bg;
865 		bg = tmp;
866 	}
867 	if(attr & THighIntensity){
868 		for(i=0; i<8; i++)
869 			if(txt == colors[i])
870 				txt = hicolors[i];
871 	}
872 
873 	draw(screen, Rpt(p, addpt(p, stringsize(font, str))), bg, nil, p);
874 	string(screen, p, txt, ZP, font, str);
875 }
876