]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vt/main.c
vt: handle application/normal mode (really fixes cursor keys)
[plan9front.git] / sys / src / cmd / vt / main.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include <bio.h>
8 #include "cons.h"
9
10 char    *menutext2[] = {
11         "backup",
12         "forward",
13         "reset",
14         "clear",
15         "paste",
16         "page",
17         0
18 };
19
20 char    *menutext3[] = {
21         "24x80",
22         "crnl",
23         "nl",
24         "raw",
25         "exit",
26         0
27 };
28
29 /* variables associated with the screen */
30
31 int     x, y;   /* character positions */
32 Rune    *backp;
33 int     backc;
34 int     atend;
35 int     nbacklines;
36 int     xmax, ymax;
37 int     blocked;
38 int     resize_flag = 1;
39 int     pagemode;
40 int     olines;
41 int     peekc;
42 int     cursoron = 1;
43 Menu    menu2;
44 Menu    menu3;
45 Rune    *histp;
46 Rune    hist[HISTSIZ];
47 Rune    *onscreenrbuf;
48 uchar   *onscreenabuf;
49 uchar   *onscreencbuf;
50 int     yscrmin, yscrmax;
51 int     attr, defattr;
52
53 Image   *bordercol;
54 Image   *colors[8];
55 Image   *hicolors[8];
56 Image   *red;
57 Image   *green;
58 Image   *fgcolor;
59 Image   *bgcolor;
60 Image   *highlight;
61
62 uint rgbacolors[8] = {
63         0x000000FF,     /* black */
64         0xAA0000FF,     /* red */
65         0x00AA00FF,     /* green */
66         0xFF5500FF,     /* brown */
67         0x0000FFFF,     /* blue */
68         0xAA00AAFF,     /* purple */
69         0x00AAAAFF,     /* cyan */
70         0x7F7F7FFF,     /* white */
71 };
72
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 */
82 };
83
84 /* terminal control */
85 struct ttystate ttystate[2] = { {0, 1}, {0, 1} };
86
87 Point   margin;
88 Point   ftsize;
89
90 Rune    kbdchar;
91
92 #define button1()       ((mc->buttons & 07)==1)
93 #define button2()       ((mc->buttons & 07)==2)
94 #define button3()       ((mc->buttons & 07)==4)
95
96 Mousectl        *mc;
97 Keyboardctl     *kc;
98 Channel         *hc;
99 Consstate       *cs;
100
101 int     nocolor;
102 int     logfd = -1;
103 int     hostfd = -1;
104 int     hostpid;
105 Biobuf  *snarffp = 0;
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;
111
112 char *term;
113 struct funckey *fk, *appfk;
114
115 /* functions */
116 void    initialize(int, char **);
117 int     waitchar(void);
118 void    waitio(void);
119 int     rcvchar(void);
120 void    bigscroll(void);
121 void    readmenu(void);
122 void    selection(void);
123 void    resize(void);
124 void    send_interrupt(void);
125 int     alnum(int);
126 void    escapedump(int,uchar *,int);
127
128 int
129 start_host(void)
130 {
131         switch((hostpid = rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG))) {
132         case 0:
133                 close(0);
134                 open("/dev/cons", OREAD);
135                 close(1);
136                 open("/dev/cons", OWRITE);
137                 dup(1, 2);
138                 execl("/bin/rc","rcX",nil);
139                 fprint(2, "failed to start up rc: %r\n");
140                 _exits("rc");
141         case -1:
142                 fprint(2,"rc startup: fork: %r\n");
143                 _exits("rc_fork");
144         }
145         return open("/mnt/cons/data", ORDWR);
146 }
147
148 void
149 send_interrupt(void)
150 {
151         postnote(PNGROUP, hostpid, "interrupt");
152 }
153
154 void
155 hostreader(void*)
156 {
157         char cb[BSIZE+1], *cp;
158         Rune *rb, *rp;
159         int n, r;
160
161         n = 0;
162         while((r = read(hostfd, cb+n, BSIZE-n)) > 0){
163                 n += r;
164                 rb = mallocz((n+1)*sizeof(Rune), 0);
165                 for(rp = rb, cp = cb; n > 0; n -= r, cp += r){
166                         if(!fullrune(cp, n))
167                                 break;
168                         r = chartorune(rp, cp);
169                         if(*rp != 0)
170                                 rp++;
171                 }
172                 if(rp > rb){
173                         *rp = 0;
174                         sendp(hc, rb);
175                 } else {
176                         free(rb);
177                 }
178                 if(n > 0) memmove(cb, cp, n);
179         }
180         sendp(hc, nil);
181 }
182
183 static void
184 shutdown(void)
185 {
186         send_interrupt();
187         postnote(PNGROUP, getpid(), "exit");
188         threadexitsall(nil);
189 }
190
191 void
192 threadmain(int argc, char **argv)
193 {
194         rfork(RFNAMEG|RFNOTEG|RFENVG);
195         atexit(shutdown);
196         initialize(argc, argv);
197         emulate();
198 }
199
200 void
201 usage(void)
202 {
203         fprint(2, "usage: %s [-2abcrx] [-f font] [-l logfile] [cmd...]\n", argv0);
204         exits("usage");
205 }
206
207 void
208 initialize(int argc, char **argv)
209 {
210         int rflag;
211         int i, blkbg;
212         char *fontname, *p;
213
214         fontname = nil;
215         fk = ansifk;
216         term = "vt100";
217         blkbg = 0;
218         rflag = 0;
219         attr = defattr;
220         ARGBEGIN{
221         case '2':
222                 term = "vt220";
223                 break;
224         case 'a':
225                 term = "ansi";
226                 break;
227         case 'b':
228                 blkbg = 1;              /* e.g., for linux colored output */
229                 break;
230         case 'c':
231                 nocolor = 1;
232                 break;
233         case 'f':
234                 fontname = EARGF(usage());
235                 break;
236         case 'l':
237                 p = EARGF(usage());
238                 logfd = create(p, OWRITE|OCEXEC, 0666);
239                 if(logfd < 0)
240                         sysfatal("could not create log file: %s: %r", p);
241                 break;
242         case 'x':
243                 fk = xtermfk;
244                 term = "xterm";
245                 break;
246         case 'r':
247                 rflag = 1;
248                 break;
249         default:
250                 usage();
251                 break;
252         }ARGEND;
253
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");
262         cs->raw = rflag;
263
264         histp = hist;
265         menu2.item = menutext2;
266         menu3.item = menutext3;
267         pagemode = 0;
268         blocked = 0;
269         ftsize.y = font->height;
270         ftsize.x = stringwidth(font, "m");
271
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);
276
277         for(i=0; i<8; i++){
278                 colors[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,
279                         rgbacolors[i]);
280                 hicolors[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,
281                         rgbahicolors[i]);
282         }
283         bgcolor = (blkbg? display->black: display->white);
284         fgcolor = (blkbg? display->white: display->black);
285         resize();
286
287         hc = chancreate(sizeof(Rune*), 5);
288         if((hostfd = start_host()) >= 0)
289                 proccreate(hostreader, nil, BSIZE+1024);
290
291         while(*argv != nil){
292                 sendnchars(strlen(*argv), *argv);
293                 if(argv[1] == nil){
294                         sendnchars(1, "\n");
295                         break;
296                 }
297                 sendnchars(1, " ");
298                 argv++;
299         }
300 }
301
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))]
305
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)
308
309 void
310 drawscreen(void)
311 {
312         int x, y, n;
313         uchar c, *ap, *cp;
314         Rune *rp;
315
316         draw(screen, screen->r, bgcolor, nil, ZP);
317
318         /* draw background */
319         for(y = 0; y <= ymax; y++){
320                 for(x = 0; x <= xmax; x += n){
321                         cp = onscreenc(x, y);
322                         if((*cp & 1) == 0){
323                                 n = 1;
324                                 continue;
325                         }
326                         ap = onscreena(x, y);
327                         c = bgcol(*ap, *cp);
328                         for(n = 1; x+n <= xmax && bgcol(ap[n], cp[n]) == c; n++)
329                                 ;
330                         draw(screen, Rpt(pt(x, y), pt(x+n, y+1)), colors[c>>1], nil, ZP);
331                 }
332         }
333
334         /* draw foreground */
335         for(y = 0; y <= ymax; y++){
336                 for(x = 0; x <= xmax; x += n){
337                         rp = onscreenr(x, y);
338                         if(*rp == 0){
339                                 n = 1;
340                                 continue;
341                         }
342                         ap = onscreena(x, y);
343                         cp = onscreenc(x, y);
344                         c = fgcol(*ap, *cp);
345                         for(n = 1; x+n <= xmax && rp[n] != 0 && fgcol(ap[n], cp[n]) == c; n++)
346                                 ;
347                         runestringn(screen, pt(x, y),
348                                 (c&1) ? (((c&16) ? hicolors : colors)[(c&15)>>1]) : fgcolor,
349                                 ZP, font, rp, n);
350                 }
351                 if(*onscreenr(x, y) == 0)
352                         runestringn(screen, pt(x, y),
353                                 bordercol,
354                                 ZP, font, L">", 1);
355         }
356 }
357
358 void
359 drawcursor(void)
360 {
361         Image *col;
362
363         if(cursoron == 0)
364                 return;
365         col = (blocked || hostfd < 0) ? red : bordercol;
366         border(screen, Rpt(pt(x, y), pt(x+1, y+1)), 2, col, ZP);
367 }
368
369 void
370 clear(int x1, int y1, int x2, int y2)
371 {
372         int c = (attr & 0x0F00)>>8; /* bgcolor */
373         
374         while(y1 < y2){
375                 if(x1 < x2){
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);
379                 }
380                 if(x2 > xmax)
381                         *onscreenr(xmax+1, y1) = '\n';
382                 y1++;
383         }
384 }
385
386 void
387 newline(void)
388 {
389         if(x > xmax)
390                 *onscreenr(xmax+1, y) = 0;      /* wrap arround, remove hidden newline */
391         nbacklines--;
392         if(y >= yscrmax) {
393                 y = yscrmax;
394                 if(pagemode && olines >= yscrmax) {
395                         blocked = 1;
396                         return;
397                 }
398                 scroll(yscrmin+1, yscrmax+1, yscrmin, yscrmax);
399         } else
400                 y++;
401         olines++;
402 }
403
404 int
405 get_next_char(void)
406 {
407         int c = peekc;
408
409         peekc = 0;
410         if(c > 0)
411                 return(c);
412         while(c <= 0) {
413                 if(backp) {
414                         c = *backp;
415                         if(c && nbacklines >= 0) {
416                                 backp++;
417                                 if(backp >= &hist[HISTSIZ])
418                                         backp = hist;
419                                 return(c);
420                         }
421                         backp = 0;
422                 }
423                 c = waitchar();
424                 if(c > 0 && logfd >= 0)
425                         fprint(logfd, "%C", (Rune)c);
426         }
427         *histp++ = c;
428         if(histp >= &hist[HISTSIZ])
429                 histp = hist;
430         *histp = '\0';
431         return(c);
432 }
433
434 char*
435 backrune(char *start, char *cp)
436 {
437         char *ep;
438
439         ep = cp;
440         cp -= UTFmax;
441         if(cp < start)
442                 cp = start;
443         while(cp < ep){
444                 Rune r;
445                 int n;
446
447                 n = chartorune(&r, cp);
448                 if(cp + n >= ep)
449                         break;
450                 cp += n;
451         }
452         return cp;
453 }
454
455 int
456 canon(char *ep, Rune c)
457 {
458         switch(c) {
459         case Kdown:
460         case Kpgdown:
461                 return SCROLL;
462         case '\b':
463                 if(sendbufp > sendbuf){
464                         sendbufp = backrune(sendbuf, sendbufp);
465                         *ep++ = '\b';
466                         *ep++ = ' ';
467                         *ep++ = '\b';
468                 }
469                 break;
470         case 0x15:      /* ^U line kill */
471                 sendbufp = sendbuf;
472                 *ep++ = '^';
473                 *ep++ = 'U';
474                 *ep++ = '\n';
475                 break;
476         case 0x17:      /* ^W word kill */
477                 while(sendbufp > sendbuf && !alnum(*sendbufp)) {
478                         sendbufp = backrune(sendbuf, sendbufp);
479                         *ep++ = '\b';
480                         *ep++ = ' ';
481                         *ep++ = '\b';
482                 }
483                 while(sendbufp > sendbuf && alnum(*sendbufp)) {
484                         sendbufp = backrune(sendbuf, sendbufp);
485                         *ep++ = '\b';
486                         *ep++ = ' ';
487                         *ep++ = '\b';
488                 }
489                 break;
490         case '\177':    /* interrupt */
491                 sendbufp = sendbuf;
492                 send_interrupt();
493                 return(NEWLINE);
494         case '\021':    /* quit */
495         case '\r':
496         case '\n':
497                 if(sendbufp < &sendbuf[BSIZE])
498                         *sendbufp++ = '\n';
499                 sendnchars((int)(sendbufp-sendbuf), sendbuf);
500                 sendbufp = sendbuf;
501                 if(c == '\n' || c == '\r')
502                         *ep++ = '\n';
503                 *ep = 0;
504                 return(NEWLINE);
505         case '\004':    /* EOT */
506                 if(sendbufp == sendbuf) {
507                         sendnchars(0,sendbuf);
508                         *ep = 0;
509                         return(NEWLINE);
510                 }
511                 /* fall through */
512         default:
513                 if(sendbufp < &sendbuf[BSIZE-UTFmax])
514                         sendbufp += runetochar(sendbufp, &c);
515                 ep += runetochar(ep, &c);
516                 break;
517         }
518         *ep = 0;
519         return(OTHER);
520 }
521
522 char*
523 lookfk(struct funckey *fk, char *name)
524 {
525         int i;
526
527         for(i=0; fk[i].name; i++){
528                 if(strcmp(name, fk[i].name)==0)
529                         return fk[i].sequence;
530         }
531         return nil;
532 }
533
534 void
535 sendfk(char *name)
536 {
537         char *s = lookfk(appfk != nil ? appfk : fk, name);
538         if(s == nil && appfk != nil)
539                 s = lookfk(fk, name);
540         if(s != nil)
541                 sendnchars(strlen(s), s);
542 }
543
544 int
545 waitchar(void)
546 {
547         static char echobuf[4*BSIZE];
548
549         for(;;) {
550                 if(resize_flag)
551                         resize();
552                 if(backp)
553                         return(0);
554                 if(snarffp) {
555                         int c;
556
557                         if((c = Bgetrune(snarffp)) < 0) {
558                                 Bterm(snarffp);
559                                 snarffp = nil;
560                                 continue;
561                         }
562                         kbdchar = c;
563                 }
564                 if(kbdchar) {
565                         if(backc){
566                                 backc = 0;
567                                 backup(backc);
568                         }
569                         if(blocked)
570                                 resize_flag = 1;
571                         if(cs->raw) {
572                                 switch(kbdchar){
573                                 case Kup:
574                                         sendfk("up key");
575                                         break;
576                                 case Kdown:
577                                         sendfk("down key");
578                                         break;
579                                 case Kleft:
580                                         sendfk("left key");
581                                         break;
582                                 case Kright:
583                                         sendfk("right key");
584                                         break;
585                                 case Kpgup:
586                                         sendfk("page up");
587                                         break;
588                                 case Kpgdown:
589                                         sendfk("page down");
590                                         break;
591                                 case KF|1:
592                                         sendfk("F1");
593                                         break;
594                                 case KF|2:
595                                         sendfk("F2");
596                                         break;
597                                 case KF|3:
598                                         sendfk("F3");
599                                         break;
600                                 case KF|4:
601                                         sendfk("F4");
602                                         break;
603                                 case KF|5:
604                                         sendfk("F5");
605                                         break;
606                                 case KF|6:
607                                         sendfk("F6");
608                                         break;
609                                 case KF|7:
610                                         sendfk("F7");
611                                         break;
612                                 case KF|8:
613                                         sendfk("F8");
614                                         break;
615                                 case KF|9:
616                                         sendfk("F9");
617                                         break;
618                                 case KF|10:
619                                         sendfk("F10");
620                                         break;
621                                 case KF|11:
622                                         sendfk("F11");
623                                         break;
624                                 case KF|12:
625                                         sendfk("F12");
626                                         break;
627                                 case '\n':
628                                         echobuf[0] = '\r';
629                                         sendnchars(1, echobuf);
630                                         break;
631                                 case '\r':
632                                         echobuf[0] = '\n';
633                                         sendnchars(1, echobuf);
634                                         break;
635                                 default:
636                                         sendnchars(runetochar(echobuf, &kbdchar), echobuf);
637                                         break;
638                                 }
639                         } else {
640                                 switch(canon(echobuf, kbdchar)){
641                                 case SCROLL:
642                                         if(!blocked)
643                                                 bigscroll();
644                                         break;
645                                 default:
646                                         strcat(echo_input,echobuf);
647                                 }
648                         }
649                         blocked = 0;
650                         kbdchar = 0;
651                         continue;
652                 }
653                 if(!blocked){
654                         if(host_avail())
655                                 return(rcvchar());
656                         free(hostbuf);
657                         hostbufp = hostbuf = nbrecvp(hc);
658                         if(host_avail() && nrand(8))
659                                 return(rcvchar());
660                 }
661                 drawscreen();
662                 drawcursor();
663                 waitio();
664         }
665 }
666
667 void
668 waitio(void)
669 {
670         enum { AMOUSE, ARESIZE, AKBD, AHOST, AEND, };
671         Alt a[AEND+1] = {
672                 { mc->c, &mc->Mouse, CHANRCV },
673                 { mc->resizec, nil, CHANRCV },
674                 { kc->c, &kbdchar, CHANRCV },
675                 { hc, &hostbuf, CHANRCV },
676                 { nil, nil, CHANEND },
677         };
678         if(blocked)
679                 a[AHOST].op = CHANNOP;
680         else if(hostbuf != nil)
681                 a[AHOST].op = CHANNOBLK;
682 Next:
683         if(display->bufp > display->buf)
684                 flushimage(display, 1);
685         switch(alt(a)){
686         case AMOUSE:
687                 if(button1())
688                         selection();
689                 else if(button2() || button3())
690                         readmenu();
691                 else if(resize_flag == 0)
692                         goto Next;
693                 break;
694         case ARESIZE:
695                 resize_flag = 2;
696                 break;
697         case AHOST:
698                 hostbufp = hostbuf;
699                 if(hostbuf == nil){
700                         close(hostfd);
701                         hostfd = -1;
702                 }
703                 break;
704         }
705 }
706
707 void
708 putenvint(char *name, int x)
709 {
710         char buf[20];
711
712         snprint(buf, sizeof buf, "%d", x);
713         putenv(name, buf);
714 }
715
716 void
717 exportsize(void)
718 {
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);
724 }
725
726 void
727 setdim(int ht, int wid)
728 {
729         int fd;
730  
731         if(wid > 0) xmax = wid-1;
732         if(ht > 0) ymax = ht-1;
733
734         x = 0;
735         y = 0;
736         yscrmin = 0;
737         yscrmax = ymax;
738         olines = 0;
739
740         margin.x = (Dx(screen->r) - (xmax+1)*ftsize.x) / 2;
741         margin.y = (Dy(screen->r) - (ymax+1)*ftsize.y) / 2;
742
743         free(onscreenrbuf);
744         onscreenrbuf = mallocz((ymax+1)*(xmax+2)*sizeof(Rune), 1);
745         free(onscreenabuf);
746         onscreenabuf = mallocz((ymax+1)*(xmax+2), 1);
747         free(onscreencbuf);
748         onscreencbuf = mallocz((ymax+1)*(xmax+2), 1);
749         clear(0,0,xmax+1,ymax+1);
750
751         if(resize_flag || backc)
752                 return;
753
754         exportsize();
755
756         fd = open("/dev/wctl", OWRITE);
757         if(fd >= 0){
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);
761                 close(fd);
762         }
763 }
764
765 void
766 resize(void)
767 {
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");
771         }
772         setdim((Dy(screen->r) - 2*INSET)/ftsize.y, (Dx(screen->r) - 2*INSET - ftsize.x)/ftsize.x);
773         exportsize();
774         if(resize_flag > 1)
775                 backup(backc);
776         resize_flag = 0;
777         werrstr("");            /* clear spurious error messages */
778 }
779
780 void
781 sendsnarf(void)
782 {
783         if(snarffp == nil)
784                 snarffp = Bopen("/dev/snarf",OREAD);
785 }
786
787 int
788 writesnarf(Rune *s, Rune *e)
789 {
790         Biobuf *b;
791         int z, p;
792
793         if(s >= e)
794                 return 0;
795         b = Bopen("/dev/snarf", OWRITE|OTRUNC);
796         if(b == nil)
797                 return 0;
798         for(z = p = 0; s < e; s++){
799                 if(*s){
800                         if(*s == '\n')
801                                 z = p = 0;
802                         else if(p++ == 0){
803                                 while(z-- > 0) Bputc(b, ' ');
804                         }
805                         Bputrune(b, *s);
806                 } else {
807                         z++;
808                 }
809         }
810         Bterm(b);
811         return 1;
812 }
813
814 Rectangle
815 drawselection(Rectangle r, Rectangle d, Image *color)
816 {
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);
819                 r.min.x = 0;
820                 r.min.y++;
821         }
822         if(r.min.x >= r.max.x)
823                 return d;
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);
826         combinerect(&d, r);
827         return d;
828 }
829
830 void
831 selection(void)
832 {
833         Point p, q;
834         Rectangle r, d;
835         Image *backup;
836
837         backup = allocimage(display, screen->r, screen->chan, 0, DNofill);
838         draw(backup, backup->r, screen, nil, backup->r.min);
839         p = pos(mc->xy);
840         do {
841                 q = pos(mc->xy);
842                 if(onscreenr(p.x, p.y) > onscreenr(q.x, q.y)){
843                         r.min = q;
844                         r.max = p;
845                 } else {
846                         r.min = p;
847                         r.max = q;
848                 }
849                 if(r.max.y > ymax)
850                         r.max.x = 0;
851                 d = drawselection(r, ZR, red);
852                 flushimage(display, 1);
853                 readmouse(mc);
854                 draw(screen, d, backup, nil, d.min);
855         } while(button1());
856         if((mc->buttons & 07) == 5)
857                 sendsnarf();
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);
861                 sleep(200);
862                 draw(screen, d, backup, nil, d.min);
863         }
864         freeimage(backup);
865 }
866
867 void
868 readmenu(void)
869 {
870         if(button3()) {
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";
874
875                 switch(menuhit(3, mc, &menu3, nil)) {
876                 case 0:         /* 24x80 */
877                         setdim(24, 80);
878                         return;
879                 case 1:         /* newline after cr? */
880                         ttystate[cs->raw].crnl = !ttystate[cs->raw].crnl;
881                         return;
882                 case 2:         /* cr after newline? */
883                         ttystate[cs->raw].nlcr = !ttystate[cs->raw].nlcr;
884                         return;
885                 case 3:         /* switch raw mode */
886                         cs->raw = !cs->raw;
887                         return;
888                 case 4:
889                         exits(0);
890                 }
891                 return;
892         }
893
894         menu2.item[5] = pagemode? "scroll": "page";
895
896         switch(menuhit(2, mc, &menu2, nil)) {
897
898         case 0:         /* back up */
899                 if(atend == 0) {
900                         backc++;
901                         backup(backc);
902                 }
903                 return;
904
905         case 1:         /* move forward */
906                 backc--;
907                 if(backc >= 0)
908                         backup(backc);
909                 else
910                         backc = 0;
911                 return;
912
913         case 2:         /* reset */
914                 backc = 0;
915                 backup(0);
916                 return;
917
918         case 3:         /* clear screen */
919                 resize_flag = 1;
920                 return;
921
922         case 4:         /* send the snarf buffer */
923                 sendsnarf();
924                 return;
925
926         case 5:         /* pause and clear at end of screen */
927                 pagemode = 1-pagemode;
928                 if(blocked && !pagemode) {
929                         resize_flag = 1;
930                         blocked = 0;
931                 }
932                 return;
933         }
934 }
935
936 void
937 backup(int count)
938 {
939         Rune *cp;
940         int n;
941
942         resize_flag = 1;
943         if(count == 0 && !pagemode) {
944                 n = ymax;
945                 nbacklines = HISTSIZ;   /* make sure we scroll to the very end */
946         } else{
947                 n = 3*(count+1)*ymax/4;
948                 nbacklines = ymax-1;
949         }
950         cp = histp;
951         atend = 0;
952         while (n >= 0) {
953                 cp--;
954                 if(cp < hist)
955                         cp = &hist[HISTSIZ-1];
956                 if(*cp == '\0') {
957                         atend = 1;
958                         break;
959                 }
960                 if(*cp == '\n')
961                         n--;
962         }
963         cp++;
964         if(cp >= &hist[HISTSIZ])
965                 cp = hist;
966         backp = cp;
967 }
968
969 Point
970 pt(int x, int y)
971 {
972         return addpt(screen->r.min, Pt(x*ftsize.x+margin.x,y*ftsize.y+margin.y));
973 }
974
975 Point
976 pos(Point pt)
977 {
978         pt.x -= screen->r.min.x + margin.x;
979         pt.y -= screen->r.min.y + margin.y;
980         pt.x /= ftsize.x;
981         pt.y /= ftsize.y;
982         if(pt.x < 0)
983                 pt.x = 0;
984         else if(pt.x > xmax+1)
985                 pt.x = xmax+1;
986         if(pt.y < 0)
987                 pt.y = 0;
988         else if(pt.y > ymax+1)
989                 pt.y = ymax+1;
990         return pt;
991 }
992
993 void
994 shift(int x1, int y, int x2, int w)
995 {
996         if(x1+w > xmax+1)
997                 w = xmax+1 - x1;
998         if(x2+w > xmax+1)
999                 w = xmax+1 - x2;
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);
1003 }
1004
1005 void
1006 scroll(int sy, int ly, int dy, int cy)  /* source, limit, dest, which line to clear */
1007 {
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);
1012 }
1013
1014 void
1015 bigscroll(void)                 /* scroll up half a page */
1016 {
1017         int half = ymax/3;
1018
1019         if(x == 0 && y == 0)
1020                 return;
1021         if(y < half) {
1022                 clear(0, 0, xmax+1, ymax+1);
1023                 x = y = 0;
1024                 return;
1025         }
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);
1030         y -= half;
1031         if(olines)
1032                 olines -= half;
1033 }
1034
1035 int
1036 number(Rune *p, int *got)
1037 {
1038         int c, n = 0;
1039
1040         if(got)
1041                 *got = 0;
1042         while ((c = get_next_char()) >= '0' && c <= '9'){
1043                 if(got)
1044                         *got = 1;
1045                 n = n*10 + c - '0';
1046         }
1047         *p = c;
1048         return(n);
1049 }
1050
1051 /* stubs */
1052
1053 void
1054 sendnchars(int n,char *p)
1055 {
1056         if(hostfd < 0)
1057                 return;
1058         if(write(hostfd,p,n) < 0){
1059                 close(hostfd);
1060                 hostfd = -1;
1061         }
1062 }
1063
1064 int
1065 host_avail(void)
1066 {
1067         if(*echop != 0 && fullrune(echop, strlen(echop)))
1068                 return 1;
1069         if(hostbuf == nil)
1070                 return 0;
1071         return *hostbufp != 0;
1072 }
1073
1074 int
1075 rcvchar(void)
1076 {
1077         Rune r;
1078
1079         if(*echop != 0) {
1080                 echop += chartorune(&r, echop);
1081                 if(*echop == 0) {
1082                         echop = echo_input;     
1083                         *echop = 0;
1084                 }
1085                 return r;
1086         }
1087         return *hostbufp++;
1088 }
1089
1090 void
1091 ringbell(void){
1092 }
1093
1094 int
1095 alnum(int c)
1096 {
1097         if(c >= 'a' && c <= 'z')
1098                 return 1;
1099         if(c >= 'A' && c <= 'Z')
1100                 return 1;
1101         if(c >= '0' && c <= '9')
1102                 return 1;
1103         return 0;
1104 }
1105
1106 void
1107 escapedump(int fd,uchar *str,int len)
1108 {
1109         int i;
1110
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]);
1117         }
1118 }
1119
1120 void
1121 drawstring(Rune *str, int n)
1122 {
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);
1126 }