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 032 };34 Image* cols[NCOL];35 Image* hcols[NCOL];36 Image *plumbcolor;38 Menu menu2 =39 {40 menu2str41 };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 void58 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 }ARGEND76 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();113 }115 void116 hangupnote(void *a, char *msg)117 {118 if(getpid() != mainpid)119 noted(NDFLT);120 if(strcmp(msg, "hangup") == 0 && rcpid != 0){121 postnote(PNPROC, rcpid, "hangup");122 noted(NDFLT);123 }124 noted(NDFLT);125 }127 void128 hostproc(void *arg)129 {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");143 }144 rcbuf[i].n = n;145 which = i;146 send(c, &which);147 }148 }150 void151 hoststart(void)152 {153 hostc = chancreate(sizeof(int), 0);154 proccreate(hostproc, hostc, 1024);155 }157 void158 loop(void)159 {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;195 }196 }197 }199 void200 doreshape(void)201 {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();207 }209 void210 geom(void)211 {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();226 }228 void229 drawhold(int holdon)230 {231 if(holdon)232 setcursor(mc, &whitearrow);233 else234 setcursor(mc, nil);236 draw(screen, screen->r, cols[BACK], nil, ZP);237 geom();238 scrdraw();239 }242 void243 mouse(void)244 {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;258 }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;284 }285 cancel = 1;286 }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 else304 plumb(newq0, newq0);305 break;306 }307 }309 void310 mselect(void)311 {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;331 }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;340 }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;349 }350 } else if (t.m.buttons&4) {351 if (state&Canpaste) {352 snarfupdate();353 if (t.nsnarf) {354 paste(t.snarf, t.nsnarf, 0);355 }356 state = Cancut|Canpaste;357 }358 }359 readmouse(mc);360 t.m = mc->m;361 }362 }363 }365 Rune newline[] = { '\n', 0 };367 void368 domenu2(int but)369 {370 if(scrolling)371 menu2str[Scroll] = "noscroll";372 else373 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();411 }412 break;413 case Plumb:414 plumb(t.q0, t.q1);415 break;416 default:417 fatal("bad menu item");418 }419 }421 void422 key(Rune r)423 {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;453 }455 if(rawon && t.q0==t.nr){456 addraw(&r, 1);457 return;458 }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;467 }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;492 }493 if(scrolling)494 show(t.q0);495 }497 int498 bswidth(Rune c)499 {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;518 }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;525 }526 --q;527 }528 return t.q0-q;529 }531 int532 consready(void)533 {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;547 }548 return 0;549 }552 void553 consread(void)554 {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 }else570 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;578 }579 }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(); */588 }589 }591 void592 conswrite(char *p, int n)593 {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;611 }612 }613 /* there is a little extra room in a message buf */614 }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++;628 }630 runewrite(buf2, q-buf2);631 }633 if(n != 0) {634 assert(n+t.npart < UTFmax);635 memcpy(t.part+t.npart, p, n);636 t.npart += n;637 }639 if(scrolling)640 show(t.qh);641 }643 void644 runewrite(Rune *r, int n)645 {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 else665 --q;666 } else if(*p)667 *q++ = *p;668 p++;669 }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;699 }700 p1 = q1 - t.org;701 if(p1 > t.f->nchars)702 p1 = t.f->nchars;703 frdelete(t.f, p0, p1);704 fill();705 }706 updatesel();707 return;708 }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 else719 t.q0 = 0;720 if(t.q1 > m)721 t.q1 -= m;722 else723 t.q1 = 0;724 t.nr -= m;725 runemove(t.r, t.r+m, t.nr);726 }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();741 }744 void745 cut(void)746 {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();776 }777 updatesel();778 }780 void781 snarfupdate(void)782 {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;793 }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;799 }801 void802 snarf(void)803 {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);815 }816 t.nsnarf = rp-t.snarf;817 *p = '\0';818 putsnarf(buf);819 }821 void822 paste(Rune *r, int n, int advance)823 {824 uint m;825 uint q0;827 if(rawon && t.q0==t.nr){828 addraw(r, n);829 return;830 }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);847 }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 else856 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();865 }867 void868 fill(void)869 {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);873 }875 void876 updatesel(void)877 {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 else891 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 else897 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();908 }910 void911 show(uint q0)912 {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 else922 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);935 }936 }938 int939 cansee(uint q0)940 {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;954 }957 void958 setorigin(uint org, int exact)959 {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++;970 }971 }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 }else980 frdelete(t.f, 0, t.f->nchars);981 t.org = org;982 fill();983 updatesel();984 }987 uint988 line2q(uint n)989 {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;994 }996 uint997 backnl(uint p, uint n)998 {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--;1009 }1010 return 0; /* alef bug */1011 }1014 void1015 addraw(Rune *r, int nr)1016 {1017 t.raw = runerealloc(t.raw, t.nraw+nr);1018 runemove(t.raw+t.nraw, r, nr);1019 t.nraw += nr;1020 /*1021 if(t.crqueue != nil)1022 t.crqueue->wakeup <-= 0;1023 */1024 }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,1036 01037 };1039 Rune *right[] = {1040 right1,1041 left2,1042 left3,1043 01044 };1046 void1047 doubleclick(uint *q0, uint *q1)1048 {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 else1061 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;1067 }1068 /* try matching character to right, looking left */1069 if(q == t.nr)1070 c = '\n';1071 else1072 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)++;1080 }1081 return;1082 }1083 }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)--;1090 }1092 void1093 plumbclick(uint *q0, uint *q1)1094 {1095 while(*q1<t.nr && !isspace(t.r[*q1]))1096 (*q1)++;1097 while(*q0>0 && !isspace(t.r[*q0-1]))1098 (*q0)--;1099 }1101 int1102 clickmatch(int cl, int cr, int dir, uint *q)1103 {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];1119 }1120 if(c == cr){1121 if(--nest==0)1122 return 1;1123 }else if(c == cl)1124 nest++;1125 }1126 return cl=='\n' && nest==1;1127 }1129 void1130 rcstart(int fd[2])1131 {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;1158 }1159 close(fd[0]);1160 fd[0] = fd[1];1162 rcpid = pid;1163 }1165 void1166 tcheck(void)1167 {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));1177 }1179 Rune*1180 strrune(Rune *s, Rune c)1181 {1182 Rune c1;1184 if(c == 0) {1185 while(*s++)1186 ;1187 return s-1;1188 }1190 while(c1 = *s++)1191 if(c1 == c)1192 return s-1;1193 return 0;1194 }1196 void1197 scrdraw(void)1198 {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");1211 }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);1223 }1224 }1226 Rectangle1227 scrpos(Rectangle r, ulong p0, ulong p1, ulong tot)1228 {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 else1246 q.min.y = q.max.y-2;1247 }1248 return q;1249 }1251 void1252 scroll(int but)1253 {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 else1283 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 else1288 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;1296 }1297 }while(t.m.buttons & (1<<(but-1)));1298 }1300 void1301 plumbstart(void)1302 {1303 char buf[256];1304 snprint(buf, sizeof buf, "%s/mnt/plumb", getenv("HOME"));1305 if((plumbfd = plumbopen(buf, OWRITE)) < 0)1306 fatal("plumbopen");1307 }1309 void1310 plumb(uint q0, uint q1)1311 {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 else1321 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);1334 }1335 if(q0==q1){1336 plumbfree(pm);1337 return;1338 }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);1347 }1349 /*1350 * Process in-band messages about window title changes.1351 * The messages are of the form:1352 *1353 * \033];xxx\0071354 *1355 * where xxx is the new directory. This format was chosen1356 * because it changes the label on xterm windows.1357 */1358 int1359 label(Rune *sr, int n)1360 {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;1385 }