29 /* variables associated with the screen */
31 int x, y; /* character positions */
62 uint rgbacolors[8] = {
63 0x000000FF, /* black */
65 0x00AA00FF, /* green */
66 0xFF5500FF, /* brown */
67 0x0000FFFF, /* blue */
68 0xAA00AAFF, /* purple */
69 0x00AAAAFF, /* cyan */
70 0x7F7F7FFF, /* white */
73 ulong rgbahicolors[8] = {
74 0x555555FF, /* light black aka grey */
75 0xFF5555FF, /* light red */
76 0x55FF55FF, /* light green */
77 0xFFFF55FF, /* light brown aka yellow */
78 0x5555FFFF, /* light blue */
79 0xFF55FFFF, /* light purple */
80 0x55FFFFFF, /* light cyan */
81 0xFFFFFFFF, /* light grey aka white */
84 /* terminal control */
85 struct ttystate ttystate[2] = { {0, 1}, {0, 1} };
92 #define button1() ((mc->buttons & 07)==1)
93 #define button2() ((mc->buttons & 07)==2)
94 #define button3() ((mc->buttons & 07)==4)
106 Rune *hostbuf, *hostbufp;
107 char echo_input[BSIZE];
108 char *echop = echo_input; /* characters to echo, after canon */
109 char sendbuf[BSIZE]; /* hope you can't type ahead more than BSIZE chars */
110 char *sendbufp = sendbuf;
113 struct funckey *fk, *appfk;
116 void initialize(int, char **);
120 void bigscroll(void);
122 void selection(void);
124 void send_interrupt(void);
126 void escapedump(int,uchar *,int);
131 switch((hostpid = rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG))) {
134 open("/dev/cons", OREAD);
136 open("/dev/cons", OWRITE);
138 execl("/bin/rc","rcX",nil);
139 fprint(2, "failed to start up rc: %r\n");
142 fprint(2,"rc startup: fork: %r\n");
145 return open("/mnt/cons/data", ORDWR);
151 postnote(PNGROUP, hostpid, "interrupt");
157 char cb[BSIZE+1], *cp;
162 while((r = read(hostfd, cb+n, BSIZE-n)) > 0){
164 rb = mallocz((n+1)*sizeof(Rune), 0);
165 for(rp = rb, cp = cb; n > 0; n -= r, cp += r){
168 r = chartorune(rp, cp);
178 if(n > 0) memmove(cb, cp, n);
187 postnote(PNGROUP, getpid(), "exit");
192 threadmain(int argc, char **argv)
194 rfork(RFNAMEG|RFNOTEG|RFENVG);
196 initialize(argc, argv);
203 fprint(2, "usage: %s [-2abcrx] [-f font] [-l logfile] [cmd...]\n", argv0);
208 initialize(int argc, char **argv)
228 blkbg = 1; /* e.g., for linux colored output */
234 fontname = EARGF(usage());
238 logfd = create(p, OWRITE|OCEXEC, 0666);
240 sysfatal("could not create log file: %s: %r", p);
254 if(initdraw(0, fontname, term) < 0)
255 sysfatal("inidraw failed: %r");
256 if((mc = initmouse("/dev/mouse", screen)) == nil)
257 sysfatal("initmouse failed: %r");
258 if((kc = initkeyboard("/dev/cons")) == nil)
259 sysfatal("initkeyboard failed: %r");
260 if((cs = consctl()) == nil)
261 sysfatal("consctl failed: %r");
265 menu2.item = menutext2;
266 menu3.item = menutext3;
269 ftsize.y = font->height;
270 ftsize.x = stringwidth(font, "m");
272 red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed);
273 green = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreen);
274 bordercol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCCC);
275 highlight = allocimage(display, Rect(0,0,1,1), CHAN1(CAlpha,8), 1, 0x80);
278 colors[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,
280 hicolors[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,
283 bgcolor = (blkbg? display->black: display->white);
284 fgcolor = (blkbg? display->white: display->black);
287 hc = chancreate(sizeof(Rune*), 5);
288 if((hostfd = start_host()) >= 0)
289 proccreate(hostreader, nil, BSIZE+1024);
292 sendnchars(strlen(*argv), *argv);
302 #define onscreenr(x, y) &onscreenrbuf[((y)*(xmax+2) + (x))]
303 #define onscreena(x, y) &onscreenabuf[((y)*(xmax+2) + (x))]
304 #define onscreenc(x, y) &onscreencbuf[((y)*(xmax+2) + (x))]
306 #define bgcol(a, c) (((a)&TReverse)!=0 ? (c)>>4 : (c&15))
307 #define fgcol(a, c) ((((a)&TReverse)==0 ? (c)>>4 : (c&15)) | (((a)&THighIntensity)!=0)<<4)
316 draw(screen, screen->r, bgcolor, nil, ZP);
318 /* draw background */
319 for(y = 0; y <= ymax; y++){
320 for(x = 0; x <= xmax; x += n){
321 cp = onscreenc(x, y);
326 ap = onscreena(x, y);
328 for(n = 1; x+n <= xmax && bgcol(ap[n], cp[n]) == c; n++)
330 draw(screen, Rpt(pt(x, y), pt(x+n, y+1)), colors[c>>1], nil, ZP);
334 /* draw foreground */
335 for(y = 0; y <= ymax; y++){
336 for(x = 0; x <= xmax; x += n){
337 rp = onscreenr(x, y);
342 ap = onscreena(x, y);
343 cp = onscreenc(x, y);
345 for(n = 1; x+n <= xmax && rp[n] != 0 && fgcol(ap[n], cp[n]) == c; n++)
347 runestringn(screen, pt(x, y),
348 (c&1) ? (((c&16) ? hicolors : colors)[(c&15)>>1]) : fgcolor,
351 if(*onscreenr(x, y) == 0)
352 runestringn(screen, pt(x, y),
365 col = (blocked || hostfd < 0) ? red : bordercol;
366 border(screen, Rpt(pt(x, y), pt(x+1, y+1)), 2, col, ZP);
370 clear(int x1, int y1, int x2, int y2)
372 int c = (attr & 0x0F00)>>8; /* bgcolor */
376 memset(onscreenr(x1, y1), 0, (x2-x1)*sizeof(Rune));
377 memset(onscreena(x1, y1), 0, x2-x1);
378 memset(onscreenc(x1, y1), c, x2-x1);
381 *onscreenr(xmax+1, y1) = '\n';
390 *onscreenr(xmax+1, y) = 0; /* wrap arround, remove hidden newline */
394 if(pagemode && olines >= yscrmax) {
398 scroll(yscrmin+1, yscrmax+1, yscrmin, yscrmax);
415 if(c && nbacklines >= 0) {
417 if(backp >= &hist[HISTSIZ])
424 if(c > 0 && logfd >= 0)
425 fprint(logfd, "%C", (Rune)c);
428 if(histp >= &hist[HISTSIZ])
435 backrune(char *start, char *cp)
447 n = chartorune(&r, cp);
456 canon(char *ep, Rune c)
463 if(sendbufp > sendbuf){
464 sendbufp = backrune(sendbuf, sendbufp);
470 case 0x15: /* ^U line kill */
476 case 0x17: /* ^W word kill */
477 while(sendbufp > sendbuf && !alnum(*sendbufp)) {
478 sendbufp = backrune(sendbuf, sendbufp);
483 while(sendbufp > sendbuf && alnum(*sendbufp)) {
484 sendbufp = backrune(sendbuf, sendbufp);
490 case '\177': /* interrupt */
494 case '\021': /* quit */
497 if(sendbufp < &sendbuf[BSIZE])
499 sendnchars((int)(sendbufp-sendbuf), sendbuf);
501 if(c == '\n' || c == '\r')
505 case '\004': /* EOT */
506 if(sendbufp == sendbuf) {
507 sendnchars(0,sendbuf);
513 if(sendbufp < &sendbuf[BSIZE-UTFmax])
514 sendbufp += runetochar(sendbufp, &c);
515 ep += runetochar(ep, &c);
523 lookfk(struct funckey *fk, char *name)
527 for(i=0; fk[i].name; i++){
528 if(strcmp(name, fk[i].name)==0)
529 return fk[i].sequence;
537 char *s = lookfk(appfk != nil ? appfk : fk, name);
538 if(s == nil && appfk != nil)
539 s = lookfk(fk, name);
541 sendnchars(strlen(s), s);
547 static char echobuf[4*BSIZE];
557 if((c = Bgetrune(snarffp)) < 0) {
629 sendnchars(1, echobuf);
633 sendnchars(1, echobuf);
636 sendnchars(runetochar(echobuf, &kbdchar), echobuf);
640 switch(canon(echobuf, kbdchar)){
646 strcat(echo_input,echobuf);
657 hostbufp = hostbuf = nbrecvp(hc);
658 if(host_avail() && nrand(8))
670 enum { AMOUSE, ARESIZE, AKBD, AHOST, AEND, };
672 { mc->c, &mc->Mouse, CHANRCV },
673 { mc->resizec, nil, CHANRCV },
674 { kc->c, &kbdchar, CHANRCV },
675 { hc, &hostbuf, CHANRCV },
676 { nil, nil, CHANEND },
679 a[AHOST].op = CHANNOP;
680 else if(hostbuf != nil)
681 a[AHOST].op = CHANNOBLK;
683 if(display->bufp > display->buf)
684 flushimage(display, 1);
689 else if(button2() || button3())
691 else if(resize_flag == 0)
708 putenvint(char *name, int x)
712 snprint(buf, sizeof buf, "%d", x);
719 putenvint("XPIXELS", (xmax+1)*ftsize.x);
720 putenvint("YPIXELS", (ymax+1)*ftsize.y);
721 putenvint("LINES", ymax+1);
722 putenvint("COLS", xmax+1);
723 putenv("TERM", term);
727 setdim(int ht, int wid)
731 if(wid > 0) xmax = wid-1;
732 if(ht > 0) ymax = ht-1;
740 margin.x = (Dx(screen->r) - (xmax+1)*ftsize.x) / 2;
741 margin.y = (Dy(screen->r) - (ymax+1)*ftsize.y) / 2;
744 onscreenrbuf = mallocz((ymax+1)*(xmax+2)*sizeof(Rune), 1);
746 onscreenabuf = mallocz((ymax+1)*(xmax+2), 1);
748 onscreencbuf = mallocz((ymax+1)*(xmax+2), 1);
749 clear(0,0,xmax+1,ymax+1);
751 if(resize_flag || backc)
756 fd = open("/dev/wctl", OWRITE);
758 ht = (ymax+1) * ftsize.y + 2*INSET + 2*Borderwidth;
759 wid = (xmax+1) * ftsize.x + ftsize.x + 2*INSET + 2*Borderwidth;
760 fprint(fd, "resize -dx %d -dy %d\n", wid, ht);
768 if(resize_flag > 1 && getwindow(display, Refnone) < 0){
769 fprint(2, "can't reattach to window: %r\n");
770 exits("can't reattach to window");
772 setdim((Dy(screen->r) - 2*INSET)/ftsize.y, (Dx(screen->r) - 2*INSET - ftsize.x)/ftsize.x);
777 werrstr(""); /* clear spurious error messages */
784 snarffp = Bopen("/dev/snarf",OREAD);
788 writesnarf(Rune *s, Rune *e)
795 b = Bopen("/dev/snarf", OWRITE|OTRUNC);
798 for(z = p = 0; s < e; s++){
803 while(z-- > 0) Bputc(b, ' ');
815 drawselection(Rectangle r, Rectangle d, Image *color)
817 while(r.min.y < r.max.y){
818 d = drawselection(Rect(r.min.x, r.min.y, xmax+1, r.min.y), d, color);
822 if(r.min.x >= r.max.x)
824 r = Rpt(pt(r.min.x, r.min.y), pt(r.max.x, r.max.y+1));
825 draw(screen, r, color, highlight, r.min);
837 backup = allocimage(display, screen->r, screen->chan, 0, DNofill);
838 draw(backup, backup->r, screen, nil, backup->r.min);
842 if(onscreenr(p.x, p.y) > onscreenr(q.x, q.y)){
851 d = drawselection(r, ZR, red);
852 flushimage(display, 1);
854 draw(screen, d, backup, nil, d.min);
856 if((mc->buttons & 07) == 5)
858 else if(writesnarf(onscreenr(r.min.x, r.min.y), onscreenr(r.max.x, r.max.y))){
859 d = drawselection(r, ZR, green);
860 flushimage(display, 1);
862 draw(screen, d, backup, nil, d.min);
871 menu3.item[1] = ttystate[cs->raw].crnl ? "cr" : "crnl";
872 menu3.item[2] = ttystate[cs->raw].nlcr ? "nl" : "nlcr";
873 menu3.item[3] = cs->raw ? "cooked" : "raw";
875 switch(menuhit(3, mc, &menu3, nil)) {
879 case 1: /* newline after cr? */
880 ttystate[cs->raw].crnl = !ttystate[cs->raw].crnl;
882 case 2: /* cr after newline? */
883 ttystate[cs->raw].nlcr = !ttystate[cs->raw].nlcr;
885 case 3: /* switch raw mode */
894 menu2.item[5] = pagemode? "scroll": "page";
896 switch(menuhit(2, mc, &menu2, nil)) {
898 case 0: /* back up */
905 case 1: /* move forward */
918 case 3: /* clear screen */
922 case 4: /* send the snarf buffer */
926 case 5: /* pause and clear at end of screen */
927 pagemode = 1-pagemode;
928 if(blocked && !pagemode) {
943 if(count == 0 && !pagemode) {
945 nbacklines = HISTSIZ; /* make sure we scroll to the very end */
947 n = 3*(count+1)*ymax/4;
955 cp = &hist[HISTSIZ-1];
964 if(cp >= &hist[HISTSIZ])
972 return addpt(screen->r.min, Pt(x*ftsize.x+margin.x,y*ftsize.y+margin.y));
978 pt.x -= screen->r.min.x + margin.x;
979 pt.y -= screen->r.min.y + margin.y;
984 else if(pt.x > xmax+1)
988 else if(pt.y > ymax+1)
994 shift(int x1, int y, int x2, int w)
1000 memmove(onscreenr(x1, y), onscreenr(x2, y), w*sizeof(Rune));
1001 memmove(onscreena(x1, y), onscreena(x2, y), w);
1002 memmove(onscreenc(x1, y), onscreenc(x2, y), w);
1006 scroll(int sy, int ly, int dy, int cy) /* source, limit, dest, which line to clear */
1008 memmove(onscreenr(0, dy), onscreenr(0, sy), (ly-sy)*(xmax+2)*sizeof(Rune));
1009 memmove(onscreena(0, dy), onscreena(0, sy), (ly-sy)*(xmax+2));
1010 memmove(onscreenc(0, dy), onscreenc(0, sy), (ly-sy)*(xmax+2));
1011 clear(0, cy, xmax+1, cy+1);
1015 bigscroll(void) /* scroll up half a page */
1019 if(x == 0 && y == 0)
1022 clear(0, 0, xmax+1, ymax+1);
1026 memmove(onscreenr(0, 0), onscreenr(0, half), (ymax-half+1)*(xmax+2)*sizeof(Rune));
1027 memmove(onscreena(0, 0), onscreena(0, half), (ymax-half+1)*(xmax+2));
1028 memmove(onscreenc(0, 0), onscreenc(0, half), (ymax-half+1)*(xmax+2));
1029 clear(0, y-half+1, xmax+1, ymax+1);
1036 number(Rune *p, int *got)
1042 while ((c = get_next_char()) >= '0' && c <= '9'){
1054 sendnchars(int n,char *p)
1058 if(write(hostfd,p,n) < 0){
1067 if(*echop != 0 && fullrune(echop, strlen(echop)))
1071 return *hostbufp != 0;
1080 echop += chartorune(&r, echop);
1097 if(c >= 'a' && c <= 'z')
1099 if(c >= 'A' && c <= 'Z')
1101 if(c >= '0' && c <= '9')
1107 escapedump(int fd,uchar *str,int len)
1111 for(i = 0; i < len; i++) {
1112 if((str[i] < ' ' || str[i] > '\177') &&
1113 str[i] != '\n' && str[i] != '\t') fprint(fd,"^%c",str[i]+64);
1114 else if(str[i] == '\177') fprint(fd,"^$");
1115 else if(str[i] == '\n') fprint(fd,"^J\n");
1116 else fprint(fd,"%c",str[i]);
1121 drawstring(Rune *str, int n)
1123 memmove(onscreenr(x, y), str, n*sizeof(Rune));
1124 memset(onscreena(x, y), attr & 0xFF, n);
1125 memset(onscreenc(x, y), attr >> 8, n);