git: 9front

Download patch

ref: ba6d694a08364b1f937570635d5fb2acbac033db
parent: b9eee991eb62dfca76b05dd750420df3c14d689e
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Sat Oct 20 18:42:01 EDT 2012

rio: preserve window z-order on resize, fix race conditions

sort the window array by w->topped before reshaping all
windows. this preserves the window z-order.

remove implicit focus change on reshape/move. it called
wcurrent() in wtcl thread which might send a wctl message
to itself, bad... also we might not want to change focus
on reshape, like for the rio resize. so we set the input
window explicitely in all call sites.

window deletion was racy. wclosewin() destroys w->i, but
it is called outside the wctl thread so it might just
free the image under libframe doing some text selection.
this is fixed the following: add wclunk() function,
which basically just marks the window as deleted and
removes the reference from the window[] and hidden[]
arrays. (called on wclose() when refcount drops to
zero). wclosewin() now just frees the image and is only
called from the wctl thread on exit or when handing
the Deleted message.

get a reference to the window when doing sweeping or
moving as the filesystem might just clunk it under
us and we might end up sending wctl messages to
a Exited window.

wctl resize message has to fail if the window is not
current as it might be hidden... would also be annoying.

--- a/sys/src/cmd/rio/dat.h
+++ b/sys/src/cmd/rio/dat.h
@@ -67,6 +67,7 @@
 	Rawoff,
 	Holdon,
 	Holdoff,
+	Repaint,
 	Deleted,
 	Exited,
 };
@@ -194,6 +195,7 @@
 uint		winsert(Window*, Rune*, int, uint);
 void		waddraw(Window*, Rune*, int);
 void		wborder(Window*, int);
+void		wclunk(Window*);
 void		wclosewin(Window*);
 void		wcurrent(Window*);
 void		wcut(Window*);
--- a/sys/src/cmd/rio/fns.h
+++ b/sys/src/cmd/rio/fns.h
@@ -1,6 +1,6 @@
 void	keyboardsend(char*, int);
 int	whide(Window*);
-int	wunhide(int);
+int	wunhide(Window*);
 void	freescrtemps(void);
 int	parsewctl(char**, Rectangle, Rectangle*, int*, int*, int*, int*, char**, char*, char*);
 int	writewctl(Xfid*, char*);
--- a/sys/src/cmd/rio/rio.c
+++ b/sys/src/cmd/rio/rio.c
@@ -347,7 +347,7 @@
 	while(s = recvp(kbdchan)){
 		if(*s == 'k' || *s == 'K')
 			shiftdown = utfrune(s+1, Kshift) != nil;
-		if(input == nil || sendp(input->ck, s) <= 0)
+		if(input == nil || input->deleted || sendp(input->ck, s) <= 0)
 			free(s);
 	}
 }
@@ -470,7 +470,7 @@
 mousethread(void*)
 {
 	int sending, inside, scrolling, moving, band;
-	Window *oin, *w, *winput;
+	Window *w, *winput;
 	Image *i;
 	Rectangle r;
 	Point xy;
@@ -513,7 +513,7 @@
 				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);
@@ -555,7 +555,7 @@
 			else
 				riosetcursor(nil, 0);
 			if(moving && (mouse->buttons&7)){
-				oin = winput;
+				incref(winput);
 				band = mouse->buttons & 3;
 				sweeping = 1;
 				if(band)
@@ -564,18 +564,19 @@
 					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);
+					if(band)
+						wsendctlmesg(winput, Reshaped, i->r, i);
+					else
+						wsendctlmesg(winput, Moved, r, i);
+					cornercursor(winput, mouse->xy, 1);
 				}
+				if(wclose(winput) == 0)
+					w = winput;
+				else {
+					riosetcursor(nil, 0);
+					w = nil;
+				}
 			}
-			if(w != nil)
-				cornercursor(w, mouse->xy, 0);
 			/* 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 */
@@ -583,8 +584,11 @@
 					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{
@@ -607,11 +611,17 @@
 		}
 }
 
+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;
@@ -627,10 +637,9 @@
 	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;
@@ -639,26 +648,25 @@
 		if(!goodrect(r))
 			r = rectsubpt(w->i->r, viewr.min);
 		r = rectaddpt(r, screen->clipr.min);
-		ishidden = 0;
 		for(j=0; j<nhidden; j++)
-			if(w == hidden[j]){
-				ishidden = 1;
+			if(w == hidden[j])
 				break;
-			}
-		if(ishidden){
+		incref(w);
+		if(j < nhidden){
 			im = allocimage(display, r, screen->chan, 0, DWhite);
 			r = ZR;
-		}else
+		} else
 			im = allocwindow(wscreen, r, Refbackup, DWhite);
 		if(im)
 			wsendctlmesg(w, Reshaped, r, im);
+		wclose(w);
 	}
 	viewr = screen->r;
 	flushimage(display, 1);
 }
 
-static int
-wcovered(Window *w, Rectangle r, int i)
+int
+obscured(Window *w, Rectangle r, int i)
 {
 	Window *t;
 
@@ -668,21 +676,21 @@
 		return 1;
 	for(; i<nwindow; i++){
 		t = window[i];
-		if(t == w || t->topped <= w->topped || t->deleted)
+		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(!wcovered(w, Rect(r.min.x, r.min.y, r.max.x, t->screenr.min.y), i))
+			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(!wcovered(w, Rect(r.min.x, r.min.y, t->screenr.min.x, r.max.y), i))
+			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(!wcovered(w, Rect(r.min.x, t->screenr.max.y, r.max.x, r.max.y), i))
+			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(!wcovered(w, Rect(t->screenr.max.x, r.min.y, r.max.x, r.max.y), i))
+			if(!obscured(w, Rect(t->screenr.max.x, r.min.y, r.max.x, r.max.y), i))
 				return 0;
 		return 1;
 	}
@@ -699,13 +707,12 @@
 		for(j=0; j<n; j++)
 			if(window[i] == hidden[j])
 				break;
-		if(j < n || window[i]->deleted)
-			continue;
-		if(wcovered(window[i], window[i]->screenr, 0)){
-			hidden[n++] = window[i];
-			if(n >= nelem(hidden))
-				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;
@@ -752,9 +759,6 @@
 void
 button2menu(Window *w)
 {
-	if(w->deleted)
-		return;
-	incref(w);
 	if(w->scrolling)
 		menu2str[Scroll] = "noscroll";
 	else
@@ -803,7 +807,6 @@
 			wshow(w, w->nr);
 		break;
 	}
-	wclose(w);
 	wsendctlmesg(w, Wakeup, ZR, nil);
 	flushimage(display, 1);
 }
@@ -1113,9 +1116,13 @@
 	w = pointto(TRUE);
 	if(w == nil)
 		return;
+	incref(w);
 	i = sweep();
-	if(i)
+	if(i){
 		wsendctlmesg(w, Reshaped, i->r, i);
+		wcurrent(w);
+	}
+	wclose(w);
 }
 
 void
@@ -1128,10 +1135,14 @@
 	w = pointto(FALSE);
 	if(w == nil)
 		return;
+	incref(w);
 	i = drag(w, &r);
-	if(i)
+	if(i){
 		wsendctlmesg(w, Moved, r, i);
-	cornercursor(input, mouse->xy, 1);
+		wcurrent(w);
+	}
+	cornercursor(w, mouse->xy, 1);
+	wclose(w);
 }
 
 int
@@ -1145,38 +1156,39 @@
 			return -1;
 	if(nhidden >= nelem(hidden))
 		return 0;
+	incref(w);
 	i = allocimage(display, w->screenr, w->i->chan, 0, DWhite);
 	if(i){
+		if(w == input)
+			input = nil;
 		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];
-	if(w == nil)
-		return 0;
-	if(h >= nhidden){
-		wtopme(w);
-		wcurrent(w);
-		flushimage(display, 1);
-		return 1;
-	}
+	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, DWhite);
 	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;
+		wcurrent(w);
 	}
-	return 0;
+	wclose(w);
+	return i!=0;
 }
 
 void
@@ -1185,16 +1197,34 @@
 	Window *w;
 
 	w = pointto(TRUE);
-	if(w == nil)
-		return;
-	whide(w);
+	if(w)
+		whide(w);
 }
 
 void
-unhide(int h)
+unhide(int j)
 {
-	if(h >= Hidden)
-		wunhide(h - Hidden);
+	Window *w;
+
+	if(j < Hidden)
+		return;
+	j -= Hidden;
+	w = hidden[j];
+	if(w == nil)
+		return;
+	if(j < nhidden){
+		wunhide(w);
+		return;
+	}
+	/* uncover obscured window */
+	for(j=0; j<nwindow; j++)
+		if(window[j] == w){
+			incref(w);
+			wcurrent(w);
+			wtopme(w);
+			wclose(w);
+			return;
+		}
 }
 
 Window*
@@ -1207,6 +1237,10 @@
 
 	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);
@@ -1221,8 +1255,6 @@
 	free(mc);	/* wmk copies *mc */
 	window = erealloc(window, ++nwindow*sizeof(Window*));
 	window[nwindow-1] = w;
-	if(nhidden >= nelem(hidden))
-		hideit = 0;
 	if(hideit){
 		hidden[nhidden++] = w;
 		w->screenr = ZR;
--- a/sys/src/cmd/rio/wctl.c
+++ b/sys/src/cmd/rio/wctl.c
@@ -346,68 +346,32 @@
 }
 
 int
-writewctl(Xfid *x, char *err)
+wctlcmd(Window *w, Rectangle r, int cmd, char *err)
 {
-	int cnt, cmd, j, id, hideit, scrollit, pid;
 	Image *i;
-	char *arg, *dir;
-	Rectangle rect;
-	Window *w;
 
-	w = x->f->w;
-	cnt = x->count;
-	x->data[cnt] = '\0';
-	id = 0;
-
-	rect = rectsubpt(w->screenr, screen->r.min);
-	cmd = parsewctl(&arg, rect, &rect, &pid, &id, &hideit, &scrollit, &dir, x->data, err);
-	if(cmd < 0)
-		return -1;
-
-	if(mouse->buttons!=0 && cmd>=Top){
-		strcpy(err, "action disallowed when mouse active");
-		return -1;
-	}
-
-	if(id != 0){
-		for(j=0; j<nwindow; j++)
-			if(window[j]->id == id)
-				break;
-		if(j == nwindow){
-			strcpy(err, "no such window id");
-			return -1;
-		}
-		w = window[j];
-		if(w->deleted || w->i==nil){
-			strcpy(err, "window deleted");
-			return -1;
-		}
-	}
-
 	switch(cmd){
-	case New:
-		return wctlnew(rect, arg, pid, hideit, scrollit, dir, err);
-	case Set:
-		if(pid > 0)
-			wsetpid(w, pid, 0);
-		return 1;
 	case Move:
-		rect = Rect(rect.min.x, rect.min.y, rect.min.x+Dx(w->screenr), rect.min.y+Dy(w->screenr));
-		rect = rectonscreen(rect);
+		r = Rect(r.min.x, r.min.y, r.min.x+Dx(w->screenr), r.min.y+Dy(w->screenr));
+		r = rectonscreen(r);
 		/* fall through */
 	case Resize:
-		if(!goodrect(rect)){
+		if(!goodrect(r)){
 			strcpy(err, Ebadwr);
 			return -1;
 		}
-		if(eqrect(rect, w->screenr))
+		if(w != input){
+			strcpy(err, "window not current");
+			return -1;
+		}
+		if(eqrect(r, w->screenr))
 			return 1;
-		i = allocwindow(wscreen, rect, Refbackup, DWhite);
+		i = allocwindow(wscreen, r, Refbackup, DWhite);
 		if(i == nil){
 			strcpy(err, Ewalloc);
 			return -1;
 		}
-		border(i, rect, Selborder, red, ZP);
+		border(i, r, Selborder, red, ZP);
 		wsendctlmesg(w, Reshaped, i->r, i);
 		return 1;
 	case Scroll:
@@ -441,16 +405,15 @@
 		}
 		return 1;
 	case Unhide:
-		for(j=0; j<nhidden; j++)
-			if(hidden[j] == w)
-				break;
-		if(j == nhidden){
+		switch(wunhide(w)){
+		case -1:
 			strcpy(err, "window not hidden");
 			return -1;
-		}
-		if(wunhide(j) == 0){
+		case 0:
 			strcpy(err, "hide failed");
 			return -1;
+		default:
+			break;
 		}
 		return 1;
 	case Delete:
@@ -457,8 +420,51 @@
 		wsendctlmesg(w, Deleted, ZR, nil);
 		return 1;
 	}
+
 	strcpy(err, "invalid wctl message");
 	return -1;
+}
+
+int
+writewctl(Xfid *x, char *err)
+{
+	int cnt, cmd, id, hideit, scrollit, pid;
+	char *arg, *dir;
+	Rectangle r;
+	Window *w;
+
+	w = x->f->w;
+	cnt = x->count;
+	x->data[cnt] = '\0';
+	id = 0;
+
+	r = rectsubpt(w->screenr, screen->r.min);
+	cmd = parsewctl(&arg, r, &r, &pid, &id, &hideit, &scrollit, &dir, x->data, err);
+	if(cmd < 0)
+		return -1;
+
+	if(id != 0){
+		w = wlookid(id);
+		if(w == 0){
+			strcpy(err, "no such window id");
+			return -1;
+		}
+	}
+
+	switch(cmd){
+	case New:
+		return wctlnew(r, arg, pid, hideit, scrollit, dir, err);
+	case Set:
+		if(pid > 0)
+			wsetpid(w, pid, 0);
+		return 1;
+	}
+
+	incref(w);
+	id = wctlcmd(w, r, cmd, err);
+	wclose(w);
+
+	return id;
 }
 
 void
--- a/sys/src/cmd/rio/wind.c
+++ b/sys/src/cmd/rio/wind.c
@@ -131,7 +131,6 @@
 		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;
@@ -151,10 +150,15 @@
 		wsetselect(w, w->q0, w->q1);
 		wscrdraw(w);
 	}
-	wborder(w, Selborder);
+	if(w == input)
+		wborder(w, Selborder);
+	else
+		wborder(w, Unselborder);
+	wsetname(w);
 	w->topped = ++topped;
 	w->resized = TRUE;
 	w->mouse.counter++;
+	w->wctlready = 1;
 }
 
 void
@@ -189,7 +193,7 @@
 	if(i < 0)
 		error("negative ref count");
 	if(!w->deleted)
-		wclosewin(w);
+		wclunk(w);
 	wsendctlmesg(w, Exited, ZR, nil);
 	return 1;
 }
@@ -1121,24 +1125,16 @@
 		w->screenr = r;
 		strcpy(buf, w->name);
 		wresize(w, i, m==Moved);
-		w->wctlready = 1;
 		proccreate(deletetimeoutproc, estrdup(buf), 4096);
-		if(Dx(r) > 0){
-			if(w != input)
-				wcurrent(w);
-		}else if(w == input)
-			wcurrent(nil);
-		flushimage(display, 1);
 		break;
 	case Refresh:
-		if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
+		if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r) || w->mouseopen)
 			break;
-		if(!w->mouseopen)
-			wrefresh(w, r);
+		wrefresh(w, r);
 		flushimage(display, 1);
 		break;
 	case Movemouse:
-		if(sweeping || !ptinrect(r.min, w->i->r))
+		if(w->deleted || Dx(w->screenr)<=0 || !ptinrect(r.min, w->i->r))
 			break;
 		wmovemouse(w, r.min);
 	case Rawon:
@@ -1154,6 +1150,7 @@
 		break;
 	case Holdon:
 	case Holdoff:
+	case Repaint:
 		if(w->deleted)
 			break;
 		wrepaint(w);
@@ -1162,11 +1159,13 @@
 	case Deleted:
 		if(w->deleted)
 			break;
+		wclunk(w);
 		write(w->notefd, "hangup", 6);
 		proccreate(deletetimeoutproc, estrdup(w->name), 4096);
 		wclosewin(w);
 		break;
 	case Exited:
+		wclosewin(w);
 		frclear(w, TRUE);
 		close(w->notefd);
 		chanfree(w->mc.c);
@@ -1193,6 +1192,8 @@
 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);
@@ -1216,7 +1217,6 @@
 		else
 			col = lighttitlecol;
 	}
-
 	border(w->i, w->i->r, Selborder, col, ZP);
 }
 
@@ -1230,7 +1230,6 @@
 	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;
 	}
@@ -1246,20 +1245,14 @@
 		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);
+			wsendctlmesg(oi, Repaint, ZR, nil);
 		}
 		if(w){
 			w->wctlready = 1;
-			wsendctlmesg(w, Wakeup, ZR, nil);
+			wsendctlmesg(w, Repaint, ZR, nil);
 		}
 	}
 }
@@ -1269,7 +1262,7 @@
 {
 	Cursor *p;
 
-	if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
+	if(w==nil || w->deleted || w->i==nil || Dx(w->screenr)<=0)
 		p = nil;
 	else if(wpointto(mouse->xy) == w){
 		p = w->cursorp;
@@ -1290,30 +1283,14 @@
 	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){
+		w->topped = ++topped;
 		topwindow(w->i);
 		flushimage(display, 1);
-		w->topped = ++topped;
 	}
 }
 
@@ -1321,13 +1298,30 @@
 wbottomme(Window *w)
 {
 	if(w!=nil && w->i!=nil && !w->deleted){
+		w->topped = - ++topped;
 		bottomwindow(w->i);
 		flushimage(display, 1);
-		w->topped = - ++topped;
 	}
 }
 
 Window*
+wtop(Point pt)
+{
+	Window *w;
+
+	w = wpointto(pt);
+	if(w){
+		if(w->topped == topped)
+			return nil;
+		incref(w);
+		wcurrent(w);
+		wtopme(w);
+		wclose(w);
+	}
+	return w;
+}
+
+Window*
 wlookid(int id)
 {
 	int i;
@@ -1339,12 +1333,10 @@
 }
 
 void
-wclosewin(Window *w)
+wclunk(Window *w)
 {
-	Rectangle r;
 	int i;
 
-	w->deleted = TRUE;
 	if(w == input){
 		input = nil;
 		wsetcursor(w, 0);
@@ -1360,16 +1352,24 @@
 	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");
+	w->deleted = TRUE;
+}
+
+void
+wclosewin(Window *w)
+{
+	Image *i;
+
+	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);
+	}
 }
 
 void
--