]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/rio/rio.c
rio: get rid of window delete thread, fix mysterious disappearing windows
[plan9front.git] / sys / src / cmd / rio / rio.c
index 1051a1d2d7be5133c5097c3df3de75d765cd4aa7..272fa5e716295cffd8131d9816c93005ee1ef08e 100644 (file)
@@ -26,27 +26,23 @@ void                delete(void);
 void           hide(void);
 void           unhide(int);
 void           newtile(int);
-Image  *sweep(void);
-Image  *bandsize(Window*);
-Image* drag(Window*, Rectangle*);
-void           refresh(Rectangle);
+Image* sweep(void);
+Image* bandsize(Window*);
+Image* drag(Window*);
 void           resized(void);
 Channel        *exitchan;      /* chan(int) */
 Channel        *winclosechan; /* chan(Window*); */
-Channel *kbdchan;      /* chan(char*); */
+Channel        *kbdchan;       /* chan(char*); */
 Rectangle      viewr;
 int            threadrforkflag = 0;    /* should be RFENVG but that hides rio from plumber */
 
 void   mousethread(void*);
 void   keyboardthread(void*);
-void winclosethread(void*);
-void deletethread(void*);
+void   winclosethread(void*);
 void   initcmd(void*);
 Channel* initkbd(void);
 
 char           *fontname;
-int            mainpid;
-int            reverse;
 
 enum
 {
@@ -64,6 +60,7 @@ enum
        Paste,
        Snarf,
        Plumb,
+       Look,
        Send,
        Scroll,
 };
@@ -73,6 +70,7 @@ char          *menu2str[] = {
  [Paste]               "paste",
  [Snarf]               "snarf",
  [Plumb]               "plumb",
+ [Look]                "look",
  [Send]                "send",
  [Scroll]              "scroll",
                        nil
@@ -114,7 +112,7 @@ derror(Display*, char *errorstr)
 void
 usage(void)
 {
-       fprint(2, "usage: rio [-f font] [-i initcmd] [-k kbdcmd] [-s]\n");
+       fprint(2, "usage: rio [-b] [-f font] [-i initcmd] [-k kbdcmd] [-s]\n");
        exits("usage");
 }
 
@@ -122,7 +120,6 @@ void
 threadmain(int argc, char *argv[])
 {
        char *initstr, *kbdin, *s;
-       static void *arg[1];
        char buf[256];
        Image *i;
        Rectangle r;
@@ -139,56 +136,60 @@ threadmain(int argc, char *argv[])
                reverse = ~0xFF;
                break;
        case 'f':
-               fontname = ARGF();
-               if(fontname == nil)
-                       usage();
+               fontname = EARGF(usage());
                break;
        case 'i':
-               initstr = ARGF();
-               if(initstr == nil)
-                       usage();
+               initstr = EARGF(usage());
                break;
        case 'k':
                if(kbdin != nil)
                        usage();
-               kbdin = ARGF();
-               if(kbdin == nil)
-                       usage();
+               kbdin = EARGF(usage());
                break;
        case 's':
                scrolling = TRUE;
                break;
+       case 'D':
+               debug++;
+               break;
+       default:
+               usage();
        }ARGEND
 
-       mainpid = getpid();
        if(getwd(buf, sizeof buf) == nil)
                startdir = estrdup(".");
        else
                startdir = estrdup(buf);
        if(fontname == nil)
                fontname = getenv("font");
-       if(fontname == nil)
-               fontname = "/lib/font/bit/lucm/unicode.9.font";
        s = getenv("tabstop");
        if(s != nil)
                maxtab = strtol(s, nil, 0);
        if(maxtab == 0)
                maxtab = 4;
        free(s);
-       /* check font before barging ahead */
-       if(access(fontname, 0) < 0){
-               fprint(2, "rio: can't access %s: %r\n", fontname);
-               exits("font open");
+
+       if(fontname){
+               /* check font before barging ahead */
+               if(access(fontname, 0) < 0){
+                       fprint(2, "rio: can't access %s: %r\n", fontname);
+                       exits("font open");
+               }
+               putenv("font", fontname);
        }
-       putenv("font", fontname);
 
        snarffd = open("/dev/snarf", OREAD|OCEXEC);
+       gotscreen = access("/dev/screen", AEXIST)==0;
 
        if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){
                fprint(2, "rio: can't open display: %r\n");
                exits("display open");
        }
        iconinit();
+
+       exitchan = chancreate(sizeof(int), 0);
+       winclosechan = chancreate(sizeof(Window*), 0);
+
        view = screen;
        viewr = view->r;
        mousectl = initmouse(nil, screen);
@@ -204,15 +205,10 @@ threadmain(int argc, char *argv[])
        draw(view, viewr, background, nil, ZP);
        flushimage(display, 1);
 
-       exitchan = chancreate(sizeof(int), 0);
-       winclosechan = chancreate(sizeof(Window*), 0);
-       deletechan = chancreate(sizeof(char*), 0);
-
        timerinit();
        threadcreate(keyboardthread, nil, STACK);
        threadcreate(mousethread, nil, STACK);
        threadcreate(winclosethread, nil, STACK);
-       threadcreate(deletethread, nil, STACK);
        filsys = filsysinit(xfidinit());
 
        if(filsys == nil)
@@ -224,9 +220,8 @@ threadmain(int argc, char *argv[])
                if(kbdin){
                        kbdargv[2] = kbdin;
                        r = screen->r;
-                       r.max.x = r.min.x+300;
-                       r.max.y = r.min.y+80;
-                       i = allocwindow(wscreen, r, Refbackup, DWhite);
+                       r.min.y = r.max.y-Dy(r)/3;
+                       i = allocwindow(wscreen, r, Refbackup, DNofill);
                        wkeyboard = new(i, FALSE, scrolling, 0, nil, "/bin/rc", kbdargv);
                        if(wkeyboard == nil)
                                error("can't create keyboard window");
@@ -267,24 +262,29 @@ void
 getsnarf(void)
 {
        int i, n, nb, nulls;
-       char *sn, buf[1024];
+       char *s, *sn;
 
        if(snarffd < 0)
                return;
        sn = nil;
        i = 0;
        seek(snarffd, 0, 0);
-       while((n = read(snarffd, buf, sizeof buf)) > 0){
-               sn = erealloc(sn, i+n+1);
-               memmove(sn+i, buf, n);
+       for(;;){
+               if(i > MAXSNARF)
+                       break;
+               if((s = realloc(sn, i+1024+1)) == nil)
+                       break;
+               sn = s;
+               if((n = read(snarffd, sn+i, 1024)) <= 0)
+                       break;
                i += n;
-               sn[i] = 0;
        }
-       if(i > 0){
-               snarf = runerealloc(snarf, i+1);
+       if(i == 0)
+               return;
+       sn[i] = 0;
+       if((snarf = runerealloc(snarf, i+1)) != nil)
                cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls);
-               free(sn);
-       }
+       free(sn);
 }
 
 void
@@ -332,7 +332,8 @@ killprocs(void)
        int i;
 
        for(i=0; i<nwindow; i++)
-               postnote(PNGROUP, window[i]->pid, "hangup");
+               if(window[i]->notefd >= 0)
+                       write(window[i]->notefd, "hangup", 6); 
 }
 
 void
@@ -343,33 +344,49 @@ keyboardthread(void*)
        threadsetname("keyboardthread");
 
        while(s = recvp(kbdchan)){
+               if(*s == 'k' || *s == 'K')
+                       shiftdown = utfrune(s+1, Kshift) != nil;
                if(input == nil || sendp(input->ck, s) <= 0)
                        free(s);
        }
 }
 
-/*
- * Used by /dev/kbdin
- */
-void
-keyboardsend(char *s, int cnt)
+int
+inborder(Rectangle r, Point xy)
 {
-       if(cnt <= 0)
-               return;
-       if(s[cnt-1] == 0)
-               chanprint(kbdchan, "%s", s);
-       else {
-               Rune *r;
-               int i, nb, nr;
-
-               r = runemalloc(cnt);
-               cvttorunes(s, cnt, r, &nb, &nr, nil);
-               for(i=0; i<nr; i++){
-                       if(r[i])
-                               chanprint(kbdchan, "c%C", r[i]);
-               }
-               free(r);
+       return ptinrect(xy, r) && !ptinrect(xy, insetrect(r, Selborder));
+}
+
+Rectangle
+whichrect(Rectangle r, Point p, int which)
+{
+       switch(which){
+       case 0: /* top left */
+               r = Rect(p.x, p.y, r.max.x, r.max.y);
+               break;
+       case 2: /* top right */
+               r = Rect(r.min.x, p.y, p.x+1, r.max.y);
+               break;
+       case 6: /* bottom left */
+               r = Rect(p.x, r.min.y, r.max.x, p.y+1);
+               break;
+       case 8: /* bottom right */
+               r = Rect(r.min.x, r.min.y, p.x+1, p.y+1);
+               break;
+       case 1: /* top edge */
+               r = Rect(r.min.x, p.y, r.max.x, r.max.y);
+               break;
+       case 5: /* right edge */
+               r = Rect(r.min.x, r.min.y, p.x+1, r.max.y);
+               break;
+       case 7: /* bottom edge */
+               r = Rect(r.min.x, r.min.y, r.max.x, p.y+1);
+               break;
+       case 3: /* left edge */
+               r = Rect(p.x, r.min.y, r.max.x, r.max.y);
+               break;
        }
+       return canonrect(r);
 }
 
 int
@@ -377,6 +394,8 @@ portion(int x, int lo, int hi)
 {
        x -= lo;
        hi -= lo;
+       if(hi < 20)
+               return x > 0 ? 2 : 0;
        if(x < 20)
                return 0;
        if(x > hi-20)
@@ -385,24 +404,15 @@ portion(int x, int lo, int hi)
 }
 
 int
-whichcorner(Window *w, Point p)
+whichcorner(Rectangle r, Point p)
 {
        int i, j;
        
-       i = portion(p.x, w->screenr.min.x, w->screenr.max.x);
-       j = portion(p.y, w->screenr.min.y, w->screenr.max.y);
+       i = portion(p.x, r.min.x, r.max.x);
+       j = portion(p.y, r.min.y, r.max.y);
        return 3*j+i;
 }
 
-void
-cornercursor(Window *w, Point p, int force)
-{
-       if(w!=nil && winborder(w, p))
-               riosetcursor(corners[whichcorner(w, p)], force);
-       else
-               wsetcursor(w, force);
-}
-
 /* thread to allow fsysproc to synchronize window closing with main proc */
 void
 winclosethread(void*)
@@ -416,36 +426,6 @@ winclosethread(void*)
        }
 }
 
-/* thread to make Deleted windows that the client still holds disappear offscreen after an interval */
-void
-deletethread(void*)
-{
-       char *s;
-       Image *i;
-
-       threadsetname("deletethread");
-       for(;;){
-               s = recvp(deletechan);
-               i = namedimage(display, s);
-               if(i != nil){
-                       /* move it off-screen to hide it, since client is slow in letting it go */
-                       originwindow(i, i->r.min, view->r.max);
-               }
-               freeimage(i);
-               free(s);
-       }
-}
-
-void
-deletetimeoutproc(void *v)
-{
-       char *s;
-
-       s = v;
-       sleep(750);     /* remove window from screen after 3/4 of a second */
-       sendp(deletechan, s);
-}
-
 /*
  * Button 6 - keyboard toggle - has been pressed.
  * Send event to keyboard, wait for button up, send that.
@@ -465,10 +445,9 @@ keyboardhide(void)
 void
 mousethread(void*)
 {
-       int sending, inside, scrolling, moving, band;
-       Window *oin, *w, *winput;
+       int sending, inside, scrolling, moving;
+       Window *w, *winput;
        Image *i;
-       Rectangle r;
        Point xy;
        Mouse tmp;
        enum {
@@ -481,7 +460,6 @@ mousethread(void*)
        threadsetname("mousethread");
        sending = FALSE;
        scrolling = FALSE;
-       moving = FALSE;
 
        alts[MReshape].c = mousectl->resizec;
        alts[MReshape].v = nil;
@@ -502,6 +480,7 @@ mousethread(void*)
                                break;
                        }
                Again:
+                       moving = FALSE;
                        winput = input;
                        /* override everything for the keyboard window */
                        if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){
@@ -509,7 +488,7 @@ mousethread(void*)
                                wtopme(wkeyboard);
                                winput = wkeyboard;
                        }
-                       if(winput!=nil && winput->i!=nil){
+                       if(winput!=nil && !winput->deleted && winput->i!=nil){
                                /* convert to logical coordinates */
                                xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x);
                                xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y);
@@ -526,52 +505,40 @@ mousethread(void*)
                                else
                                        scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
                                /* topped will be zero or less if window has been bottomed */
-                               if(sending == FALSE && !scrolling && winborder(winput, mouse->xy) && winput->topped>0){
+                               if(sending == FALSE && !scrolling && inborder(winput->screenr, mouse->xy) && winput->topped>0)
                                        moving = TRUE;
-                               }else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
+                               else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
                                        sending = TRUE;
                        }else
                                sending = FALSE;
                        if(sending){
                        Sending:
-                               if(mouse->buttons == 0){
-                                       cornercursor(winput, mouse->xy, 0);
+                               wsetcursor(winput, FALSE);
+                               if(mouse->buttons == 0)
                                        sending = FALSE;
-                               }else
-                                       wsetcursor(winput, 0);
                                tmp = mousectl->Mouse;
                                tmp.xy = xy;
                                send(winput->mc.c, &tmp);
                                continue;
                        }
-                       w = wpointto(mouse->xy);
-                       /* change cursor if over anyone's border */
-                       if(w != nil)
-                               cornercursor(w, mouse->xy, 0);
-                       else
-                               riosetcursor(nil, 0);
                        if(moving && (mouse->buttons&7)){
-                               oin = winput;
-                               band = mouse->buttons & 3;
-                               sweeping = 1;
-                               if(band)
+                               incref(winput);
+                               sweeping = TRUE;
+                               if(mouse->buttons & 3)
                                        i = bandsize(winput);
                                else
-                                       i = drag(winput, &r);
-                               sweeping = 0;
-                               if(i != nil){
-                                       if(winput == oin){
-                                               if(band)
-                                                       wsendctlmesg(winput, Reshaped, i->r, i);
-                                               else
-                                                       wsendctlmesg(winput, Moved, r, i);
-                                               cornercursor(winput, mouse->xy, 1);
-                                       }else
-                                               freeimage(i);
-                               }
+                                       i = drag(winput);
+                               sweeping = FALSE;
+                               if(i != nil)
+                                       wsendctlmesg(winput, Reshaped, i->r, i);
+                               wclose(winput);
+                               continue;
                        }
-                       if(w != nil)
-                               cornercursor(w, mouse->xy, 0);
+                       w = wpointto(mouse->xy);
+                       if(w!=nil && inborder(w->screenr, mouse->xy))
+                               riosetcursor(corners[whichcorner(w->screenr, mouse->xy)]);
+                       else
+                               wsetcursor(w, FALSE);
                        /* we're not sending the event, but if button is down maybe we should */
                        if(mouse->buttons){
                                /* w->topped will be zero or less if window has been bottomed */
@@ -579,35 +546,42 @@ mousethread(void*)
                                        if(mouse->buttons & 1){
                                                ;
                                        }else if(mouse->buttons & 2){
-                                               if(winput && !winput->mouseopen)
+                                               if(winput && !winput->deleted && !winput->mouseopen){
+                                                       incref(winput);
                                                        button2menu(winput);
+                                                       wclose(winput);
+                                               }
                                        }else if(mouse->buttons & 4)
                                                button3menu();
                                }else{
                                        /* if button 1 event in the window, top the window and wait for button up. */
                                        /* otherwise, top the window and pass the event on */
-                                       if(wtop(mouse->xy) && (mouse->buttons!=1 || winborder(w, mouse->xy)))
+                                       if(wtop(mouse->xy) && (mouse->buttons!=1 || inborder(w->screenr, mouse->xy)))
                                                goto Again;
                                        goto Drain;
                                }
                        }
-                       moving = FALSE;
                        break;
 
                Drain:
                        do
                                readmouse(mousectl);
                        while(mousectl->buttons);
-                       moving = FALSE;
                        goto Again;     /* recalculate mouse position, cursor */
                }
 }
 
+int
+wtopcmp(void *a, void *b)
+{
+       return (*(Window**)a)->topped - (*(Window**)b)->topped;
+}
+
 void
 resized(void)
 {
        Image *im;
-       int i, j, ishidden;
+       int i, j;
        Rectangle r;
        Point o, n;
        Window *w;
@@ -623,44 +597,120 @@ resized(void)
        draw(view, view->r, background, nil, ZP);
        o = subpt(viewr.max, viewr.min);
        n = subpt(view->clipr.max, view->clipr.min);
+       qsort(window, nwindow, sizeof(window[0]), wtopcmp);
        for(i=0; i<nwindow; i++){
                w = window[i];
-               if(w->deleted)
-                       continue;
                r = rectsubpt(w->i->r, viewr.min);
                r.min.x = (r.min.x*n.x)/o.x;
                r.min.y = (r.min.y*n.y)/o.y;
                r.max.x = (r.max.x*n.x)/o.x;
                r.max.y = (r.max.y*n.y)/o.y;
-               r = rectaddpt(r, screen->clipr.min);
-               ishidden = 0;
+               r = rectaddpt(r, view->clipr.min);
+               if(!goodrect(r))
+                       r = rectsubpt(w->i->r, subpt(w->i->r.min, r.min));
                for(j=0; j<nhidden; j++)
-                       if(w == hidden[j]){
-                               ishidden = 1;
+                       if(w == hidden[j])
                                break;
-                       }
-               if(ishidden){
-                       im = allocimage(display, r, screen->chan, 0, DWhite);
+               incref(w);
+               if(j < nhidden){
+                       im = allocimage(display, r, screen->chan, 0, DNofill);
                        r = ZR;
-               }else
-                       im = allocwindow(wscreen, r, Refbackup, DWhite);
+               } else
+                       im = allocwindow(wscreen, r, Refbackup, DNofill);
                if(im)
                        wsendctlmesg(w, Reshaped, r, im);
+               wclose(w);
        }
-       viewr = screen->r;
+       viewr = view->r;
        flushimage(display, 1);
 }
 
+int
+obscured(Window *w, Rectangle r, int i)
+{
+       Window *t;
+
+       if(Dx(r) < font->height || Dy(r) < font->height)
+               return 1;
+       if(!rectclip(&r, screen->r))
+               return 1;
+       for(; i<nwindow; i++){
+               t = window[i];
+               if(t == w || t->topped <= w->topped)
+                       continue;
+               if(Dx(t->screenr) == 0 || Dy(t->screenr) == 0 || rectXrect(r, t->screenr) == 0)
+                       continue;
+               if(r.min.y < t->screenr.min.y)
+                       if(!obscured(w, Rect(r.min.x, r.min.y, r.max.x, t->screenr.min.y), i))
+                               return 0;
+               if(r.min.x < t->screenr.min.x)
+                       if(!obscured(w, Rect(r.min.x, r.min.y, t->screenr.min.x, r.max.y), i))
+                               return 0;
+               if(r.max.y > t->screenr.max.y)
+                       if(!obscured(w, Rect(r.min.x, t->screenr.max.y, r.max.x, r.max.y), i))
+                               return 0;
+               if(r.max.x > t->screenr.max.x)
+                       if(!obscured(w, Rect(t->screenr.max.x, r.min.y, r.max.x, r.max.y), i))
+                               return 0;
+               return 1;
+       }
+       return 0;
+}
+
+static char*
+shortlabel(char *s)
+{
+       enum { NBUF=60 };
+       static char buf[NBUF*UTFmax];
+       int i, k, l;
+       Rune r;
+
+       l = utflen(s);
+       if(l < NBUF-2)
+               return estrdup(s);
+       k = i = 0;
+       while(i < NBUF/2){
+               k += chartorune(&r, s+k);
+               i++;
+       }
+       strncpy(buf, s, k);
+       strcpy(buf+k, "...");
+       while((l-i) >= NBUF/2-4){
+               k += chartorune(&r, s+k);
+               i++;
+       }
+       strcat(buf, s+k);
+       return estrdup(buf);
+}
+
 void
 button3menu(void)
 {
-       int i;
-
-       for(i=0; i<nhidden; i++)
-               menu3str[i+Hidden] = hidden[i]->label;
-       menu3str[i+Hidden] = nil;
+       int i, j, n;
 
-       sweeping = 1;
+       n = nhidden;
+       for(i=0; i<nwindow; i++){
+               for(j=0; j<n; j++)
+                       if(window[i] == hidden[j])
+                               break;
+               if(j == n)
+                       if(obscured(window[i], window[i]->screenr, 0)){
+                               hidden[n++] = window[i];
+                               if(n >= nelem(hidden))
+                                       break;
+                       }
+       }
+       if(n >= nelem(menu3str)-Hidden)
+               n = nelem(menu3str)-Hidden-1;
+       for(i=0; i<n; i++){
+               free(menu3str[i+Hidden]);
+               menu3str[i+Hidden] = shortlabel(hidden[i]->label);
+       }
+       for(i+=Hidden; menu3str[i]; i++){
+               free(menu3str[i]);
+               menu3str[i] = nil;
+       }
+       sweeping = TRUE;
        switch(i = menuhit(3, mousectl, &menu3, wscreen)){
        case -1:
                break;
@@ -689,15 +739,12 @@ button3menu(void)
                unhide(i);
                break;
        }
-       sweeping = 0;
+       sweeping = FALSE;
 }
 
 void
 button2menu(Window *w)
 {
-       if(w->deleted)
-               return;
-       incref(w);
        if(w->scrolling)
                menu2str[Scroll] = "noscroll";
        else
@@ -723,6 +770,10 @@ button2menu(Window *w)
                wplumb(w);
                break;
 
+       case Look:
+               wlook(w);
+               break;
+
        case Send:
                getsnarf();
                wsnarf(w);
@@ -746,18 +797,17 @@ button2menu(Window *w)
                        wshow(w, w->nr);
                break;
        }
-       wclose(w);
-       wsendctlmesg(w, Wakeup, ZR, nil);
        flushimage(display, 1);
+       wsendctlmesg(w, Wakeup, ZR, nil);
 }
 
 Point
 onscreen(Point p)
 {
        p.x = max(screen->clipr.min.x, p.x);
-       p.x = min(screen->clipr.max.x, p.x);
+       p.x = min(screen->clipr.max.x-1, p.x);
        p.y = max(screen->clipr.min.y, p.y);
-       p.y = min(screen->clipr.max.y, p.y);
+       p.y = min(screen->clipr.max.y-1, p.y);
        return p;
 }
 
@@ -770,7 +820,7 @@ sweep(void)
 
        i = nil;
        menuing = TRUE;
-       riosetcursor(&crosscursor, 1);
+       riosetcursor(&crosscursor);
        while(mouse->buttons == 0)
                readmouse(mousectl);
        p0 = onscreen(mouse->xy);
@@ -779,45 +829,43 @@ sweep(void)
        r.max = p;
        oi = nil;
        while(mouse->buttons == 4){
-               readmouse(mousectl);
-               if(mouse->buttons != 4 && mouse->buttons != 0)
-                       break;
                if(!eqpt(mouse->xy, p)){
                        p = onscreen(mouse->xy);
                        r = canonrect(Rpt(p0, p));
+                       r = whichrect(r, p, whichcorner(r, p));
                        if(Dx(r)>5 && Dy(r)>5){
-                               i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
+                               i = allocwindow(wscreen, r, Refnone, DNofill);
                                freeimage(oi);
                                if(i == nil)
                                        goto Rescue;
                                oi = i;
-                               border(i, r, Selborder, red, ZP);
-                               flushimage(display, 1);
+                               border(i, r, Selborder, sizecol, ZP);
+                               draw(i, insetrect(r, Selborder), cols[BACK], nil, ZP);
                        }
                }
+               readmouse(mousectl);
        }
        if(mouse->buttons != 0)
                goto Rescue;
-       if(i==nil || Dx(i->r)<100 || Dy(i->r)<3*font->height)
+       if(i==nil || !goodrect(r))
                goto Rescue;
        oi = i;
-       i = allocwindow(wscreen, oi->r, Refbackup, DWhite);
+       i = allocwindow(wscreen, oi->r, Refbackup, DNofill);
        freeimage(oi);
        if(i == nil)
                goto Rescue;
-       border(i, r, Selborder, red, ZP);
-       cornercursor(input, mouse->xy, 1);
+       riosetcursor(corners[whichcorner(i->r, mouse->xy)]);
        goto Return;
 
  Rescue:
+       riosetcursor(nil);
        freeimage(i);
        i = nil;
-       cornercursor(input, mouse->xy, 1);
+       flushimage(display, 1);
        while(mouse->buttons)
                readmouse(mousectl);
 
  Return:
-       moveto(mousectl, mouse->xy);    /* force cursor update; ugly */
        menuing = FALSE;
        return i;
 }
@@ -830,7 +878,9 @@ drawedge(Image **bp, Rectangle r)
                originwindow(b, r.min, r.min);
        else{
                freeimage(b);
-               *bp = allocwindow(wscreen, r, Refbackup, DRed);
+               b = allocwindow(wscreen, r, Refbackup, DNofill);
+               if(b != nil) draw(b, r, sizecol, nil, ZP);
+               *bp = b;
        }
 }
 
@@ -854,157 +904,75 @@ drawborder(Rectangle r, int show)
 }
 
 Image*
-drag(Window *w, Rectangle *rp)
+drag(Window *w)
 {
-       Image *i, *ni;
        Point p, op, d, dm, om;
        Rectangle r;
 
-       i = w->i;
        menuing = TRUE;
+       riosetcursor(&boxcursor);
        om = mouse->xy;
-       riosetcursor(&boxcursor, 1);
-       dm = subpt(mouse->xy, w->screenr.min);
-       d = subpt(i->r.max, i->r.min);
-       op = subpt(mouse->xy, dm);
+       dm = subpt(om, w->screenr.min);
+       d = subpt(w->screenr.max, w->screenr.min);
+       op = subpt(om, dm);
        drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
-       flushimage(display, 1);
-       while(mouse->buttons == 4){
+       while(mouse->buttons==4){
                p = subpt(mouse->xy, dm);
                if(!eqpt(p, op)){
                        drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
-                       flushimage(display, 1);
                        op = p;
                }
                readmouse(mousectl);
        }
        r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
        drawborder(r, 0);
-       cornercursor(w, mouse->xy, 1);
-       moveto(mousectl, mouse->xy);    /* force cursor update; ugly */
+       p = mouse->xy;
+       riosetcursor(inborder(r, p) ? corners[whichcorner(r, p)] : nil);
        menuing = FALSE;
-       flushimage(display, 1);
-       if(mouse->buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DWhite))==nil){
-               moveto(mousectl, om);
+       if(mouse->buttons!=0 || !goodrect(r) || eqrect(r, w->screenr)){
+               flushimage(display, 1);
                while(mouse->buttons)
                        readmouse(mousectl);
-               *rp = Rect(0, 0, 0, 0);
                return nil;
        }
-       draw(ni, ni->r, i, nil, i->r.min);
-       *rp = r;
-       return ni;
-}
-
-Point
-cornerpt(Rectangle r, Point p, int which)
-{
-       switch(which){
-       case 0: /* top left */
-               p = Pt(r.min.x, r.min.y);
-               break;
-       case 2: /* top right */
-               p = Pt(r.max.x,r.min.y);
-               break;
-       case 6: /* bottom left */
-               p = Pt(r.min.x, r.max.y);
-               break;
-       case 8: /* bottom right */
-               p = Pt(r.max.x, r.max.y);
-               break;
-       case 1: /* top edge */
-               p = Pt(p.x,r.min.y);
-               break;
-       case 5: /* right edge */
-               p = Pt(r.max.x, p.y);
-               break;
-       case 7: /* bottom edge */
-               p = Pt(p.x, r.max.y);
-               break;
-       case 3:         /* left edge */
-               p = Pt(r.min.x, p.y);
-               break;
-       }
-       return p;
-}
-
-Rectangle
-whichrect(Rectangle r, Point p, int which)
-{
-       switch(which){
-       case 0: /* top left */
-               r = Rect(p.x, p.y, r.max.x, r.max.y);
-               break;
-       case 2: /* top right */
-               r = Rect(r.min.x, p.y, p.x, r.max.y);
-               break;
-       case 6: /* bottom left */
-               r = Rect(p.x, r.min.y, r.max.x, p.y);
-               break;
-       case 8: /* bottom right */
-               r = Rect(r.min.x, r.min.y, p.x, p.y);
-               break;
-       case 1: /* top edge */
-               r = Rect(r.min.x, p.y, r.max.x, r.max.y);
-               break;
-       case 5: /* right edge */
-               r = Rect(r.min.x, r.min.y, p.x, r.max.y);
-               break;
-       case 7: /* bottom edge */
-               r = Rect(r.min.x, r.min.y, r.max.x, p.y);
-               break;
-       case 3:         /* left edge */
-               r = Rect(p.x, r.min.y, r.max.x, r.max.y);
-               break;
-       }
-       return canonrect(r);
+       return allocwindow(wscreen, r, Refbackup, DNofill);
 }
 
 Image*
 bandsize(Window *w)
 {
-       Image *i;
        Rectangle r, or;
        Point p, startp;
-       int which, but;
+       int which, owhich, but;
 
-       p = mouse->xy;
+       owhich = -1;
+       or = w->screenr;
        but = mouse->buttons;
-       which = whichcorner(w, p);
-       p = cornerpt(w->screenr, p, which);
-       wmovemouse(w, p);
-       readmouse(mousectl);
-       r = whichrect(w->screenr, p, which);
-       drawborder(r, 1);
-       or = r;
-       startp = p;
-       
-       while(mouse->buttons == but){
+       startp = onscreen(mouse->xy);
+       drawborder(or, 1);
+       while(mouse->buttons == but) {
                p = onscreen(mouse->xy);
-               r = whichrect(w->screenr, p, which);
+               which = whichcorner(or, p);
+               if(which != owhich && which != 4 && (owhich|~which) & 1){
+                       owhich = which;
+                       riosetcursor(corners[which]);
+               }
+               r = whichrect(or, p, owhich);
                if(!eqrect(r, or) && goodrect(r)){
-                       drawborder(r, 1);
-                       flushimage(display, 1);
                        or = r;
+                       drawborder(r, 1);
                }
                readmouse(mousectl);
        }
-       p = mouse->xy;
        drawborder(or, 0);
-       flushimage(display, 1);
-       wsetcursor(w, 1);
-       if(mouse->buttons!=0 || Dx(or)<100 || Dy(or)<3*font->height){
+       if(mouse->buttons!=0 || !goodrect(or) || eqrect(or, w->screenr)
+       || abs(p.x-startp.x)+abs(p.y-startp.y) <= 1){
+               flushimage(display, 1);
                while(mouse->buttons)
                        readmouse(mousectl);
                return nil;
        }
-       if(abs(p.x-startp.x)+abs(p.y-startp.y) <= 1)
-               return nil;
-       i = allocwindow(wscreen, or, Refbackup, DWhite);
-       if(i == nil)
-               return nil;
-       border(i, r, Selborder, red, ZP);
-       return i;
+       return allocwindow(wscreen, or, Refbackup, DNofill);
 }
 
 Window*
@@ -1013,7 +981,7 @@ pointto(int wait)
        Window *w;
 
        menuing = TRUE;
-       riosetcursor(&sightcursor, 1);
+       riosetcursor(&sightcursor);
        while(mouse->buttons == 0)
                readmouse(mousectl);
        if(mouse->buttons == 4)
@@ -1023,7 +991,7 @@ pointto(int wait)
        if(wait){
                while(mouse->buttons){
                        if(mouse->buttons!=4 && w !=nil){       /* cancel */
-                               cornercursor(input, mouse->xy, 0);
+                               riosetcursor(nil);
                                w = nil;
                        }
                        readmouse(mousectl);
@@ -1031,8 +999,7 @@ pointto(int wait)
                if(w != nil && wpointto(mouse->xy) != w)
                        w = nil;
        }
-       cornercursor(input, mouse->xy, 0);
-       moveto(mousectl, mouse->xy);    /* force cursor update; ugly */
+       riosetcursor(nil);
        menuing = FALSE;
        return w;
 }
@@ -1056,9 +1023,11 @@ resize(void)
        w = pointto(TRUE);
        if(w == nil)
                return;
+       incref(w);
        i = sweep();
        if(i)
                wsendctlmesg(w, Reshaped, i->r, i);
+       wclose(w);
 }
 
 void
@@ -1066,15 +1035,15 @@ move(void)
 {
        Window *w;
        Image *i;
-       Rectangle r;
 
        w = pointto(FALSE);
        if(w == nil)
                return;
-       i = drag(w, &r);
+       incref(w);
+       i = drag(w);
        if(i)
-               wsendctlmesg(w, Moved, r, i);
-       cornercursor(input, mouse->xy, 1);
+               wsendctlmesg(w, Reshaped, i->r, i);
+       wclose(w);
 }
 
 int
@@ -1086,30 +1055,38 @@ whide(Window *w)
        for(j=0; j<nhidden; j++)
                if(hidden[j] == w)      /* already hidden */
                        return -1;
-       i = allocimage(display, w->screenr, w->i->chan, 0, DWhite);
+       if(nhidden >= nelem(hidden))
+               return 0;
+       incref(w);
+       i = allocimage(display, w->screenr, w->i->chan, 0, DNofill);
        if(i){
                hidden[nhidden++] = w;
                wsendctlmesg(w, Reshaped, ZR, i);
-               return 1;
        }
-       return 0;
+       wclose(w);
+       return i!=0;
 }
 
 int
-wunhide(int h)
+wunhide(Window *w)
 {
+       int j;
        Image *i;
-       Window *w;
 
-       w = hidden[h];
-       i = allocwindow(wscreen, w->i->r, Refbackup, DWhite);
+       for(j=0; j<nhidden; j++)
+               if(hidden[j] == w)
+                       break;
+       if(j == nhidden)
+               return -1;      /* not hidden */
+       incref(w);
+       i = allocwindow(wscreen, w->i->r, Refbackup, DNofill);
        if(i){
                --nhidden;
-               memmove(hidden+h, hidden+h+1, (nhidden-h)*sizeof(Window*));
+               memmove(hidden+j, hidden+j+1, (nhidden-j)*sizeof(Window*));
                wsendctlmesg(w, Reshaped, w->i->r, i);
-               return 1;
        }
-       return 0;
+       wclose(w);
+       return i!=0;
 }
 
 void
@@ -1118,21 +1095,34 @@ hide(void)
        Window *w;
 
        w = pointto(TRUE);
-       if(w == nil)
-               return;
-       whide(w);
+       if(w)
+               whide(w);
 }
 
 void
-unhide(int h)
+unhide(int j)
 {
        Window *w;
 
-       h -= Hidden;
-       w = hidden[h];
+       if(j < Hidden)
+               return;
+       j -= Hidden;
+       w = hidden[j];
        if(w == nil)
                return;
-       wunhide(h);
+       if(j < nhidden){
+               wunhide(w);
+               return;
+       }
+       /* uncover obscured window */
+       for(j=0; j<nwindow; j++)
+               if(window[j] == w){
+                       incref(w);
+                       wtopme(w);
+                       wcurrent(w);
+                       wclose(w);
+                       return;
+               }
 }
 
 Window*
@@ -1145,6 +1135,10 @@ new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **ar
 
        if(i == nil)
                return nil;
+       if(hideit && nhidden >= nelem(hidden)){
+               freeimage(i);
+               return nil;
+       }
        cm = chancreate(sizeof(Mouse), 0);
        ck = chancreate(sizeof(char*), 0);
        cctl = chancreate(sizeof(Wctlmesg), 4);
@@ -1166,7 +1160,6 @@ new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **ar
        threadcreate(winctl, w, 8192);
        if(!hideit)
                wcurrent(w);
-       flushimage(display, 1);
        if(pid == 0){
                arg = emalloc(5*sizeof(void*));
                arg[0] = w;
@@ -1189,8 +1182,10 @@ new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **ar
        }
        wsetpid(w, pid, 1);
        wsetname(w);
-       if(dir)
+       if(dir){
+               free(w->dir);
                w->dir = estrdup(dir);
+       }
        chanfree(cpid);
        return w;
 }
@@ -1220,10 +1215,17 @@ kbdproc(void *arg)
        if((kfd = open("/dev/kbd", OREAD)) >= 0){
                close(fd);
 
+               /* only serve a kbd file per window when we got one */
+               servekbd = 1;
+
                /* read kbd state */
-               while((n = read(kfd, buf, sizeof(buf))) > 0)
-                       chanprint(c, "%.*s", n, buf);
-               close(kfd);
+               while((n = read(kfd, buf, sizeof(buf)-1)) > 0){
+                       e = buf+n;
+                       e[-1] = 0;
+                       e[0] = 0;
+                       for(p = buf; p < e; p += strlen(p)+1)
+                               chanprint(c, "%s", p);
+               }
        } else {
                /* read single characters */
                p = buf;
@@ -1244,6 +1246,7 @@ kbdproc(void *arg)
                        p = buf + n;
                }
        }
+       send(exitchan, nil);
 }
 
 Channel*
@@ -1257,7 +1260,7 @@ initkbd(void)
        if(e = recvp(c)){
                chanfree(c);
                c = nil;
-               werrstr(e);
+               werrstr("%s", e);
                free(e);
        }
        return c;