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