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