commit cfabc3ed1638efc186ebd26bdaa3dfb5663dff17 from: rsc date: Sun Nov 23 18:02:15 2003 UTC Dhog's 9term. Updated for current libraries by Caerwyn Jones. Button-3 plumbing (like in acme) by rsc. commit - b8c14089d8f4be73a908f82f62fce80ed2c14a8d commit + cfabc3ed1638efc186ebd26bdaa3dfb5663dff17 blob - /dev/null blob + ec59756f106e26ac496deb85d3a8aac941709ddf (mode 644) --- /dev/null +++ src/cmd/9term/9term.c @@ -0,0 +1,1380 @@ +#include "9term.h" + +Rectangle scrollr; /* scroll bar rectangle */ +Rectangle lastsr; /* used for scroll bar */ +int holdon; /* hold mode */ +int rawon; /* raw mode */ +int scrolling; /* window scrolls */ +int clickmsec; /* time of last click */ +uint clickq0; /* point of last click */ +int rcfd[2]; +int rcpid; +int maxtab; +Mousectl* mc; +Keyboardctl* kc; +Channel* hostc; +Readbuf rcbuf[2]; +int mainpid; +int plumbfd; +int label(Rune*, int); +char wdir[1024]; +void hangupnote(void*, char*); + +char *menu2str[] = { + "cut", + "paste", + "snarf", + "send", + "scroll", + "plumb", + 0 +}; + +Image* cols[NCOL]; +Image* hcols[NCOL]; +Image *plumbcolor; + +Menu menu2 = +{ + menu2str +}; + +Text t; + +Cursor whitearrow = { + {0, 0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, + 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, + 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, + 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }, + {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C, + 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C, + 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C, + 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, } +}; + +void +threadmain(int argc, char *argv[]) +{ + char *p; + + rfork(RFNOTEG); + mainpid = getpid(); + ARGBEGIN{ + case 'T': + p = ARGF(); + if(p == 0) + break; + maxtab = strtoul(p, 0, 0); + break; + case 's': + scrolling++; + break; + }ARGEND + + p = getenv("tabstop"); + if(p == 0) + p = getenv("TABSTOP"); + if(p != 0 && maxtab <= 0) + maxtab = strtoul(p, 0, 0); + if(maxtab <= 0) + maxtab = 8; + + initdraw(nil, nil, "9term"); + notify(hangupnote); + + mc = initmouse(nil, screen); + kc = initkeyboard(nil); + rcstart(rcfd); + hoststart(); + plumbstart(); + + t.f = mallocz(sizeof(Frame), 1); + + cols[BACK] = allocimagemix(display, DPaleyellow, DWhite); + cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkyellow); + cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DYellowgreen); + cols[TEXT] = display->black; + cols[HTEXT] = display->black; + + hcols[BACK] = cols[BACK]; + hcols[HIGH] = cols[HIGH]; + hcols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMedblue); + hcols[TEXT] = hcols[BORD]; + hcols[HTEXT] = hcols[TEXT]; + + plumbcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x006600FF); + + draw(screen, screen->r, cols[BACK], nil, ZP); + geom(); + + loop(); +} + +void +hangupnote(void *a, char *msg) +{ + if(getpid() != mainpid) + noted(NDFLT); + if(strcmp(msg, "hangup") == 0 && rcpid != 0){ + postnote(PNPROC, rcpid, "hangup"); + noted(NDFLT); + } + noted(NDFLT); +} + +void +hostproc(void *arg) +{ + Channel *c; + int i, n, which; + + c = arg; + + i = 0; + for(;;){ + i = 1-i; /* toggle */ + n = read(rcfd[0], rcbuf[i].data, sizeof rcbuf[i].data); + if(n <= 0){ + if(n < 0) + fprint(2, "9term: host read error: %r\n"); + threadexitsall("host"); + } + rcbuf[i].n = n; + which = i; + send(c, &which); + } +} + +void +hoststart(void) +{ + hostc = chancreate(sizeof(int), 0); + proccreate(hostproc, hostc, 1024); +} + +void +loop(void) +{ + Rune r; + int i; + Alt a[] = { + {mc->c, &mc->m, CHANRCV}, + {kc->c, &r, CHANRCV}, + {hostc, &i, CHANRCV}, + {mc->resizec, nil, CHANRCV}, + {nil, nil, CHANEND}, + }; + + for(;;) { + tcheck(); + + scrdraw(); + flushimage(display, 1); + a[2].op = CHANRCV; + if(!scrolling && t.qh > t.org+t.f->nchars) + a[2].op = CHANNOP;; + + switch(alt(a)) { + default: + fatal("impossible"); + case 0: + t.m = mc->m; + mouse(); + break; + case 1: + key(r); + break; + case 2: + conswrite(rcbuf[i].data, rcbuf[i].n); + break; + case 3: + doreshape(); + break; + } + } +} + +void +doreshape(void) +{ + if(getwindow(display, Refnone) < 0) + fatal("can't reattach to window"); + draw(screen, screen->r, cols[BACK], nil, ZP); + geom(); + scrdraw(); +} + +void +geom(void) +{ + Rectangle r; + + r = screen->r; + scrollr = screen->r; + scrollr.max.x = r.min.x+Scrollwid; + lastsr = Rect(0,0,0,0); + + r.min.x += Scrollwid+Scrollgap; + + frclear(t.f, 0); + frinit(t.f, r, font, screen, holdon ? hcols : cols); + t.f->maxtab = maxtab*stringwidth(font, "0"); + fill(); + updatesel(); +} + +void +drawhold(int holdon) +{ + if(holdon) + setcursor(mc, &whitearrow); + else + setcursor(mc, nil); + + draw(screen, screen->r, cols[BACK], nil, ZP); + geom(); + scrdraw(); +} + + +void +mouse(void) +{ + int cancel, but; + uint oldq0, oldq1, newq0, newq1; + + but = t.m.buttons; + + if(but != 1 && but != 2 && but != 4) + return; + + if (ptinrect(t.m.xy, scrollr)) { + scroll(but); + if(t.qh<=t.org+t.f->nchars) + consread();; + return; + } + + switch(but) { + case 1: + mselect(); + break; + case 2: + domenu2(2); + break; + case 4: + /* save old selection */ + oldq0 = t.q0; + oldq1 = t.q1; + + /* sweep out plumb area and record it */ + t.f->cols[HIGH] = plumbcolor; + t.f->cols[HTEXT] = display->white; + mselect(); + newq0 = t.q0; + newq1 = t.q1; + + cancel = 0; + if(t.m.buttons != 0){ + while(t.m.buttons){ + readmouse(mc); + t.m = mc->m; + } + cancel = 1; + } + + /* restore old selection */ + t.f->cols[HIGH] = cols[HIGH]; + t.f->cols[HTEXT] = cols[HTEXT]; + t.q0 = oldq0; + t.q1 = oldq1; + updatesel(); + + if(cancel) + break; + + /* process plumb area */ + if(newq0 < newq1) + plumb(newq0, newq1); + else if(oldq0 <= newq0 && newq0 < oldq1) + plumb(oldq0, oldq1); + else + plumb(newq0, newq0); + break; + } +} + +void +mselect(void) +{ + int b, x, y; + uint q0; + + b = t.m.buttons; + q0 = frcharofpt(t.f, t.m.xy) + t.org; + if(t.m.msec-clickmsec<500 && clickq0 == q0 && t.q0==t.q1 && b==1){ + doubleclick(&t.q0, &t.q1); + updatesel(); +/* t.t.i->flush(); */ + x = t.m.xy.x; + y = t.m.xy.y; + /* stay here until something interesting happens */ + do { + readmouse(mc); + t.m = mc->m; + } while(t.m.buttons==b && abs(t.m.xy.x-x)<4 && abs(t.m.xy.y-y)<4); + t.m.xy.x = x; /* in case we're calling frselect */ + t.m.xy.y = y; + clickmsec = 0; + } + + if(t.m.buttons == b) { + frselect(t.f, mc); + t.m = mc->m; + t.q0 = t.f->p0 + t.org; + t.q1 = t.f->p1 + t.org; + clickmsec = t.m.msec; + clickq0 = t.q0; + } + if((t.m.buttons != b) && (b&1)){ + enum {Cancut = 1, Canpaste = 2} state = Cancut | Canpaste; + while(t.m.buttons){ + if(t.m.buttons&2) { + if (state&Cancut) { + snarf(); + cut(); + state = Canpaste; + } + } else if (t.m.buttons&4) { + if (state&Canpaste) { + snarfupdate(); + if (t.nsnarf) { + paste(t.snarf, t.nsnarf, 0); + } + state = Cancut|Canpaste; + } + } + readmouse(mc); + t.m = mc->m; + } + } +} + +Rune newline[] = { '\n', 0 }; + +void +domenu2(int but) +{ + if(scrolling) + menu2str[Scroll] = "noscroll"; + else + menu2str[Scroll] = "scroll"; + + switch(menuhit(but, mc, &menu2, nil)){ + case -1: + break; + case Cut: + snarf(); + cut(); + if(scrolling) + show(t.q0); + break; + case Paste: + snarfupdate(); + paste(t.snarf, t.nsnarf, 0); + if(scrolling) + show(t.q0); + break; + case Snarf: + snarf(); + if(scrolling) + show(t.q0); + break; + case Send: + snarf(); + t.q0 = t.q1 = t.nr; + updatesel(); + snarfupdate(); + paste(t.snarf, t.nsnarf, 1); + if(t.nsnarf == 0 || t.snarf[t.nsnarf-1] != '\n') + paste(newline, 1, 1); + show(t.nr); + consread(); + break; + case Scroll: + scrolling = !scrolling; + if (scrolling) { + show(t.nr); + consread(); + } + break; + case Plumb: + plumb(t.q0, t.q1); + break; + default: + fatal("bad menu item"); + } +} + +void +key(Rune r) +{ + char buf[1]; + + if(r == 0) + return; + if(r==SCROLLKEY){ /* scroll key */ + setorigin(line2q(t.f->maxlines*2/3), 1); + if(t.qh<=t.org+t.f->nchars) + consread(); + return; + }else if(r == BACKSCROLLKEY){ + setorigin(backnl(t.org, t.f->maxlines*2/3), 1); + return; + }else if(r == CUT){ + snarf(); + cut(); + if(scrolling) + show(t.q0); + return; + }else if(r == COPY){ + snarf(); + if(scrolling) + show(t.q0); + return; + }else if(r == PASTE){ + snarfupdate(); + paste(t.snarf, t.nsnarf, 0); + if(scrolling) + show(t.q0); + return; + } + + if(rawon && t.q0==t.nr){ + addraw(&r, 1); + return; + } + + if(r==ESC || (holdon && r==0x7F)){ /* toggle hold */ + holdon = !holdon; + drawhold(holdon); + if(!holdon) + consread(); + if(r == 0x1B) + return; + } + + snarf(); + + switch(r) { + case 0x7F: /* DEL: send interrupt */ + t.qh = t.q0 = t.q1 = t.nr; + show(t.q0); + buf[0] = 0x7f; + if(write(rcfd[1], buf, 1) < 0) + exits(0); + /* get rc to print prompt */ +// r = '\n'; +// paste(&r, 1, 1); + break; + case 0x08: /* ^H: erase character */ + case 0x15: /* ^U: erase line */ + case 0x17: /* ^W: erase word */ + if (t.q0 != 0 && t.q0 != t.qh) + t.q0 -= bswidth(r); + cut(); + break; + default: + paste(&r, 1, 1); + break; + } + if(scrolling) + show(t.q0); +} + +int +bswidth(Rune c) +{ + uint q, eq, stop; + Rune r; + int skipping; + + /* there is known to be at least one character to erase */ + if(c == 0x08) /* ^H: erase character */ + return 1; + q = t.q0; + stop = 0; + if(q > t.qh) + stop = t.qh; + skipping = 1; + while(q > stop){ + r = t.r[q-1]; + if(r == '\n'){ /* eat at most one more character */ + if(q == t.q0) /* eat the newline */ + --q; + break; + } + if(c == 0x17){ + eq = isalnum(r); + if(eq && skipping) /* found one; stop skipping */ + skipping = 0; + else if(!eq && !skipping) + break; + } + --q; + } + return t.q0-q; +} + +int +consready(void) +{ + int i, c; + + if(holdon) + return 0; + + if(rawon) + return t.nraw != 0; + + /* look to see if there is a complete line */ + for(i=t.qh; i= UTFmax && (t.qh 0)) { + if(t.qh == t.nr){ + width = runetochar(p, &t.raw[0]); + t.nraw--; + runemove(t.raw, t.raw+1, t.nraw); + }else + width = runetochar(p, &t.r[t.qh++]); + c = *p; + p += width; + n -= width; + if(!rawon && (c == '\n' || c == '\004')) { + if(c == '\004') + p--; + break; + } + } + if(n < UTFmax && t.qh 0){ + /* handle partial runes */ + while(i < UTFmax && n>0) { + t.part[i] = *p; + i++; + p++; + n--; + if(fullrune(t.part, i)) { + t.npart = 0; + chartorune(buf2, t.part); + runewrite(buf2, 1); + break; + } + } + /* there is a little extra room in a message buf */ + } + + while(n >= UTFmax || fullrune(p, n)) { + n2 = nelem(buf2); + q = buf2; + + while(n2) { + if(n < UTFmax && !fullrune(p, n)) + break; + i = chartorune(q, p); + p += i; + n -= i; + n2--; + q++; + } + + runewrite(buf2, q-buf2); + } + + if(n != 0) { + assert(n+t.npart < UTFmax); + memcpy(t.part+t.npart, p, n); + t.npart += n; + } + + if(scrolling) + show(t.qh); +} + +void +runewrite(Rune *r, int n) +{ + uint m; + int i; + uint initial; + uint q0, q1; + uint p0, p1; + Rune *p, *q; + + n = label(r, n); + if(n == 0) + return; + + /* get ride of backspaces */ + initial = 0; + p = q = r; + for(i=0; i t.qh) + initial = t.qh; + q0 = t.qh-initial; + q1 = t.qh; + + runemove(t.r+q0, t.r+q1, t.nr-q1); + t.nr -= initial; + t.qh -= initial; + if(t.q0 > q1) + t.q0 -= initial; + else if(t.q0 > q0) + t.q0 = q0; + if(t.q1 > q1) + t.q1 -= initial; + else if(t.q1 > q0) + t.q1 = q0; + if(t.org > q1) + t.org -= initial; + else if(q0 < t.org+t.f->nchars){ + if(t.org < q0) + p0 = q0 - t.org; + else { + t.org = q0; + p0 = 0; + } + p1 = q1 - t.org; + if(p1 > t.f->nchars) + p1 = t.f->nchars; + frdelete(t.f, p0, p1); + fill(); + } + updatesel(); + return; + } + + if(t.nr>HiWater && t.qh>=t.org){ + m = HiWater-LoWater; + if(m > t.org); + m = t.org; + t.org -= m; + t.qh -= m; + if(t.q0 > m) + t.q0 -= m; + else + t.q0 = 0; + if(t.q1 > m) + t.q1 -= m; + else + t.q1 = 0; + t.nr -= m; + runemove(t.r, t.r+m, t.nr); + } + t.r = runerealloc(t.r, t.nr+n); + runemove(t.r+t.qh+n, t.r+t.qh, t.nr-t.qh); + runemove(t.r+t.qh, r, n); + t.nr += n; + if(t.qh < t.org) + t.org += n; + else if(t.qh <= t.f->nchars+t.org) + frinsert(t.f, r, r+n, t.qh-t.org); + if (t.qh <= t.q0) + t.q0 += n; + if (t.qh <= t.q1) + t.q1 += n; + t.qh += n; + updatesel(); +} + + +void +cut(void) +{ + uint n, p0, p1; + uint q0, q1; + + q0 = t.q0; + q1 = t.q1; + + if (q0 < t.org && q1 >= t.org) + show(q0); + + n = q1-q0; + if(n == 0) + return; + runemove(t.r+q0, t.r+q1, t.nr-q1); + t.nr -= n; + t.q0 = t.q1 = q0; + if(q1 < t.qh) + t.qh -= n; + else if(q0 < t.qh) + t.qh = q0; + if(q1 < t.org) + t.org -= n; + else if(q0 < t.org+t.f->nchars){ + assert(q0 >= t.org); + p0 = q0 - t.org; + p1 = q1 - t.org; + if(p1 > t.f->nchars) + p1 = t.f->nchars; + frdelete(t.f, p0, p1); + fill(); + } + updatesel(); +} + +void +snarfupdate(void) +{ + + char *pp; + int n, i; + Rune *p; + + pp = getsnarf(); + n = strlen(pp); + if(n <= 0) { + /*t.nsnarf = 0;*/ + return; + } + t.snarf = runerealloc(t.snarf, n); + for(i=0,p=t.snarf; iHiWater && t.q0>=t.org && t.q0>=t.qh){ + m = HiWater-LoWater; + if(m > t.org) + m = t.org; + if(m > t.qh); + m = t.qh; + t.org -= m; + t.qh -= m; + t.q0 -= m; + t.q1 -= m; + t.nr -= m; + runemove(t.r, t.r+m, t.nr); + } + t.r = runerealloc(t.r, t.nr+n); + q0 = t.q0; + runemove(t.r+q0+n, t.r+q0, t.nr-q0); + runemove(t.r+q0, r, n); + t.nr += n; + if(q0 < t.qh) + t.qh += n; + else + consread(); + if(q0 < t.org) + t.org += n; + else if(q0 <= t.f->nchars+t.org) + frinsert(t.f, r, r+n, q0-t.org); + if(advance) + t.q0 += n; + t.q1 += n; + updatesel(); +} + +void +fill(void) +{ + if (t.f->nlines >= t.f->maxlines) + return; + frinsert(t.f, t.r + t.org + t.f->nchars, t.r + t.nr, t.f->nchars); +} + +void +updatesel(void) +{ + Frame *f; + uint n; + + f = t.f; + if(t.org+f->p0 == t.q0 && t.org+f->p1 == t.q1) + return; + + n = t.f->nchars; + + frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0); + if (t.q0 >= t.org) + f->p0 = t.q0-t.org; + else + f->p0 = 0; + if(f->p0 > n) + f->p0 = n; + if (t.q1 >= t.org) + f->p1 = t.q1-t.org; + else + f->p1 = 0; + if(f->p1 > n) + f->p1 = n; + frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1); + +/* + if(t.qh<=t.org+t.f.nchars && t.cwqueue != 0) + t.cwqueue->wakeup <-= 0; +*/ + + tcheck(); +} + +void +show(uint q0) +{ + int nl; + uint q, oq; + + if(cansee(q0)) + return; + + if (q0maxlines/5; + else + nl = 4*t.f->maxlines/5; + q = backnl(q0, nl); + /* avoid going in the wrong direction */ + if (q0>t.org && q= t.org); + oq = q; + q = line2q(t.f->maxlines-nl); + assert(q > oq); + setorigin(q, 1); + } +} + +int +cansee(uint q0) +{ + uint qe; + + qe = t.org+t.f->nchars; + + if(q0>=t.org && q0 < qe) + return 1; + if (q0 != qe) + return 0; + if (t.f->nlines < t.f->maxlines) + return 1; + if (q0 > 0 && t.r[t.nr-1] == '\n') + return 0; + return 1; +} + + +void +setorigin(uint org, int exact) +{ + int i, a; + uint n; + + if(org>0 && !exact){ + /* try and start after a newline */ + /* don't try harder than 256 chars */ + for(i=0; i<256 && org=0 && anchars) + frdelete(t.f, 0, a); + else if(a<0 && -a<100*t.f->maxlines){ + n = t.org - org; + frinsert(t.f, t.r+org, t.r+org+n, 0); + }else + frdelete(t.f, 0, t.f->nchars); + t.org = org; + fill(); + updatesel(); +} + + +uint +line2q(uint n) +{ + Frame *f; + + f = t.f; + return frcharofpt(f, Pt(f->r.min.x, f->r.min.y + n*font->height))+t.org; +} + +uint +backnl(uint p, uint n) +{ + int i, j; + + for (i = n;; i--) { + /* at 256 chars, call it a line anyway */ + for(j=256; --j>0 && p>0; p--) + if(t.r[p-1]=='\n') + break; + if (p == 0 || i == 0) + return p; + p--; + } + return 0; /* alef bug */ +} + + +void +addraw(Rune *r, int nr) +{ + t.raw = runerealloc(t.raw, t.nraw+nr); + runemove(t.raw+t.nraw, r, nr); + t.nraw += nr; +/* + if(t.crqueue != nil) + t.crqueue->wakeup <-= 0; +*/ +} + + +Rune left1[] = { '{', '[', '(', '<', 0xab, 0 }; +Rune right1[] = { '}', ']', ')', '>', 0xbb, 0 }; +Rune left2[] = { '\n', 0 }; +Rune left3[] = { '\'', '"', '`', 0 }; + +Rune *left[] = { + left1, + left2, + left3, + 0 +}; + +Rune *right[] = { + right1, + left2, + left3, + 0 +}; + +void +doubleclick(uint *q0, uint *q1) +{ + int c, i; + Rune *r, *l, *p; + uint q; + + for(i=0; left[i]!=0; i++){ + q = *q0; + l = left[i]; + r = right[i]; + /* try matching character to left, looking right */ + if(q == 0) + c = '\n'; + else + c = t.r[q-1]; + p = strrune(l, c); + if(p != 0){ + if(clickmatch(c, r[p-l], 1, &q)) + *q1 = q-(c!='\n'); + return; + } + /* try matching character to right, looking left */ + if(q == t.nr) + c = '\n'; + else + c = t.r[q]; + p = strrune(r, c); + if(p != 0){ + if(clickmatch(c, l[p-r], -1, &q)){ + *q1 = *q0+(*q00 && isalnum(t.r[*q0-1])) + (*q0)--; +} + +void +plumbclick(uint *q0, uint *q1) +{ + while(*q10 && !isspace(t.r[*q0-1])) + (*q0)--; +} + +int +clickmatch(int cl, int cr, int dir, uint *q) +{ + Rune c; + int nest; + + nest = 1; + for(;;){ + if(dir > 0){ + if(*q == t.nr) + break; + c = t.r[*q]; + (*q)++; + }else{ + if(*q == 0) + break; + (*q)--; + c = t.r[*q]; + } + if(c == cr){ + if(--nest==0) + return 1; + }else if(c == cl) + nest++; + } + return cl=='\n' && nest==1; +} + +void +rcstart(int fd[2]) +{ + int pid; + char *argv[3]; + char slave[256]; + int sfd; + + argv[0] = "rc"; + argv[1] = "-i"; + argv[2] = 0; + + getpts(fd, slave); + switch(pid = fork()) { + case 0: + putenv("TERM=9term"); + close(fd[1]); + setsid(); + sfd = open(slave, ORDWR); + // ioctl(sfd, I_PUSH, "ptem"); + // ioctl(sfd, I_PUSH, "ldterm"); + dup(sfd, 0); + dup(sfd, 1); + dup(sfd, 2); + execvp(argv[0], argv); + break; + case -1: + fatal("proc failed: %r"); + break; + } + close(fd[0]); + fd[0] = fd[1]; + + rcpid = pid; +} + +void +tcheck(void) +{ + Frame *f; + + f = t.f; + + assert(t.q0 <= t.q1 && t.q1 <= t.nr); + assert(t.org <= t.nr && t.qh <= t.nr); + assert(f->p0 <= f->p1 && f->p1 <= f->nchars); + assert(t.org + f->nchars <= t.nr); + assert(t.org+f->nchars==t.nr || (f->nlines >= f->maxlines)); +} + +Rune* +strrune(Rune *s, Rune c) +{ + Rune c1; + + if(c == 0) { + while(*s++) + ; + return s-1; + } + + while(c1 = *s++) + if(c1 == c) + return s-1; + return 0; +} + +void +scrdraw(void) +{ + Rectangle r, r1, r2; + static Image *scrx; + + r = scrollr; + r.min.x += 1; /* border between margin and bar */ + r1 = r; + if(scrx==0 || scrx->r.max.y < r.max.y){ + if(scrx) + freeimage(scrx); + scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow); + if(scrx == 0) + fatal("scroll balloc"); + } + r1.min.x = 0; + r1.max.x = Dx(r); + r2 = scrpos(r1, t.org, t.org+t.f->nchars, t.nr); + if(!eqrect(r2, lastsr)){ + lastsr = r2; + draw(scrx, r1, cols[BORD], nil, ZP); + draw(scrx, r2, cols[BACK], nil, r2.min); +// r2 = r1; +// r2.min.x = r2.max.x-1; +// draw(scrx, r2, cols[BORD], nil, ZP); + draw(screen, r, scrx, nil, r1.min); + } +} + +Rectangle +scrpos(Rectangle r, ulong p0, ulong p1, ulong tot) +{ + long h; + Rectangle q; + + q = insetrect(r, 1); + h = q.max.y-q.min.y; + if(tot == 0) + return q; + if(tot > 1024L*1024L) + tot >>= 10, p0 >>= 10, p1 >>= 10; + if(p0 > 0) + q.min.y += h*p0/tot; + if(p1 < tot) + q.max.y -= h*(tot-p1)/tot; + if(q.max.y < q.min.y+2){ + if(q.min.y+2 <= r.max.y) + q.max.y = q.min.y+2; + else + q.min.y = q.max.y-2; + } + return q; +} + +void +scroll(int but) +{ + uint p0, oldp0; + Rectangle s; + int x, y, my, h, first, exact; + + s = insetrect(scrollr, 1); + h = s.max.y-s.min.y; + x = (s.min.x+s.max.x)/2; + oldp0 = ~0; + first = 1; + do{ + if(t.m.xy.xm; + }else{ + my = t.m.xy.y; + if(my < s.min.y) + my = s.min.y; + if(my >= s.max.y) + my = s.max.y; +// if(!eqpt(t.m.xy, Pt(x, my))) +// cursorset(Pt(x, my)); + exact = 1; + if(but == 2){ + y = my; + if(y > s.max.y-2) + y = s.max.y-2; + if(t.nr > 1024*1024) + p0 = ((t.nr>>10)*(y-s.min.y)/h)<<10; + else + p0 = t.nr*(y-s.min.y)/h; + exact = 0; + } else if(but == 1) + p0 = backnl(t.org, (my-s.min.y)/font->height); + else + p0 = t.org+frcharofpt(t.f, Pt(s.max.x, my)); + + if(oldp0 != p0) + setorigin(p0, exact); + oldp0 = p0; + scrdraw(); + readmouse(mc); + t.m = mc->m; + } + }while(t.m.buttons & (1<<(but-1))); +} + +void +plumbstart(void) +{ + char buf[256]; + snprint(buf, sizeof buf, "%s/mnt/plumb", getenv("HOME")); + if((plumbfd = plumbopen(buf, OWRITE)) < 0) + fatal("plumbopen"); +} + +void +plumb(uint q0, uint q1) +{ + Plumbmsg *pm; + char *p; + int i, p0, n; + char cbuf[100]; + + pm = malloc(sizeof(Plumbmsg)); + pm->src = strdup("9term"); + pm->dst = 0; + pm->wdir = strdup(wdir); + pm->type = strdup("text"); + if(q1 > q0) + pm->attr = nil; + else{ + p0 = q0; + plumbclick(&q0, &q1); + sprint(cbuf, "click=%d", p0-q0); + pm->attr = plumbunpackattr(cbuf); + } + if(q0==q1){ + plumbfree(pm); + return; + } + pm->data = malloc(SnarfSize); + n = q1 - q0; + for(i=0,p=pm->data; idata + SnarfSize-UTFmax; i++) + p += runetochar(p, t.r+q0+i); + *p = '\0'; + pm->ndata = strlen(pm->data); + plumbsend(plumbfd, pm); + plumbfree(pm); +} + +/* + * Process in-band messages about window title changes. + * The messages are of the form: + * + * \033];xxx\007 + * + * where xxx is the new directory. This format was chosen + * because it changes the label on xterm windows. + */ +int +label(Rune *sr, int n) +{ + Rune *sl, *el, *er, *r; + + er = sr+n; + for(r=er-1; r>=sr; r--) + if(*r == '\007') + break; + if(r < sr) + return n; + + el = r+1; + if(el-sr > sizeof wdir) + sr = el - sizeof wdir; + for(sl=el-3; sl>=sr; sl--) + if(sl[0]=='\033' && sl[1]==']' && sl[2]==';') + break; + if(sl < sr) + return n; + + snprint(wdir, sizeof wdir, "%.*S", (el-1)-(sl+3), sl+3); + drawsetlabel(display, wdir); + + runemove(sl, el, er-el); + n -= (el-sl); + return n; +} + blob - /dev/null blob + be2fa4c9097ef37ea036c47bd95ee4cadc8bf61b (mode 644) --- /dev/null +++ src/cmd/9term/9term.h @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define fatal sysfatal + +typedef struct Text Text; +typedef struct Readbuf Readbuf; + +enum +{ + /* these are chosen to use malloc()'s properties well */ + HiWater = 640000, /* max size of history */ + LoWater = 330000, /* min size of history after max'ed */ +}; + +/* various geometric paramters */ +enum +{ + Scrollwid = 12, /* width of scroll bar */ + Scrollgap = 4, /* gap right of scroll bar */ + Maxtab = 4, +}; + +enum +{ + Cut, + Paste, + Snarf, + Send, + Scroll, + Plumb, +}; + + +#define SCROLLKEY Kdown +#define ESC 0x1B +#define CUT 0x18 /* ctrl-x */ +#define COPY 0x03 /* crtl-c */ +#define PASTE 0x16 /* crtl-v */ +#define BACKSCROLLKEY Kup + +#define READBUFSIZE 8192 + +struct Text +{ + Frame *f; /* frame ofr terminal */ + Mouse m; + uint nr; /* num of runes in term */ + Rune *r; /* runes for term */ + uint nraw; /* num of runes in raw buffer */ + Rune *raw; /* raw buffer */ + uint org; /* first rune on the screen */ + uint q0; /* start of selection region */ + uint q1; /* end of selection region */ + uint qh; /* unix point */ + int npart; /* partial runes read from console */ + char part[UTFmax]; + int nsnarf; /* snarf buffer */ + Rune *snarf; +}; + +struct Readbuf +{ + short n; /* # bytes in buf */ + uchar data[READBUFSIZE]; /* data bytes */ +}; + +void mouse(void); +void domenu2(int); +void loop(void); +void geom(void); +void fill(void); +void tcheck(void); +void updatesel(void); +void doreshape(void); +void rcstart(int fd[2]); +void runewrite(Rune*, int); +void consread(void); +void conswrite(char*, int); +int bswidth(Rune c); +void cut(void); +void paste(Rune*, int, int); +void snarfupdate(void); +void snarf(void); +void show(uint); +void key(Rune); +void setorigin(uint org, int exact); +uint line2q(uint); +uint backnl(uint, uint); +int cansee(uint); +uint backnl(uint, uint); +void addraw(Rune*, int); +void mselect(void); +void doubleclick(uint *q0, uint *q1); +int clickmatch(int cl, int cr, int dir, uint *q); +Rune *strrune(Rune *s, Rune c); +int consready(void); +Rectangle scrpos(Rectangle r, ulong p0, ulong p1, ulong tot); +void scrdraw(void); +void scroll(int); +void hostproc(void *arg); +void hoststart(void); +void pdx(int, char*, int); +void plumbstart(void); +void plumb(uint, uint); +void plumbclick(uint*, uint*); +int getpts(int fd[], char *slave); + +#define runemalloc(n) malloc((n)*sizeof(Rune)) +#define runerealloc(a, n) realloc(a, (n)*sizeof(Rune)) +#define runemove(a, b, n) memmove(a, b, (n)*sizeof(Rune)) blob - /dev/null blob + 37dabe9c909f047f45cd39c8daf9ec699061528c (mode 644) --- /dev/null +++ src/cmd/9term/FreeBSD.c @@ -0,0 +1 @@ +#include "Linux.c" blob - /dev/null blob + 7cdb513e5fa0a45cf168f1536663d770f07dcafe (mode 644) --- /dev/null +++ src/cmd/9term/Linux.c @@ -0,0 +1,22 @@ +#include "9term.h" + +void +pdx(int pid, char *wdir, int bufn) +{ + char path[256]; + int n; + + snprint(path, sizeof path, "/proc/%d/cwd", pid); + n = readlink(path, wdir, bufn); + if(n < 0) + n = 0; + wdir[n] = '\0'; +} + +int +getpts(int fd[], char *slave) +{ + + openpty(&fd[1], &fd[0], slave, 0, 0); + return 0; +} blob - /dev/null blob + d524605bc4eaf9b9c3e1de9d341e68bad9e0b2ce (mode 644) --- /dev/null +++ src/cmd/9term/mkfile @@ -0,0 +1,14 @@ +PLAN9=../../.. +<$PLAN9/src/mkhdr + +TARG=9term + +OFILES=\ + 9term.$O\ + $SYSNAME.$O\ + +<$PLAN9/src/mkone + +LDFLAGS=-lframe -ldraw -lplumb -lthread -l9 -lfmt -lutf -L$X11/lib -lX11 -lutil + +