Blob


1 #include "9term.h"
3 Rectangle scrollr; /* scroll bar rectangle */
4 Rectangle lastsr; /* used for scroll bar */
5 int holdon; /* hold mode */
6 int rawon; /* raw mode */
7 int scrolling; /* window scrolls */
8 int clickmsec; /* time of last click */
9 uint clickq0; /* point of last click */
10 int rcfd[2];
11 int rcpid;
12 int maxtab;
13 Mousectl* mc;
14 Keyboardctl* kc;
15 Channel* hostc;
16 Readbuf rcbuf[2];
17 int mainpid;
18 int plumbfd;
19 int button2exec;
20 int label(Rune*, int);
21 char wdir[1024];
22 char childwdir[1024];
23 void hangupnote(void*, char*);
25 char *menu2str[] = {
26 "cut",
27 "paste",
28 "snarf",
29 "send",
30 "scroll",
31 "plumb",
32 0
33 };
35 Image* cols[NCOL];
36 Image* hcols[NCOL];
37 Image *plumbcolor;
38 Image *execcolor;
40 Menu menu2 =
41 {
42 menu2str
43 };
45 Text t;
47 Cursor whitearrow = {
48 {0, 0},
49 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
50 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC,
51 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
52 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
53 {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C,
54 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C,
55 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C,
56 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
57 };
59 void
60 usage(void)
61 {
62 fprint(2, "usage: 9term [-ars] [cmd ...]\n");
63 threadexitsall("usage");
64 }
66 void
67 threadmain(int argc, char *argv[])
68 {
69 char *p;
71 rfork(RFNOTEG);
72 mainpid = getpid();
73 ARGBEGIN{
74 default:
75 usage();
76 case 'a': /* acme mode */
77 button2exec++;
78 break;
79 case 'r':
80 /* not clear this is useful */
81 rawon = 1;
82 break;
83 case 's':
84 scrolling++;
85 break;
86 }ARGEND
88 p = getenv("tabstop");
89 if(p == 0)
90 p = getenv("TABSTOP");
91 if(p != 0 && maxtab <= 0)
92 maxtab = strtoul(p, 0, 0);
93 if(maxtab <= 0)
94 maxtab = 8;
96 initdraw(nil, nil, "9term");
97 notify(hangupnote);
99 mc = initmouse(nil, screen);
100 kc = initkeyboard(nil);
101 rcstart(rcfd, argc, argv);
102 hoststart();
103 plumbstart();
105 t.f = mallocz(sizeof(Frame), 1);
107 cols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
108 cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkyellow);
109 cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DYellowgreen);
110 cols[TEXT] = display->black;
111 cols[HTEXT] = display->black;
113 hcols[BACK] = cols[BACK];
114 hcols[HIGH] = cols[HIGH];
115 hcols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x006600FF);
116 hcols[TEXT] = hcols[BORD];
117 hcols[HTEXT] = hcols[TEXT];
119 plumbcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x006600FF);
120 execcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAA0000FF);
122 draw(screen, screen->r, cols[BACK], nil, ZP);
123 geom();
124 loop();
127 void
128 hangupnote(void *a, char *msg)
130 if(getpid() != mainpid)
131 noted(NDFLT);
132 if(strcmp(msg, "hangup") == 0 && rcpid != 0){
133 postnote(PNGROUP, rcpid, "hangup");
134 noted(NDFLT);
136 noted(NDFLT);
139 void
140 hostproc(void *arg)
142 Channel *c;
143 int i, n, which;
145 c = arg;
147 i = 0;
148 for(;;){
149 i = 1-i; /* toggle */
150 n = read(rcfd[0], rcbuf[i].data, sizeof rcbuf[i].data);
151 if(n <= 0){
152 if(n < 0)
153 fprint(2, "9term: host read error: %r\n");
154 threadexitsall("host");
156 rcbuf[i].n = n;
157 which = i;
158 send(c, &which);
162 void
163 hoststart(void)
165 hostc = chancreate(sizeof(int), 0);
166 proccreate(hostproc, hostc, 32*1024);
169 void
170 loop(void)
172 Rune r;
173 int i;
174 Alt a[] = {
175 {mc->c, &mc->m, CHANRCV},
176 {kc->c, &r, CHANRCV},
177 {hostc, &i, CHANRCV},
178 {mc->resizec, nil, CHANRCV},
179 {nil, nil, CHANEND},
180 };
182 for(;;) {
183 tcheck();
185 scrdraw();
186 flushimage(display, 1);
187 a[2].op = CHANRCV;
188 if(!scrolling && t.qh > t.org+t.f->nchars)
189 a[2].op = CHANNOP;;
190 switch(alt(a)) {
191 default:
192 fatal("impossible");
193 case 0:
194 t.m = mc->m;
195 mouse();
196 break;
197 case 1:
198 key(r);
199 break;
200 case 2:
201 conswrite(rcbuf[i].data, rcbuf[i].n);
202 break;
203 case 3:
204 doreshape();
205 break;
210 void
211 doreshape(void)
213 if(getwindow(display, Refnone) < 0)
214 fatal("can't reattach to window");
215 draw(screen, screen->r, cols[BACK], nil, ZP);
216 geom();
217 scrdraw();
220 struct winsize ows;
222 void
223 geom(void)
225 struct winsize ws;
226 Point p;
227 Rectangle r;
229 r = screen->r;
230 scrollr = screen->r;
231 scrollr.max.x = r.min.x+Scrollwid;
232 lastsr = Rect(0,0,0,0);
234 r.min.x += Scrollwid+Scrollgap;
236 frclear(t.f, 0);
237 frinit(t.f, r, font, screen, holdon ? hcols : cols);
238 t.f->maxtab = maxtab*stringwidth(font, "0");
239 fill();
240 updatesel();
242 p = stringsize(font, "0");
243 if(p.x == 0 || p.y == 0)
244 return;
246 ws.ws_row = Dy(r)/p.y;
247 ws.ws_col = Dx(r)/p.x;
248 ws.ws_xpixel = Dx(r);
249 ws.ws_ypixel = Dy(r);
250 if(ws.ws_row != ows.ws_row || ws.ws_col != ows.ws_col)
251 if(ioctl(rcfd[0], TIOCSWINSZ, &ws) < 0)
252 fprint(2, "ioctl: %r\n");
255 void
256 drawhold(int holdon)
258 if(holdon)
259 setcursor(mc, &whitearrow);
260 else
261 setcursor(mc, nil);
263 draw(screen, screen->r, cols[BACK], nil, ZP);
264 geom();
265 scrdraw();
268 void
269 wordclick(uint *q0, uint *q1)
271 while(*q1<t.nr && !isspace(t.r[*q1]))
272 (*q1)++;
273 while(*q0>0 && !isspace(t.r[*q0-1]))
274 (*q0)--;
277 int
278 aselect(uint *q0, uint *q1, Image *color)
280 int cancel;
281 uint oldq0, oldq1, newq0, newq1;
283 /* save old selection */
284 oldq0 = t.q0;
285 oldq1 = t.q1;
287 /* sweep out area and record it */
288 t.f->cols[HIGH] = color;
289 t.f->cols[HTEXT] = display->white;
290 mselect();
291 newq0 = t.q0;
292 newq1 = t.q1;
294 cancel = 0;
295 if(t.m.buttons != 0){
296 while(t.m.buttons){
297 readmouse(mc);
298 t.m = mc->m;
300 cancel = 1;
303 /* restore old selection */
304 t.f->cols[HIGH] = cols[HIGH];
305 t.f->cols[HTEXT] = cols[HTEXT];
306 t.q0 = oldq0;
307 t.q1 = oldq1;
308 updatesel();
310 if(cancel)
311 return -1;
313 /* selected a region */
314 if(newq0 < newq1){
315 *q0 = newq0;
316 *q1 = newq1;
317 return 0;
320 /* clicked inside previous selection */
321 /* the "<=" in newq0 <= oldq1 allows us to click the right edge */
322 if(oldq0 <= newq0 && newq0 <= oldq1){
323 *q0 = oldq0;
324 *q1 = oldq1;
325 return 0;
328 /* just a click */
329 *q0 = newq0;
330 *q1 = newq1;
331 return 0;
334 static Rune Lnl[1] = { '\n' };
336 void
337 mouse(void)
339 int but;
340 uint q0, q1;
342 but = t.m.buttons;
344 if(but != 1 && but != 2 && but != 4)
345 return;
347 if (ptinrect(t.m.xy, scrollr)) {
348 scroll(but);
349 if(t.qh<=t.org+t.f->nchars)
350 consread();
351 return;
354 switch(but) {
355 case 1:
356 mselect();
357 break;
358 case 2:
359 if(button2exec){
360 if(aselect(&q0, &q1, execcolor) >= 0){
361 if(q0 == q1)
362 wordclick(&q0, &q1);
363 if(q0 == q1)
364 break;
365 t.q0 = t.q1 = t.nr;
366 updatesel();
367 paste(t.r+q0, q1-q0, 1);
368 if(t.r[q1-1] != '\n')
369 paste(Lnl, 1, 1);
371 break;
373 domenu2(2);
374 break;
375 case 4:
376 if(aselect(&q0, &q1, plumbcolor) >= 0)
377 plumb(q0, q1);
378 break;
382 void
383 mselect(void)
385 int b, x, y;
386 uint q0;
388 b = t.m.buttons;
389 q0 = frcharofpt(t.f, t.m.xy) + t.org;
390 if(t.m.msec-clickmsec<500 && clickq0==q0 && t.q0==t.q1 && b==1){
391 doubleclick(&t.q0, &t.q1);
392 updatesel();
393 /* t.t.i->flush(); */
394 x = t.m.xy.x;
395 y = t.m.xy.y;
396 /* stay here until something interesting happens */
397 do {
398 readmouse(mc);
399 t.m = mc->m;
400 } while(t.m.buttons==b && abs(t.m.xy.x-x)<4 && abs(t.m.xy.y-y)<4);
401 t.m.xy.x = x; /* in case we're calling frselect */
402 t.m.xy.y = y;
403 clickmsec = 0;
406 if(t.m.buttons == b) {
407 frselect(t.f, mc);
408 t.m = mc->m;
409 t.q0 = t.f->p0 + t.org;
410 t.q1 = t.f->p1 + t.org;
411 clickmsec = t.m.msec;
412 clickq0 = t.q0;
414 if((t.m.buttons != b) &&(b&1)){
415 enum{Cancut = 1, Canpaste = 2} state = Cancut | Canpaste;
416 while(t.m.buttons){
417 if(t.m.buttons&2){
418 if(state&Cancut){
419 snarf();
420 cut();
421 state = Canpaste;
423 }else if(t.m.buttons&4){
424 if(state&Canpaste){
425 snarfupdate();
426 if(t.nsnarf){
427 paste(t.snarf, t.nsnarf, 0);
429 state = Cancut|Canpaste;
432 readmouse(mc);
433 t.m = mc->m;
438 Rune newline[] = { '\n', 0 };
440 void
441 domenu2(int but)
443 if(scrolling)
444 menu2str[Scroll] = "noscroll";
445 else
446 menu2str[Scroll] = "scroll";
448 switch(menuhit(but, mc, &menu2, nil)){
449 case -1:
450 break;
451 case Cut:
452 snarf();
453 cut();
454 if(scrolling)
455 show(t.q0);
456 break;
457 case Paste:
458 snarfupdate();
459 paste(t.snarf, t.nsnarf, 0);
460 if(scrolling)
461 show(t.q0);
462 break;
463 case Snarf:
464 snarf();
465 if(scrolling)
466 show(t.q0);
467 break;
468 case Send:
469 snarf();
470 t.q0 = t.q1 = t.nr;
471 updatesel();
472 snarfupdate();
473 paste(t.snarf, t.nsnarf, 1);
474 if(t.nsnarf == 0 || t.snarf[t.nsnarf-1] != '\n')
475 paste(newline, 1, 1);
476 show(t.nr);
477 consread();
478 break;
479 case Scroll:
480 scrolling = !scrolling;
481 if (scrolling) {
482 show(t.nr);
483 consread();
485 break;
486 case Plumb:
487 plumb(t.q0, t.q1);
488 break;
489 default:
490 fatal("bad menu item");
494 void
495 key(Rune r)
497 uint sig;
499 if(r == 0)
500 return;
501 if(r==SCROLLKEY){ /* scroll key */
502 setorigin(line2q(t.f->maxlines*2/3), 1);
503 if(t.qh<=t.org+t.f->nchars)
504 consread();
505 return;
506 }else if(r == BACKSCROLLKEY){
507 setorigin(backnl(t.org, t.f->maxlines*2/3), 1);
508 return;
509 }else if(r == CUT){
510 snarf();
511 cut();
512 if(scrolling)
513 show(t.q0);
514 return;
515 }else if(r == COPY){
516 snarf();
517 if(scrolling)
518 show(t.q0);
519 return;
520 }else if(r == PASTE){
521 snarfupdate();
522 paste(t.snarf, t.nsnarf, 0);
523 if(scrolling)
524 show(t.q0);
525 return;
528 if(rawon && t.q0==t.nr){
529 addraw(&r, 1);
530 consread();
531 return;
534 if(r==ESC || (holdon && r==0x7F)){ /* toggle hold */
535 holdon = !holdon;
536 drawhold(holdon);
537 if(!holdon)
538 consread();
539 if(r == 0x1B)
540 return;
543 snarf();
545 switch(r) {
546 case 0x7F: /* DEL: send interrupt */
547 t.qh = t.q0 = t.q1 = t.nr;
548 show(t.q0);
549 sig = 2; /* SIGINT */
550 if(ioctl(rcfd[0], TIOCSIG, &sig) < 0)
551 fprint(2, "sending interrupt: %r\n");
552 break;
553 case 0x08: /* ^H: erase character */
554 case 0x15: /* ^U: erase line */
555 case 0x17: /* ^W: erase word */
556 if (t.q0 != 0 && t.q0 != t.qh)
557 t.q0 -= bswidth(r);
558 cut();
559 break;
560 default:
561 paste(&r, 1, 1);
562 break;
564 if(scrolling)
565 show(t.q0);
568 int
569 bswidth(Rune c)
571 uint q, eq, stop;
572 Rune r;
573 int skipping;
575 /* there is known to be at least one character to erase */
576 if(c == 0x08) /* ^H: erase character */
577 return 1;
578 q = t.q0;
579 stop = 0;
580 if(q > t.qh)
581 stop = t.qh;
582 skipping = 1;
583 while(q > stop){
584 r = t.r[q-1];
585 if(r == '\n'){ /* eat at most one more character */
586 if(q == t.q0) /* eat the newline */
587 --q;
588 break;
590 if(c == 0x17){
591 eq = isalnum(r);
592 if(eq && skipping) /* found one; stop skipping */
593 skipping = 0;
594 else if(!eq && !skipping)
595 break;
597 --q;
599 return t.q0-q;
602 int
603 consready(void)
605 int i, c;
607 if(holdon)
608 return 0;
610 if(rawon)
611 return t.nraw != 0;
613 /* look to see if there is a complete line */
614 for(i=t.qh; i<t.nr; i++){
615 c = t.r[i];
616 if(c=='\n' || c=='\004')
617 return 1;
619 return 0;
623 void
624 consread(void)
626 char buf[8000], *p;
627 int c, width, n;
629 for(;;) {
630 if(!consready())
631 return;
633 n = sizeof(buf);
634 p = buf;
635 c = 0;
636 while(n >= UTFmax && (t.qh<t.nr || t.nraw > 0)) {
637 if(t.qh == t.nr){
638 width = runetochar(p, &t.raw[0]);
639 t.nraw--;
640 runemove(t.raw, t.raw+1, t.nraw);
641 }else
642 width = runetochar(p, &t.r[t.qh++]);
643 c = *p;
644 p += width;
645 n -= width;
646 if(!rawon && (c == '\n' || c == '\004'))
647 break;
649 /* take out control-d when not doing a zero length write */
650 n = p-buf;
651 if(write(rcfd[1], buf, n) < 0)
652 exits(0);
653 /* mallocstats(); */
657 void
658 conswrite(char *p, int n)
660 int n2, i;
661 Rune buf2[1000], *q;
663 /* convert to runes */
664 i = t.npart;
665 if(i > 0){
666 /* handle partial runes */
667 while(i < UTFmax && n>0) {
668 t.part[i] = *p;
669 i++;
670 p++;
671 n--;
672 if(fullrune(t.part, i)) {
673 t.npart = 0;
674 chartorune(buf2, t.part);
675 runewrite(buf2, 1);
676 break;
679 /* there is a little extra room in a message buf */
682 while(n >= UTFmax || fullrune(p, n)) {
683 n2 = nelem(buf2);
684 q = buf2;
686 while(n2) {
687 if(n < UTFmax && !fullrune(p, n))
688 break;
689 i = chartorune(q, p);
690 p += i;
691 n -= i;
692 n2--;
693 q++;
695 runewrite(buf2, q-buf2);
698 if(n != 0) {
699 assert(n+t.npart < UTFmax);
700 memcpy(t.part+t.npart, p, n);
701 t.npart += n;
704 if(scrolling)
705 show(t.qh);
708 void
709 runewrite(Rune *r, int n)
711 uint m;
712 int i;
713 uint initial;
714 uint q0, q1;
715 uint p0, p1;
716 Rune *p, *q;
718 n = label(r, n);
719 if(n == 0)
720 return;
722 /* get rid of backspaces */
723 initial = 0;
724 p = q = r;
725 for(i=0; i<n; i++) {
726 if(*p == '\b') {
727 if(q == r)
728 initial++;
729 else
730 --q;
731 } else if(*p)
732 *q++ = *p;
733 p++;
735 n = q-r;
737 if(initial){
738 /* write turned into a delete */
740 if(initial > t.qh)
741 initial = t.qh;
742 q0 = t.qh-initial;
743 q1 = t.qh;
745 runemove(t.r+q0, t.r+q1, t.nr-q1);
746 t.nr -= initial;
747 t.qh -= initial;
748 if(t.q0 > q1)
749 t.q0 -= initial;
750 else if(t.q0 > q0)
751 t.q0 = q0;
752 if(t.q1 > q1)
753 t.q1 -= initial;
754 else if(t.q1 > q0)
755 t.q1 = q0;
756 if(t.org > q1)
757 t.org -= initial;
758 else if(q0 < t.org+t.f->nchars){
759 if(t.org < q0)
760 p0 = q0 - t.org;
761 else {
762 t.org = q0;
763 p0 = 0;
765 p1 = q1 - t.org;
766 if(p1 > t.f->nchars)
767 p1 = t.f->nchars;
768 frdelete(t.f, p0, p1);
769 fill();
771 updatesel();
774 if(t.nr>HiWater && t.qh>=t.org){
775 m = HiWater-LoWater;
776 if(m > t.org);
777 m = t.org;
778 t.org -= m;
779 t.qh -= m;
780 if(t.q0 > m)
781 t.q0 -= m;
782 else
783 t.q0 = 0;
784 if(t.q1 > m)
785 t.q1 -= m;
786 else
787 t.q1 = 0;
788 t.nr -= m;
789 runemove(t.r, t.r+m, t.nr);
791 t.r = runerealloc(t.r, t.nr+n);
792 runemove(t.r+t.qh+n, t.r+t.qh, t.nr-t.qh);
793 runemove(t.r+t.qh, r, n);
794 t.nr += n;
795 if(t.qh < t.org)
796 t.org += n;
797 else if(t.qh <= t.f->nchars+t.org)
798 frinsert(t.f, r, r+n, t.qh-t.org);
799 if (t.qh <= t.q0)
800 t.q0 += n;
801 if (t.qh <= t.q1)
802 t.q1 += n;
803 t.qh += n;
804 updatesel();
808 void
809 cut(void)
811 uint n, p0, p1;
812 uint q0, q1;
814 q0 = t.q0;
815 q1 = t.q1;
817 if (q0 < t.org && q1 >= t.org)
818 show(q0);
820 n = q1-q0;
821 if(n == 0)
822 return;
823 runemove(t.r+q0, t.r+q1, t.nr-q1);
824 t.nr -= n;
825 t.q0 = t.q1 = q0;
826 if(q1 < t.qh)
827 t.qh -= n;
828 else if(q0 < t.qh)
829 t.qh = q0;
830 if(q1 < t.org)
831 t.org -= n;
832 else if(q0 < t.org+t.f->nchars){
833 assert(q0 >= t.org);
834 p0 = q0 - t.org;
835 p1 = q1 - t.org;
836 if(p1 > t.f->nchars)
837 p1 = t.f->nchars;
838 frdelete(t.f, p0, p1);
839 fill();
841 updatesel();
844 void
845 snarfupdate(void)
847 char *pp;
848 int n, i;
849 Rune *p;
851 pp = getsnarf();
852 if(pp == nil)
853 return;
854 n = strlen(pp);
855 if(n <= 0) {
856 /*t.nsnarf = 0;*/
857 return;
859 t.snarf = runerealloc(t.snarf, n);
860 for(i=0,p=t.snarf; i<n; p++)
861 i += chartorune(p, pp+i);
862 t.nsnarf = p-t.snarf;
866 char sbuf[SnarfSize];
867 void
868 snarf(void)
870 char *p;
871 int i, n;
872 Rune *rp;
874 if(t.q1 == t.q0)
875 return;
876 n = t.q1-t.q0;
877 t.snarf = runerealloc(t.snarf, n);
878 for(i=0,p=sbuf,rp=t.snarf; i<n && p < sbuf+SnarfSize-UTFmax; i++){
879 *rp++ = *(t.r+t.q0+i);
880 p += runetochar(p, t.r+t.q0+i);
882 t.nsnarf = rp-t.snarf;
883 *p = '\0';
884 putsnarf(sbuf);
887 void
888 paste(Rune *r, int n, int advance)
890 Rune *rbuf;
891 uint m;
892 uint q0;
894 if(rawon && t.q0==t.nr){
895 addraw(r, n);
896 return;
899 cut();
900 if(n == 0)
901 return;
902 if(t.nr>HiWater && t.q0>=t.org && t.q0>=t.qh){
903 m = HiWater-LoWater;
904 if(m > t.org)
905 m = t.org;
906 if(m > t.qh);
907 m = t.qh;
908 t.org -= m;
909 t.qh -= m;
910 t.q0 -= m;
911 t.q1 -= m;
912 t.nr -= m;
913 runemove(t.r, t.r+m, t.nr);
916 /*
917 * if this is a button2 execute then we might have been passed
918 * runes inside the buffer. must save them before realloc.
919 */
920 rbuf = nil;
921 if(t.r <= r && r < t.r+n){
922 rbuf = runemalloc(n);
923 runemove(rbuf, r, n);
924 r = rbuf;
926 t.r = runerealloc(t.r, t.nr+n);
927 q0 = t.q0;
928 runemove(t.r+q0+n, t.r+q0, t.nr-q0);
929 runemove(t.r+q0, r, n);
930 t.nr += n;
931 if(q0 < t.qh)
932 t.qh += n;
933 else
934 consread();
935 if(q0 < t.org)
936 t.org += n;
937 else if(q0 <= t.f->nchars+t.org)
938 frinsert(t.f, r, r+n, q0-t.org);
939 if(advance)
940 t.q0 += n;
941 t.q1 += n;
942 updatesel();
943 free(rbuf);
946 void
947 fill(void)
949 if (t.f->nlines >= t.f->maxlines)
950 return;
951 frinsert(t.f, t.r + t.org + t.f->nchars, t.r + t.nr, t.f->nchars);
954 void
955 updatesel(void)
957 Frame *f;
958 uint n;
960 f = t.f;
961 if(t.org+f->p0 == t.q0 && t.org+f->p1 == t.q1)
962 return;
964 n = t.f->nchars;
966 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
967 if (t.q0 >= t.org)
968 f->p0 = t.q0-t.org;
969 else
970 f->p0 = 0;
971 if(f->p0 > n)
972 f->p0 = n;
973 if (t.q1 >= t.org)
974 f->p1 = t.q1-t.org;
975 else
976 f->p1 = 0;
977 if(f->p1 > n)
978 f->p1 = n;
979 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
981 /*
982 if(t.qh<=t.org+t.f.nchars && t.cwqueue != 0)
983 t.cwqueue->wakeup <-= 0;
984 */
986 tcheck();
989 void
990 show(uint q0)
992 int nl;
993 uint q, oq;
995 if(cansee(q0))
996 return;
998 if (q0<t.org)
999 nl = t.f->maxlines/5;
1000 else
1001 nl = 4*t.f->maxlines/5;
1002 q = backnl(q0, nl);
1003 /* avoid going in the wrong direction */
1004 if (q0>t.org && q<t.org)
1005 q = t.org;
1006 setorigin(q, 0);
1007 /* keep trying until q0 is on the screen */
1008 while(!cansee(q0)) {
1009 assert(q0 >= t.org);
1010 oq = q;
1011 q = line2q(t.f->maxlines-nl);
1012 assert(q > oq);
1013 setorigin(q, 1);
1017 int
1018 cansee(uint q0)
1020 uint qe;
1022 qe = t.org+t.f->nchars;
1024 if(q0>=t.org && q0 < qe)
1025 return 1;
1026 if (q0 != qe)
1027 return 0;
1028 if (t.f->nlines < t.f->maxlines)
1029 return 1;
1030 if (q0 > 0 && t.r[t.nr-1] == '\n')
1031 return 0;
1032 return 1;
1036 void
1037 setorigin(uint org, int exact)
1039 int i, a;
1040 uint n;
1042 if(org>0 && !exact){
1043 /* try and start after a newline */
1044 /* don't try harder than 256 chars */
1045 for(i=0; i<256 && org<t.nr; i++){
1046 if(t.r[org-1] == '\n')
1047 break;
1048 org++;
1051 a = org-t.org;
1053 if(a>=0 && a<t.f->nchars)
1054 frdelete(t.f, 0, a);
1055 else if(a<0 && -a<100*t.f->maxlines){
1056 n = t.org - org;
1057 frinsert(t.f, t.r+org, t.r+org+n, 0);
1058 }else
1059 frdelete(t.f, 0, t.f->nchars);
1060 t.org = org;
1061 fill();
1062 updatesel();
1066 uint
1067 line2q(uint n)
1069 Frame *f;
1071 f = t.f;
1072 return frcharofpt(f, Pt(f->r.min.x, f->r.min.y + n*font->height))+t.org;
1075 uint
1076 backnl(uint p, uint n)
1078 int i, j;
1080 for (i = n;; i--) {
1081 /* at 256 chars, call it a line anyway */
1082 for(j=256; --j>0 && p>0; p--)
1083 if(t.r[p-1]=='\n')
1084 break;
1085 if (p == 0 || i == 0)
1086 return p;
1087 p--;
1089 return 0; /* alef bug */
1092 void
1093 addraw(Rune *r, int nr)
1095 t.raw = runerealloc(t.raw, t.nraw+nr);
1096 runemove(t.raw+t.nraw, r, nr);
1097 t.nraw += nr;
1099 if(t.crqueue != nil)
1100 t.crqueue->wakeup <-= 0;
1105 Rune left1[] = { '{', '[', '(', '<', 0xab, 0 };
1106 Rune right1[] = { '}', ']', ')', '>', 0xbb, 0 };
1107 Rune left2[] = { '\n', 0 };
1108 Rune left3[] = { '\'', '"', '`', 0 };
1110 Rune *left[] = {
1111 left1,
1112 left2,
1113 left3,
1117 Rune *right[] = {
1118 right1,
1119 left2,
1120 left3,
1124 void
1125 doubleclick(uint *q0, uint *q1)
1127 int c, i;
1128 Rune *r, *l, *p;
1129 uint q;
1131 for(i=0; left[i]!=0; i++){
1132 q = *q0;
1133 l = left[i];
1134 r = right[i];
1135 /* try matching character to left, looking right */
1136 if(q == 0)
1137 c = '\n';
1138 else
1139 c = t.r[q-1];
1140 p = strrune(l, c);
1141 if(p != 0){
1142 if(clickmatch(c, r[p-l], 1, &q))
1143 *q1 = q-(c!='\n');
1144 return;
1146 /* try matching character to right, looking left */
1147 if(q == t.nr)
1148 c = '\n';
1149 else
1150 c = t.r[q];
1151 p = strrune(r, c);
1152 if(p != 0){
1153 if(clickmatch(c, l[p-r], -1, &q)){
1154 *q1 = *q0+(*q0<t.nr && c=='\n');
1155 *q0 = q;
1156 if(c!='\n' || q!=0 || t.r[0]=='\n')
1157 (*q0)++;
1159 return;
1162 /* try filling out word to right */
1163 while(*q1<t.nr && isalnum(t.r[*q1]))
1164 (*q1)++;
1165 /* try filling out word to left */
1166 while(*q0>0 && isalnum(t.r[*q0-1]))
1167 (*q0)--;
1170 int
1171 clickmatch(int cl, int cr, int dir, uint *q)
1173 Rune c;
1174 int nest;
1176 nest = 1;
1177 for(;;){
1178 if(dir > 0){
1179 if(*q == t.nr)
1180 break;
1181 c = t.r[*q];
1182 (*q)++;
1183 }else{
1184 if(*q == 0)
1185 break;
1186 (*q)--;
1187 c = t.r[*q];
1189 if(c == cr){
1190 if(--nest==0)
1191 return 1;
1192 }else if(c == cl)
1193 nest++;
1195 return cl=='\n' && nest==1;
1198 void
1199 rcstart(int fd[2], int argc, char **argv)
1201 int pid;
1202 char *xargv[3];
1203 char slave[256];
1204 int sfd;
1206 if(argc == 0){
1207 argc = 2;
1208 argv = xargv;
1209 argv[0] = getenv("SHELL");
1210 if(argv[0] == 0)
1211 argv[0] = "rc";
1212 argv[1] = "-i";
1213 argv[2] = 0;
1216 * fd0 is slave (tty), fd1 is master (pty)
1218 fd[0] = fd[1] = -1;
1219 if(getpts(fd, slave) < 0)
1220 fprint(2, "getpts: %r\n");
1222 switch(pid = fork()) {
1223 case 0:
1224 putenv("TERM", "9term");
1225 close(fd[1]);
1226 setsid();
1227 // tcsetpgrp(0, pid);
1228 sfd = open(slave, ORDWR);
1229 if(sfd < 0)
1230 fprint(2, "open %s: %r\n", slave);
1231 if(ioctl(sfd, TIOCSCTTY, 0) < 0)
1232 fprint(2, "ioctl TIOCSCTTY: %r\n");
1233 // ioctl(sfd, I_PUSH, "ptem");
1234 // ioctl(sfd, I_PUSH, "ldterm");
1235 dup(sfd, 0);
1236 dup(sfd, 1);
1237 dup(sfd, 2);
1238 system("stty tabs -onlcr -echo");
1239 execvp(argv[0], argv);
1240 fprint(2, "exec %s failed: %r\n", argv[0]);
1241 _exits("oops");
1242 break;
1243 case -1:
1244 fatal("proc failed: %r");
1245 break;
1247 close(fd[0]);
1248 fd[0] = fd[1];
1250 rcpid = pid;
1253 void
1254 tcheck(void)
1256 Frame *f;
1258 f = t.f;
1260 assert(t.q0 <= t.q1 && t.q1 <= t.nr);
1261 assert(t.org <= t.nr && t.qh <= t.nr);
1262 assert(f->p0 <= f->p1 && f->p1 <= f->nchars);
1263 assert(t.org + f->nchars <= t.nr);
1264 assert(t.org+f->nchars==t.nr || (f->nlines >= f->maxlines));
1267 Rune*
1268 strrune(Rune *s, Rune c)
1270 Rune c1;
1272 if(c == 0) {
1273 while(*s++)
1275 return s-1;
1278 while(c1 = *s++)
1279 if(c1 == c)
1280 return s-1;
1281 return 0;
1284 void
1285 scrdraw(void)
1287 Rectangle r, r1, r2;
1288 static Image *scrx;
1290 r = scrollr;
1291 r.min.x += 1; /* border between margin and bar */
1292 r1 = r;
1293 if(scrx==0 || scrx->r.max.y < r.max.y){
1294 if(scrx)
1295 freeimage(scrx);
1296 scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow);
1297 if(scrx == 0)
1298 fatal("scroll balloc");
1300 r1.min.x = 0;
1301 r1.max.x = Dx(r);
1302 r2 = scrpos(r1, t.org, t.org+t.f->nchars, t.nr);
1303 if(!eqrect(r2, lastsr)){
1304 lastsr = r2;
1305 draw(scrx, r1, cols[BORD], nil, ZP);
1306 draw(scrx, r2, cols[BACK], nil, r2.min);
1307 // r2 = r1;
1308 // r2.min.x = r2.max.x-1;
1309 // draw(scrx, r2, cols[BORD], nil, ZP);
1310 draw(screen, r, scrx, nil, r1.min);
1314 Rectangle
1315 scrpos(Rectangle r, ulong p0, ulong p1, ulong tot)
1317 long h;
1318 Rectangle q;
1320 q = insetrect(r, 1);
1321 h = q.max.y-q.min.y;
1322 if(tot == 0)
1323 return q;
1324 if(tot > 1024L*1024L)
1325 tot >>= 10, p0 >>= 10, p1 >>= 10;
1326 if(p0 > 0)
1327 q.min.y += h*p0/tot;
1328 if(p1 < tot)
1329 q.max.y -= h*(tot-p1)/tot;
1330 if(q.max.y < q.min.y+2){
1331 if(q.min.y+2 <= r.max.y)
1332 q.max.y = q.min.y+2;
1333 else
1334 q.min.y = q.max.y-2;
1336 return q;
1339 void
1340 scroll(int but)
1342 uint p0, oldp0;
1343 Rectangle s;
1344 int x, y, my, h, first, exact;
1346 s = insetrect(scrollr, 1);
1347 h = s.max.y-s.min.y;
1348 x = (s.min.x+s.max.x)/2;
1349 oldp0 = ~0;
1350 first = 1;
1351 do{
1352 if(t.m.xy.x<s.min.x || s.max.x<=t.m.xy.x){
1353 readmouse(mc);
1354 t.m = mc->m;
1355 }else{
1356 my = t.m.xy.y;
1357 if(my < s.min.y)
1358 my = s.min.y;
1359 if(my >= s.max.y)
1360 my = s.max.y;
1361 // if(!eqpt(t.m.xy, Pt(x, my)))
1362 // cursorset(Pt(x, my));
1363 exact = 1;
1364 if(but == 2){
1365 y = my;
1366 if(y > s.max.y-2)
1367 y = s.max.y-2;
1368 if(t.nr > 1024*1024)
1369 p0 = ((t.nr>>10)*(y-s.min.y)/h)<<10;
1370 else
1371 p0 = t.nr*(y-s.min.y)/h;
1372 exact = 0;
1373 } else if(but == 1)
1374 p0 = backnl(t.org, (my-s.min.y)/font->height);
1375 else
1376 p0 = t.org+frcharofpt(t.f, Pt(s.max.x, my));
1378 if(oldp0 != p0)
1379 setorigin(p0, exact);
1380 oldp0 = p0;
1381 scrdraw();
1382 readmouse(mc);
1383 t.m = mc->m;
1385 }while(t.m.buttons & (1<<(but-1)));
1388 void
1389 plumbstart(void)
1391 if((plumbfd = plumbopen("send", OWRITE)) < 0)
1392 fatal("plumbopen");
1395 void
1396 plumb(uint q0, uint q1)
1398 Plumbmsg *pm;
1399 char *p;
1400 int i, p0, n;
1401 char cbuf[100];
1402 char *w;
1404 if(getchildwd(rcpid, childwdir, sizeof childwdir) == 0)
1405 w = childwdir;
1406 else
1407 w = wdir;
1408 pm = malloc(sizeof(Plumbmsg));
1409 pm->src = strdup("9term");
1410 pm->dst = 0;
1411 pm->wdir = strdup(w);
1412 pm->type = strdup("text");
1413 if(q1 > q0)
1414 pm->attr = nil;
1415 else{
1416 p0 = q0;
1417 wordclick(&q0, &q1);
1418 sprint(cbuf, "click=%d", p0-q0);
1419 pm->attr = plumbunpackattr(cbuf);
1421 if(q0==q1){
1422 plumbfree(pm);
1423 return;
1425 pm->data = malloc(SnarfSize);
1426 n = q1 - q0;
1427 for(i=0,p=pm->data; i<n && p < pm->data + SnarfSize-UTFmax; i++)
1428 p += runetochar(p, t.r+q0+i);
1429 *p = '\0';
1430 pm->ndata = strlen(pm->data);
1431 plumbsend(plumbfd, pm);
1432 plumbfree(pm);
1436 * Process in-band messages about window title changes.
1437 * The messages are of the form:
1439 * \033];xxx\007
1441 * where xxx is the new directory. This format was chosen
1442 * because it changes the label on xterm windows.
1444 int
1445 label(Rune *sr, int n)
1447 Rune *sl, *el, *er, *r;
1449 er = sr+n;
1450 for(r=er-1; r>=sr; r--)
1451 if(*r == '\007')
1452 break;
1453 if(r < sr)
1454 return n;
1456 el = r+1;
1457 if(el-sr > sizeof wdir)
1458 sr = el - sizeof wdir;
1459 for(sl=el-3; sl>=sr; sl--)
1460 if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
1461 break;
1462 if(sl < sr)
1463 return n;
1465 snprint(wdir, sizeof wdir, "%.*S", (el-1)-(sl+3), sl+3);
1466 drawsetlabel(display, wdir);
1468 runemove(sl, el, er-el);
1469 n -= (el-sl);
1470 return n;