URI: 
       tNew 9term using rio sources more directly. - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit e830a908498c8f0270948fd08c50f6d773315880
   DIR parent a6c0ff35ee294c0a808e1792eb4f43820fed8f16
  HTML Author: rsc <devnull@localhost>
       Date:   Sun,  6 Nov 2005 22:16:48 +0000
       
       New 9term using rio sources more directly.
       
       Diffstat:
         M src/cmd/9term/9term.c               |    2150 +++++--------------------------
         A src/cmd/9term/dat.h                 |     243 +++++++++++++++++++++++++++++++
         A src/cmd/9term/data.c                |     180 +++++++++++++++++++++++++++++++
         A src/cmd/9term/fns.h                 |      36 +++++++++++++++++++++++++++++++
         A src/cmd/9term/malloc.c              |      63 +++++++++++++++++++++++++++++++
         M src/cmd/9term/mkfile                |       4 ++++
         A src/cmd/9term/scrl.c                |     183 +++++++++++++++++++++++++++++++
         A src/cmd/9term/time.c                |     125 +++++++++++++++++++++++++++++++
         A src/cmd/9term/util.c                |     149 +++++++++++++++++++++++++++++++
         A src/cmd/9term/wind.c                |    1628 +++++++++++++++++++++++++++++++
       
       10 files changed, 2941 insertions(+), 1820 deletions(-)
       ---
   DIR diff --git a/src/cmd/9term/9term.c b/src/cmd/9term/9term.c
       t@@ -10,208 +10,37 @@
        #include <frame.h>
        #include <plumb.h>
        #include <complete.h>
       +#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");
        }
        
       t@@ -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
       t@@ -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; i<nelem(buf[0])-1; i++)
       +                        if(nbrecv(keyboardctl->c, 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(*q1<t.nr && !isspace(t.r[*q1]))
       -                (*q1)++;
       -        while(*q0>0 && !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; i<c->nfile; 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<t.nr && t.r[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<t.nr; i++){
       -                c = t.r[i];
       -                if(c=='\n' || c=='\004' || c==0x7F)
       -                        return 1;
       -        }
       -        return 0;
       -}
       -
       -
       -void
       -consread(void)
       -{
       -        char buf[8000], *p;
       -        int c, width, n;
       -        int s, raw;
       -
       -        raw = rawon();
       -        for(;;) {
       -                if(!consready())
       -                        return;
       -                n = sizeof(buf);
       -                p = buf;
       -                c = 0;
       -                while(n >= UTFmax && (t.qh<t.nr || t.nraw > 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<n; i++) {
       -                if(*p == '\b') {
       -                        if(q == r)
       -                                initial++;
       -                        else
       -                                --q;
       -                } else if(*p == '\r') {        /* treat like ^U */
       -                        /* convert CR without NL into erased line */
       -                        /* i feel really sleazy about this but it helps */
       -                        while(i<n-1 && *(p+1) == '\r'){
       -                                i++;
       -                                p++;
       -                        }
       -                        if(i<n-1 && *(p+1) != '\n'){
       -                                while(q > 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<n; p++)
       -                i += chartorune(p, pp+i);
       -        t.nsnarf = p-t.snarf;
       -
       -}
       -
       -char sbuf[SnarfSize];
       -void
       -snarf(void)
       -{
       -        char *p;
       -        int i, n;
       -        Rune *rp;
       -
       -        if(t.q1 == t.q0)
       -                return;
       -        n = t.q1-t.q0;
       -        t.snarf = runerealloc(t.snarf, n);
       -        for(i=0,p=sbuf,rp=t.snarf; i<n && p < sbuf+SnarfSize-UTFmax; i++){
       -                *rp++ = *(t.r+t.q0+i);
       -                p += runetochar(p, t.r+t.q0+i);
       -        }
       -        t.nsnarf = rp-t.snarf;
       -        *p = '\0';
       -        putsnarf(sbuf);
       -}
       -
       -uint
       -min(uint x, uint y)
       -{
       -        if(x < y)
       -                return x;
       -        return y;
       -}
       -
       -uint
       -max(uint x, uint y)
       -{
       -        if(x > 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 (q0<t.org)
       -                nl = t.f->maxlines/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)
       -                q = t.org;
       -        setorigin(q, 0);
       -        /* keep trying until q0 is on the screen */
       -        while(!cansee(q0)) {
       -                assert(q0 >= 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<t.nr; i++){
       -                        if(t.r[org-1] == '\n')
       -                                break;
       -                        org++;
       -                }
       -        }
       -        a = org-t.org;
       -
       -        if(a>=0 && a<t.f->nchars)
       -                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+(*q0<t.nr && c=='\n');
       -                                *q0 = q;
       -                                if(c!='\n' || q!=0 || t.r[0]=='\n')
       -                                        (*q0)++;
       -                        }
       -                        return;
       -                }
       -        }
       -        /* try filling out word to right */
       -        while(*q1<t.nr && isexpand(t.r[*q1]))
       -                (*q1)++;
       -        /* try filling out word to left */
       -        while(*q0>0 && 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.x<s.min.x || s.max.x<=t.m.xy.x){
       -                        readmouse(mc);
       -                        t.m = mc->m;
       -                }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; i<n && p < pm->data + 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<p-buf; x+=n)
       -                                if((n = write(fd, buf+x, (p-x)-buf)) <= 0)
       -                                        goto break2;
       -                        
       -                        if(i >= 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);
       -}
   DIR diff --git a/src/cmd/9term/dat.h b/src/cmd/9term/dat.h
       t@@ -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 */
   DIR diff --git a/src/cmd/9term/data.c b/src/cmd/9term/data.c
       t@@ -0,0 +1,180 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <draw.h>
       +#include <thread.h>
       +#include <cursor.h>
       +#include <mouse.h>
       +#include <keyboard.h>
       +#include <frame.h>
       +#include <fcall.h>
       +#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);
       +}
   DIR diff --git a/src/cmd/9term/fns.h b/src/cmd/9term/fns.h
       t@@ -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))
   DIR diff --git a/src/cmd/9term/malloc.c b/src/cmd/9term/malloc.c
       t@@ -0,0 +1,63 @@
       +/*
       + * These are here mainly so that I can link against
       + * debugmalloc.c instead and not recompile the world.
       + */
       +
       +#include <u.h>
       +#define NOPLAN9DEFINES
       +#include <libc.h>
       +
       +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;
       +}
       +
   DIR diff --git a/src/cmd/9term/mkfile b/src/cmd/9term/mkfile
       t@@ -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
       +
   DIR diff --git a/src/cmd/9term/scrl.c b/src/cmd/9term/scrl.c
       t@@ -0,0 +1,183 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <draw.h>
       +#include <thread.h>
       +#include <cursor.h>
       +#include <mouse.h>
       +#include <keyboard.h>
       +#include <frame.h>
       +#include <fcall.h>
       +#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.x<s.min.x || s.max.x<=w->mc.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);
       +}
   DIR diff --git a/src/cmd/9term/time.c b/src/cmd/9term/time.c
       t@@ -0,0 +1,125 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <draw.h>
       +#include <thread.h>
       +#include <cursor.h>
       +#include <mouse.h>
       +#include <keyboard.h>
       +#include <frame.h>
       +#include <fcall.h>
       +#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; i<nt; i++){
       +                        x = t[i];
       +                        x->dt -= 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;
       +}
   DIR diff --git a/src/cmd/9term/util.c b/src/cmd/9term/util.c
       t@@ -0,0 +1,149 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <draw.h>
       +#include <thread.h>
       +#include <cursor.h>
       +#include <mouse.h>
       +#include <keyboard.h>
       +#include <frame.h>
       +#include <fcall.h>
       +#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<n; j+=w){
       +                if(*q < Runeself){
       +                        w = 1;
       +                        *s = *q++;
       +                }else{
       +                        w = chartorune(s, (char*)q);
       +                        q += w;
       +                }
       +                if(*s)
       +                        s++;
       +                else if(nulls)
       +                                *nulls = TRUE;
       +        }
       +        *nb = (char*)q-p;
       +        *nr = s-r;
       +}
       +
       +void
       +error(char *s)
       +{
       +        fprint(2, "rio: %s: %r\n", s);
       +        if(errorshouldabort)
       +                abort();
       +        threadexitsall("error");
       +}
       +
       +void*
       +erealloc(void *p, uint n)
       +{
       +        p = realloc(p, n);
       +        if(p == nil)
       +                error("realloc failed");
       +        return p;
       +}
       +
       +void*
       +emalloc(uint n)
       +{
       +        void *p;
       +
       +        p = malloc(n);
       +        if(p == nil)
       +                error("malloc failed");
       +        memset(p, 0, n);
       +        return p;
       +}
       +
       +char*
       +estrdup(char *s)
       +{
       +        char *p;
       +
       +        p = malloc(strlen(s)+1);
       +        if(p == nil)
       +                error("strdup failed");
       +        strcpy(p, s);
       +        return p;
       +}
       +
       +int
       +isalnum(Rune c)
       +{
       +        /*
       +         * Hard to get absolutely right.  Use what we know about ASCII
       +         * and assume anything above the Latin control characters is
       +         * potentially an alphanumeric.
       +         */
       +        if(c <= ' ')
       +                return FALSE;
       +        if(0x7F<=c && c<=0xA0)
       +                return FALSE;
       +        if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", 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;
       +}
       +
   DIR diff --git a/src/cmd/9term/wind.c b/src/cmd/9term/wind.c
       t@@ -0,0 +1,1628 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <draw.h>
       +#include <thread.h>
       +#include <cursor.h>
       +#include <mouse.h>
       +#include <keyboard.h>
       +#include <frame.h>
       +#include <fcall.h>
       +#include <plumb.h>
       +#include <complete.h>
       +#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("<unnamed>");
       +        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; i<w->nr; 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<nr; i++)
       +                                if(*bp++ == '\b'){
       +                                        --bp;
       +                                        initial = 0;
       +                                        tp = runemalloc(nr);
       +                                        runemove(tp, rp, i);
       +                                        up = tp+i;
       +                                        for(; i<nr; i++){
       +                                                *up = *bp++;
       +                                                if(*up == '\b')
       +                                                        if(up == tp)
       +                                                                initial++;
       +                                                        else
       +                                                                --up;
       +                                                else
       +                                                        up++;
       +                                        }
       +                                        if(initial){
       +                                                if(initial > 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(i<nb && (w->qh<w->nr || 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->qh<w->nr && 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; i<nhidden; i++)
       +                                        if(hidden[i] == w){
       +                                                s = "hidden";
       +                                                break;
       +                                        }
       +                                t = "notcurrent";
       +                                if(w == input)
       +                                        t = "current";
       +                                pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
       +                                        w->i->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; i<c->nfile; 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->q0<w->nr && 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(p1<w->nr && 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; i<nwindow; i++)
       +                if(window[i]->id == 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; i<nhidden; i++)
       +                if(hidden[i] == w){
       +                        --nhidden;
       +                        memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
       +                        break;
       +                }
       +        for(i=0; i<nwindow; i++)
       +                if(window[i] == w){
       +                        --nwindow;
       +                        memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
       +                        w->deleted = 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+(*q0<w->nr && c=='\n');
       +                                *q0 = q;
       +                                if(c!='\n' || q!=0 || w->r[0]=='\n')
       +                                        (*q0)++;
       +                        }
       +                        return;
       +                }
       +        }
       +        /* try filling out word to right */
       +        while(*q1<w->nr && 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 && (q0<qe || (q0==qe && qe==w->nr)))
       +                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 && q<w->org))
       +                        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 && org<w->nr; i++){
       +                        if(w->r[org] == '\n'){
       +                                org++;
       +                                break;
       +                        }
       +                        org++;
       +                }
       +        }
       +        a = org-w->org;
       +        fixup = 0;
       +        if(a>=0 && a<w->f.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 && -a<w->f.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<n; ){
       +                        if(rp[i++] == '\n'){
       +                                m++;
       +                                if(m >= 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);
       +}