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