]> 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 fab34f95a831bc9dfc6177653a15605e1cf5cf5d..f29fb6a431c451ce48eeaca62ae8aa34ba5ed01e 100644 (file)
@@ -41,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;
@@ -85,13 +87,10 @@ 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;
        w->mc.image = i;
@@ -100,20 +99,17 @@ wresize(Window *w, Image *i, int move)
        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;
@@ -122,9 +118,10 @@ wresize(Window *w, Image *i, int move)
 }
 
 void
-wrefresh(Window *w, Rectangle r)
+wrefresh(Window *w)
 {
-       /* BUG: rectangle is ignored */
+       Rectangle r;
+
        if(w == input)
                wborder(w, Selborder);
        else
@@ -151,51 +148,47 @@ wclose(Window *w)
                return 0;
        if(i < 0)
                error("negative ref count");
-       if(!w->deleted)
-               wclunk(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;
@@ -213,68 +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(w->kbdopen){
-                               i = (kbdqw+1) % nelem(kbdq);
-                               if(i != kbdqr)
-                                       kbdqw = i;
-                       } else if(*kbds == 'c'){
-                               Rune r;
-
-                               chartorune(&r, kbds+1);
-                               if(r)
-                                       wkeyctl(w, r);
+                       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("K"));
+                       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++;
@@ -310,15 +324,12 @@ winctl(void *arg)
                        continue;
                case WCtl:
                        if(wctlmesg(w, wcm.type, wcm.r, wcm.p) == Exited){
-                               for(i=0; i<nelem(kbdq); i++)
-                                       free(kbdq[i]);
+                               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;
@@ -326,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)
@@ -351,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;
@@ -400,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);
        }
 }
@@ -456,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;
@@ -471,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);
@@ -490,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){
@@ -576,7 +602,7 @@ wkeyctl(Window *w, Rune r)
                return;
        }
 
-       if(w->deleted)
+       if(w->i==nil)
                return;
        /* navigation keys work only when mouse and kbd is not open */
        if(!w->mouseopen)
@@ -647,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);
@@ -657,7 +687,7 @@ wkeyctl(Window *w, Rune r)
                        --w->holding;
                else
                        w->holding++;
-               wsetcursor(w, 0);
+               wsetcursor(w, FALSE);
                wrepaint(w);
                if(r == Kesc)
                        return;
@@ -670,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 = 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 */
@@ -710,24 +735,24 @@ 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] = cols[TEXT];
+               if(topped)
+                       w->cols[TEXT] = cols[TEXT];
                else
-                       w->cols[TEXT] = w->cols[HTEXT] = paletextcol;
+                       w->cols[TEXT] = paletextcol;
 }
 
 void
 wrepaint(Window *w)
 {
-       wsetcols(w);
+       wsetcols(w, w == input);
        if(!w->mouseopen)
                frredraw(w);
        if(w == input)
@@ -846,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
@@ -864,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);
 }
 
@@ -990,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 */
@@ -1030,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;
@@ -1047,7 +1079,6 @@ wselect(Window *w)
                        }
                }
                wscrdraw(w);
-               flushimage(display, 1);
                while(w->mc.buttons == b)
                        readmouse(&w->mc);
                clickwin = nil;
@@ -1079,7 +1110,6 @@ wctlmesg(Window *w, int m, Rectangle r, void *p)
                if(p!=nil)
                        sendp((Channel*)p, w);
                break;
-       case Moved:
        case Reshaped:
                if(w->deleted){
                        freeimage(i);
@@ -1087,9 +1117,8 @@ wctlmesg(Window *w, int m, Rectangle r, void *p)
                }
                w->screenr = r;
                strcpy(buf, w->name);
-               wresize(w, i, m==Moved);
+               wresize(w, i);
                proccreate(deletetimeoutproc, estrdup(buf), 4096);
-               flushimage(display, 1);
                if(Dx(r)<=0){   /* window got hidden, if we had the input, drop it */
                        if(w==input)
                                input = nil;
@@ -1135,26 +1164,24 @@ wctlmesg(Window *w, int m, Rectangle r, void *p)
                        break;
                /* fall thrugh for redraw after input change */
        case Repaint:
-               if(w->deleted || Dx(w->screenr)<=0)
+               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) || w->mouseopen)
+               if(w->i==nil || Dx(w->screenr)<=0 || w->mouseopen)
                        break;
-               wrefresh(w, r);
+               wrefresh(w);
                flushimage(display, 1);
                break;
        case Movemouse:
-               if(w->deleted || Dx(w->screenr)<=0 || !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;
@@ -1163,25 +1190,27 @@ wctlmesg(Window *w, int m, Rectangle r, void *p)
                break;
        case Holdon:
        case Holdoff:
-               if(w->deleted)
+               if(w->i==nil)
                        break;
                if(w==input)
-                       wsetcursor(w, 0);
+                       wsetcursor(w, FALSE);
                wrepaint(w);
                flushimage(display, 1);
                break;
        case Deleted:
-               if(w->deleted)
-                       break;
                wclunk(w);
-               write(w->notefd, "hangup", 6);
-               proccreate(deletetimeoutproc, estrdup(w->name), 4096);
-               wclosewin(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);
@@ -1190,6 +1219,8 @@ wctlmesg(Window *w, int m, Rectangle r, void *p)
                chanfree(w->mouseread);
                chanfree(w->wctlread);
                chanfree(w->kbdread);
+               chanfree(w->complete);
+               chanfree(w->gone);
                free(w->raw);
                free(w->r);
                free(w->dir);
@@ -1262,7 +1293,9 @@ wsetcursor(Window *w, int force)
 {
        Cursor *p;
 
-       if(w==nil || w->deleted || 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;
@@ -1270,24 +1303,24 @@ 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;
 }
 
-
 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);
@@ -1297,7 +1330,7 @@ wtopme(Window *w)
 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);
@@ -1335,9 +1368,12 @@ wclunk(Window *w)
 {
        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;
@@ -1353,7 +1389,6 @@ wclunk(Window *w)
                        memmove(window+i, window+i+1, (nwindow-i)*sizeof(window[0]));
                        break;
                }
-       w->deleted = TRUE;
 }
 
 void
@@ -1361,12 +1396,15 @@ wclosewin(Window *w)
 {
        Image *i;
 
+       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);
        }
 }
 
@@ -1595,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;
@@ -1719,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.
@@ -1743,8 +1777,7 @@ wfill(Window *w)
                        }
                }
                frinsert(w, rp, rp+i, w->nchars);
-       }while(w->lastlinefull == FALSE);
-       free(rp);
+       }
 }
 
 char*