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 128 main(int argc, char **argv) 129 { 130 initialize(argc, argv); 131 emulate(); 132 } 133 134 void 135 usage(void) 136 { 137 fprint(2, "usage: %s [-2abcx] [-f font] [-l logfile]\n", argv0); 138 exits("usage"); 139 } 140 141 void 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 230 clear(Rectangle r) 231 { 232 draw(screen, r, bgcolor, nil, ZP); 233 } 234 235 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 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 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 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 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 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 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 540 eresized(int new) 541 { 542 resize_flag = 1+new; 543 } 544 545 void 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 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 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 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 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 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 709 pt(int x, int y) 710 { 711 return addpt(screen->r.min, Pt(x*CW+XMARGIN,y*NS+YMARGIN)); 712 } 713 714 void 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 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 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 761 sendnchars(int n,char *p) 762 { 763 sendnchars2(n, p); 764 p[n+1] = 0; 765 } 766 767 void 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 780 host_avail(void) 781 { 782 return(*echop || ((hostp - host_buf) < hostlength)); 783 } 784 785 int 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 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 814 ringbell(void){ 815 } 816 817 int 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 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 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 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