]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/rio/wind.c
rio: second attempt...
[plan9front.git] / sys / src / cmd / rio / wind.c
index 534d0cdb6c8d3ff85938aea1bf581777483dd1f8..f29fb6a431c451ce48eeaca62ae8aa34ba5ed01e 100644 (file)
@@ -21,18 +21,9 @@ enum
        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 int     topped;
+static int     id;
 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)
@@ -40,21 +31,6 @@ 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);
@@ -65,9 +41,11 @@ wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
        w->cursorp = nil;
        w->conswrite = chancreate(sizeof(Conswritemesg), 0);
        w->consread =  chancreate(sizeof(Consreadmesg), 0);
-       w->kbdread =  chancreate(sizeof(Kbdreadmesg), 0);
+       w->kbdread =  chancreate(sizeof(Consreadmesg), 0);
        w->mouseread =  chancreate(sizeof(Mousereadmesg), 0);
        w->wctlread =  chancreate(sizeof(Consreadmesg), 0);
+       w->complete = chancreate(sizeof(Completion*), 0);
+       w->gone = chancreate(sizeof(char*), 0);
        w->scrollr = r;
        w->scrollr.max.x = r.min.x+Scrollwid;
        w->lastsr = ZR;
@@ -94,7 +72,7 @@ wsetname(Window *w)
        int i, n;
        char err[ERRMAX];
        
-       n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
+       n = snprint(w->name, sizeof(w->name)-2, "window.%d.%d", w->id, w->namecount++);
        for(i='A'; i<='Z'; i++){
                if(nameimage(w->i, w->name, 1) > 0)
                        return;
@@ -109,52 +87,47 @@ wsetname(Window *w)
 }
 
 void
-wresize(Window *w, Image *i, int move)
+wresize(Window *w, Image *i)
 {
-       Rectangle r, or;
+       Rectangle r;
 
-       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);
        freeimage(w->i);
        w->i = i;
-       wsetname(w);
        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, r, w->i);
-       else{
-               frclear(w, FALSE);
-               frinit(w, r, w->font, w->i, cols);
-               wsetcols(w);
-               w->maxtab = maxtab*stringwidth(w->font, "0");
-               r = insetrect(w->i->r, Selborder);
-               draw(w->i, r, cols[BACK], nil, w->entire.min);
-               wfill(w);
-               wsetselect(w, w->q0, w->q1);
-               wscrdraw(w);
-       }
+       frclear(w, FALSE);
+       frinit(w, r, w->font, w->i, cols);
+       wsetcols(w, 1);
+       w->maxtab = maxtab*stringwidth(w->font, "0");
+       r = insetrect(w->i->r, Selborder);
+       draw(w->i, r, cols[BACK], nil, w->entire.min);
+       wfill(w);
+       wsetselect(w, w->q0, w->q1);
+       wscrdraw(w);
        wborder(w, Selborder);
+       flushimage(display, 1);
+       wsetname(w);
        w->topped = ++topped;
        w->resized = TRUE;
        w->mouse.counter++;
+       w->wctlready = 1;
 }
 
 void
-wrefresh(Window *w, Rectangle)
+wrefresh(Window *w)
 {
-       /* BUG: rectangle is ignored */
+       Rectangle r;
+
        if(w == input)
                wborder(w, Selborder);
        else
                wborder(w, Unselborder);
-       if(w->mouseopen)
-               return;
-       draw(w->i, insetrect(w->i->r, Borderwidth), w->cols[BACK], nil, w->i->r.min);
+       r = insetrect(w->i->r, Selborder);
+       draw(w->i, r, w->cols[BACK], nil, w->entire.min);
        w->ticked = 0;
        if(w->p0 > 0)
                frdrawsel(w, frptofchar(w, 0), 0, w->p0, 0);
@@ -175,51 +148,47 @@ wclose(Window *w)
                return 0;
        if(i < 0)
                error("negative ref count");
-       if(!w->deleted)
-               wclosewin(w);
+       wclunk(w);
        wsendctlmesg(w, Exited, ZR, nil);
        return 1;
 }
 
+void
+showcandidates(Window *, Completion *);
 
 void
 winctl(void *arg)
 {
-       Rune *rp, *bp, *tp, *up;
-       uint qh;
+       Rune *rp, *up, r;
+       uint qh, q0;
        int nr, nb, c, wid, i, npart, initial, lastb;
        char *s, *t, part[3];
        Window *w;
        Mousestate *mp, m;
-       enum { WKbd, WKbdread, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
+       enum { WKbd, WKbdread, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, WComplete, Wgone, NWALT };
        Alt alts[NWALT+1];
+       Consreadmesg crm;
        Mousereadmesg mrm;
-       Kbdreadmesg krm;
        Conswritemesg cwm;
-       Consreadmesg crm;
-       Consreadmesg cwrm;
        Stringpair pair;
        Wctlmesg wcm;
-       char buf[4*12+1], *kbdq[8], *kbds;
-       int kbdqr, kbdqw;
+       Completion *cr;
+       char *kbdq[32], *kbds;
+       uint kbdqr, kbdqw;
 
        w = arg;
-       snprint(buf, sizeof buf, "winctl-id%d", w->id);
-       threadsetname(buf);
+       threadsetname("winctl-id%d", w->id);
 
        mrm.cm = chancreate(sizeof(Mouse), 0);
-       krm.ck = chancreate(sizeof(char*), 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);
+       cwm.cw = chancreate(sizeof(Stringpair), 0);
        
        alts[WKbd].c = w->ck;
        alts[WKbd].v = &kbds;
        alts[WKbd].op = CHANRCV;
        alts[WKbdread].c = w->kbdread;
-       alts[WKbdread].v = &krm;
+       alts[WKbdread].v = &crm;
        alts[WKbdread].op = CHANSND;
        alts[WMouse].c = w->mc.c;
        alts[WMouse].v = &w->mc.Mouse;
@@ -237,71 +206,89 @@ winctl(void *arg)
        alts[WCread].v = &crm;
        alts[WCread].op = CHANSND;
        alts[WWread].c = w->wctlread;
-       alts[WWread].v = &cwrm;
+       alts[WWread].v = &crm;
        alts[WWread].op = CHANSND;
+       alts[WComplete].c = w->complete;
+       alts[WComplete].v = &cr;
+       alts[WComplete].op = CHANRCV;
+       alts[Wgone].c = w->gone;
+       alts[Wgone].v = "window deleted";
+       alts[Wgone].op = CHANNOP;
        alts[NWALT].op = CHANEND;
 
-       memset(kbdq, 0, sizeof(kbdq));
        kbdqr = kbdqw = 0;
        npart = 0;
        lastb = -1;
        for(;;){
-               alts[WKbdread].op = (w->kbdopen && kbdqw != kbdqr) ?
-                       CHANSND : CHANNOP;
-               alts[WMouseread].op = (w->mouseopen && w->mouse.counter != w->mouse.lastcounter) ? 
-                       CHANSND : CHANNOP;
-               alts[WCwrite].op = (!w->scrolling && !w->mouseopen && w->qh>w->org+w->nchars) ?
-                       CHANNOP : CHANSND;
-               alts[WWread].op = (w->deleted || !w->wctlready) ?
-                       CHANNOP : 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{
+               if(w->i==nil){
+                       /* window deleted */
+                       alts[Wgone].op = CHANSND;
+
+                       alts[WKbdread].op = CHANNOP;
+                       alts[WMouseread].op = CHANNOP;
+                       alts[WCwrite].op = CHANNOP;
+                       alts[WWread].op = CHANNOP;
                        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;
+               } else {
+                       alts[WKbdread].op = (w->kbdopen && kbdqw != kbdqr) ?
+                               CHANSND : CHANNOP;
+                       alts[WMouseread].op = (w->mouseopen && w->mouse.counter != w->mouse.lastcounter) ? 
+                               CHANSND : CHANNOP;
+                       alts[WCwrite].op = w->scrolling || w->mouseopen || (w->qh <= w->org+w->nchars) ?
+                               CHANSND : CHANNOP;
+                       alts[WWread].op = w->wctlready ?
+                               CHANSND : CHANNOP;
+                       /* 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 WKbd:
-                       if(utflen(kbds) >= utflen(kbdq[kbdqw] ? kbdq[kbdqw] : "")){
-                               Rune r;
-
-                               i = 0;
-                               r = 0;
-                               while(kbds[i])
-                                       i += chartorune(&r, kbds+i);
-                               wkeyctl(w, r);
-                       }
-                       if(w->kbdopen){
-                               i = (kbdqw+1) % nelem(kbdq);
-                               if(i != kbdqr)
-                                       kbdqw = i;
+                       if(kbdqw - kbdqr < nelem(kbdq))
+                               kbdq[kbdqw++ % nelem(kbdq)] = kbds;
+                       else
+                               free(kbds);
+                       if(w->kbdopen)
+                               continue;
+                       while(kbdqr != kbdqw){
+                               kbds = kbdq[kbdqr++ % nelem(kbdq)];
+                               if(*kbds == 'c'){
+                                       chartorune(&r, kbds+1);
+                                       if(r)
+                                               wkeyctl(w, r);
+                               }
+                               free(kbds);
                        }
-                       free(kbdq[kbdqw]);
-                       kbdq[kbdqw] = kbds;
                        break;
-
                case WKbdread:
-                       i = (kbdqr+1) % nelem(kbdq);
-                       if(kbdqr != kbdqw)
-                               kbdqr = i;
-                       if(kbdq[i]){
-                               sendp(krm.ck, kbdq[i]);
-                               kbdq[i] = nil;
-                       }else
-                               sendp(krm.ck, strdup(""));
+                       recv(crm.c1, &pair);
+                       nb = 0;
+                       while(kbdqr != kbdqw){
+                               kbds = kbdq[kbdqr % nelem(kbdq)];
+                               i = strlen(kbds)+1;
+                               if(nb+i > pair.ns)
+                                       break;
+                               memmove((char*)pair.s + nb, kbds, i);
+                               free(kbds);
+                               nb += i;
+                               kbdqr++;
+                       }
+                       pair.ns = nb;
+                       send(crm.c2, &pair);
                        continue;
-
                case WMouse:
                        if(w->mouseopen) {
                                w->mouse.counter++;
@@ -336,16 +323,13 @@ winctl(void *arg)
                        send(mrm.cm, &m.Mouse);
                        continue;
                case WCtl:
-                       if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
-                               for(i=0; i<nelem(kbdq); i++)
-                                       free(kbdq[i]);
+                       if(wctlmesg(w, wcm.type, wcm.r, wcm.p) == Exited){
+                               while(kbdqr != kbdqw)
+                                       free(kbdq[kbdqr++ % nelem(kbdq)]);
                                chanfree(crm.c1);
                                chanfree(crm.c2);
                                chanfree(mrm.cm);
-                               chanfree(krm.ck);
                                chanfree(cwm.cw);
-                               chanfree(cwrm.c1);
-                               chanfree(cwrm.c2);
                                threadexits(nil);
                        }
                        continue;
@@ -353,23 +337,18 @@ winctl(void *arg)
                        recv(cwm.cw, &pair);
                        rp = pair.s;
                        nr = pair.ns;
-                       bp = rp;
                        for(i=0; i<nr; i++)
-                               if(*bp++ == '\b'){
-                                       --bp;
+                               if(rp[i] == '\b'){
+                                       up = rp+i;
                                        initial = 0;
-                                       tp = runemalloc(nr);
-                                       runemove(tp, rp, i);
-                                       up = tp+i;
                                        for(; i<nr; i++){
-                                               *up = *bp++;
-                                               if(*up == '\b')
-                                                       if(up == tp)
+                                               if(rp[i] == '\b'){
+                                                       if(up == rp)
                                                                initial++;
                                                        else
-                                                               --up;
-                                               else
-                                                       up++;
+                                                               up--;
+                                               }else
+                                                       *up++ = rp[i];
                                        }
                                        if(initial){
                                                if(initial > w->qh)
@@ -378,10 +357,7 @@ winctl(void *arg)
                                                wdelete(w, qh, qh+initial);
                                                w->qh = qh;
                                        }
-                                       free(rp);
-                                       rp = tp;
-                                       nr = up-tp;
-                                       rp[nr] = 0;
+                                       nr = up - rp;
                                        break;
                                }
                        w->qh = winsert(w, rp, nr, w->qh)+nr;
@@ -427,26 +403,34 @@ winctl(void *arg)
                        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;
+                       recv(crm.c1, &pair);
+                       s = Dx(w->screenr) > 0 ? "visible" : "hidden";
+                       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(crm.c2, &pair);
+                       continue;
+               case WComplete:
+                       if(w->i!=nil){
+                               if(!cr->advance)
+                                       showcandidates(w, cr);
+                               if(cr->advance){
+                                       rp = runesmprint("%s", cr->string);
+                                       if(rp){
+                                               nr = runestrlen(rp);
+                                               q0 = w->q0;
+                                               q0 = winsert(w, rp, nr, q0);
+                                               wshow(w, q0+nr);
+                                               free(rp);
                                        }
-                               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;
+                       freecompletion(cr);
+                       break;
                }
-               if(!w->deleted)
+               if(w->i!=nil && Dx(w->screenr) > 0 && display->bufp > display->buf)
                        flushimage(display, 1);
        }
 }
@@ -470,6 +454,7 @@ interruptproc(void *v)
 
        notefd = v;
        write(*notefd, "interrupt", 9);
+       close(*notefd);
        free(notefd);
 }
 
@@ -482,7 +467,7 @@ windfilewidth(Window *w, uint q0, int oneelement)
        q = q0;
        while(q > 0){
                r = w->r[q-1];
-               if(r<=' ')
+               if(r<=' ' || r=='=' || r=='^' || r=='(' || r=='{')
                        break;
                if(oneelement && r=='/')
                        break;
@@ -497,7 +482,7 @@ showcandidates(Window *w, Completion *c)
        int i;
        Fmt f;
        Rune *rp;
-       uint nr, qline, q0;
+       uint nr, qline;
        char *s;
 
        runefmtstrinit(&f);
@@ -516,79 +501,94 @@ showcandidates(Window *w, Completion *c)
                }
                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;
+       /* place text at beginning of line before cursor and host point */
+       qline = min(w->qh, w->q0);
+       while(qline>0 && w->r[qline-1] != '\n')
+               qline--;
+
+       if(qline == w->qh){
+               /* advance host point to avoid readback */
+               w->qh = winsert(w, rp, nr, qline)+nr;
+       } else {
+               winsert(w, rp, nr, qline);
+       }
        free(rp);
-       wsetselect(w, q0+nr, q0+nr);
 }
 
-Rune*
+typedef struct Completejob Completejob;
+struct Completejob
+{
+       char    *dir;
+       char    *str;
+       Window  *win;
+};
+
+void
+completeproc(void *arg)
+{
+       Completejob *job;
+       Completion *c;
+
+       job = arg;
+       threadsetname("namecomplete %s", job->dir);
+
+       c = complete(job->dir, job->str);
+       if(c != nil && sendp(job->win->complete, c) <= 0)
+               freecompletion(c);
+
+       wclose(job->win);
+
+       free(job->dir);
+       free(job->str);
+       free(job);
+}
+
+void
 namecomplete(Window *w)
 {
        int nstr, npath;
-       Rune *rp, *path, *str;
-       Completion *c;
-       char *s, *dir, *root;
+       Rune *path, *str;
+       char *dir, *root;
+       Completejob *job;
 
        /* 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;
+               return;
        nstr = windfilewidth(w, w->q0, TRUE);
-       str = runemalloc(nstr);
-       runemove(str, w->r+(w->q0-nstr), nstr);
+       str = w->r+(w->q0-nstr);
        npath = windfilewidth(w, w->q0-nstr, FALSE);
-       path = runemalloc(npath);
-       runemove(path, w->r+(w->q0-nstr-npath), npath);
-       rp = nil;
+       path = w->r+(w->q0-nstr-npath);
 
        /* 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(npath>0 && path[0]=='/')
+               dir = runetobyte(path, npath, &npath);
+       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 = smprint("%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);
+       if(dir == nil)
+               return;
 
-  Return:
-       freecompletion(c);
-       free(dir);
-       free(path);
-       free(str);
-       return rp;
+       /* run in background, winctl will collect the result on w->complete chan */
+       job = emalloc(sizeof *job);
+       job->str = runetobyte(str, nstr, &nstr);
+       job->dir = cleanname(dir);
+       job->win = w;
+       incref(w);
+       proccreate(completeproc, job, STACK);
 }
 
 void
 wkeyctl(Window *w, Rune r)
 {
        uint q0 ,q1;
-       int n, nb, nr;
-       Rune *rp;
+       int n, nb;
        int *notefd;
 
        switch(r){
@@ -602,13 +602,13 @@ wkeyctl(Window *w, Rune r)
                return;
        }
 
-       if(w->deleted)
+       if(w->i==nil)
                return;
-       /* navigation keys work only when mouse is not open */
+       /* navigation keys work only when mouse and kbd is not open */
        if(!w->mouseopen)
                switch(r){
                case Kdown:
-                       n = w->maxlines/3;
+                       n = shiftdown ? 1 : w->maxlines/3;
                        goto case_Down;
                case Kscrollonedown:
                        n = mousescrollsize(w->maxlines);
@@ -622,7 +622,7 @@ wkeyctl(Window *w, Rune r)
                        wsetorigin(w, q0, TRUE);
                        return;
                case Kup:
-                       n = w->maxlines/3;
+                       n = shiftdown ? 1 : w->maxlines/3;
                        goto case_Up;
                case Kscrolloneup:
                        n = mousescrollsize(w->maxlines);
@@ -655,6 +655,10 @@ wkeyctl(Window *w, Rune r)
                case Kend:
                        wshow(w, w->nr);
                        return;
+               case Kscroll:
+                       w->scrolling ^= 1;
+                       wshow(w, w->nr);
+                       return;
                case Ksoh:      /* ^A: beginning of line */
                        if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
                                return;
@@ -669,6 +673,10 @@ wkeyctl(Window *w, Rune r)
                        wsetselect(w, q0, q0);
                        wshow(w, w->q0);
                        return;
+               case Kstx:      /* ^B: output point */
+                       wsetselect(w, w->qh, w->qh);
+                       wshow(w, w->q0);
+                       return;
                }
        if(w->rawing && (w->q0==w->nr || w->mouseopen)){
                waddraw(w, &r, 1);
@@ -679,6 +687,7 @@ wkeyctl(Window *w, Rune r)
                        --w->holding;
                else
                        w->holding++;
+               wsetcursor(w, FALSE);
                wrepaint(w);
                if(r == Kesc)
                        return;
@@ -691,20 +700,15 @@ wkeyctl(Window *w, Rune r)
        case Kdel:      /* send interrupt */
                w->qh = w->nr;
                wshow(w, w->qh);
+               if(w->notefd < 0)
+                       return;
                notefd = emalloc(sizeof(int));
-               *notefd = w->notefd;
+               *notefd = dup(w->notefd, -1);
                proccreate(interruptproc, notefd, 4096);
                return;
        case Kack:      /* ^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);
+               namecomplete(w);
                return;
        case Kbs:       /* ^H: erase character */
        case Knack:     /* ^U: erase line */
@@ -731,30 +735,29 @@ wkeyctl(Window *w, Rune r)
 }
 
 void
-wsetcols(Window *w)
+wsetcols(Window *w, int topped)
 {
        if(w->holding)
-               if(w == input)
-                       w->cols[TEXT] = w->cols[HTEXT] = holdcol;
+               if(topped)
+                       w->cols[TEXT] = holdcol;
                else
-                       w->cols[TEXT] = w->cols[HTEXT] = lightholdcol;
+                       w->cols[TEXT] = lightholdcol;
        else
-               if(w == input)
-                       w->cols[TEXT] = w->cols[HTEXT] = display->black;
+               if(topped)
+                       w->cols[TEXT] = cols[TEXT];
                else
-                       w->cols[TEXT] = w->cols[HTEXT] = darkgrey;
+                       w->cols[TEXT] = paletextcol;
 }
 
 void
 wrepaint(Window *w)
 {
-       wsetcols(w);
+       wsetcols(w, w == input);
        if(!w->mouseopen)
                frredraw(w);
-       if(w == input){
+       if(w == input)
                wborder(w, Selborder);
-               wsetcursor(w, 0);
-       }else
+       else
                wborder(w, Unselborder);
 }
 
@@ -858,7 +861,7 @@ wplumb(Window *w)
                        p0--;
                while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
                        p1++;
-               sprint(buf, "click=%d", w->q0-p0);
+               snprint(buf, sizeof(buf), "click=%d", w->q0-p0);
                m->attr = plumbunpackattr(buf);
        }
        if(p1-p0 > messagesize-1024){
@@ -868,17 +871,36 @@ wplumb(Window *w)
        m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
        if(plumbsend(fd, m) < 0){
                c = lastcursor;
-               riosetcursor(&query, 1);
+               riosetcursor(&query);
                sleep(300);
-               riosetcursor(c, 1);
+               riosetcursor(c);
        }
        plumbfree(m);
 }
 
-int
-winborder(Window *w, Point xy)
+void
+wlook(Window *w)
 {
-       return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
+       int i, n, e;
+
+       i = w->q1;
+       n = i - w->q0;
+       e = w->nr - n;
+       if(n <= 0 || e < n)
+               return;
+
+       if(i > e)
+               i = 0;
+
+       while(runestrncmp(w->r+w->q0, w->r+i, n) != 0){
+               if(i < e)
+                       i++;
+               else
+                       i = 0;
+       }
+
+       wsetselect(w, i, i+n);
+       wshow(w, i);
 }
 
 void
@@ -886,32 +908,22 @@ wmousectl(Window *w)
 {
        int but;
 
-       if(w->mc.buttons == 1)
-               but = 1;
-       else if(w->mc.buttons == 2)
-               but = 2;
-       else if(w->mc.buttons == 4)
-               but = 3;
-       else{
-               if(w->mc.buttons == 8)
-                       wkeyctl(w, Kscrolloneup);
-               if(w->mc.buttons == 16)
-                       wkeyctl(w, Kscrollonedown);
-               return;
+       for(but=1;; but++){
+               if(but > 5)
+                       return;
+               if(w->mc.buttons == 1<<(but-1))
+                       break;
        }
 
        incref(w);              /* hold up window while we track */
-       if(w->deleted)
-               goto Return;
-       if(ptinrect(w->mc.xy, w->scrollr)){
-               if(but)
+       if(w->i != nil){
+               if(shiftdown && but > 3)
+                       wkeyctl(w, but == 4 ? Kscrolloneup : Kscrollonedown);
+               else if(ptinrect(w->mc.xy, w->scrollr) || (but > 3))
                        wscroll(w, but);
-               goto Return;
+               else if(but == 1)
+                       wselect(w);
        }
-       if(but == 1)
-               wselect(w);
-       /* else all is handled by main process */
-   Return:
        wclose(w);
 }
 
@@ -1012,7 +1024,6 @@ wselect(Window *w)
        if(q0==q1 && selectq==w->q0){
                wdoubleclick(w, &q0, &q1);
                wsetselect(w, q0, q1);
-               flushimage(display, 1);
                x = w->mc.xy.x;
                y = w->mc.xy.y;
                /* stay here until something interesting happens */
@@ -1052,7 +1063,6 @@ wselect(Window *w)
        }else
                clickwin = nil;
        wsetselect(w, q0, q1);
-       flushimage(display, 1);
        while(w->mc.buttons){
                w->mc.msec = 0;
                b = w->mc.buttons;
@@ -1069,7 +1079,6 @@ wselect(Window *w)
                        }
                }
                wscrdraw(w);
-               flushimage(display, 1);
                while(w->mc.buttons == b)
                        readmouse(&w->mc);
                clickwin = nil;
@@ -1077,28 +1086,30 @@ wselect(Window *w)
 }
 
 void
-wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
+wsendctlmesg(Window *w, int type, Rectangle r, void *p)
 {
        Wctlmesg wcm;
 
        wcm.type = type;
        wcm.r = r;
-       wcm.image = image;
+       wcm.p = p;
        send(w->cctl, &wcm);
 }
 
 int
-wctlmesg(Window *w, int m, Rectangle r, Image *i)
+wctlmesg(Window *w, int m, Rectangle r, void *p)
 {
        char buf[64];
+       Image *i = p;
 
        switch(m){
        default:
                error("unknown control message");
                break;
        case Wakeup:
+               if(p!=nil)
+                       sendp((Channel*)p, w);
                break;
-       case Moved:
        case Reshaped:
                if(w->deleted){
                        freeimage(i);
@@ -1106,32 +1117,71 @@ wctlmesg(Window *w, int m, Rectangle r, Image *i)
                }
                w->screenr = r;
                strcpy(buf, w->name);
-               wresize(w, i, m==Moved);
-               w->wctlready = 1;
+               wresize(w, i);
                proccreate(deletetimeoutproc, estrdup(buf), 4096);
-               if(Dx(r) > 0){
-                       if(w != input)
-                               wcurrent(w);
-               }else if(w == input)
-                       wcurrent(nil);
+               if(Dx(r)<=0){   /* window got hidden, if we had the input, drop it */
+                       if(w==input)
+                               input = nil;
+                       break;
+               }
+               /* fall through to get input if needed */
+       case Topped:
+               if(w->deleted || w==input)
+                       break;
+               if(input!=nil){
+                       Window *oi;
+                       Channel *c;
+       
+                       oi = input;
+                       incref(oi);
+
+                       /*
+                        * have to wait until old input responds before
+                        * changing input to us because the window might
+                        * currently be mouse tracking and it is not
+                        * prepared for getting its input revoked.
+                        */
+                       c = chancreate(sizeof(void*), 0);
+                       wsendctlmesg(oi, Wakeup, ZR, c);
+                       recv(c, nil);
+                       chanfree(c);
+
+                       /*
+                        * if we are still top window and nobody else has taken
+                        * input from original window, take the input.
+                        */
+                       if(!w->deleted && w->topped==topped && oi==input){
+                               input = w;
+
+                               oi->wctlready = 1;
+                               wsendctlmesg(oi, Repaint, ZR, nil);
+                       }
+                       wclose(oi);
+               } else
+                       input = w;
+               w->wctlready = 1;
+               if(m!=Topped && w==input)
+                       break;
+               /* fall thrugh for redraw after input change */
+       case Repaint:
+               if(w->i==nil || Dx(w->screenr)<=0)
+                       break;
+               wrepaint(w);
                flushimage(display, 1);
                break;
        case Refresh:
-               if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
+               if(w->i==nil || Dx(w->screenr)<=0 || w->mouseopen)
                        break;
-               if(!w->mouseopen)
-                       wrefresh(w, r);
+               wrefresh(w);
                flushimage(display, 1);
                break;
        case Movemouse:
-               if(sweeping || !ptinrect(r.min, w->i->r))
+               if(w->i==nil || Dx(w->screenr)<=0 || !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;
@@ -1140,21 +1190,27 @@ wctlmesg(Window *w, int m, Rectangle r, Image *i)
                break;
        case Holdon:
        case Holdoff:
-               if(w->deleted)
+               if(w->i==nil)
                        break;
+               if(w==input)
+                       wsetcursor(w, FALSE);
                wrepaint(w);
                flushimage(display, 1);
                break;
        case Deleted:
-               if(w->deleted)
-                       break;
-               write(w->notefd, "hangup", 6);
-               proccreate(deletetimeoutproc, estrdup(w->name), 4096);
-               wclosewin(w);
+               wclunk(w);
+               if(w->notefd >= 0)
+                       write(w->notefd, "hangup", 6);
+               if(w->i!=nil){
+                       proccreate(deletetimeoutproc, estrdup(w->name), 4096);
+                       wclosewin(w);
+               }
                break;
        case Exited:
+               wclosewin(w);
                frclear(w, TRUE);
-               close(w->notefd);
+               if(w->notefd >= 0)
+                       close(w->notefd);
                chanfree(w->mc.c);
                chanfree(w->ck);
                chanfree(w->cctl);
@@ -1163,6 +1219,8 @@ wctlmesg(Window *w, int m, Rectangle r, Image *i)
                chanfree(w->mouseread);
                chanfree(w->wctlread);
                chanfree(w->kbdread);
+               chanfree(w->complete);
+               chanfree(w->gone);
                free(w->raw);
                free(w->r);
                free(w->dir);
@@ -1179,6 +1237,8 @@ wctlmesg(Window *w, int m, Rectangle r, Image *i)
 void
 wmovemouse(Window *w, Point p)
 {
+       if(w != input || menuing || sweeping)
+               return;
        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);
@@ -1202,7 +1262,6 @@ wborder(Window *w, int type)
                else
                        col = lighttitlecol;
        }
-
        border(w->i, w->i->r, Selborder, col, ZP);
 }
 
@@ -1216,7 +1275,6 @@ wpointto(Point pt)
        for(i=0; i<nwindow; i++){
                v = window[i];
                if(ptinrect(pt, v->screenr))
-               if(!v->deleted)
                if(w==nil || v->topped>w->topped)
                        w = v;
        }
@@ -1226,28 +1284,8 @@ wpointto(Point pt)
 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);
-               }
-       }
+       if(w!=nil && w!=input)
+               wsendctlmesg(w, Topped, ZR, nil);
 }
 
 void
@@ -1255,7 +1293,9 @@ wsetcursor(Window *w, int force)
 {
        Cursor *p;
 
-       if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
+       if(menuing || sweeping)
+               return;
+       if(w==nil || w->i==nil || Dx(w->screenr)<=0)
                p = nil;
        else if(wpointto(mouse->xy) == w){
                p = w->cursorp;
@@ -1263,56 +1303,55 @@ wsetcursor(Window *w, int force)
                        p = &whitearrow;
        }else
                p = nil;
-       if(!menuing)
-               riosetcursor(p, force && !menuing);
+       if(force)       /* force cursor reload */
+               lastcursor = (void*)~0;
+       riosetcursor(p);
 }
 
 void
-riosetcursor(Cursor *p, int force)
+riosetcursor(Cursor *p)
 {
-       if(!force && p==lastcursor)
+       if(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){
+       if(w!=nil && w->i!=nil && w->topped!=topped){
+               w->topped = ++topped;
                topwindow(w->i);
                flushimage(display, 1);
-               w->topped = ++ topped;
        }
 }
 
 void
 wbottomme(Window *w)
 {
-       if(w!=nil && w->i!=nil && !w->deleted){
+       if(w!=nil && w->i!=nil){
+               w->topped = - ++topped;
                bottomwindow(w->i);
                flushimage(display, 1);
-               w->topped = - ++topped;
        }
 }
 
+Window*
+wtop(Point pt)
+{
+       Window *w;
+
+       w = wpointto(pt);
+       if(w){
+               incref(w);
+               wtopme(w);
+               wcurrent(w);
+               wclose(w);
+       }
+       return w;
+}
+
 Window*
 wlookid(int id)
 {
@@ -1325,15 +1364,16 @@ wlookid(int id)
 }
 
 void
-wclosewin(Window *w)
+wclunk(Window *w)
 {
-       Rectangle r;
        int i;
 
+       if(w->deleted)
+               return;
        w->deleted = TRUE;
        if(w == input){
                input = nil;
-               wsetcursor(w, 0);
+               wsetcursor(w, FALSE);
        }
        if(w == wkeyboard)
                wkeyboard = nil;
@@ -1341,41 +1381,53 @@ wclosewin(Window *w)
                if(hidden[i] == w){
                        --nhidden;
                        memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
-                       hidden[nhidden] = nil;
                        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;
+                       memmove(window+i, window+i+1, (nwindow-i)*sizeof(window[0]));
+                       break;
                }
-       error("unknown window in closewin");
 }
 
 void
-wsetpid(Window *w, int pid, int dolabel)
+wclosewin(Window *w)
 {
-       char buf[128];
-       int fd;
+       Image *i;
 
-       w->pid = pid;
-       if(dolabel){
-               sprint(buf, "rc %d", pid);
-               free(w->label);
-               w->label = estrdup(buf);
+       assert(w->deleted==TRUE);
+
+       i = w->i;
+       if(i){
+               w->i = nil;
+               /* move it off-screen to hide it, in case client is slow in letting it go */
+               MOVEIT originwindow(i, i->r.min, view->r.max);
+               freeimage(i);
+               flushimage(display, 1);
        }
-       sprint(buf, "/proc/%d/notepg", pid);
-       fd = open(buf, OWRITE|OCEXEC);
-       if(w->notefd > 0)
-               close(w->notefd);
-       w->notefd = fd;
+}
+
+void
+wsetpid(Window *w, int pid, int dolabel)
+{
+       char buf[64];
+       int ofd;
+
+       ofd = w->notefd;
+       if(pid <= 0)
+               w->notefd = -1;
+       else {
+               if(dolabel){
+                       snprint(buf, sizeof(buf), "rc %d", pid);
+                       free(w->label);
+                       w->label = estrdup(buf);
+               }
+               snprint(buf, sizeof(buf), "/proc/%d/notepg", pid);
+               w->notefd = open(buf, OWRITE|OCEXEC);
+       }
+       if(ofd >= 0)
+               close(ofd);
 }
 
 void
@@ -1581,10 +1633,8 @@ wsetorigin(Window *w, uint org, int exact)
                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->nchars){
                n = w->org - org;
-               r = runemalloc(n);
-               runemove(r, w->r+org, n);
+               r = w->r+org;
                frinsert(w, r, r+n, 0);
-               free(r);
        }else
                frdelete(w, 0, w->nchars);
        w->org = org;
@@ -1705,16 +1755,14 @@ wfill(Window *w)
        Rune *rp;
        int i, n, m, nl;
 
-       if(w->lastlinefull)
-               return;
-       rp = malloc(messagesize);
-       do{
+       while(w->lastlinefull == FALSE){
                n = w->nr-(w->org+w->nchars);
                if(n == 0)
                        break;
                if(n > 2000)    /* educated guess at reasonable amount */
                        n = 2000;
-               runemove(rp, w->r+(w->org+w->nchars), n);
+               rp = w->r+(w->org+w->nchars);
+
                /*
                 * it's expensive to frinsert more than we need, so
                 * count newlines.
@@ -1729,8 +1777,7 @@ wfill(Window *w)
                        }
                }
                frinsert(w, rp, rp+i, w->nchars);
-       }while(w->lastlinefull == FALSE);
-       free(rp);
+       }
 }
 
 char*