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 label(Rune*, int);
20 char wdir[1024];
21 char childwdir[1024];
22 void hangupnote(void*, char*);
24 char *menu2str[] = {
25 "cut",
26 "paste",
27 "snarf",
28 "send",
29 "scroll",
30 "plumb",
31 0
32 };
34 Image* cols[NCOL];
35 Image* hcols[NCOL];
36 Image *plumbcolor;
38 Menu menu2 =
39 {
40 menu2str
41 };
43 Text t;
45 Cursor whitearrow = {
46 {0, 0},
47 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
48 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC,
49 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
50 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
51 {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C,
52 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C,
53 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C,
54 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
55 };
57 void
58 threadmain(int argc, char *argv[])
59 {
60 char *p;
62 rfork(RFNOTEG);
63 mainpid = getpid();
64 ARGBEGIN{
65 case 'T':
66 p = ARGF();
67 if(p == 0)
68 break;
69 maxtab = strtoul(p, 0, 0);
70 break;
71 case 's':
72 scrolling++;
73 break;
74 }ARGEND
76 p = getenv("tabstop");
77 if(p == 0)
78 p = getenv("TABSTOP");
79 if(p != 0 && maxtab <= 0)
80 maxtab = strtoul(p, 0, 0);
81 if(maxtab <= 0)
82 maxtab = 8;
84 initdraw(nil, nil, "9term");
85 notify(hangupnote);
87 mc = initmouse(nil, screen);
88 kc = initkeyboard(nil);
89 rcstart(rcfd);
90 hoststart();
91 plumbstart();
93 t.f = mallocz(sizeof(Frame), 1);
95 cols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
96 cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkyellow);
97 cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DYellowgreen);
98 cols[TEXT] = display->black;
99 cols[HTEXT] = display->black;
101 hcols[BACK] = cols[BACK];
102 hcols[HIGH] = cols[HIGH];
103 hcols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMedblue);
104 hcols[TEXT] = hcols[BORD];
105 hcols[HTEXT] = hcols[TEXT];
107 plumbcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x006600FF);
109 draw(screen, screen->r, cols[BACK], nil, ZP);
110 geom();
112 loop();
115 void
116 hangupnote(void *a, char *msg)
118 if(getpid() != mainpid)
119 noted(NDFLT);
120 if(strcmp(msg, "hangup") == 0 && rcpid != 0){
121 postnote(PNPROC, rcpid, "hangup");
122 noted(NDFLT);
124 noted(NDFLT);
127 void
128 hostproc(void *arg)
130 Channel *c;
131 int i, n, which;
133 c = arg;
135 i = 0;
136 for(;;){
137 i = 1-i; /* toggle */
138 n = read(rcfd[0], rcbuf[i].data, sizeof rcbuf[i].data);
139 if(n <= 0){
140 if(n < 0)
141 fprint(2, "9term: host read error: %r\n");
142 threadexitsall("host");
144 rcbuf[i].n = n;
145 which = i;
146 send(c, &which);
150 void
151 hoststart(void)
153 hostc = chancreate(sizeof(int), 0);
154 proccreate(hostproc, hostc, 1024);
157 void
158 loop(void)
160 Rune r;
161 int i;
162 Alt a[] = {
163 {mc->c, &mc->m, CHANRCV},
164 {kc->c, &r, CHANRCV},
165 {hostc, &i, CHANRCV},
166 {mc->resizec, nil, CHANRCV},
167 {nil, nil, CHANEND},
168 };
170 for(;;) {
171 tcheck();
173 scrdraw();
174 flushimage(display, 1);
175 a[2].op = CHANRCV;
176 if(!scrolling && t.qh > t.org+t.f->nchars)
177 a[2].op = CHANNOP;;
179 switch(alt(a)) {
180 default:
181 fatal("impossible");
182 case 0:
183 t.m = mc->m;
184 mouse();
185 break;
186 case 1:
187 key(r);
188 break;
189 case 2:
190 conswrite(rcbuf[i].data, rcbuf[i].n);
191 break;
192 case 3:
193 doreshape();
194 break;
199 void
200 doreshape(void)
202 if(getwindow(display, Refnone) < 0)
203 fatal("can't reattach to window");
204 draw(screen, screen->r, cols[BACK], nil, ZP);
205 geom();
206 scrdraw();
209 void
210 geom(void)
212 Rectangle r;
214 r = screen->r;
215 scrollr = screen->r;
216 scrollr.max.x = r.min.x+Scrollwid;
217 lastsr = Rect(0,0,0,0);
219 r.min.x += Scrollwid+Scrollgap;
221 frclear(t.f, 0);
222 frinit(t.f, r, font, screen, holdon ? hcols : cols);
223 t.f->maxtab = maxtab*stringwidth(font, "0");
224 fill();
225 updatesel();
228 void
229 drawhold(int holdon)
231 if(holdon)
232 setcursor(mc, &whitearrow);
233 else
234 setcursor(mc, nil);
236 draw(screen, screen->r, cols[BACK], nil, ZP);
237 geom();
238 scrdraw();
242 void
243 mouse(void)
245 int cancel, but;
246 uint oldq0, oldq1, newq0, newq1;
248 but = t.m.buttons;
250 if(but != 1 && but != 2 && but != 4)
251 return;
253 if (ptinrect(t.m.xy, scrollr)) {
254 scroll(but);
255 if(t.qh<=t.org+t.f->nchars)
256 consread();;
257 return;
260 switch(but) {
261 case 1:
262 mselect();
263 break;
264 case 2:
265 domenu2(2);
266 break;
267 case 4:
268 /* save old selection */
269 oldq0 = t.q0;
270 oldq1 = t.q1;
272 /* sweep out plumb area and record it */
273 t.f->cols[HIGH] = plumbcolor;
274 t.f->cols[HTEXT] = display->white;
275 mselect();
276 newq0 = t.q0;
277 newq1 = t.q1;
279 cancel = 0;
280 if(t.m.buttons != 0){
281 while(t.m.buttons){
282 readmouse(mc);
283 t.m = mc->m;
285 cancel = 1;
288 /* restore old selection */
289 t.f->cols[HIGH] = cols[HIGH];
290 t.f->cols[HTEXT] = cols[HTEXT];
291 t.q0 = oldq0;
292 t.q1 = oldq1;
293 updatesel();
295 if(cancel)
296 break;
298 /* process plumb area */
299 if(newq0 < newq1)
300 plumb(newq0, newq1);
301 else if(oldq0 <= newq0 && newq0 < oldq1)
302 plumb(oldq0, oldq1);
303 else
304 plumb(newq0, newq0);
305 break;
309 void
310 mselect(void)
312 int b, x, y;
313 uint q0;
315 b = t.m.buttons;
316 q0 = frcharofpt(t.f, t.m.xy) + t.org;
317 if(t.m.msec-clickmsec<500 && clickq0 == q0 && t.q0==t.q1 && b==1){
318 doubleclick(&t.q0, &t.q1);
319 updatesel();
320 /* t.t.i->flush(); */
321 x = t.m.xy.x;
322 y = t.m.xy.y;
323 /* stay here until something interesting happens */
324 do {
325 readmouse(mc);
326 t.m = mc->m;
327 } while(t.m.buttons==b && abs(t.m.xy.x-x)<4 && abs(t.m.xy.y-y)<4);
328 t.m.xy.x = x; /* in case we're calling frselect */
329 t.m.xy.y = y;
330 clickmsec = 0;
333 if(t.m.buttons == b) {
334 frselect(t.f, mc);
335 t.m = mc->m;
336 t.q0 = t.f->p0 + t.org;
337 t.q1 = t.f->p1 + t.org;
338 clickmsec = t.m.msec;
339 clickq0 = t.q0;
341 if((t.m.buttons != b) && (b&1)){
342 enum {Cancut = 1, Canpaste = 2} state = Cancut | Canpaste;
343 while(t.m.buttons){
344 if(t.m.buttons&2) {
345 if (state&Cancut) {
346 snarf();
347 cut();
348 state = Canpaste;
350 } else if (t.m.buttons&4) {
351 if (state&Canpaste) {
352 snarfupdate();
353 if (t.nsnarf) {
354 paste(t.snarf, t.nsnarf, 0);
356 state = Cancut|Canpaste;
359 readmouse(mc);
360 t.m = mc->m;
365 Rune newline[] = { '\n', 0 };
367 void
368 domenu2(int but)
370 if(scrolling)
371 menu2str[Scroll] = "noscroll";
372 else
373 menu2str[Scroll] = "scroll";
375 switch(menuhit(but, mc, &menu2, nil)){
376 case -1:
377 break;
378 case Cut:
379 snarf();
380 cut();
381 if(scrolling)
382 show(t.q0);
383 break;
384 case Paste:
385 snarfupdate();
386 paste(t.snarf, t.nsnarf, 0);
387 if(scrolling)
388 show(t.q0);
389 break;
390 case Snarf:
391 snarf();
392 if(scrolling)
393 show(t.q0);
394 break;
395 case Send:
396 snarf();
397 t.q0 = t.q1 = t.nr;
398 updatesel();
399 snarfupdate();
400 paste(t.snarf, t.nsnarf, 1);
401 if(t.nsnarf == 0 || t.snarf[t.nsnarf-1] != '\n')
402 paste(newline, 1, 1);
403 show(t.nr);
404 consread();
405 break;
406 case Scroll:
407 scrolling = !scrolling;
408 if (scrolling) {
409 show(t.nr);
410 consread();
412 break;
413 case Plumb:
414 plumb(t.q0, t.q1);
415 break;
416 default:
417 fatal("bad menu item");
421 void
422 key(Rune r)
424 char buf[1];
426 if(r == 0)
427 return;
428 if(r==SCROLLKEY){ /* scroll key */
429 setorigin(line2q(t.f->maxlines*2/3), 1);
430 if(t.qh<=t.org+t.f->nchars)
431 consread();
432 return;
433 }else if(r == BACKSCROLLKEY){
434 setorigin(backnl(t.org, t.f->maxlines*2/3), 1);
435 return;
436 }else if(r == CUT){
437 snarf();
438 cut();
439 if(scrolling)
440 show(t.q0);
441 return;
442 }else if(r == COPY){
443 snarf();
444 if(scrolling)
445 show(t.q0);
446 return;
447 }else if(r == PASTE){
448 snarfupdate();
449 paste(t.snarf, t.nsnarf, 0);
450 if(scrolling)
451 show(t.q0);
452 return;
455 if(rawon && t.q0==t.nr){
456 addraw(&r, 1);
457 return;
460 if(r==ESC || (holdon && r==0x7F)){ /* toggle hold */
461 holdon = !holdon;
462 drawhold(holdon);
463 if(!holdon)
464 consread();
465 if(r == 0x1B)
466 return;
469 snarf();
471 switch(r) {
472 case 0x7F: /* DEL: send interrupt */
473 t.qh = t.q0 = t.q1 = t.nr;
474 show(t.q0);
475 buf[0] = 0x7f;
476 if(write(rcfd[1], buf, 1) < 0)
477 exits(0);
478 /* get rc to print prompt */
479 // r = '\n';
480 // paste(&r, 1, 1);
481 break;
482 case 0x08: /* ^H: erase character */
483 case 0x15: /* ^U: erase line */
484 case 0x17: /* ^W: erase word */
485 if (t.q0 != 0 && t.q0 != t.qh)
486 t.q0 -= bswidth(r);
487 cut();
488 break;
489 default:
490 paste(&r, 1, 1);
491 break;
493 if(scrolling)
494 show(t.q0);
497 int
498 bswidth(Rune c)
500 uint q, eq, stop;
501 Rune r;
502 int skipping;
504 /* there is known to be at least one character to erase */
505 if(c == 0x08) /* ^H: erase character */
506 return 1;
507 q = t.q0;
508 stop = 0;
509 if(q > t.qh)
510 stop = t.qh;
511 skipping = 1;
512 while(q > stop){
513 r = t.r[q-1];
514 if(r == '\n'){ /* eat at most one more character */
515 if(q == t.q0) /* eat the newline */
516 --q;
517 break;
519 if(c == 0x17){
520 eq = isalnum(r);
521 if(eq && skipping) /* found one; stop skipping */
522 skipping = 0;
523 else if(!eq && !skipping)
524 break;
526 --q;
528 return t.q0-q;
531 int
532 consready(void)
534 int i, c;
536 if(holdon)
537 return 0;
539 if(rawon)
540 return t.nraw != 0;
542 /* look to see if there is a complete line */
543 for(i=t.qh; i<t.nr; i++){
544 c = t.r[i];
545 if(c=='\n' || c=='\004')
546 return 1;
548 return 0;
552 void
553 consread(void)
555 char buf[8000], *p;
556 int c, width, n;
558 for(;;) {
559 if(!consready())
560 return;
562 n = sizeof(buf);
563 p = buf;
564 while(n >= UTFmax && (t.qh<t.nr || t.nraw > 0)) {
565 if(t.qh == t.nr){
566 width = runetochar(p, &t.raw[0]);
567 t.nraw--;
568 runemove(t.raw, t.raw+1, t.nraw);
569 }else
570 width = runetochar(p, &t.r[t.qh++]);
571 c = *p;
572 p += width;
573 n -= width;
574 if(!rawon && (c == '\n' || c == '\004')) {
575 if(c == '\004')
576 p--;
577 break;
580 if(n < UTFmax && t.qh<t.nr && t.r[t.qh]=='\004')
581 t.qh++;
582 /* put in control-d when doing a zero length write */
583 if(p == buf)
584 *p++ = '\004';
585 if(write(rcfd[1], buf, p-buf) < 0)
586 exits(0);
587 /* mallocstats(); */
591 void
592 conswrite(char *p, int n)
594 int n2, i;
595 Rune buf2[1000], *q;
597 /* convert to runes */
598 i = t.npart;
599 if(i > 0){
600 /* handle partial runes */
601 while(i < UTFmax && n>0) {
602 t.part[i] = *p;
603 i++;
604 p++;
605 n--;
606 if(fullrune(t.part, i)) {
607 t.npart = 0;
608 chartorune(buf2, t.part);
609 runewrite(buf2, 1);
610 break;
613 /* there is a little extra room in a message buf */
616 while(n >= UTFmax || fullrune(p, n)) {
617 n2 = nelem(buf2);
618 q = buf2;
620 while(n2) {
621 if(n < UTFmax && !fullrune(p, n))
622 break;
623 i = chartorune(q, p);
624 p += i;
625 n -= i;
626 n2--;
627 q++;
630 runewrite(buf2, q-buf2);
633 if(n != 0) {
634 assert(n+t.npart < UTFmax);
635 memcpy(t.part+t.npart, p, n);
636 t.npart += n;
639 if(scrolling)
640 show(t.qh);
643 void
644 runewrite(Rune *r, int n)
646 uint m;
647 int i;
648 uint initial;
649 uint q0, q1;
650 uint p0, p1;
651 Rune *p, *q;
653 n = label(r, n);
654 if(n == 0)
655 return;
657 /* get ride of backspaces */
658 initial = 0;
659 p = q = r;
660 for(i=0; i<n; i++) {
661 if(*p == '\b') {
662 if(q == r)
663 initial++;
664 else
665 --q;
666 } else if(*p)
667 *q++ = *p;
668 p++;
670 n = q-r;
672 if(initial){
673 /* write turned into a delete */
675 if(initial > t.qh)
676 initial = t.qh;
677 q0 = t.qh-initial;
678 q1 = t.qh;
680 runemove(t.r+q0, t.r+q1, t.nr-q1);
681 t.nr -= initial;
682 t.qh -= initial;
683 if(t.q0 > q1)
684 t.q0 -= initial;
685 else if(t.q0 > q0)
686 t.q0 = q0;
687 if(t.q1 > q1)
688 t.q1 -= initial;
689 else if(t.q1 > q0)
690 t.q1 = q0;
691 if(t.org > q1)
692 t.org -= initial;
693 else if(q0 < t.org+t.f->nchars){
694 if(t.org < q0)
695 p0 = q0 - t.org;
696 else {
697 t.org = q0;
698 p0 = 0;
700 p1 = q1 - t.org;
701 if(p1 > t.f->nchars)
702 p1 = t.f->nchars;
703 frdelete(t.f, p0, p1);
704 fill();
706 updatesel();
707 return;
710 if(t.nr>HiWater && t.qh>=t.org){
711 m = HiWater-LoWater;
712 if(m > t.org);
713 m = t.org;
714 t.org -= m;
715 t.qh -= m;
716 if(t.q0 > m)
717 t.q0 -= m;
718 else
719 t.q0 = 0;
720 if(t.q1 > m)
721 t.q1 -= m;
722 else
723 t.q1 = 0;
724 t.nr -= m;
725 runemove(t.r, t.r+m, t.nr);
727 t.r = runerealloc(t.r, t.nr+n);
728 runemove(t.r+t.qh+n, t.r+t.qh, t.nr-t.qh);
729 runemove(t.r+t.qh, r, n);
730 t.nr += n;
731 if(t.qh < t.org)
732 t.org += n;
733 else if(t.qh <= t.f->nchars+t.org)
734 frinsert(t.f, r, r+n, t.qh-t.org);
735 if (t.qh <= t.q0)
736 t.q0 += n;
737 if (t.qh <= t.q1)
738 t.q1 += n;
739 t.qh += n;
740 updatesel();
744 void
745 cut(void)
747 uint n, p0, p1;
748 uint q0, q1;
750 q0 = t.q0;
751 q1 = t.q1;
753 if (q0 < t.org && q1 >= t.org)
754 show(q0);
756 n = q1-q0;
757 if(n == 0)
758 return;
759 runemove(t.r+q0, t.r+q1, t.nr-q1);
760 t.nr -= n;
761 t.q0 = t.q1 = q0;
762 if(q1 < t.qh)
763 t.qh -= n;
764 else if(q0 < t.qh)
765 t.qh = q0;
766 if(q1 < t.org)
767 t.org -= n;
768 else if(q0 < t.org+t.f->nchars){
769 assert(q0 >= t.org);
770 p0 = q0 - t.org;
771 p1 = q1 - t.org;
772 if(p1 > t.f->nchars)
773 p1 = t.f->nchars;
774 frdelete(t.f, p0, p1);
775 fill();
777 updatesel();
780 void
781 snarfupdate(void)
784 char *pp;
785 int n, i;
786 Rune *p;
788 pp = getsnarf();
789 n = strlen(pp);
790 if(n <= 0) {
791 /*t.nsnarf = 0;*/
792 return;
794 t.snarf = runerealloc(t.snarf, n);
795 for(i=0,p=t.snarf; i<n; p++)
796 i += chartorune(p, pp+i);
797 t.nsnarf = p-t.snarf;
801 void
802 snarf(void)
804 char buf[SnarfSize], *p;
805 int i, n;
806 Rune *rp;
808 if(t.q1 == t.q0)
809 return;
810 n = t.q1-t.q0;
811 t.snarf = runerealloc(t.snarf, n);
812 for(i=0,p=buf,rp=t.snarf; i<n && p < buf + SnarfSize-UTFmax; i++){
813 *rp++ = *(t.r+t.q0+i);
814 p += runetochar(p, t.r+t.q0+i);
816 t.nsnarf = rp-t.snarf;
817 *p = '\0';
818 putsnarf(buf);
821 void
822 paste(Rune *r, int n, int advance)
824 uint m;
825 uint q0;
827 if(rawon && t.q0==t.nr){
828 addraw(r, n);
829 return;
832 cut();
833 if(n == 0)
834 return;
835 if(t.nr>HiWater && t.q0>=t.org && t.q0>=t.qh){
836 m = HiWater-LoWater;
837 if(m > t.org)
838 m = t.org;
839 if(m > t.qh);
840 m = t.qh;
841 t.org -= m;
842 t.qh -= m;
843 t.q0 -= m;
844 t.q1 -= m;
845 t.nr -= m;
846 runemove(t.r, t.r+m, t.nr);
848 t.r = runerealloc(t.r, t.nr+n);
849 q0 = t.q0;
850 runemove(t.r+q0+n, t.r+q0, t.nr-q0);
851 runemove(t.r+q0, r, n);
852 t.nr += n;
853 if(q0 < t.qh)
854 t.qh += n;
855 else
856 consread();
857 if(q0 < t.org)
858 t.org += n;
859 else if(q0 <= t.f->nchars+t.org)
860 frinsert(t.f, r, r+n, q0-t.org);
861 if(advance)
862 t.q0 += n;
863 t.q1 += n;
864 updatesel();
867 void
868 fill(void)
870 if (t.f->nlines >= t.f->maxlines)
871 return;
872 frinsert(t.f, t.r + t.org + t.f->nchars, t.r + t.nr, t.f->nchars);
875 void
876 updatesel(void)
878 Frame *f;
879 uint n;
881 f = t.f;
882 if(t.org+f->p0 == t.q0 && t.org+f->p1 == t.q1)
883 return;
885 n = t.f->nchars;
887 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
888 if (t.q0 >= t.org)
889 f->p0 = t.q0-t.org;
890 else
891 f->p0 = 0;
892 if(f->p0 > n)
893 f->p0 = n;
894 if (t.q1 >= t.org)
895 f->p1 = t.q1-t.org;
896 else
897 f->p1 = 0;
898 if(f->p1 > n)
899 f->p1 = n;
900 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
902 /*
903 if(t.qh<=t.org+t.f.nchars && t.cwqueue != 0)
904 t.cwqueue->wakeup <-= 0;
905 */
907 tcheck();
910 void
911 show(uint q0)
913 int nl;
914 uint q, oq;
916 if(cansee(q0))
917 return;
919 if (q0<t.org)
920 nl = t.f->maxlines/5;
921 else
922 nl = 4*t.f->maxlines/5;
923 q = backnl(q0, nl);
924 /* avoid going in the wrong direction */
925 if (q0>t.org && q<t.org)
926 q = t.org;
927 setorigin(q, 0);
928 /* keep trying until q0 is on the screen */
929 while(!cansee(q0)) {
930 assert(q0 >= t.org);
931 oq = q;
932 q = line2q(t.f->maxlines-nl);
933 assert(q > oq);
934 setorigin(q, 1);
938 int
939 cansee(uint q0)
941 uint qe;
943 qe = t.org+t.f->nchars;
945 if(q0>=t.org && q0 < qe)
946 return 1;
947 if (q0 != qe)
948 return 0;
949 if (t.f->nlines < t.f->maxlines)
950 return 1;
951 if (q0 > 0 && t.r[t.nr-1] == '\n')
952 return 0;
953 return 1;
957 void
958 setorigin(uint org, int exact)
960 int i, a;
961 uint n;
963 if(org>0 && !exact){
964 /* try and start after a newline */
965 /* don't try harder than 256 chars */
966 for(i=0; i<256 && org<t.nr; i++){
967 if(t.r[org-1] == '\n')
968 break;
969 org++;
972 a = org-t.org;
974 if(a>=0 && a<t.f->nchars)
975 frdelete(t.f, 0, a);
976 else if(a<0 && -a<100*t.f->maxlines){
977 n = t.org - org;
978 frinsert(t.f, t.r+org, t.r+org+n, 0);
979 }else
980 frdelete(t.f, 0, t.f->nchars);
981 t.org = org;
982 fill();
983 updatesel();
987 uint
988 line2q(uint n)
990 Frame *f;
992 f = t.f;
993 return frcharofpt(f, Pt(f->r.min.x, f->r.min.y + n*font->height))+t.org;
996 uint
997 backnl(uint p, uint n)
999 int i, j;
1001 for (i = n;; i--) {
1002 /* at 256 chars, call it a line anyway */
1003 for(j=256; --j>0 && p>0; p--)
1004 if(t.r[p-1]=='\n')
1005 break;
1006 if (p == 0 || i == 0)
1007 return p;
1008 p--;
1010 return 0; /* alef bug */
1014 void
1015 addraw(Rune *r, int nr)
1017 t.raw = runerealloc(t.raw, t.nraw+nr);
1018 runemove(t.raw+t.nraw, r, nr);
1019 t.nraw += nr;
1021 if(t.crqueue != nil)
1022 t.crqueue->wakeup <-= 0;
1027 Rune left1[] = { '{', '[', '(', '<', 0xab, 0 };
1028 Rune right1[] = { '}', ']', ')', '>', 0xbb, 0 };
1029 Rune left2[] = { '\n', 0 };
1030 Rune left3[] = { '\'', '"', '`', 0 };
1032 Rune *left[] = {
1033 left1,
1034 left2,
1035 left3,
1039 Rune *right[] = {
1040 right1,
1041 left2,
1042 left3,
1046 void
1047 doubleclick(uint *q0, uint *q1)
1049 int c, i;
1050 Rune *r, *l, *p;
1051 uint q;
1053 for(i=0; left[i]!=0; i++){
1054 q = *q0;
1055 l = left[i];
1056 r = right[i];
1057 /* try matching character to left, looking right */
1058 if(q == 0)
1059 c = '\n';
1060 else
1061 c = t.r[q-1];
1062 p = strrune(l, c);
1063 if(p != 0){
1064 if(clickmatch(c, r[p-l], 1, &q))
1065 *q1 = q-(c!='\n');
1066 return;
1068 /* try matching character to right, looking left */
1069 if(q == t.nr)
1070 c = '\n';
1071 else
1072 c = t.r[q];
1073 p = strrune(r, c);
1074 if(p != 0){
1075 if(clickmatch(c, l[p-r], -1, &q)){
1076 *q1 = *q0+(*q0<t.nr && c=='\n');
1077 *q0 = q;
1078 if(c!='\n' || q!=0 || t.r[0]=='\n')
1079 (*q0)++;
1081 return;
1084 /* try filling out word to right */
1085 while(*q1<t.nr && isalnum(t.r[*q1]))
1086 (*q1)++;
1087 /* try filling out word to left */
1088 while(*q0>0 && isalnum(t.r[*q0-1]))
1089 (*q0)--;
1092 void
1093 plumbclick(uint *q0, uint *q1)
1095 while(*q1<t.nr && !isspace(t.r[*q1]))
1096 (*q1)++;
1097 while(*q0>0 && !isspace(t.r[*q0-1]))
1098 (*q0)--;
1101 int
1102 clickmatch(int cl, int cr, int dir, uint *q)
1104 Rune c;
1105 int nest;
1107 nest = 1;
1108 for(;;){
1109 if(dir > 0){
1110 if(*q == t.nr)
1111 break;
1112 c = t.r[*q];
1113 (*q)++;
1114 }else{
1115 if(*q == 0)
1116 break;
1117 (*q)--;
1118 c = t.r[*q];
1120 if(c == cr){
1121 if(--nest==0)
1122 return 1;
1123 }else if(c == cl)
1124 nest++;
1126 return cl=='\n' && nest==1;
1129 void
1130 rcstart(int fd[2])
1132 int pid;
1133 char *argv[3];
1134 char slave[256];
1135 int sfd;
1137 argv[0] = "rc";
1138 argv[1] = "-i";
1139 argv[2] = 0;
1141 getpts(fd, slave);
1142 switch(pid = fork()) {
1143 case 0:
1144 putenv("TERM=9term");
1145 close(fd[1]);
1146 setsid();
1147 sfd = open(slave, ORDWR);
1148 // ioctl(sfd, I_PUSH, "ptem");
1149 // ioctl(sfd, I_PUSH, "ldterm");
1150 dup(sfd, 0);
1151 dup(sfd, 1);
1152 dup(sfd, 2);
1153 execvp(argv[0], argv);
1154 break;
1155 case -1:
1156 fatal("proc failed: %r");
1157 break;
1159 close(fd[0]);
1160 fd[0] = fd[1];
1162 rcpid = pid;
1165 void
1166 tcheck(void)
1168 Frame *f;
1170 f = t.f;
1172 assert(t.q0 <= t.q1 && t.q1 <= t.nr);
1173 assert(t.org <= t.nr && t.qh <= t.nr);
1174 assert(f->p0 <= f->p1 && f->p1 <= f->nchars);
1175 assert(t.org + f->nchars <= t.nr);
1176 assert(t.org+f->nchars==t.nr || (f->nlines >= f->maxlines));
1179 Rune*
1180 strrune(Rune *s, Rune c)
1182 Rune c1;
1184 if(c == 0) {
1185 while(*s++)
1187 return s-1;
1190 while(c1 = *s++)
1191 if(c1 == c)
1192 return s-1;
1193 return 0;
1196 void
1197 scrdraw(void)
1199 Rectangle r, r1, r2;
1200 static Image *scrx;
1202 r = scrollr;
1203 r.min.x += 1; /* border between margin and bar */
1204 r1 = r;
1205 if(scrx==0 || scrx->r.max.y < r.max.y){
1206 if(scrx)
1207 freeimage(scrx);
1208 scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow);
1209 if(scrx == 0)
1210 fatal("scroll balloc");
1212 r1.min.x = 0;
1213 r1.max.x = Dx(r);
1214 r2 = scrpos(r1, t.org, t.org+t.f->nchars, t.nr);
1215 if(!eqrect(r2, lastsr)){
1216 lastsr = r2;
1217 draw(scrx, r1, cols[BORD], nil, ZP);
1218 draw(scrx, r2, cols[BACK], nil, r2.min);
1219 // r2 = r1;
1220 // r2.min.x = r2.max.x-1;
1221 // draw(scrx, r2, cols[BORD], nil, ZP);
1222 draw(screen, r, scrx, nil, r1.min);
1226 Rectangle
1227 scrpos(Rectangle r, ulong p0, ulong p1, ulong tot)
1229 long h;
1230 Rectangle q;
1232 q = insetrect(r, 1);
1233 h = q.max.y-q.min.y;
1234 if(tot == 0)
1235 return q;
1236 if(tot > 1024L*1024L)
1237 tot >>= 10, p0 >>= 10, p1 >>= 10;
1238 if(p0 > 0)
1239 q.min.y += h*p0/tot;
1240 if(p1 < tot)
1241 q.max.y -= h*(tot-p1)/tot;
1242 if(q.max.y < q.min.y+2){
1243 if(q.min.y+2 <= r.max.y)
1244 q.max.y = q.min.y+2;
1245 else
1246 q.min.y = q.max.y-2;
1248 return q;
1251 void
1252 scroll(int but)
1254 uint p0, oldp0;
1255 Rectangle s;
1256 int x, y, my, h, first, exact;
1258 s = insetrect(scrollr, 1);
1259 h = s.max.y-s.min.y;
1260 x = (s.min.x+s.max.x)/2;
1261 oldp0 = ~0;
1262 first = 1;
1263 do{
1264 if(t.m.xy.x<s.min.x || s.max.x<=t.m.xy.x){
1265 readmouse(mc);
1266 t.m = mc->m;
1267 }else{
1268 my = t.m.xy.y;
1269 if(my < s.min.y)
1270 my = s.min.y;
1271 if(my >= s.max.y)
1272 my = s.max.y;
1273 // if(!eqpt(t.m.xy, Pt(x, my)))
1274 // cursorset(Pt(x, my));
1275 exact = 1;
1276 if(but == 2){
1277 y = my;
1278 if(y > s.max.y-2)
1279 y = s.max.y-2;
1280 if(t.nr > 1024*1024)
1281 p0 = ((t.nr>>10)*(y-s.min.y)/h)<<10;
1282 else
1283 p0 = t.nr*(y-s.min.y)/h;
1284 exact = 0;
1285 } else if(but == 1)
1286 p0 = backnl(t.org, (my-s.min.y)/font->height);
1287 else
1288 p0 = t.org+frcharofpt(t.f, Pt(s.max.x, my));
1290 if(oldp0 != p0)
1291 setorigin(p0, exact);
1292 oldp0 = p0;
1293 scrdraw();
1294 readmouse(mc);
1295 t.m = mc->m;
1297 }while(t.m.buttons & (1<<(but-1)));
1300 void
1301 plumbstart(void)
1303 char buf[256];
1304 snprint(buf, sizeof buf, "%s/mnt/plumb", getenv("HOME"));
1305 if((plumbfd = plumbopen(buf, OWRITE)) < 0)
1306 fatal("plumbopen");
1309 void
1310 plumb(uint q0, uint q1)
1312 Plumbmsg *pm;
1313 char *p;
1314 int i, p0, n;
1315 char cbuf[100];
1316 char *w;
1318 if(getchildwdir(pid, childwdir, sizeof childwdir) == 0)
1319 w = childwdir;
1320 else
1321 w = wdir;
1322 pm = malloc(sizeof(Plumbmsg));
1323 pm->src = strdup("9term");
1324 pm->dst = 0;
1325 pm->wdir = strdup(w);
1326 pm->type = strdup("text");
1327 if(q1 > q0)
1328 pm->attr = nil;
1329 else{
1330 p0 = q0;
1331 plumbclick(&q0, &q1);
1332 sprint(cbuf, "click=%d", p0-q0);
1333 pm->attr = plumbunpackattr(cbuf);
1335 if(q0==q1){
1336 plumbfree(pm);
1337 return;
1339 pm->data = malloc(SnarfSize);
1340 n = q1 - q0;
1341 for(i=0,p=pm->data; i<n && p < pm->data + SnarfSize-UTFmax; i++)
1342 p += runetochar(p, t.r+q0+i);
1343 *p = '\0';
1344 pm->ndata = strlen(pm->data);
1345 plumbsend(plumbfd, pm);
1346 plumbfree(pm);
1350 * Process in-band messages about window title changes.
1351 * The messages are of the form:
1353 * \033];xxx\007
1355 * where xxx is the new directory. This format was chosen
1356 * because it changes the label on xterm windows.
1358 int
1359 label(Rune *sr, int n)
1361 Rune *sl, *el, *er, *r;
1363 er = sr+n;
1364 for(r=er-1; r>=sr; r--)
1365 if(*r == '\007')
1366 break;
1367 if(r < sr)
1368 return n;
1370 el = r+1;
1371 if(el-sr > sizeof wdir)
1372 sr = el - sizeof wdir;
1373 for(sl=el-3; sl>=sr; sl--)
1374 if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
1375 break;
1376 if(sl < sr)
1377 return n;
1379 snprint(wdir, sizeof wdir, "%.*S", (el-1)-(sl+3), sl+3);
1380 drawsetlabel(display, wdir);
1382 runemove(sl, el, er-el);
1383 n -= (el-sl);
1384 return n;