--- //.git/fs/object/2f8a59f4b5bfe028c022855acc19666d69eed909/tree//sys/src/cmd/rio/dat.h
+++ /sys/src/cmd/rio/dat.h
@@ -10,6 +10,7 @@
 	Qlabel,
 	Qkbd,
 	Qmouse,
+	Qmousectl,
 	Qnew,
 	Qscreen,
 	Qsnarf,
@@ -154,6 +155,9 @@
 	 * Now they're always the same but the code doesn't assume so.
 	*/
 	Rectangle		screenr;	/* screen coordinates of window */
+	Rectangle	mclipr;
+	Fid			*mclipfid;
+	int			mclip;
 	int			resized;
 	int			wctlready;
 	Rectangle		lastsr;
@@ -304,11 +308,14 @@
 Image	*lightholdcol;
 Image	*paleholdcol;
 Image	*paletextcol;
+Image	*grabcol;
+Image	*lightgrabcol;
 Image	*sizecol;
 int	reverse;	/* there are no pastel paints in the dungeons and dragons world -- rob pike */
 
 Window	**window;
 Window	*wkeyboard;	/* window of simulated keyboard */
+Window	*wclip;
 int		nwindow;
 int		snarffd;
 int		gotscreen;
--- //.git/fs/object/2f8a59f4b5bfe028c022855acc19666d69eed909/tree//sys/src/cmd/rio/data.c
+++ /sys/src/cmd/rio/data.c
@@ -195,6 +195,8 @@
 	lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
 	paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
 	paletextcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF^reverse);
+	grabcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xB22222FF);
+	lightgrabcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCD5C5CFF);
 	sizecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DRed);
 
 	if(reverse == 0)
--- //.git/fs/object/2f8a59f4b5bfe028c022855acc19666d69eed909/tree//sys/src/cmd/rio/fns.h
+++ /sys/src/cmd/rio/fns.h
@@ -5,6 +5,7 @@
 int	writewctl(Xfid*, char*);
 Window *new(Image*, int, int, int, char*, char*, char**);
 void	riosetcursor(Cursor*);
+void	updatemclip(void);
 int	min(int, int);
 int	max(int, int);
 Rune*	strrune(Rune*, Rune);
--- //.git/fs/object/2f8a59f4b5bfe028c022855acc19666d69eed909/tree//sys/src/cmd/rio/fsys.c
+++ /sys/src/cmd/rio/fsys.c
@@ -30,6 +30,7 @@
 	{ "label",		QTFILE,	Qlabel,		0600 },
 	{ "kbd",	QTFILE,	Qkbd,		0600 },
 	{ "mouse",	QTFILE,	Qmouse,		0600 },
+	{ "mousectl",	QTFILE,	Qmousectl,		0600 },
 	{ "screen",		QTFILE,	Qscreen,		0400 },
 	{ "snarf",		QTFILE,	Qsnarf,		0600 },
 	{ "text",		QTFILE,	Qtext,		0600 },
--- //.git/fs/object/2f8a59f4b5bfe028c022855acc19666d69eed909/tree//sys/src/cmd/rio/rio.c
+++ /sys/src/cmd/rio/rio.c
@@ -327,6 +327,27 @@
 }
 
 void
+updatemclip(void)
+{
+	Window *w;
+
+	if(input == nil || !input->mclip || menuing || sweeping)
+		w = nil;
+	else
+		w = input;
+	if(w == wclip)
+		return;
+	if(w != nil){
+		clipmouse(mousectl, w->mclipr);
+		wsendctlmesg(w, Repaint, ZR, nil);
+	}else
+		unclipmouse(mousectl);
+	if(wclip)
+		wsendctlmesg(wclip, Repaint, ZR, nil);
+	wclip = w;
+}
+
+void
 killprocs(void)
 {
 	int i;
@@ -489,6 +510,9 @@
 				wtopme(wkeyboard);
 				winput = wkeyboard;
 			}
+			w = wpointto(mouse->xy);
+			if(sending == FALSE && !scrolling && w!=nil && !w->deleted && inborder(w->screenr, mouse->xy))
+				moving = TRUE;
 			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);
@@ -505,10 +529,7 @@
 					scrolling = mouse->buttons;
 				else
 					scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
-				/* topped will be zero or less if window has been bottomed */
-				if(sending == FALSE && !scrolling && inborder(winput->screenr, mouse->xy) && winput->topped>0)
-					moving = TRUE;
-				else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
+				if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
 					sending = TRUE;
 			}else
 				sending = FALSE;
@@ -523,21 +544,21 @@
 				continue;
 			}
 			if(moving && (mouse->buttons&7)){
-				incref(winput);
+				incref(w);
 				sweeping = TRUE;
 				if(mouse->buttons & 3)
-					i = bandsize(winput);
+					i = bandsize(w);
 				else
-					i = drag(winput);
+					i = drag(w);
 				sweeping = FALSE;
-				if(i != nil){
-					wcurrent(winput);
-					wsendctlmesg(winput, Reshaped, i->r, i);
-				}
-				wclose(winput);
+				if(i != nil)
+					wsendctlmesg(w, Reshaped, i->r, i);
+				wcurrent(w);
+				wtopme(w);
+				wsendctlmesg(w, Topped, ZR, nil);
+				wclose(w);
 				continue;
 			}
-			w = wpointto(mouse->xy);
 			if(w!=nil && inborder(w->screenr, mouse->xy))
 				riosetcursor(corners[whichcorner(w->screenr, mouse->xy)]);
 			else
@@ -715,6 +736,7 @@
 		menu3str[i] = nil;
 	}
 	sweeping = TRUE;
+	updatemclip();
 	switch(i = menuhit(3, mousectl, &menu3, wscreen)){
 	case -1:
 		break;
@@ -744,6 +766,7 @@
 		break;
 	}
 	sweeping = FALSE;
+	updatemclip();
 }
 
 void
@@ -753,6 +776,8 @@
 		menu2str[Scroll] = "noscroll";
 	else
 		menu2str[Scroll] = "scroll";
+	sweeping = TRUE;
+	updatemclip();
 	switch(menuhit(2, mousectl, &menu2, wscreen)){
 	case Cut:
 		wsnarf(w);
@@ -787,6 +812,8 @@
 			wshow(w, w->nr);
 		break;
 	}
+	sweeping = FALSE;
+	updatemclip();
 	flushimage(display, 1);
 	wsendctlmesg(w, Wakeup, ZR, nil);
 }
--- //.git/fs/object/2f8a59f4b5bfe028c022855acc19666d69eed909/tree//sys/src/cmd/rio/wind.c
+++ /sys/src/cmd/rio/wind.c
@@ -302,6 +302,11 @@
 			col = holdcol;
 		else
 			col = paleholdcol;
+	}else if(w->mclip){
+		if(type == Selborder)
+			col = grabcol;
+		else
+			col = lightgrabcol;
 	}else{
 		if(type == Selborder)
 			col = titlecol;
@@ -358,6 +363,8 @@
 	w->scrollr.max.x = r.min.x+Scrollwid;
 	w->lastsr = ZR;
 	r.min.x += Scrollwid+Scrollgap;
+	w->mclip = FALSE;
+	updatemclip();
 	frclear(w, FALSE);
 	frinit(w, r, w->font, w->i, cols);
 	wsetcols(w, w == input);
@@ -1264,6 +1271,7 @@
 	w->gone = chancreate(sizeof(char*), 0);
 	w->scrollr = r;
 	w->scrollr.max.x = r.min.x+Scrollwid;
+	w->mclip = FALSE;
 	w->lastsr = ZR;
 	r.min.x += Scrollwid+Scrollgap;
 	frinit(w, r, font, i, cols);
@@ -1306,6 +1314,7 @@
 		input = nil;
 		riosetcursor(nil);
 	}
+	updatemclip();
 	if(w == wkeyboard)
 		wkeyboard = nil;
 	for(i=0; i<nhidden; i++)
@@ -1390,6 +1399,7 @@
 
 			sendp(c, w);
 		}
+		updatemclip();
 		if(w->i==nil || Dx(w->screenr)<=0)
 			break;
 		wrepaint(w);
--- //.git/fs/object/2f8a59f4b5bfe028c022855acc19666d69eed909/tree//sys/src/cmd/rio/xfid.c
+++ /sys/src/cmd/rio/xfid.c
@@ -18,6 +18,7 @@
 char Elong[] = 		"snarf buffer too long";
 char Eunkid[] = 	"unknown id in attach";
 char Ebadrect[] = 	"bad rectangle in attach";
+char Eclip[] =		"invalid clip rectangle";
 char Ewindow[] = 	"cannot make window";
 char Enowindow[] = 	"window has no image";
 char Ebadmouse[] = 	"bad format on /dev/mouse";
@@ -342,6 +343,14 @@
 	case Qkbd:
 		w->kbdopen = FALSE;
 		break;
+	case Qmousectl:
+		if(w->mclip && w->mclipfid == x->f){
+			w->mclip = FALSE;
+			w->mclipfid = nil;
+			updatemclip();
+			wsendctlmesg(w, Repaint, ZR, nil);
+		}
+		break;
 	case Qmouse:
 		w->resized = FALSE;
 		w->mouseopen = FALSE;
@@ -371,7 +380,8 @@
 {
 	Fcall fc;
 	int cnt, qid, nb, off, nr;
-	char err[ERRMAX], *p;
+	char err[ERRMAX], *coords[4], *p;
+	Rectangle mclipr;
 	Point pt;
 	Window *w;
 	Rune *r;
@@ -500,6 +510,50 @@
 		w->label = p;
 		w->label[cnt] = 0;
 		memmove(w->label, x->data, cnt);
+		break;
+
+	case Qmousectl:
+		if(strncmp(x->data, "clip", 4)==0){
+			if(tokenize(x->data+4, coords, 4)!=4){
+				filsysrespond(x->fs, x, &fc, Eclip);
+				return;
+			}
+			mclipr.min.x = strtol(coords[0], &p, 0);
+			if(*p != '\0'){
+				filsysrespond(x->fs, x, &fc, Eclip);
+				return;
+			}
+			mclipr.min.y = strtol(coords[1], &p, 0);
+			if(*p != '\0'){
+				filsysrespond(x->fs, x, &fc, Eclip);
+				return;
+			}
+			mclipr.max.x = strtol(coords[2], &p, 0);
+			if(*p != '\0'){
+				filsysrespond(x->fs, x, &fc, Eclip);
+				return;
+			}
+			mclipr.max.y = strtol(coords[3], &p, 0);
+			if(*p != '\0'){
+				filsysrespond(x->fs, x, &fc, Eclip);
+				return;
+			}
+			if(!rectinrect(mclipr, w->screenr)){
+				filsysrespond(x->fs, x, &fc, Eclip);
+				return;
+			}
+			w->mclipr = mclipr;
+			w->mclipfid = x->f;
+			w->mclip = TRUE;
+			updatemclip();
+			wsendctlmesg(w, Repaint, ZR, nil);
+		}else if(strncmp(x->data, "unclip", 6)==0){
+			w->mclip = FALSE;
+			w->mclipfid = nil;
+			updatemclip();
+			wsendctlmesg(w, Repaint, ZR, nil);
+		}else
+			write(mousectl->mctlfd, x->data, cnt);
 		break;
 
 	case Qmouse:
