From e830a908498c8f0270948fd08c50f6d773315880 Mon Sep 17 00:00:00 2001 From: rsc Date: Sun, 6 Nov 2005 22:16:48 +0000 Subject: New 9term using rio sources more directly. --- src/cmd/9term/9term.c | 2150 ++++++++---------------------------------------- src/cmd/9term/dat.h | 243 ++++++ src/cmd/9term/data.c | 180 ++++ src/cmd/9term/fns.h | 36 + src/cmd/9term/malloc.c | 63 ++ src/cmd/9term/mkfile | 4 + src/cmd/9term/scrl.c | 183 +++++ src/cmd/9term/time.c | 125 +++ src/cmd/9term/util.c | 149 ++++ src/cmd/9term/wind.c | 1628 ++++++++++++++++++++++++++++++++++++ 10 files changed, 2941 insertions(+), 1820 deletions(-) create mode 100644 src/cmd/9term/dat.h create mode 100644 src/cmd/9term/data.c create mode 100644 src/cmd/9term/fns.h create mode 100644 src/cmd/9term/malloc.c create mode 100644 src/cmd/9term/scrl.c create mode 100644 src/cmd/9term/time.c create mode 100644 src/cmd/9term/util.c create mode 100644 src/cmd/9term/wind.c (limited to 'src/cmd/9term') diff --git a/src/cmd/9term/9term.c b/src/cmd/9term/9term.c index 33104df1..285878e8 100644 --- a/src/cmd/9term/9term.c +++ b/src/cmd/9term/9term.c @@ -10,208 +10,37 @@ #include #include #include +#define Extern +#include "dat.h" +#include "fns.h" #include "term.h" -enum -{ - STACK = 32768 -}; - -int noecho = 0; - -void servedevtext(void); -void listenproc(void*); -void textthread(void*); - -typedef struct Text Text; -typedef struct Readbuf Readbuf; - -enum -{ - HiWater = 640000, /* max size of history */ - LoWater = 400000, /* min size of history after max'ed */ - MinWater = 20000, -}; - -/* various geometric paramters */ -enum -{ - Scrollwid = 12, /* width of scroll bar */ - Scrollgap = 4, /* gap right of scroll bar */ - Maxtab = 4, -}; - -enum -{ - Cut, - Paste, - Snarf, - Send, - Plumb, - Scroll, - Cooked, -}; - -#define ESC 0x1B -#define CUT 0x18 /* ctrl-x */ -#define COPY 0x03 /* crtl-c */ -#define PASTE 0x16 /* crtl-v */ - -#define READBUFSIZE 8192 -#define TRUE 1 -#define FALSE 0 - - -struct Text -{ - Frame *f; /* frame ofr terminal */ - Mouse m; - uint nr; /* num of runes in term */ - uint maxr; /* max num of runes in r */ - 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 runewrite(Rune*, int); -void consread(void); -void conswrite(char*, int); -int bswidth(Rune c, uint start, int eatnl); -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 plumbstart(void); -void plumb(uint, uint); -void plumbclick(uint*, uint*); -uint insert(Rune*, int, uint, int); -void scrolldown(int); -void scrollup(int); - -#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)) -Rectangle scrollr; /* scroll bar rectangle */ -Rectangle lastsr; /* used for scroll bar */ -int holdon; /* hold mode */ -int rawon(void); /* raw mode */ -int cooked; /* force cooked */ -int scrolling; /* window scrolls */ -int clickmsec; /* time of last click */ -uint clickq0; /* point of last click */ -int rcfd; -int sfd; /* slave fd, to get/set terminal mode */ -int rcpid; -int maxtab; -int use9wm; -Mousectl* mc; -Keyboardctl* kc; -Channel* hostc; -Readbuf rcbuf[2]; -int mainpid; -int acmecolors; -int plumbfd; -int button2exec; -int label(Rune*, int); -char wdir[1024]; -char childwdir[1024]; -void hangupnote(void*, char*); -char thesocket[100]; - -char *menu2str[] = { - "cut", - "paste", - "snarf", - "send", - "plumb", - "scroll", - "cooked", - 0 -}; - -Image* cols[NCOL]; -Image* hcols[NCOL]; -Image* palegrey; -Image* paleblue; -Image* blue; -Image *plumbcolor; -Image *execcolor; - -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, } -}; - -Cursor query = { - {-7,-7}, - {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, - 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, - 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, - 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, }, - {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, - 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, - 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, - 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } -}; +int use9wm; +int mainpid; +int plumbfd; +int rcpid; +int rcfd; +int sfd; +int noecho; +Window *w; +char *fontname; + +void derror(Display*, char*); +void mousethread(void*); +void keyboardthread(void*); +void winclosethread(void*); +void deletethread(void*); +void rcoutputproc(void*); +void rcinputproc(void*); +void hangupnote(void*, char*); +void resizethread(void*); + +int errorshouldabort = 0; void usage(void) { - fprint(2, "usage: 9term [-ars] [-W winsize] [cmd ...]\n"); + fprint(2, "usage: 9term [-s] [-f font] [-W winsize] [cmd ...]\n"); threadexitsall("usage"); } @@ -219,95 +48,79 @@ void threadmain(int argc, char *argv[]) { char *p, *font; - char buf[32]; - + rfork(RFNOTEG); font = nil; _wantfocuschanges = 1; mainpid = getpid(); + messagesize = 8192; + ARGBEGIN{ default: usage(); - case 'a': /* acme mode */ - button2exec++; - break; case 'f': font = EARGF(usage()); break; case 's': - scrolling++; + scrolling = TRUE; break; - case 'w': /* started from "rio" window manager */ - use9wm = 1; + case 'w': /* started from rio or 9wm */ + use9wm = TRUE; break; case 'W': winsize = EARGF(usage()); break; }ARGEND - + if(font) putenv("font", font); - + p = getenv("tabstop"); if(p == 0) p = getenv("TABSTOP"); - if(p != 0 && maxtab <= 0) + if(p && maxtab <= 0) maxtab = strtoul(p, 0, 0); if(maxtab <= 0) - maxtab = 4; /* be like rio */ - - snprint(buf, sizeof buf, "%d", maxtab); - putenv("tabstop", buf); - - initdraw(0, nil, "9term"); + maxtab = 4; + free(p); + + startdir = "."; + + initdraw(derror, nil, "9term"); notify(hangupnote); noteenable("sys: child"); - servedevtext(); - - mc = initmouse(nil, screen); - kc = initkeyboard(nil); - rcpid = rcstart(argc, argv, &rcfd, &sfd); - hoststart(); - plumbstart(); - - t.f = mallocz(sizeof(Frame), 1); - - if(acmecolors){ - 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); - }else{ - cols[BACK] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DWhite); - cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF); - cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x999999FF); - } - cols[TEXT] = display->black; - cols[HTEXT] = display->black; - palegrey = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x666666FF); - - hcols[BACK] = cols[BACK]; - hcols[HIGH] = cols[HIGH]; - blue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMedblue); - paleblue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DGreyblue); +// servedevtext(); + + mousectl = initmouse(nil, screen); + if(mousectl == nil) + error("cannot find mouse"); + keyboardctl = initkeyboard(nil); + if(keyboardctl == nil) + error("cannot find keyboard"); + if((plumbfd = plumbopen("send", OWRITE)) < 0) + fprint(2, "9term: plumbopen: %r\n"); + mouse = &mousectl->m; - hcols[BORD] = blue; - hcols[TEXT] = hcols[BORD]; - hcols[HTEXT] = hcols[TEXT]; + winclosechan = chancreate(sizeof(Window*), 0); + deletechan = chancreate(sizeof(char*), 0); - plumbcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x006600FF); - execcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAA0000FF); + timerinit(); + rcpid = rcstart(argc, argv, &rcfd, &sfd); + w = new(screen, FALSE, scrolling, rcpid, ".", nil, nil); - if(!blue || !palegrey || !paleblue || !plumbcolor || !execcolor) - sysfatal("alloc colors: %r"); - draw(screen, screen->r, cols[BACK], nil, ZP); - geom(); - loop(); + threadcreate(keyboardthread, nil, STACK); + threadcreate(mousethread, nil, STACK); + threadcreate(resizethread, nil, STACK); + + proccreate(rcoutputproc, nil, STACK); + proccreate(rcinputproc, nil, STACK); } -int -isexpand(Rune r) +void +derror(Display *d, char *errorstr) { - return r=='_' || ('0' <= r && r <= '9') || isalpharune(r); + USED(d); + error(errorstr); } void @@ -335,1653 +148,350 @@ hangupnote(void *a, char *msg) } void -hostproc(void *arg) +keyboardthread(void *v) { - Channel *c; - int i, n, which; - - c = arg; + Rune buf[2][20], *rp; + int i, n; - i = 0; + USED(v); + threadsetname("keyboardthread"); + n = 0; for(;;){ - /* Let typing have a go -- maybe there's a rubout waiting. */ - yield(); - - i = 1-i; /* toggle */ - n = read(rcfd, 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); + rp = buf[n]; + n = 1-n; + recv(keyboardctl->c, rp); + for(i=1; ic, rp+i) <= 0) + break; + rp[i] = L'\0'; + sendp(w->ck, rp); } } void -hoststart(void) -{ - hostc = chancreate(sizeof(int), 0); - proccreate(hostproc, hostc, 32*1024); -} - -void -loop(void) +resizethread(void *v) { - Rune r; - int i; - Alt a[5]; - - a[0].c = mc->c; - a[0].v = &mc->m; - a[0].op = CHANRCV; - - a[1].c = kc->c; - a[1].v = &r; - a[1].op = CHANRCV; - - a[2].c = hostc; - a[2].v = &i; - a[2].op = CHANRCV; - - a[3].c = mc->resizec; - a[3].v = nil; - a[3].op = CHANRCV; - - a[4].c = nil; - a[4].v = nil; - a[4].op = 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: - sysfatal("impossible"); - case 0: - t.m = mc->m; - mouse(); - break; - case 1: - key(r); - break; - case 2: - conswrite((char*)rcbuf[i].data, rcbuf[i].n); - break; - case 3: - doreshape(); - break; - } + USED(v); + + while(recv(mousectl->resizec, nil) == 1){ + if(getwindow(display, Refnone) < 0) + sysfatal("can't reattach to window"); + wresize(w, screen, 0); } } - + void -doreshape(void) +mousethread(void *v) { - if(getwindow(display, Refnone) < 0) - sysfatal("can't reattach to window"); - draw(screen, screen->r, cols[BACK], nil, ZP); - geom(); - scrdraw(); -} + int sending; + Mouse tmp; -void -geom(void) -{ - Point p; - Rectangle r; + USED(v); - if(!acmecolors){ - if(_windowhasfocus){ - cols[TEXT] = cols[HTEXT] = display->black; - hcols[TEXT] = hcols[HTEXT] = blue; - }else{ - cols[TEXT] = cols[HTEXT] = palegrey; - hcols[TEXT] = hcols[HTEXT] = paleblue; + sending = FALSE; + threadsetname("mousethread"); + while(readmouse(mousectl) >= 0){ + if(sending){ + Send: + /* send to window */ + if(mouse->buttons == 0) + sending = FALSE; + else + wsetcursor(w, 0); + tmp = mousectl->m; + send(w->mc.c, &tmp); + continue; } + if(mouse->buttons&1){ + sending = TRUE; + goto Send; + }else if(mouse->buttons&2) + button2menu(w); + else + /* send to rio */; } - - r = screen->r; - r.min.y++; - r.max.y--; - - scrollr = 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(); - - p = stringsize(font, "0"); - if(p.x == 0 || p.y == 0) - return; - - updatewinsize(Dy(r)/p.y, Dx(r)/p.x, Dx(r), Dy(r)); } - + void -drawhold(int holdon) +wborder(Window *w, int type) { - if(holdon) - setcursor(mc, &whitearrow); - else - setcursor(mc, nil); - - draw(screen, screen->r, cols[BACK], nil, ZP); - geom(); - scrdraw(); } -void -wordclick(uint *q0, uint *q1) +Window* +wpointto(Point pt) { - while(*q10 && !isspace(t.r[*q0-1])) - (*q0)--; + return w; } -int -aselect(uint *q0, uint *q1, Image *color) +Window* +new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv) { - int cancel; - uint oldq0, oldq1, newq0, newq1; - - /* save old selection */ - oldq0 = t.q0; - oldq1 = t.q1; - - /* sweep out area and record it */ - t.f->cols[HIGH] = color; - 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) - return -1; - - /* selected a region */ - if(newq0 < newq1){ - *q0 = newq0; - *q1 = newq1; - return 0; - } - - /* clicked inside previous selection */ - /* the "<=" in newq0 <= oldq1 allows us to click the right edge */ - if(oldq0 <= newq0 && newq0 <= oldq1){ - *q0 = oldq0; - *q1 = oldq1; - return 0; - } + Window *w; + Mousectl *mc; + Channel *cm, *ck, *cctl; - /* just a click */ - *q0 = newq0; - *q1 = newq1; - return 0; + if(i == nil) + return nil; + cm = chancreate(sizeof(Mouse), 0); + ck = chancreate(sizeof(Rune*), 0); + cctl = chancreate(sizeof(Wctlmesg), 4); + if(cm==nil || ck==nil || cctl==nil) + error("new: channel alloc failed"); + mc = emalloc(sizeof(Mousectl)); + *mc = *mousectl; +// mc->image = i; + mc->c = cm; + w = wmk(i, mc, ck, cctl, scrollit); + free(mc); /* wmk copies *mc */ + window = erealloc(window, ++nwindow*sizeof(Window*)); + window[nwindow-1] = w; + if(hideit){ + hidden[nhidden++] = w; + w->screenr = ZR; + } + threadcreate(winctl, w, 8192); + if(!hideit) + wcurrent(w); + flushimage(display, 1); + wsetpid(w, pid, 1); + wsetname(w); + if(dir) + w->dir = estrdup(dir); + return w; } -static Rune Lnl[1] = { '\n' }; +/* + * Button 2 menu. Extra entry for always cook + */ +int cooked; -void -mouse(void) +enum { - int but; - uint q0, q1; - - but = t.m.buttons; + Cut, + Paste, + Snarf, + Plumb, + Send, + Scroll, + Cook, +}; - if(but != 1 && but != 2 && but != 4 && but != 8 && but != 16) - return; +char *menu2str[] = { + "cut", + "paste", + "snarf", + "plumb", + "send", + "scroll", + "cook", + nil +}; - 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: - if(button2exec){ - if(aselect(&q0, &q1, execcolor) >= 0){ - if(q0 == q1) - wordclick(&q0, &q1); - if(q0 == q1) - break; - t.q0 = t.q1 = t.nr; - updatesel(); - paste(t.r+q0, q1-q0, 1); - if(t.r[q1-1] != '\n') - paste(Lnl, 1, 1); - } - break; - } - domenu2(2); - break; - case 4: - bouncemouse(&t.m); - break; - /* - if(aselect(&q0, &q1, plumbcolor) >= 0) - plumb(q0, q1); - break; - */ - case 8: - scrollup(mousescrollsize(t.f->maxlines)); - break; - case 16: - scrolldown(mousescrollsize(t.f->maxlines)); - break; - } -} -void -mselect(void) +Menu menu2 = { - 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; - } - } - readmouse(mc); - t.m = mc->m; - } - } -} + menu2str +}; -Rune newline[] = { '\n', 0 }; +Rune newline[] = { '\n' }; void -domenu2(int but) +button2menu(Window *w) { - if(scrolling) - menu2str[Scroll] = "+ scroll"; + if(w->deleted) + return; + incref(&w->ref); + if(w->scrolling) + menu2str[Scroll] = "noscroll"; else - menu2str[Scroll] = "- scroll"; + menu2str[Scroll] = "scroll"; if(cooked) - menu2str[Cooked] = "+ mustecho"; + menu2str[Cook] = "nocook"; else - menu2str[Cooked] = "- mustecho"; + menu2str[Cook] = "cook"; - switch(menuhit(but, mc, &menu2, nil)){ - case -1: - break; + switch(menuhit(2, mousectl, &menu2, wscreen)){ case Cut: - snarf(); - cut(); - if(scrolling) - show(t.q0); + wsnarf(w); + wcut(w); + wscrdraw(w); + break; + + case Snarf: + wsnarf(w); break; + case Paste: - snarfupdate(); - paste(t.snarf, t.nsnarf, 0); - if(scrolling) - show(t.q0); + //XXX getsnarf(); + wpaste(w); + wscrdraw(w); break; - case Snarf: - snarf(); - if(scrolling) - show(t.q0); + + case Plumb: + wplumb(w); break; + case Send: - if(t.q0 != t.q1) - snarf(); - else - snarfupdate(); - t.q0 = t.q1 = t.nr; - updatesel(); - 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(); + //XXX getsnarf(); + wsnarf(w); + if(nsnarf == 0) + break; + if(w->rawing){ + waddraw(w, snarf, nsnarf); + if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004') + waddraw(w, newline, 1); + }else{ + winsert(w, snarf, nsnarf, w->nr); + if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004') + winsert(w, newline, 1, w->nr); } + wsetselect(w, w->nr, w->nr); + wshow(w, w->nr); break; - case Plumb: - plumb(t.q0, t.q1); + + case Scroll: + if(w->scrolling ^= 1) + wshow(w, w->nr); break; - case Cooked: - cooked = !cooked; + + case Cook: + cooked ^= 1; break; - default: - sysfatal("bad menu item"); } + wclose(w); + wsendctlmesg(w, Wakeup, ZR, nil); + flushimage(display, 1); } int -windfilewidth(uint q0, int oneelement) +rawon(void) { - uint q; - Rune r; - - q = q0; - while(q > 0){ - r = t.r[q-1]; - if(r<=' ') - break; - if(oneelement && r=='/') - break; - --q; - } - return q0-q; + return !cooked && !isecho(sfd); } +/* + * I/O with child rc. + */ + +int label(Rune*, int); + void -showcandidates(Completion *c) +rcoutputproc(void *arg) { - int i; - Fmt f; - Rune *rp; - uint nr, qline, q0; - char *s; - - runefmtstrinit(&f); - if (c->nmatch == 0) - s = "[no matches in "; - else - s = "["; - if(c->nfile > 32) - fmtprint(&f, "%s%d files]\n", s, c->nfile); - else{ - fmtprint(&f, "%s", s); - for(i=0; infile; i++){ - if(i > 0) - fmtprint(&f, " "); - fmtprint(&f, "%s", c->filename[i]); + int i, cnt, n, nb, nr; + static char data[9000]; + Conswritemesg cwm; + Rune *r; + Stringpair pair; + + i = 0; + cnt = 0; + for(;;){ + /* XXX Let typing have a go -- maybe there's a rubout waiting. */ + i = 1-i; + n = read(rcfd, data+cnt, sizeof data-cnt); + if(n <= 0){ + if(n < 0) + fprint(2, "9term: rc read error: %r\n"); + threadexitsall("eof on rc output"); } - fmtprint(&f, "]\n"); - } - /* place text at beginning of line before host point */ - qline = t.qh; - while(qline>0 && t.r[qline-1] != '\n') - qline--; - - rp = runefmtstrflush(&f); - nr = runestrlen(rp); + cnt += n; + r = runemalloc(cnt); + cvttorunes(data, cnt-UTFmax, r, &nb, &nr, nil); + /* approach end of buffer */ + while(fullrune(data+nb, cnt-nb)){ + nb += chartorune(&r[nr], data+nb); + if(r[nr]) + nr++; + } + if(nb < cnt) + memmove(data, data+nb, cnt-nb); + cnt -= nb; + + nr = label(r, nr); + if(nr == 0) + continue; - q0 = t.q0; - q0 += insert(rp, nr, qline, 0) - qline; - free(rp); - t.q0 = q0+nr; - t.q1 = q0+nr; - updatesel(); + recv(w->conswrite, &cwm); + pair.s = r; + pair.ns = nr; + send(cwm.cw, &pair); + } } -Rune* -namecomplete(void) +/* + * 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) { - int nstr, npath; - Rune *rp, *path, *str; - Completion *c; - char *s, *dir, *root; + Rune *sl, *el, *er, *r; + char *p, *dir; + + er = sr+n; + for(r=er-1; r>=sr; r--) + if(*r == '\007') + break; + if(r < sr) + return n; - /* control-f: filename completion; works back to white space or / */ - if(t.q0' ') /* must be at end of word */ - return nil; - nstr = windfilewidth(t.q0, TRUE); - str = runemalloc(nstr); - runemove(str, t.r+(t.q0-nstr), nstr); - npath = windfilewidth(t.q0-nstr, FALSE); - path = runemalloc(npath); - runemove(path, t.r+(t.q0-nstr-npath), npath); - rp = nil; + el = r+1; + for(sl=el-3; sl>=sr; sl--) + if(sl[0]=='\033' && sl[1]==']' && sl[2]==';') + break; + if(sl < sr) + return n; - /* is path rooted? if not, we need to make it relative to window path */ - if(npath>0 && path[0]=='/'){ - dir = malloc(UTFmax*npath+1); - sprint(dir, "%.*S", npath, path); - }else{ - if(strcmp(wdir, "") == 0) - root = "."; - else - root = wdir; - dir = malloc(strlen(root)+1+UTFmax*npath+1); - sprint(dir, "%s/%.*S", root, npath, path); + dir = smprint("%.*S", (el-1)-(sl+3), sl+3); + if(dir){ + drawsetlabel(dir); + free(w->dir); + w->dir = dir; } - dir = cleanname(dir); - s = smprint("%.*S", nstr, str); - c = complete(dir, s); - free(s); - if(c == nil) - goto Return; - - if(!c->advance) - showcandidates(c); - - if(c->advance) - rp = runesmprint("%s", c->string); - - Return: - freecompletion(c); - free(dir); - free(path); - free(str); - return rp; -} - -void -scrollup(int n) -{ - setorigin(backnl(t.org, n), 1); -} - -void -scrolldown(int n) -{ - setorigin(line2q(n), 1); - if(t.qh<=t.org+t.f->nchars) - consread(); -} - -void -key(Rune r) -{ - Rune *rp; - int nr; - - if(r == 0) - return; - switch(r){ - case Kpgup: - scrollup(t.f->maxlines*2/3); - return; - case Kpgdown: - scrolldown(t.f->maxlines*2/3); - return; - case Kup: - scrollup(t.f->maxlines/3); - return; - case Kdown: - scrolldown(t.f->maxlines/3); - return; - case Kleft: - if(t.q0 > 0){ - t.q0--; - t.q1 = t.q0; - updatesel(); - show(t.q0); - } - return; - case Kright: - if(t.q1 < t.nr){ - t.q1++; - t.q0 = t.q1; - updatesel(); - show(t.q1); - } - return; - case Khome: - show(0); - return; - case Kend: - case 0x05: - show(t.nr); - return; - - /* - * Non-standard extensions. - */ - case CUT: - snarf(); - cut(); - if(scrolling) - show(t.q0); - return; - case COPY: - snarf(); - if(scrolling) - show(t.q0); - return; - case PASTE: - snarfupdate(); - paste(t.snarf, t.nsnarf, 0); - if(scrolling) - show(t.q0); - return; - } - - /* - * This if used to be below the if(rawon() && t.q0==t.nr), - * but let's try putting it here. This will allow ESC-processing - * to toggle hold mode even in remote SSH connections. - * The drawback is that vi-style processing gets harder. - * If you find yourself in some weird readline mode, good - * luck getting out without ESC. Let's see who complains. - */ - if(r==ESC){ /* toggle hold */ - holdon = !holdon; - drawhold(holdon); - /* replaceintegerproperty("_9WM_HOLD_MODE", 1, 32, holdon); */ - if(!holdon) - consread(); - return; - } - - if(!holdon && rawon() && t.q0 == t.nr){ - addraw(&r, 1); - consread(); - return; - } - - if(r == 0x7F){ /* DEL: send interrupt; what a mess */ - char rubout[1]; - - if(holdon){ - holdon = 0; - drawhold(holdon); - } - t.qh = t.q0 = t.q1 = t.nr; - show(t.q0); - rubout[0] = getintr(sfd); - write(rcfd, rubout, 1); - return; - } - - snarf(); - - switch(r) { - case 0x06: /* ^F: file name completion */ - case Kins: /* Insert: file name completion */ - rp = namecomplete(); - if(rp == nil) - return; - nr = runestrlen(rp); - paste(rp, nr, 1); - free(rp); - return; - 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, t.q0, 1); - cut(); - break; - default: - paste(&r, 1, 1); - break; - } - if(scrolling) - show(t.q0); -} - -int -bswidth(Rune c, uint start, int eatnl) -{ - 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 = start; - 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 == start && eatnl) /* eat the newline */ - --q; - break; - } - if(c == 0x17){ - eq = isexpand(r); - if(eq && skipping) /* found one; stop skipping */ - skipping = 0; - else if(!eq && !skipping) - break; - } - --q; - } - return start-q; -} - -int -consready(void) -{ - int i, c; - - if(holdon) - return 0; - - if(rawon()) - return t.nraw != 0 || t.qh < t.nr; - - /* 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(c == 0x7F){ - *(p-1) = getintr(sfd); - if(!raw) - break; - } - if(!raw && (c == '\n' || c == '\004')) - break; - } - n = p-buf; - - /* - * If we've been echoing, make sure the terminal isn't - * while we do the write. This screws up if someone - * else tries to turn off echo at the same time we do - * (we'll turn it on again after the write), but that's not - * too likely. - */ - s = setecho(sfd, 0); - if(write(rcfd, buf, n) < 0) - threadexitsall(0); - if(s) - setecho(sfd, s); - } -} - -void -conswrite(char *p, int n) -{ - int n2, i; - Rune buf2[1000], *q; - - /* convert to runes */ - i = t.npart; - if(i > 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) -{ - static int havecr; - int i; - uint initial; - uint q0, q1; - uint p0, p1; - Rune *p, *q; - - n = label(r, n); - if(n == 0) - return; - - /* process trailing \r from previous write */ - initial = 0; - if(havecr && *r != '\r' && *r != '\n') - initial = bswidth(0x15, t.qh, 0); - havecr = 0; - - /* get rid of backspaces */ - p = q = r; - for(i=0; i r && *(q-1) != '\n') - q--; - if(q==r) - initial = bswidth(0x15, t.qh, 0); - }else if(i == n-1) - havecr = 1; - } else if(*p) - *q++ = *p; - p++; - } - n = q-r; - - if(initial){ - /* write turned into a delete */ - - if(initial > 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(); - } - - insert(r, n, t.qh, 1); -} - - -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(); - if(pp == nil) - return; - n = strlen(pp); - if(n <= 0) { - /*t.nsnarf = 0;*/ - return; - } - t.snarf = runerealloc(t.snarf, n); - for(i=0,p=t.snarf; i y) - return x; - return y; -} - -uint -insert(Rune *r, int n, uint q0, int hostwrite) -{ - uint m; - - if(n == 0) - return q0; - if(t.nr+n>HiWater && q0>=t.org && q0>=t.qh){ - m = min(HiWater-LoWater, min(t.org, t.qh)); - 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); - q0 -= m; - } - if(t.nr+n > t.maxr){ - /* - * Minimize realloc breakage: - * Allocate at least MinWater - * Double allocation size each time - * But don't go much above HiWater - */ - m = max(min(2*(t.nr+n), HiWater), t.nr+n)+MinWater; - if(m > HiWater) - m = max(HiWater+MinWater, t.nr+n); - if(m > t.maxr){ - t.r = runerealloc(t.r, m); - t.maxr = m; - } - } - runemove(t.r+q0+n, t.r+q0, t.nr-q0); - runemove(t.r+q0, r, n); - t.nr += n; - /* if output touches, advance selection, not qh; works best for keyboard and output */ - if(q0 <= t.q1) - t.q1 += n; - if(q0 <= t.q0) - t.q0 += n; - if(q0 < t.qh || (q0==t.qh && hostwrite)) - t.qh += n; - else - consread(); - if(q0 < t.org) - t.org += n; - else if(q0 <= t.org+t.f->nchars) - frinsert(t.f, r, r+n, q0-t.org); - return q0; -} - -void -paste(Rune *r, int n, int advance) -{ - Rune *rbuf; - - if(!holdon && rawon() && t.q0==t.nr){ - addraw(r, n); - consread(); - return; - } - - cut(); - if(n == 0) - return; - - /* - * if this is a button2 execute then we might have been passed - * runes inside the buffer. must save them before realloc. - */ - rbuf = nil; - if(t.r <= r && r < t.r+n){ - rbuf = runemalloc(n); - runemove(rbuf, r, n); - r = rbuf; - } - - insert(r, n, t.q0, 0); - updatesel(); - free(rbuf); -} - -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--; - } -} - -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 && isexpand(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 -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) - sysfatal("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) -{ - if((plumbfd = plumbopen("send", OWRITE)) < 0) - fprint(2, "9term: plumbopen: %r\n"); -} - -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"); - pm->data = nil; - if(q1 > q0) - pm->attr = nil; - else{ - p0 = q0; - wordclick(&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); - if(plumbsend(plumbfd, pm) < 0){ - setcursor(mc, &query); - sleep(500); - if(holdon) - setcursor(mc, &whitearrow); - else - setcursor(mc, nil); - } - 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; - char *p; - - 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(wdir); - - /* remove trailing /-sysname if present */ - p = strrchr(wdir, '/'); - if(p && *(p+1) == '-'){ - if(p == wdir) - p++; - *p = 0; - } + /* remove trailing /-sysname if present */ + p = strrchr(dir, '/'); + if(p && *(p+1) == '-'){ + if(p == dir) + p++; + *p = 0; + } runemove(sl, el, er-el); n -= (el-sl); return n; } -int -rawon(void) -{ - return !cooked && !isecho(sfd); -} - -/* - * Clumsy hack to make " and "" work. - * Then again, what's not a clumsy hack here in Unix land? - */ - -char adir[100]; -int afd; - -void -removethesocket(void) -{ - if(thesocket[0]) - if(remove(thesocket) < 0) - fprint(2, "remove %s: %r\n", thesocket); -} - void -servedevtext(void) +rcinputproc(void *arg) { - char buf[100]; - - snprint(buf, sizeof buf, "unix!/tmp/9term-text.%d", getpid()); + static char data[9000]; + int s; + Consreadmesg crm; + Channel *c1, *c2; + Stringpair pair; - if((afd = announce(buf, adir)) < 0){ - putenv("text9term", ""); - return; - } - - putenv("text9term", buf); - proccreate(listenproc, nil, STACK); - strcpy(thesocket, buf+5); - atexit(removethesocket); -} - -void -listenproc(void *arg) -{ - int fd; - char dir[100]; - - USED(arg); for(;;){ - fd = listen(adir, dir); - if(fd < 0){ - close(afd); - return; - } - proccreate(textthread, (void*)fd, STACK); + recv(w->consread, &crm); + c1 = crm.c1; + c2 = crm.c2; + + pair.s = data; + pair.ns = sizeof data; + send(c1, &pair); + recv(c2, &pair); + + s = setecho(sfd, 0); + if(write(rcfd, pair.s, pair.ns) < 0) + threadexitsall(nil); + if(s) + setecho(sfd, s); } } -void -textthread(void *arg) -{ - int fd, i, x, n, end; - Rune r; - char buf[4096], *p, *ep; - - fd = (int)arg; - p = buf; - ep = buf+sizeof buf; - end = t.org+t.nr; /* avoid possible output loop */ - for(i=t.org;; i++){ - if(i >= end || ep-p < UTFmax){ - for(x=0; x= end) - break; - p = buf; - } - if(i < t.org) - i = t.org; - r = t.r[i-t.org]; - if(r < Runeself) - *p++ = r; - else - p += runetochar(p, &r); - } -break2: - close(fd); -} diff --git a/src/cmd/9term/dat.h b/src/cmd/9term/dat.h new file mode 100644 index 00000000..cf7aa9f9 --- /dev/null +++ b/src/cmd/9term/dat.h @@ -0,0 +1,243 @@ +#define STACK 32768 +#undef Borderwidth +#define Borderwidth 0 + +typedef struct Consreadmesg Consreadmesg; +typedef struct Conswritemesg Conswritemesg; +typedef struct Stringpair Stringpair; +typedef struct Dirtab Dirtab; +typedef struct Mouseinfo Mouseinfo; +typedef struct Mousereadmesg Mousereadmesg; +typedef struct Mousestate Mousestate; +typedef struct Timer Timer; +typedef struct Wctlmesg Wctlmesg; +typedef struct Window Window; + +enum +{ + Selborder = 0, /* border of selected window */ + Unselborder = 0, /* border of unselected window */ + Scrollwid = 12, /* width of scroll bar */ + Scrollgap = 4, /* gap right of scroll bar */ + BIG = 3, /* factor by which window dimension can exceed screen */ + TRUE = 1, + FALSE = 0, +}; + +enum +{ + Kscrolloneup = KF|0x20, + Kscrollonedown = KF|0x21, +}; + +enum /* control messages */ +{ + Wakeup, + Reshaped, + Moved, + Refresh, + Movemouse, + Rawon, + Rawoff, + Holdon, + Holdoff, + Deleted, + Exited, +}; + +struct Wctlmesg +{ + int type; + Rectangle r; + Image *image; +}; + +struct Conswritemesg +{ + Channel *cw; /* chan(Stringpair) */ +}; + +struct Consreadmesg +{ + Channel *c1; /* chan(tuple(char*, int) == Stringpair) */ + Channel *c2; /* chan(tuple(char*, int) == Stringpair) */ +}; + +struct Mousereadmesg +{ + Channel *cm; /* chan(Mouse) */ +}; + +struct Stringpair /* rune and nrune or byte and nbyte */ +{ + void *s; + int ns; +}; + +struct Mousestate +{ + Mouse m; + ulong counter; /* serial no. of mouse event */ +}; + +struct Mouseinfo +{ + Mousestate queue[16]; + int ri; /* read index into queue */ + int wi; /* write index */ + ulong counter; /* serial no. of last mouse event we received */ + ulong lastcounter; /* serial no. of last mouse event sent to client */ + int lastb; /* last button state we received */ + uchar qfull; /* filled the queue; no more recording until client comes back */ +}; + +struct Window +{ + Ref ref; + QLock lk; + Frame f; + Image *i; + Mousectl mc; + Mouseinfo mouse; + Channel *ck; /* chan(Rune[10]) */ + Channel *cctl; /* chan(Wctlmesg)[20] */ + Channel *conswrite; /* chan(Conswritemesg) */ + Channel *consread; /* chan(Consreadmesg) */ + Channel *mouseread; /* chan(Mousereadmesg) */ + Channel *wctlread; /* chan(Consreadmesg) */ + uint nr; /* number of runes in window */ + uint maxr; /* number of runes allocated in r */ + Rune *r; + uint nraw; + Rune *raw; + uint org; + uint q0; + uint q1; + uint qh; + int id; + char name[32]; + uint namecount; + Rectangle scrollr; + /* + * Rio once used originwindow, so screenr could be different from i->r. + * Now they're always the same but the code doesn't assume so. + */ + Rectangle screenr; /* screen coordinates of window */ + int resized; + int wctlready; + Rectangle lastsr; + int topped; + int notefd; + uchar scrolling; + Cursor cursor; + Cursor *cursorp; + uchar holding; + uchar rawing; + uchar ctlopen; + uchar wctlopen; + uchar deleted; + uchar mouseopen; + char *label; + int pid; + char *dir; +}; + +int winborder(Window*, Point); +void winctl(void*); +void winshell(void*); +Window* wlookid(int); +Window* wmk(Image*, Mousectl*, Channel*, Channel*, int); +Window* wpointto(Point); +Window* wtop(Point); +void wtopme(Window*); +void wbottomme(Window*); +char* wcontents(Window*, int*); +int wbswidth(Window*, Rune); +int wclickmatch(Window*, int, int, int, uint*); +int wclose(Window*); +int wctlmesg(Window*, int, Rectangle, Image*); +int wctlmesg(Window*, int, Rectangle, Image*); +uint wbacknl(Window*, uint, uint); +uint winsert(Window*, Rune*, int, uint); +void waddraw(Window*, Rune*, int); +void wborder(Window*, int); +void wclosewin(Window*); +void wcurrent(Window*); +void wcut(Window*); +void wdelete(Window*, uint, uint); +void wdoubleclick(Window*, uint*, uint*); +void wfill(Window*); +void wframescroll(Window*, int); +void wkeyctl(Window*, Rune); +void wmousectl(Window*); +void wmovemouse(Window*, Point); +void wpaste(Window*); +void wplumb(Window*); +void wrefresh(Window*, Rectangle); +void wrepaint(Window*); +void wresize(Window*, Image*, int); +void wscrdraw(Window*); +void wscroll(Window*, int); +void wselect(Window*); +void wsendctlmesg(Window*, int, Rectangle, Image*); +void wsetcursor(Window*, int); +void wsetname(Window*); +void wsetorigin(Window*, uint, int); +void wsetpid(Window*, int, int); +void wsetselect(Window*, uint, uint); +void wshow(Window*, uint); +void wsnarf(Window*); +void wscrsleep(Window*, uint); +void wsetcols(Window*); + +void deletetimeoutproc(void*); + +struct Timer +{ + int dt; + int cancel; + Channel *c; /* chan(int) */ + Timer *next; +}; + +#ifndef Extern +#define Extern extern +#endif + +Extern Font *font; +Extern Mousectl *mousectl; +Extern Mouse *mouse; +Extern Keyboardctl *keyboardctl; +Extern Display *display; +Extern Image *view; +Extern Screen *wscreen; +Extern Cursor boxcursor; +Extern Cursor crosscursor; +Extern Cursor sightcursor; +Extern Cursor whitearrow; +Extern Cursor query; +Extern Cursor *corners[9]; +Extern Image *background; +Extern Image *lightgrey; +Extern Image *red; +Extern Window **window; +Extern Window *wkeyboard; /* window of simulated keyboard */ +Extern int nwindow; +Extern int snarffd; +Extern Window *input; +Extern QLock all; /* BUG */ +Extern Window *hidden[100]; +Extern int nhidden; +Extern int nsnarf; +Extern Rune* snarf; +Extern int scrolling; +Extern int maxtab; +Extern Channel* winclosechan; +Extern Channel* deletechan; +Extern char *startdir; +Extern int sweeping; +Extern int wctlfd; +Extern int errorshouldabort; +Extern int menuing; /* menu action is pending; waiting for window to be indicated */ +Extern int snarfversion; /* updated each time it is written */ +Extern int messagesize; /* negotiated in 9P version setup */ diff --git a/src/cmd/9term/data.c b/src/cmd/9term/data.c new file mode 100644 index 00000000..86e589b3 --- /dev/null +++ b/src/cmd/9term/data.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +Cursor crosscursor = { + {-7, -7}, + {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, }, + {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE, + 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, } +}; + +Cursor boxcursor = { + {-7, -7}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, + 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, + {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, + 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, + 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, + 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00, } +}; + +Cursor sightcursor = { + {-7, -7}, + {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, + 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, + 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, }, + {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, + 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, + 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, + 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, } +}; + +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, } +}; + +Cursor query = { + {-7,-7}, + {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, + 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, + 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, + 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, }, + {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, + 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, + 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, + 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } +}; + +Cursor tl = { + {-4, -4}, + {0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff, + 0xa0, 0x01, 0xb0, 0x01, 0xd0, 0x01, 0x11, 0xff, + 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, + 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00, }, + {0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00, + 0x5f, 0xfe, 0x4f, 0xfe, 0x0f, 0xfe, 0x0e, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, } +}; + +Cursor t = { + {-7, -8}, + {0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0, + 0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80, + 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } +}; + +Cursor tr = { + {-11, -4}, + {0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1, + 0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88, + 0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88, + 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, }, + {0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e, + 0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70, + 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, + 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, } +}; + +Cursor r = { + {-8, -7}, + {0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58, + 0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02, + 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58, + 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, }, + {0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, + 0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc, + 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80, + 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } +}; + +Cursor br = { + {-11, -11}, + {0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, + 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, + 0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05, + 0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, }, + {0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, + 0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, + 0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa, + 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, } +}; + +Cursor b = { + {-7, -7}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70, + 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, }, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, + 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } +}; + +Cursor bl = { + {-4, -11}, + {0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, + 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, + 0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01, + 0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, }, + {0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe, + 0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, } +}; + +Cursor l = { + {-7, -7}, + {0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20, + 0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20, + 0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20, + 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, }, + {0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, + 0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0, + 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0, + 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, } +}; + +Cursor *corners[9] = { + &tl, &t, &tr, + &l, nil, &r, + &bl, &b, &br, +}; + +void +iconinit(void) +{ + background = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0x777777FF); + red = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0xDD0000FF); +} diff --git a/src/cmd/9term/fns.h b/src/cmd/9term/fns.h new file mode 100644 index 00000000..9fc30933 --- /dev/null +++ b/src/cmd/9term/fns.h @@ -0,0 +1,36 @@ +#undef isalnum +#define isalnum runeisalnum + +void keyboardsend(char*, int); +int whide(Window*); +int wunhide(int); +void freescrtemps(void); +int parsewctl(char**, Rectangle, Rectangle*, int*, int*, int*, int*, char**, char*, char*); +Window *new(Image*, int, int, int, char*, char*, char**); +void riosetcursor(Cursor*, int); +int min(int, int); +int max(int, int); +Rune* strrune(Rune*, Rune); +int isalnum(Rune); +void timerstop(Timer*); +void timercancel(Timer*); +Timer* timerstart(int); +void error(char*); +void killprocs(void); +int shutdown(void*, char*); +void iconinit(void); +void *erealloc(void*, uint); +void *emalloc(uint); +char *estrdup(char*); +void button3menu(void); +void button2menu(Window*); +void cvttorunes(char*, int, Rune*, int*, int*, int*); +/* was (byte*,int) runetobyte(Rune*, int); */ +char* runetobyte(Rune*, int, int*); +void timerinit(void); +int goodrect(Rectangle); +int rawon(void); + +#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)) diff --git a/src/cmd/9term/malloc.c b/src/cmd/9term/malloc.c new file mode 100644 index 00000000..130942bd --- /dev/null +++ b/src/cmd/9term/malloc.c @@ -0,0 +1,63 @@ +/* + * These are here mainly so that I can link against + * debugmalloc.c instead and not recompile the world. + */ + +#include +#define NOPLAN9DEFINES +#include + +static Lock malloclock; + +void* +p9malloc(ulong n) +{ + void *v; + + if(n == 0) + n++; + lock(&malloclock); + v = malloc(n); + unlock(&malloclock); + print("p9malloc %lud => %p; pc %lux\n", n, v, getcallerpc(&n)); + return v; +} + +void +p9free(void *v) +{ + if(v == nil) + return; + lock(&malloclock); + print("p9free %p; pc %lux\n", v, getcallerpc(&v)); + free(v); + unlock(&malloclock); +} + +void* +p9calloc(ulong a, ulong b) +{ + void *v; + + if(a*b == 0) + a = b = 1; + + lock(&malloclock); + v = calloc(a*b, 1); + unlock(&malloclock); + print("p9calloc %lud %lud => %p; pc %lux\n", a, b, v, getcallerpc(&a)); + return v; +} + +void* +p9realloc(void *v, ulong n) +{ + void *vv; + + lock(&malloclock); + vv = realloc(v, n); + unlock(&malloclock); + print("p9realloc %p %lud => %p; pc %lux\n", v, n, vv, getcallerpc(&v)); + return vv; +} + diff --git a/src/cmd/9term/mkfile b/src/cmd/9term/mkfile index ae2618f4..e1ef4d68 100644 --- a/src/cmd/9term/mkfile +++ b/src/cmd/9term/mkfile @@ -6,7 +6,11 @@ OFILES=\ rcstart.$O\ $SYSNAME.$O\ +HFILES=dat.h fns.h term.h + <$PLAN9/src/mkmany Darwin.$O Linux.$O FreeBSD.$O: bsdpty.c +$O.9term: data.$O scrl.$O time.$O util.$O wind.$O + diff --git a/src/cmd/9term/scrl.c b/src/cmd/9term/scrl.c new file mode 100644 index 00000000..8a614ad6 --- /dev/null +++ b/src/cmd/9term/scrl.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +static Image *scrtmp; + +static +void +scrtemps(void) +{ + int h; + + if(scrtmp) + return; + h = BIG*Dy(screen->r); + scrtmp = allocimage(display, Rect(0, 0, 32, h), screen->chan, 0, DWhite); + if(scrtmp == nil) + error("scrtemps"); +} + +void +freescrtemps(void) +{ + freeimage(scrtmp); + scrtmp = nil; +} + +static +Rectangle +scrpos(Rectangle r, uint p0, uint p1, uint tot) +{ + Rectangle q; + int h; + + q = r; + h = q.max.y-q.min.y; + if(tot == 0) + return q; + if(tot > 1024*1024){ + 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 +wscrdraw(Window *w) +{ + Rectangle r, r1, r2; + Image *b; + + scrtemps(); + if(w->i == nil) + error("scrdraw"); + r = w->scrollr; + b = scrtmp; + r1 = r; + r1.min.x = 0; + r1.max.x = Dx(r); + r2 = scrpos(r1, w->org, w->org+w->f.nchars, w->nr); + if(!eqrect(r2, w->lastsr)){ + w->lastsr = r2; + /* move r1, r2 to (0,0) to avoid clipping */ + r2 = rectsubpt(r2, r1.min); + r1 = rectsubpt(r1, r1.min); + draw(b, r1, w->f.cols[BORD], nil, ZP); + draw(b, r2, w->f.cols[BACK], nil, ZP); + r2.min.x = r2.max.x-1; + draw(b, r2, w->f.cols[BORD], nil, ZP); + draw(w->i, r, b, nil, Pt(0, r1.min.y)); + } +} + +void +wscrsleep(Window *w, uint dt) +{ + Timer *timer; + int y, b; + static Alt alts[3]; + + timer = timerstart(dt); + y = w->mc.m.xy.y; + b = w->mc.m.buttons; + alts[0].c = timer->c; + alts[0].v = nil; + alts[0].op = CHANRCV; + alts[1].c = w->mc.c; + alts[1].v = &w->mc.m; + alts[1].op = CHANRCV; + alts[2].op = CHANEND; + for(;;) + switch(alt(alts)){ + case 0: + timerstop(timer); + return; + case 1: + if(abs(w->mc.m.xy.y-y)>2 || w->mc.m.buttons!=b){ + timercancel(timer); + return; + } + break; + } +} + +void +wscroll(Window *w, int but) +{ + uint p0, oldp0; + Rectangle s; + int x, y, my, h, first; + + s = insetrect(w->scrollr, 1); + h = s.max.y-s.min.y; + x = (s.min.x+s.max.x)/2; + oldp0 = ~0; + first = TRUE; + do{ + flushimage(display, 1); + if(w->mc.m.xy.xmc.m.xy.x){ + readmouse(&w->mc); + }else{ + my = w->mc.m.xy.y; + if(my < s.min.y) + my = s.min.y; + if(my >= s.max.y) + my = s.max.y; + if(!eqpt(w->mc.m.xy, Pt(x, my))){ + wmovemouse(w, Pt(x, my)); + readmouse(&w->mc); /* absorb event generated by moveto() */ + } + if(but == 2){ + y = my; + if(y > s.max.y-2) + y = s.max.y-2; + if(w->nr > 1024*1024) + p0 = ((w->nr>>10)*(y-s.min.y)/h)<<10; + else + p0 = w->nr*(y-s.min.y)/h; + if(oldp0 != p0) + wsetorigin(w, p0, FALSE); + oldp0 = p0; + readmouse(&w->mc); + continue; + } + if(but == 1) + p0 = wbacknl(w, w->org, (my-s.min.y)/w->f.font->height); + else + p0 = w->org+frcharofpt(&w->f, Pt(s.max.x, my)); + if(oldp0 != p0) + wsetorigin(w, p0, TRUE); + oldp0 = p0; + /* debounce */ + if(first){ + flushimage(display, 1); + sleep(200); + nbrecv(w->mc.c, &w->mc.m); + first = FALSE; + } + wscrsleep(w, 100); + } + }while(w->mc.m.buttons & (1<<(but-1))); + while(w->mc.m.buttons) + readmouse(&w->mc); +} diff --git a/src/cmd/9term/time.c b/src/cmd/9term/time.c new file mode 100644 index 00000000..5dedf144 --- /dev/null +++ b/src/cmd/9term/time.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +static Channel* ctimer; /* chan(Timer*)[100] */ +static Timer *timer; + +static +uint +msec(void) +{ + return nsec()/1000000; +} + +void +timerstop(Timer *t) +{ + t->next = timer; + timer = t; +} + +void +timercancel(Timer *t) +{ + t->cancel = TRUE; +} + +static +void +timerproc(void *a) +{ + int i, nt, na, dt, del; + Timer **t, *x; + uint old, new; + + USED(a); + rfork(RFFDG); + threadsetname("TIMERPROC"); + t = nil; + na = 0; + nt = 0; + old = msec(); + for(;;){ + sleep(1); /* will sleep minimum incr */ + new = msec(); + dt = new-old; + old = new; + if(dt < 0) /* timer wrapped; go around, losing a tick */ + continue; + for(i=0; idt -= dt; + del = 0; + if(x->cancel){ + timerstop(x); + del = 1; + }else if(x->dt <= 0){ + /* + * avoid possible deadlock if client is + * now sending on ctimer + */ + if(nbsendul(x->c, 0) > 0) + del = 1; + } + if(del){ + memmove(&t[i], &t[i+1], (nt-i-1)*sizeof t[0]); + --nt; + --i; + } + } + if(nt == 0){ + x = recvp(ctimer); + gotit: + if(nt == na){ + na += 10; + t = realloc(t, na*sizeof(Timer*)); + if(t == nil) + abort(); + } + t[nt++] = x; + old = msec(); + } + if(nbrecv(ctimer, &x) > 0) + goto gotit; + } +} + +void +timerinit(void) +{ + ctimer = chancreate(sizeof(Timer*), 100); + proccreate(timerproc, nil, STACK); +} + +/* + * timeralloc() and timerfree() don't lock, so can only be + * called from the main proc. + */ + +Timer* +timerstart(int dt) +{ + Timer *t; + + t = timer; + if(t) + timer = timer->next; + else{ + t = emalloc(sizeof(Timer)); + t->c = chancreate(sizeof(int), 0); + } + t->next = nil; + t->dt = dt; + t->cancel = FALSE; + sendp(ctimer, t); + return t; +} diff --git a/src/cmd/9term/util.c b/src/cmd/9term/util.c new file mode 100644 index 00000000..8a35d1d7 --- /dev/null +++ b/src/cmd/9term/util.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +void +cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls) +{ + uchar *q; + Rune *s; + int j, w; + + /* + * Always guaranteed that n bytes may be interpreted + * without worrying about partial runes. This may mean + * reading up to UTFmax-1 more bytes than n; the caller + * knows this. If n is a firm limit, the caller should + * set p[n] = 0. + */ + q = (uchar*)p; + s = r; + for(j=0; j?@[\\]^`{|}~", c)) + return FALSE; + return TRUE; +} + +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 nil; +} + +int +min(int a, int b) +{ + if(a < b) + return a; + return b; +} + +int +max(int a, int b) +{ + if(a > b) + return a; + return b; +} + +char* +runetobyte(Rune *r, int n, int *ip) +{ + char *s; + int m; + + s = emalloc(n*UTFmax+1); + m = snprint(s, n*UTFmax+1, "%.*S", n, r); + *ip = m; + return s; +} + diff --git a/src/cmd/9term/wind.c b/src/cmd/9term/wind.c new file mode 100644 index 00000000..600a4e42 --- /dev/null +++ b/src/cmd/9term/wind.c @@ -0,0 +1,1628 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +#define MOVEIT if(0) + +enum +{ + HiWater = 640000, /* max size of history */ + LoWater = 400000, /* min size of history after max'ed */ + MinWater = 20000, /* room to leave available when reallocating */ +}; + +static int topped; +static int id; + +static Image *cols[NCOL]; +static Image *grey; +static Image *darkgrey; +static Cursor *lastcursor; +static Image *titlecol; +static Image *lighttitlecol; +static Image *holdcol; +static Image *lightholdcol; +static Image *paleholdcol; + +Window* +wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling) +{ + Window *w; + Rectangle r; + + if(cols[0] == nil){ + /* greys are multiples of 0x11111100+0xFF, 14* being palest */ + grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF); + darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF); + cols[BACK] = display->white; + cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF); + cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF); + cols[TEXT] = display->black; + cols[HTEXT] = display->black; + titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen); + lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen); + holdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue); + lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue); + paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue); + } + w = emalloc(sizeof(Window)); + w->screenr = i->r; + r = insetrect(i->r, Selborder+1); + w->i = i; + w->mc = *mc; + w->ck = ck; + w->cctl = cctl; + w->cursorp = nil; + w->conswrite = chancreate(sizeof(Conswritemesg), 0); + w->consread = chancreate(sizeof(Consreadmesg), 0); + w->mouseread = chancreate(sizeof(Mousereadmesg), 0); + w->wctlread = chancreate(sizeof(Consreadmesg), 0); + w->scrollr = r; + w->scrollr.max.x = r.min.x+Scrollwid; + w->lastsr = ZR; + r.min.x += Scrollwid+Scrollgap; + frinit(&w->f, r, font, i, cols); + w->f.maxtab = maxtab*stringwidth(font, "0"); + w->topped = ++topped; + w->id = ++id; + w->notefd = -1; + w->scrolling = scrolling; + w->dir = estrdup(startdir); + w->label = estrdup(""); + r = insetrect(w->i->r, Selborder); + draw(w->i, r, cols[BACK], nil, w->f.entire.min); + wborder(w, Selborder); + wscrdraw(w); + incref(&w->ref); /* ref will be removed after mounting; avoids delete before ready to be deleted */ + return w; +} + +void +wsetname(Window *w) +{ + int i, n; + char err[ERRMAX]; + + n = sprint(w->name, "window.%d.%d", w->id, w->namecount++); + for(i='A'; i<='Z'; i++){ + if(nameimage(w->i, w->name, 1) > 0) + return; + errstr(err, sizeof err); + if(strcmp(err, "image name in use") != 0) + break; + w->name[n] = i; + w->name[n+1] = 0; + } + w->name[0] = 0; + fprint(2, "rio: setname failed: %s\n", err); +} + +void +wresize(Window *w, Image *i, int move) +{ + Rectangle r, or; + + or = w->i->r; + if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r))) + draw(i, i->r, w->i, nil, w->i->r.min); + if(w->i != i){ +fprint(2, "res %p %p\n", w->i, i); + freeimage(w->i); + w->i = i; + } +// wsetname(w); +//XXX w->mc.image = i; + r = insetrect(i->r, Selborder+1); + w->scrollr = r; + w->scrollr.max.x = r.min.x+Scrollwid; + w->lastsr = ZR; + r.min.x += Scrollwid+Scrollgap; + if(move) + frsetrects(&w->f, r, w->i); + else{ + frclear(&w->f, FALSE); + frinit(&w->f, r, w->f.font, w->i, cols); + wsetcols(w); + w->f.maxtab = maxtab*stringwidth(w->f.font, "0"); + r = insetrect(w->i->r, Selborder); + draw(w->i, r, cols[BACK], nil, w->f.entire.min); + wfill(w); + wsetselect(w, w->q0, w->q1); + wscrdraw(w); + } + wborder(w, Selborder); + w->topped = ++topped; + w->resized = TRUE; + w->mouse.counter++; +} + +void +wrefresh(Window *w, Rectangle r) +{ + /* USED(r); */ + + /* BUG: rectangle is ignored */ + if(w == input) + wborder(w, Selborder); + else + wborder(w, Unselborder); + if(w->mouseopen) + return; + draw(w->i, insetrect(w->i->r, Borderwidth), w->f.cols[BACK], nil, w->i->r.min); + w->f.ticked = 0; + if(w->f.p0 > 0) + frdrawsel(&w->f, frptofchar(&w->f, 0), 0, w->f.p0, 0); + if(w->f.p1 < w->f.nchars) + frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, w->f.nchars, 0); + frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 1); + w->lastsr = ZR; + wscrdraw(w); +} + +int +wclose(Window *w) +{ + int i; + + i = decref(&w->ref); + if(i > 0) + return 0; + if(i < 0) + error("negative ref count"); + if(!w->deleted) + wclosewin(w); + wsendctlmesg(w, Exited, ZR, nil); + return 1; +} + + +void +winctl(void *arg) +{ + Rune *rp, *bp, *tp, *up, *kbdr; + uint qh; + int nr, nb, c, wid, i, npart, initial, lastb; + char *s, *t, part[3]; + Window *w; + Mousestate *mp, m; + enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT }; + Alt alts[NWALT+1]; + Mousereadmesg mrm; + Conswritemesg cwm; + Consreadmesg crm; + Consreadmesg cwrm; + Stringpair pair; + Wctlmesg wcm; + char buf[4*12+1]; + + w = arg; + snprint(buf, sizeof buf, "winctl-id%d", w->id); + threadsetname(buf); + + mrm.cm = chancreate(sizeof(Mouse), 0); + cwm.cw = chancreate(sizeof(Stringpair), 0); + crm.c1 = chancreate(sizeof(Stringpair), 0); + crm.c2 = chancreate(sizeof(Stringpair), 0); + cwrm.c1 = chancreate(sizeof(Stringpair), 0); + cwrm.c2 = chancreate(sizeof(Stringpair), 0); + + + alts[WKey].c = w->ck; + alts[WKey].v = &kbdr; + alts[WKey].op = CHANRCV; + alts[WMouse].c = w->mc.c; + alts[WMouse].v = &w->mc.m; + alts[WMouse].op = CHANRCV; + alts[WMouseread].c = w->mouseread; + alts[WMouseread].v = &mrm; + alts[WMouseread].op = CHANSND; + alts[WCtl].c = w->cctl; + alts[WCtl].v = &wcm; + alts[WCtl].op = CHANRCV; + alts[WCwrite].c = w->conswrite; + alts[WCwrite].v = &cwm; + alts[WCwrite].op = CHANSND; + alts[WCread].c = w->consread; + alts[WCread].v = &crm; + alts[WCread].op = CHANSND; + alts[WWread].c = w->wctlread; + alts[WWread].v = &cwrm; + alts[WWread].op = CHANSND; + alts[NWALT].op = CHANEND; + + npart = 0; + lastb = -1; + for(;;){ + if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter) + alts[WMouseread].op = CHANSND; + else + alts[WMouseread].op = CHANNOP; + if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->f.nchars) + alts[WCwrite].op = CHANNOP; + else + alts[WCwrite].op = CHANSND; + if(w->deleted || !w->wctlready) + alts[WWread].op = CHANNOP; + else + alts[WWread].op = CHANSND; + /* this code depends on NL and EOT fitting in a single byte */ + /* kind of expensive for each loop; worth precomputing? */ + if(w->holding) + alts[WCread].op = CHANNOP; + else if(npart || (w->rawing && w->nraw>0)) + alts[WCread].op = CHANSND; + else{ + alts[WCread].op = CHANNOP; + for(i=w->qh; inr; i++){ + c = w->r[i]; + if(c=='\n' || c=='\004'){ + alts[WCread].op = CHANSND; + break; + } + } + } + switch(alt(alts)){ + case WKey: + for(i=0; kbdr[i]!=L'\0'; i++) + wkeyctl(w, kbdr[i]); +// wkeyctl(w, r); +// while(nbrecv(w->ck, &r)) +// wkeyctl(w, r); + break; + case WMouse: + if(w->mouseopen) { + w->mouse.counter++; + + /* queue click events */ + if(!w->mouse.qfull && lastb != w->mc.m.buttons) { /* add to ring */ + mp = &w->mouse.queue[w->mouse.wi]; + if(++w->mouse.wi == nelem(w->mouse.queue)) + w->mouse.wi = 0; + if(w->mouse.wi == w->mouse.ri) + w->mouse.qfull = TRUE; + mp->m = w->mc.m; + mp->counter = w->mouse.counter; + lastb = w->mc.m.buttons; + } + } else + wmousectl(w); + break; + case WMouseread: + /* send a queued event or, if the queue is empty, the current state */ + /* if the queue has filled, we discard all the events it contained. */ + /* the intent is to discard frantic clicking by the user during long latencies. */ + w->mouse.qfull = FALSE; + if(w->mouse.wi != w->mouse.ri) { + m = w->mouse.queue[w->mouse.ri]; + if(++w->mouse.ri == nelem(w->mouse.queue)) + w->mouse.ri = 0; + } else + m = (Mousestate){w->mc.m, w->mouse.counter}; + + w->mouse.lastcounter = m.counter; + send(mrm.cm, &m.m); + continue; + case WCtl: + if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){ + chanfree(crm.c1); + chanfree(crm.c2); + chanfree(mrm.cm); + chanfree(cwm.cw); + chanfree(cwrm.c1); + chanfree(cwrm.c2); + threadexits(nil); + } + continue; + case WCwrite: + recv(cwm.cw, &pair); + rp = pair.s; + nr = pair.ns; + bp = rp; + for(i=0; i w->qh) + initial = w->qh; + qh = w->qh-initial; + wdelete(w, qh, qh+initial); + w->qh = qh; + } + free(rp); + rp = tp; + nr = up-tp; + rp[nr] = 0; + break; + } + w->qh = winsert(w, rp, nr, w->qh)+nr; + if(w->scrolling || w->mouseopen) + wshow(w, w->qh); + wsetselect(w, w->q0, w->q1); + wscrdraw(w); + free(rp); + break; + case WCread: + recv(crm.c1, &pair); + t = pair.s; + nb = pair.ns; + i = npart; + npart = 0; + if(i) + memmove(t, part, i); + while(iqhnr || w->nraw>0)){ + if(w->qh == w->nr){ + wid = runetochar(t+i, &w->raw[0]); + w->nraw--; + runemove(w->raw, w->raw+1, w->nraw); + }else + wid = runetochar(t+i, &w->r[w->qh++]); + c = t[i]; /* knows break characters fit in a byte */ + i += wid; + if(!w->rawing && (c == '\n' || c=='\004')){ + // if(c == '\004') + // i--; + break; + } + } + // if(i==nb && w->qhnr && w->r[w->qh]=='\004') + // w->qh++; + if(i > nb){ + npart = i-nb; + memmove(part, t+nb, npart); + i = nb; + } + pair.s = t; + pair.ns = i; + send(crm.c2, &pair); + continue; + case WWread: + w->wctlready = 0; + recv(cwrm.c1, &pair); + if(w->deleted || w->i==nil) + pair.ns = sprint(pair.s, ""); + else{ + s = "visible"; + for(i=0; ii->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s); + } + send(cwrm.c2, &pair); + continue; + } + if(!w->deleted) + flushimage(display, 1); + } +} + +void +waddraw(Window *w, Rune *r, int nr) +{ + w->raw = runerealloc(w->raw, w->nraw+nr); + runemove(w->raw+w->nraw, r, nr); + w->nraw += nr; +} + +/* + * Need to do this in a separate proc because if process we're interrupting + * is dying and trying to print tombstone, kernel is blocked holding p->debug lock. + */ +void +interruptproc(void *v) +{ + int *notefd; + + notefd = v; + write(*notefd, "interrupt", 9); + free(notefd); +} + +int +windfilewidth(Window *w, uint q0, int oneelement) +{ + uint q; + Rune r; + + q = q0; + while(q > 0){ + r = w->r[q-1]; + if(r<=' ') + break; + if(oneelement && r=='/') + break; + --q; + } + return q0-q; +} + +void +showcandidates(Window *w, Completion *c) +{ + int i; + Fmt f; + Rune *rp; + uint nr, qline, q0; + char *s; + + runefmtstrinit(&f); + if (c->nmatch == 0) + s = "[no matches in "; + else + s = "["; + if(c->nfile > 32) + fmtprint(&f, "%s%d files]\n", s, c->nfile); + else{ + fmtprint(&f, "%s", s); + for(i=0; infile; i++){ + if(i > 0) + fmtprint(&f, " "); + fmtprint(&f, "%s", c->filename[i]); + } + fmtprint(&f, "]\n"); + } + /* place text at beginning of line before host point */ + qline = w->qh; + while(qline>0 && w->r[qline-1] != '\n') + qline--; + + rp = runefmtstrflush(&f); + nr = runestrlen(rp); + + q0 = w->q0; + q0 += winsert(w, rp, runestrlen(rp), qline) - qline; + free(rp); + wsetselect(w, q0+nr, q0+nr); +} + +Rune* +namecomplete(Window *w) +{ + int nstr, npath; + Rune *rp, *path, *str; + Completion *c; + char *s, *dir, *root; + + /* control-f: filename completion; works back to white space or / */ + if(w->q0nr && w->r[w->q0]>' ') /* must be at end of word */ + return nil; + nstr = windfilewidth(w, w->q0, TRUE); + str = runemalloc(nstr); + runemove(str, w->r+(w->q0-nstr), nstr); + npath = windfilewidth(w, w->q0-nstr, FALSE); + path = runemalloc(npath); + runemove(path, w->r+(w->q0-nstr-npath), npath); + rp = nil; + + /* is path rooted? if not, we need to make it relative to window path */ + if(npath>0 && path[0]=='/'){ + dir = malloc(UTFmax*npath+1); + sprint(dir, "%.*S", npath, path); + }else{ + if(strcmp(w->dir, "") == 0) + root = "."; + else + root = w->dir; + dir = malloc(strlen(root)+1+UTFmax*npath+1); + sprint(dir, "%s/%.*S", root, npath, path); + } + dir = cleanname(dir); + + s = smprint("%.*S", nstr, str); + c = complete(dir, s); + free(s); + if(c == nil) + goto Return; + + if(!c->advance) + showcandidates(w, c); + + if(c->advance) + rp = runesmprint("%s", c->string); + + Return: + freecompletion(c); + free(dir); + free(path); + free(str); + return rp; +} + +void +wkeyctl(Window *w, Rune r) +{ + uint q0 ,q1; + int n, nb, nr; + Rune *rp; + int *notefd; + + if(r == 0) + return; + if(w->deleted) + return; + w->rawing = rawon(); + /* navigation keys work only when mouse is not open */ + if(!w->mouseopen) + switch(r){ + case Kdown: + n = w->f.maxlines/3; + goto case_Down; + case Kscrollonedown: + n = mousescrollsize(w->f.maxlines); + if(n <= 0) + n = 1; + goto case_Down; + case Kpgdown: + n = 2*w->f.maxlines/3; + case_Down: + q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+n*w->f.font->height)); + wsetorigin(w, q0, TRUE); + return; + case Kup: + n = w->f.maxlines/3; + goto case_Up; + case Kscrolloneup: + n = mousescrollsize(w->f.maxlines); + if(n <= 0) + n = 1; + goto case_Up; + case Kpgup: + n = 2*w->f.maxlines/3; + case_Up: + q0 = wbacknl(w, w->org, n); + wsetorigin(w, q0, TRUE); + return; + case Kleft: + if(w->q0 > 0){ + q0 = w->q0-1; + wsetselect(w, q0, q0); + wshow(w, q0); + } + return; + case Kright: + if(w->q1 < w->nr){ + q1 = w->q1+1; + wsetselect(w, q1, q1); + wshow(w, q1); + } + return; + case Khome: + wshow(w, 0); + return; + case Kend: + wshow(w, w->nr); + return; + case 0x01: /* ^A: beginning of line */ + if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n') + return; + nb = wbswidth(w, 0x15 /* ^U */); + wsetselect(w, w->q0-nb, w->q0-nb); + wshow(w, w->q0); + return; + case 0x05: /* ^E: end of line */ + q0 = w->q0; + while(q0 < w->nr && w->r[q0]!='\n') + q0++; + wsetselect(w, q0, q0); + wshow(w, w->q0); + return; + } + /* + * This if used to be below the if(w->rawing ...), + * but let's try putting it here. This will allow ESC-processing + * to toggle hold mode even in remote SSH connections. + * The drawback is that vi-style processing gets harder. + * If you find yourself in some weird readline mode, good + * luck getting out without ESC. Let's see who complains. + */ + if(r==0x1B || (w->holding && r==0x7F)){ /* toggle hold */ + if(w->holding) + --w->holding; + else + w->holding++; + wrepaint(w); + if(r == 0x1B) + return; + } + if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){ + waddraw(w, &r, 1); + return; + } + if(r != 0x7F){ + wsnarf(w); + wcut(w); + } + switch(r){ + case 0x7F: /* send interrupt */ + w->qh = w->nr; + wshow(w, w->qh); + notefd = emalloc(sizeof(int)); + *notefd = w->notefd; + proccreate(interruptproc, notefd, 4096); + return; + case 0x06: /* ^F: file name completion */ + case Kins: /* Insert: file name completion */ + rp = namecomplete(w); + if(rp == nil) + return; + nr = runestrlen(rp); + q0 = w->q0; + q0 = winsert(w, rp, nr, q0); + wshow(w, q0+nr); + free(rp); + return; + case 0x08: /* ^H: erase character */ + case 0x15: /* ^U: erase line */ + case 0x17: /* ^W: erase word */ + if(w->q0==0 || w->q0==w->qh) + return; + nb = wbswidth(w, r); + q1 = w->q0; + q0 = q1-nb; + if(q0 < w->org){ + q0 = w->org; + nb = q1-q0; + } + if(nb > 0){ + wdelete(w, q0, q0+nb); + wsetselect(w, q0, q0); + } + return; + } + /* otherwise ordinary character; just insert */ + q0 = w->q0; + q0 = winsert(w, &r, 1, q0); + wshow(w, q0+1); +} + +void +wsetcols(Window *w) +{ + if(w->holding) + if(w == input) + w->f.cols[TEXT] = w->f.cols[HTEXT] = holdcol; + else + w->f.cols[TEXT] = w->f.cols[HTEXT] = lightholdcol; + else + if(w == input) + w->f.cols[TEXT] = w->f.cols[HTEXT] = display->black; + else + w->f.cols[TEXT] = w->f.cols[HTEXT] = darkgrey; +} + +void +wrepaint(Window *w) +{ + wsetcols(w); + if(!w->mouseopen) + _frredraw(&w->f, w->f.r.min); + if(w == input){ + wborder(w, Selborder); + wsetcursor(w, 0); + }else + wborder(w, Unselborder); +} + +int +wbswidth(Window *w, 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 = w->q0; + stop = 0; + if(q > w->qh) + stop = w->qh; + skipping = TRUE; + while(q > stop){ + r = w->r[q-1]; + if(r == '\n'){ /* eat at most one more character */ + if(q == w->q0) /* eat the newline */ + --q; + break; + } + if(c == 0x17){ + eq = isalnum(r); + if(eq && skipping) /* found one; stop skipping */ + skipping = FALSE; + else if(!eq && !skipping) + break; + } + --q; + } + return w->q0-q; +} + +void +wsnarf(Window *w) +{ + if(w->q1 == w->q0) + return; + nsnarf = w->q1-w->q0; + snarf = runerealloc(snarf, nsnarf); + snarfversion++; /* maybe modified by parent */ + runemove(snarf, w->r+w->q0, nsnarf); +//XXX putsnarf(); +} + +void +wcut(Window *w) +{ + if(w->q1 == w->q0) + return; + wdelete(w, w->q0, w->q1); + wsetselect(w, w->q0, w->q0); +} + +void +wpaste(Window *w) +{ + uint q0; + + if(nsnarf == 0) + return; + wcut(w); + q0 = w->q0; + if(w->rawing && q0==w->nr){ + waddraw(w, snarf, nsnarf); + wsetselect(w, q0, q0); + }else{ + q0 = winsert(w, snarf, nsnarf, w->q0); + wsetselect(w, q0, q0+nsnarf); + } +} + +void +wplumb(Window *w) +{ + Plumbmsg *m; + static int fd = -2; + char buf[32]; + uint p0, p1; + Cursor *c; + + if(fd == -2) + fd = plumbopen("send", OWRITE|OCEXEC); + if(fd < 0) + return; + m = emalloc(sizeof(Plumbmsg)); + m->src = estrdup("rio"); + m->dst = nil; + m->wdir = estrdup(w->dir); + m->type = estrdup("text"); + p0 = w->q0; + p1 = w->q1; + if(w->q1 > w->q0) + m->attr = nil; + else{ + while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n') + p0--; + while(p1nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n') + p1++; + sprint(buf, "click=%d", w->q0-p0); + m->attr = plumbunpackattr(buf); + } + if(p1-p0 > messagesize-1024){ + plumbfree(m); + return; /* too large for 9P */ + } + m->data = runetobyte(w->r+p0, p1-p0, &m->ndata); + if(plumbsend(fd, m) < 0){ + c = lastcursor; + riosetcursor(&query, 1); + sleep(300); + riosetcursor(c, 1); + } + plumbfree(m); +} + +int +winborder(Window *w, Point xy) +{ + return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder)); +} + +void +wmousectl(Window *w) +{ + int but; + + if(w->mc.m.buttons == 1) + but = 1; + else if(w->mc.m.buttons == 2) + but = 2; + else if(w->mc.m.buttons == 4) + but = 3; + else{ + if(w->mc.m.buttons == 8) + wkeyctl(w, Kscrolloneup); + if(w->mc.m.buttons == 16) + wkeyctl(w, Kscrollonedown); + return; + } + + incref(&w->ref); /* hold up window while we track */ + if(w->deleted) + goto Return; + if(ptinrect(w->mc.m.xy, w->scrollr)){ + if(but) + wscroll(w, but); + goto Return; + } + if(but == 1) + wselect(w); + /* else all is handled by main process */ + Return: + wclose(w); +} + +void +wdelete(Window *w, uint q0, uint q1) +{ + uint n, p0, p1; + + n = q1-q0; + if(n == 0) + return; + runemove(w->r+q0, w->r+q1, w->nr-q1); + w->nr -= n; + if(q0 < w->q0) + w->q0 -= min(n, w->q0-q0); + if(q0 < w->q1) + w->q1 -= min(n, w->q1-q0); + if(q1 < w->qh) + w->qh -= n; + else if(q0 < w->qh) + w->qh = q0; + if(q1 <= w->org) + w->org -= n; + else if(q0 < w->org+w->f.nchars){ + p1 = q1 - w->org; + if(p1 > w->f.nchars) + p1 = w->f.nchars; + if(q0 < w->org){ + w->org = q0; + p0 = 0; + }else + p0 = q0 - w->org; + frdelete(&w->f, p0, p1); + wfill(w); + } +} + + +static Window *clickwin; +static uint clickmsec; +static Window *selectwin; +static uint selectq; + +/* + * called from frame library + */ +void +framescroll(Frame *f, int dl) +{ + if(f != &selectwin->f) + error("frameselect not right frame"); + wframescroll(selectwin, dl); +} + +void +wframescroll(Window *w, int dl) +{ + uint q0; + + if(dl == 0){ + wscrsleep(w, 100); + return; + } + if(dl < 0){ + q0 = wbacknl(w, w->org, -dl); + if(selectq > w->org+w->f.p0) + wsetselect(w, w->org+w->f.p0, selectq); + else + wsetselect(w, selectq, w->org+w->f.p0); + }else{ + if(w->org+w->f.nchars == w->nr) + return; + q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+dl*w->f.font->height)); + if(selectq >= w->org+w->f.p1) + wsetselect(w, w->org+w->f.p1, selectq); + else + wsetselect(w, selectq, w->org+w->f.p1); + } + wsetorigin(w, q0, TRUE); +} + +void +wselect(Window *w) +{ + uint q0, q1; + int b, x, y, first; + + first = 1; + selectwin = w; + /* + * Double-click immediately if it might make sense. + */ + b = w->mc.m.buttons; + q0 = w->q0; + q1 = w->q1; + selectq = w->org+frcharofpt(&w->f, w->mc.m.xy); + if(clickwin==w && w->mc.m.msec-clickmsec<500) + if(q0==q1 && selectq==w->q0){ + wdoubleclick(w, &q0, &q1); + wsetselect(w, q0, q1); + flushimage(display, 1); + x = w->mc.m.xy.x; + y = w->mc.m.xy.y; + /* stay here until something interesting happens */ + do + readmouse(&w->mc); + while(w->mc.m.buttons==b && abs(w->mc.m.xy.x-x)<3 && abs(w->mc.m.xy.y-y)<3); + w->mc.m.xy.x = x; /* in case we're calling frselect */ + w->mc.m.xy.y = y; + q0 = w->q0; /* may have changed */ + q1 = w->q1; + selectq = q0; + } + if(w->mc.m.buttons == b){ + w->f.scroll = framescroll; + frselect(&w->f, &w->mc); + /* horrible botch: while asleep, may have lost selection altogether */ + if(selectq > w->nr) + selectq = w->org + w->f.p0; + w->f.scroll = nil; + if(selectq < w->org) + q0 = selectq; + else + q0 = w->org + w->f.p0; + if(selectq > w->org+w->f.nchars) + q1 = selectq; + else + q1 = w->org+w->f.p1; + } + if(q0 == q1){ + if(q0==w->q0 && clickwin==w && w->mc.m.msec-clickmsec<500){ + wdoubleclick(w, &q0, &q1); + clickwin = nil; + }else{ + clickwin = w; + clickmsec = w->mc.m.msec; + } + }else + clickwin = nil; + wsetselect(w, q0, q1); + flushimage(display, 1); + while(w->mc.m.buttons){ + w->mc.m.msec = 0; + b = w->mc.m.buttons; + if(b & 6){ + if(b & 2){ + wsnarf(w); + wcut(w); + }else{ + if(first){ + first = 0; + getsnarf(); + } + wpaste(w); + } + } + wscrdraw(w); + flushimage(display, 1); + while(w->mc.m.buttons == b) + readmouse(&w->mc); + clickwin = nil; + } +} + +void +wsendctlmesg(Window *w, int type, Rectangle r, Image *image) +{ + Wctlmesg wcm; + + wcm.type = type; + wcm.r = r; + wcm.image = image; + send(w->cctl, &wcm); +} + +int +wctlmesg(Window *w, int m, Rectangle r, Image *i) +{ + char buf[64]; + + switch(m){ + default: + error("unknown control message"); + break; + case Wakeup: + break; + case Moved: + case Reshaped: + if(w->deleted){ + freeimage(i); + break; + } + w->screenr = r; + strcpy(buf, w->name); + wresize(w, i, m==Moved); + w->wctlready = 1; + if(Dx(r) > 0){ + if(w != input) + wcurrent(w); + }else if(w == input) + wcurrent(nil); + flushimage(display, 1); + break; + case Refresh: + if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r)) + break; + if(!w->mouseopen) + wrefresh(w, r); + flushimage(display, 1); + break; + case Movemouse: + if(sweeping || !ptinrect(r.min, w->i->r)) + break; + wmovemouse(w, r.min); + case Rawon: + break; + case Rawoff: + if(w->deleted) + break; + while(w->nraw > 0){ + wkeyctl(w, w->raw[0]); + --w->nraw; + runemove(w->raw, w->raw+1, w->nraw); + } + break; + case Holdon: + case Holdoff: + if(w->deleted) + break; + wrepaint(w); + flushimage(display, 1); + break; + case Deleted: + if(w->deleted) + break; + write(w->notefd, "hangup", 6); + wclosewin(w); + break; + case Exited: + frclear(&w->f, TRUE); + close(w->notefd); + chanfree(w->mc.c); + chanfree(w->ck); + chanfree(w->cctl); + chanfree(w->conswrite); + chanfree(w->consread); + chanfree(w->mouseread); + chanfree(w->wctlread); + free(w->raw); + free(w->r); + free(w->dir); + free(w->label); + free(w); + break; + } + return m; +} + +/* + * Convert back to physical coordinates + */ +void +wmovemouse(Window *w, Point p) +{ + p.x += w->screenr.min.x-w->i->r.min.x; + p.y += w->screenr.min.y-w->i->r.min.y; + moveto(mousectl, p); +} + +void +wcurrent(Window *w) +{ + Window *oi; + + if(wkeyboard!=nil && w==wkeyboard) + return; + oi = input; + input = w; + if(oi!=w && oi!=nil) + wrepaint(oi); + if(w !=nil){ + wrepaint(w); + wsetcursor(w, 0); + } + if(w != oi){ + if(oi){ + oi->wctlready = 1; + wsendctlmesg(oi, Wakeup, ZR, nil); + } + if(w){ + w->wctlready = 1; + wsendctlmesg(w, Wakeup, ZR, nil); + } + } +} + +void +wsetcursor(Window *w, int force) +{ + Cursor *p; + + if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0) + p = nil; + else if(wpointto(mouse->xy) == w){ + p = w->cursorp; + if(p==nil && w->holding) + p = &whitearrow; + }else + p = nil; + if(!menuing) + riosetcursor(p, force && !menuing); +} + +void +riosetcursor(Cursor *p, int force) +{ + if(!force && p==lastcursor) + return; + setcursor(mousectl, p); + lastcursor = p; +} + +Window* +wtop(Point pt) +{ + Window *w; + + w = wpointto(pt); + if(w){ + if(w->topped == topped) + return nil; + topwindow(w->i); + wcurrent(w); + flushimage(display, 1); + w->topped = ++topped; + } + return w; +} + +void +wtopme(Window *w) +{ + if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){ + topwindow(w->i); + flushimage(display, 1); + w->topped = ++ topped; + } +} + +void +wbottomme(Window *w) +{ + if(w!=nil && w->i!=nil && !w->deleted){ + bottomwindow(w->i); + flushimage(display, 1); + w->topped = 0; + } +} + +Window* +wlookid(int id) +{ + int i; + + for(i=0; iid == id) + return window[i]; + return nil; +} + +void +wclosewin(Window *w) +{ + Rectangle r; + int i; + + w->deleted = TRUE; + if(w == input){ + input = nil; + wsetcursor(w, 0); + } + if(w == wkeyboard) + wkeyboard = nil; + for(i=0; ideleted = TRUE; + r = w->i->r; + /* move it off-screen to hide it, in case client is slow in letting it go */ + MOVEIT originwindow(w->i, r.min, view->r.max); + freeimage(w->i); + w->i = nil; + return; + } + error("unknown window in closewin"); +} + +void +wsetpid(Window *w, int pid, int dolabel) +{ + char buf[128]; + int fd; + + w->pid = pid; + if(dolabel){ + sprint(buf, "rc %d", pid); + free(w->label); + w->label = estrdup(buf); + } + sprint(buf, "/proc/%d/notepg", pid); + fd = open(buf, OWRITE|OCEXEC); + if(w->notefd > 0) + close(w->notefd); + w->notefd = fd; +} + +static Rune left1[] = { '{', '[', '(', '<', 0xAB, 0 }; +static Rune right1[] = { '}', ']', ')', '>', 0xBB, 0 }; +static Rune left2[] = { '\n', 0 }; +static Rune left3[] = { '\'', '"', '`', 0 }; + +Rune *left[] = { + left1, + left2, + left3, + nil +}; +Rune *right[] = { + right1, + left2, + left3, + nil +}; + +void +wdoubleclick(Window *w, uint *q0, uint *q1) +{ + int c, i; + Rune *r, *l, *p; + uint q; + + for(i=0; left[i]!=nil; i++){ + q = *q0; + l = left[i]; + r = right[i]; + /* try matching character to left, looking right */ + if(q == 0) + c = '\n'; + else + c = w->r[q-1]; + p = strrune(l, c); + if(p != nil){ + if(wclickmatch(w, c, r[p-l], 1, &q)) + *q1 = q-(c!='\n'); + return; + } + /* try matching character to right, looking left */ + if(q == w->nr) + c = '\n'; + else + c = w->r[q]; + p = strrune(r, c); + if(p != nil){ + if(wclickmatch(w, c, l[p-r], -1, &q)){ + *q1 = *q0+(*q0nr && c=='\n'); + *q0 = q; + if(c!='\n' || q!=0 || w->r[0]=='\n') + (*q0)++; + } + return; + } + } + /* try filling out word to right */ + while(*q1nr && isalnum(w->r[*q1])) + (*q1)++; + /* try filling out word to left */ + while(*q0>0 && isalnum(w->r[*q0-1])) + (*q0)--; +} + +int +wclickmatch(Window *w, int cl, int cr, int dir, uint *q) +{ + Rune c; + int nest; + + nest = 1; + for(;;){ + if(dir > 0){ + if(*q == w->nr) + break; + c = w->r[*q]; + (*q)++; + }else{ + if(*q == 0) + break; + (*q)--; + c = w->r[*q]; + } + if(c == cr){ + if(--nest==0) + return 1; + }else if(c == cl) + nest++; + } + return cl=='\n' && nest==1; +} + + +uint +wbacknl(Window *w, uint p, uint n) +{ + int i, j; + + /* look for start of this line if n==0 */ + if(n==0 && p>0 && w->r[p-1]!='\n') + n = 1; + i = n; + while(i-->0 && p>0){ + --p; /* it's at a newline now; back over it */ + if(p == 0) + break; + /* at 128 chars, call it a line anyway */ + for(j=128; --j>0 && p>0; p--) + if(w->r[p-1]=='\n') + break; + } + return p; +} + +void +wshow(Window *w, uint q0) +{ + int qe; + int nl; + uint q; + + qe = w->org+w->f.nchars; + if(w->org<=q0 && (q0nr))) + wscrdraw(w); + else{ + nl = 4*w->f.maxlines/5; + q = wbacknl(w, q0, nl); + /* avoid going backwards if trying to go forwards - long lines! */ + if(!(q0>w->org && qorg)) + wsetorigin(w, q, TRUE); + while(q0 > w->org+w->f.nchars) + wsetorigin(w, w->org+1, FALSE); + } +} + +void +wsetorigin(Window *w, uint org, int exact) +{ + int i, a, fixup; + Rune *r; + uint n; + + if(org>0 && !exact){ + /* org is an estimate of the char posn; find a newline */ + /* don't try harder than 256 chars */ + for(i=0; i<256 && orgnr; i++){ + if(w->r[org] == '\n'){ + org++; + break; + } + org++; + } + } + a = org-w->org; + fixup = 0; + if(a>=0 && af.nchars){ + frdelete(&w->f, 0, a); + fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */ + }else if(a<0 && -af.nchars){ + n = w->org - org; + r = runemalloc(n); + runemove(r, w->r+org, n); + frinsert(&w->f, r, r+n, 0); + free(r); + }else + frdelete(&w->f, 0, w->f.nchars); + w->org = org; + wfill(w); + wscrdraw(w); + wsetselect(w, w->q0, w->q1); + if(fixup && w->f.p1 > w->f.p0) + frdrawsel(&w->f, frptofchar(&w->f, w->f.p1-1), w->f.p1-1, w->f.p1, 1); +} + +void +wsetselect(Window *w, uint q0, uint q1) +{ + int p0, p1; + + /* w->f.p0 and w->f.p1 are always right; w->q0 and w->q1 may be off */ + w->q0 = q0; + w->q1 = q1; + /* compute desired p0,p1 from q0,q1 */ + p0 = q0-w->org; + p1 = q1-w->org; + if(p0 < 0) + p0 = 0; + if(p1 < 0) + p1 = 0; + if(p0 > w->f.nchars) + p0 = w->f.nchars; + if(p1 > w->f.nchars) + p1 = w->f.nchars; + if(p0==w->f.p0 && p1==w->f.p1) + return; + /* screen disagrees with desired selection */ + if(w->f.p1<=p0 || p1<=w->f.p0 || p0==p1 || w->f.p1==w->f.p0){ + /* no overlap or too easy to bother trying */ + frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 0); + frdrawsel(&w->f, frptofchar(&w->f, p0), p0, p1, 1); + goto Return; + } + /* overlap; avoid unnecessary painting */ + if(p0 < w->f.p0){ + /* extend selection backwards */ + frdrawsel(&w->f, frptofchar(&w->f, p0), p0, w->f.p0, 1); + }else if(p0 > w->f.p0){ + /* trim first part of selection */ + frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, p0, 0); + } + if(p1 > w->f.p1){ + /* extend selection forwards */ + frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, p1, 1); + }else if(p1 < w->f.p1){ + /* trim last part of selection */ + frdrawsel(&w->f, frptofchar(&w->f, p1), p1, w->f.p1, 0); + } + + Return: + w->f.p0 = p0; + w->f.p1 = p1; +} + +uint +winsert(Window *w, Rune *r, int n, uint q0) +{ + uint m; + + if(n == 0) + return q0; + if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){ + m = min(HiWater-LoWater, min(w->org, w->qh)); + w->org -= m; + w->qh -= m; + if(w->q0 > m) + w->q0 -= m; + else + w->q0 = 0; + if(w->q1 > m) + w->q1 -= m; + else + w->q1 = 0; + w->nr -= m; + runemove(w->r, w->r+m, w->nr); + q0 -= m; + } + if(w->nr+n > w->maxr){ + /* + * Minimize realloc breakage: + * Allocate at least MinWater + * Double allocation size each time + * But don't go much above HiWater + */ + m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater; + if(m > HiWater) + m = max(HiWater+MinWater, w->nr+n); + if(m > w->maxr){ + w->r = runerealloc(w->r, m); + w->maxr = m; + } + } + runemove(w->r+q0+n, w->r+q0, w->nr-q0); + runemove(w->r+q0, r, n); + w->nr += n; + /* if output touches, advance selection, not qh; works best for keyboard and output */ + if(q0 <= w->q1) + w->q1 += n; + if(q0 <= w->q0) + w->q0 += n; + if(q0 < w->qh) + w->qh += n; + if(q0 < w->org) + w->org += n; + else if(q0 <= w->org+w->f.nchars) + frinsert(&w->f, r, r+n, q0-w->org); + return q0; +} + +void +wfill(Window *w) +{ + Rune *rp; + int i, n, m, nl; + + if(w->f.lastlinefull) + return; + rp = malloc(messagesize); + do{ + n = w->nr-(w->org+w->f.nchars); + if(n == 0) + break; + if(n > 2000) /* educated guess at reasonable amount */ + n = 2000; + runemove(rp, w->r+(w->org+w->f.nchars), n); + /* + * it's expensive to frinsert more than we need, so + * count newlines. + */ + nl = w->f.maxlines-w->f.nlines; + m = 0; + for(i=0; i= nl) + break; + } + } + frinsert(&w->f, rp, rp+i, w->f.nchars); + }while(w->f.lastlinefull == FALSE); + free(rp); +} + +char* +wcontents(Window *w, int *ip) +{ + return runetobyte(w->r, w->nr, ip); +} -- cgit v1.2.3