Version 3 of the patch fixes region border calculation in
client_keep_visible().

Quoth Vadim Vygonets on Mon, Jul 18, 2016:
> cwm makes a client active only when the mouse pointer enters the
> window.  Thus, once a client is off screen and another becomes
> active, it's lost forever.
> 
> The attached patch is a bit big, as it does several things (will
> split if interested):
> 
> - In lots of places, it deals with regions (that's CRTCs (or
>   outputs?) for XRandr affictionados, and monitors for you and
>   me) instead of screens.
> 
> - Clients now have regions associated with them and cached in
>   client_ctx.  The client's region is:
>   - the region containing the centre of the window;
>   - failing that, the region that has the biggest overlapping
>     area with the window;
>   - if the window is fully off screen, the region closest to it
>     by Manhattan distance (edge to edge).
> 
> - When a client is moved or resized (whether by the user or by
>   XConfigureRequestEvent), it's forced to remain "visible", i.e.,
>   overlapping with a region.  The client's associated region is
>   recalculated on every move or resize.
> 
> - When a client is created, its place is calculated to be within
>   the region of its desired position if position hints are set,
>   or within the working are of the region containing the mouse
>   pointer otherwise.  If it's bigger than the region, it will at
>   least overlap with it.
>   - I kept cwm's behaviour of not minding the gap during
>     placement calculations when position hints are set, which
>     makes sense for panels.
>   - Size hints are now respected.
> 
> - Tiling now works on regions.
> 
> - When screen geometry changes (XRandR monitors are added,
>   removed or rotated), for every old region a matching new region
>   is found (using the same method as for clients' regions), and
>   all clients within it are migrated to the new region, keeping
>   their relative positions within the region, with rounding
>   errors.  Full screen and maximized clients are resized
>   accordingly.  Tiled regions may cease to be tiled.
>   - A relative position is something like:
>       { (client.x - region.x) / (region.w - client.w),
>         (client.y - region.y) / (region.h - client.h) }
>     (whatever's in the corner or in the centre stays there).
> 
> - I added bind commands to move clients between regions, as most
>   of the code was already there.
> 
> This patch does not handle XShape, so non-rectangular clients can
> still be moved off screen.
> 
> Vadik.

-- 
Computer games don't affect kids; I mean if Pac-Man affected us
as kids, we'd all be running around in darkened rooms, munching
magic pills and listening to repetitive electronic music.
                -- Marcus Brigstocke, British Comedian
Index: calmwm.h
===================================================================
RCS file: /cvs/xenocara/app/cwm/calmwm.h,v
retrieving revision 1.311
diff -u -r1.311 calmwm.h
--- calmwm.h	12 Nov 2015 21:28:03 -0000	1.311
+++ calmwm.h	18 Jul 2016 20:40:12 -0000
@@ -141,6 +141,7 @@
 	TAILQ_ENTRY(client_ctx)	 entry;
 	TAILQ_ENTRY(client_ctx)	 group_entry;
 	struct screen_ctx	*sc;
+	struct region_ctx	*rc;
 	struct group_ctx	*gc;
 	Window			 win;
 	Colormap		 colormap;
@@ -218,9 +219,7 @@
 struct region_ctx {
 	TAILQ_ENTRY(region_ctx)	 entry;
 	int			 num;
-	struct geom		 area;
 	struct geom		 view; /* viewable area */
-	struct geom		 work; /* workable area, gap-applied */
 };
 TAILQ_HEAD(region_ctx_q, region_ctx);
 
@@ -394,8 +393,11 @@
 void			 client_getsizehints(struct client_ctx *);
 void			 client_hide(struct client_ctx *);
 void 			 client_htile(struct client_ctx *);
+int			 client_keep_visible(struct client_ctx *);
 void			 client_lower(struct client_ctx *);
 void			 client_map(struct client_ctx *);
+void			 client_migrate_region(struct client_ctx *,
+			     struct region_ctx *);
 void			 client_msg(struct client_ctx *, Atom, Time);
 void			 client_move(struct client_ctx *);
 struct client_ctx	*client_init(Window, struct screen_ctx *);
@@ -408,6 +410,7 @@
 void			 client_setactive(struct client_ctx *);
 void			 client_setname(struct client_ctx *);
 int			 client_snapcalc(int, int, int, int, int);
+struct geom		 client_region_area(struct client_ctx *, int);
 void			 client_toggle_freeze(struct client_ctx *);
 void			 client_toggle_fullscreen(struct client_ctx *);
 void			 client_toggle_hidden(struct client_ctx *);
@@ -453,7 +456,7 @@
 void			 search_print_cmd(struct menu *, int);
 void			 search_print_group(struct menu *, int);
 
-struct region_ctx	*region_find(struct screen_ctx *, int, int);
+struct region_ctx	*region_find(struct screen_ctx *, struct geom);
 struct geom		 screen_apply_gap(struct screen_ctx *, struct geom);
 struct screen_ctx	*screen_find(Window);
 struct geom		 screen_area(struct screen_ctx *, int, int, int);
@@ -468,11 +471,12 @@
 void			 kbfunc_client_hide(struct client_ctx *, union arg *);
 void			 kbfunc_client_label(struct client_ctx *, union arg *);
 void			 kbfunc_client_lower(struct client_ctx *, union arg *);
+void			 kbfunc_client_migrateregion(struct client_ctx *,
+			     union arg *);
 void			 kbfunc_client_move(struct client_ctx *, union arg *);
 void			 kbfunc_client_movetogroup(struct client_ctx *,
 			     union arg *);
 void			 kbfunc_client_raise(struct client_ctx *, union arg *);
-void			 kbfunc_client_rcycle(struct client_ctx *, union arg *);
 void			 kbfunc_client_resize(struct client_ctx *, union arg *);
 void 			 kbfunc_client_tile(struct client_ctx *, union arg *);
 void			 kbfunc_client_toggle_freeze(struct client_ctx *,
Index: client.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/client.c,v
retrieving revision 1.214
diff -u -r1.214 client.c
--- client.c	12 Nov 2015 18:33:30 -0000	1.214
+++ client.c	15 Aug 2016 19:51:00 -0000
@@ -101,6 +101,7 @@
 		if ((cc->wmh) && (cc->wmh->flags & StateHint))
 			client_set_wm_state(cc, cc->wmh->initial_state);
 	} else {
+		cc->rc = region_find(sc, cc->geom);
 		if ((XQueryPointer(X_Dpy, cc->win, &rwin, &cwin,
 		    &x, &y, &wx, &wy, &mask)) && (cwin != None))
 			activate = 1;
@@ -250,6 +251,16 @@
 	return(curcc);
 }
 
+struct geom
+client_region_area(struct client_ctx *cc, int flags)
+{
+	struct geom	 area = cc->rc->view;
+
+	if (flags & CWM_GAP)
+		area = screen_apply_gap(cc->sc, area);
+	return(area);
+}
+
 void
 client_toggle_freeze(struct client_ctx *cc)
 {
@@ -289,8 +300,7 @@
 void
 client_toggle_fullscreen(struct client_ctx *cc)
 {
-	struct screen_ctx	*sc = cc->sc;
-	struct geom		 area;
+	struct geom	 area;
 
 	if ((cc->flags & CLIENT_FREEZE) &&
 	    !(cc->flags & CLIENT_FULLSCREEN))
@@ -305,9 +315,7 @@
 
 	cc->fullgeom = cc->geom;
 
-	area = screen_area(sc,
-	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_NOGAP);
+	area = client_region_area(cc, CWM_NOGAP);
 
 	cc->bwidth = 0;
 	cc->geom = area;
@@ -321,8 +329,7 @@
 void
 client_toggle_maximize(struct client_ctx *cc)
 {
-	struct screen_ctx	*sc = cc->sc;
-	struct geom		 area;
+	struct geom	 area;
 
 	if (cc->flags & CLIENT_FREEZE)
 		return;
@@ -343,14 +350,7 @@
 		cc->savegeom.x = cc->geom.x;
 	}
 
-	/*
-	 * pick screen that the middle of the window is on.
-	 * that's probably more fair than if just the origin of
-	 * a window is poking over a boundary
-	 */
-	area = screen_area(sc,
-	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	area = client_region_area(cc, CWM_GAP);
 
 	cc->geom.x = area.x;
 	cc->geom.y = area.y;
@@ -366,8 +366,7 @@
 void
 client_toggle_vmaximize(struct client_ctx *cc)
 {
-	struct screen_ctx	*sc = cc->sc;
-	struct geom		 area;
+	struct geom	 area;
 
 	if (cc->flags & CLIENT_FREEZE)
 		return;
@@ -382,9 +381,7 @@
 	cc->savegeom.y = cc->geom.y;
 	cc->savegeom.h = cc->geom.h;
 
-	area = screen_area(sc,
-	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	area = client_region_area(cc, CWM_GAP);
 
 	cc->geom.y = area.y;
 	cc->geom.h = area.h - (cc->bwidth * 2);
@@ -398,8 +395,7 @@
 void
 client_toggle_hmaximize(struct client_ctx *cc)
 {
-	struct screen_ctx	*sc = cc->sc;
-	struct geom		 area;
+	struct geom	 area;
 
 	if (cc->flags & CLIENT_FREEZE)
 		return;
@@ -414,9 +410,7 @@
 	cc->savegeom.x = cc->geom.x;
 	cc->savegeom.w = cc->geom.w;
 
-	area = screen_area(sc,
-	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	area = client_region_area(cc, CWM_GAP);
 
 	cc->geom.x = area.x;
 	cc->geom.w = area.w - (cc->bwidth * 2);
@@ -442,6 +436,32 @@
 	client_config(cc);
 }
 
+int
+client_keep_visible(struct client_ctx *cc)
+{
+	struct region_ctx	*rc;
+	int			 changed = 0;
+
+	/* Recalculate region for the client */
+	cc->rc = region_find(cc->sc, cc->geom);
+	rc = cc->rc;
+	if (cc->geom.x + cc->geom.w + (int)(cc->bwidth * 2) <= rc->view.x) {
+		cc->geom.x = rc->view.x - (cc->geom.w + (cc->bwidth * 2) - 1);
+		changed = 1;
+	} else if (cc->geom.x >= (rc->view.x + rc->view.w)) {
+		cc->geom.x = rc->view.x + rc->view.w - 1;
+		changed = 1;
+	}
+	if (cc->geom.y + cc->geom.h + (int)(cc->bwidth * 2) <= rc->view.y) {
+		cc->geom.y = rc->view.y - (cc->geom.h + (cc->bwidth * 2) - 1);
+		changed = 1;
+	} else if (cc->geom.y >= (rc->view.y + rc->view.h)) {
+		cc->geom.y = rc->view.y + rc->view.h - 1;
+		changed = 1;
+	}
+	return(changed);
+}
+
 void
 client_move(struct client_ctx *cc)
 {
@@ -749,6 +769,7 @@
 client_placecalc(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = cc->sc;
+	struct geom		 area;
 	int			 xslack, yslack;
 
 	if (cc->hint.flags & (USPosition | PPosition)) {
@@ -759,40 +780,29 @@
 		 * XRandR bits mean that {x,y}max shouldn't be outside what's
 		 * currently there.
 		 */
-		xslack = sc->view.w - cc->geom.w - cc->bwidth * 2;
-		yslack = sc->view.h - cc->geom.h - cc->bwidth * 2;
-		cc->geom.x = MIN(cc->geom.x, xslack);
-		cc->geom.y = MIN(cc->geom.y, yslack);
+		cc->rc = region_find(sc, cc->geom);
+		area = client_region_area(cc, CWM_NOGAP);
 	} else {
-		struct geom		 area;
+		struct geom		 point;
 		int			 xmouse, ymouse;
 
 		xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse);
-		area = screen_area(sc, xmouse, ymouse, CWM_GAP);
-		area.w += area.x;
-		area.h += area.y;
-		xmouse = MAX(xmouse, area.x) - cc->geom.w / 2;
-		ymouse = MAX(ymouse, area.y) - cc->geom.h / 2;
-
-		xmouse = MAX(xmouse, area.x);
-		ymouse = MAX(ymouse, area.y);
-
-		xslack = area.w - cc->geom.w - cc->bwidth * 2;
-		yslack = area.h - cc->geom.h - cc->bwidth * 2;
-
-		if (xslack >= area.x) {
-			cc->geom.x = MAX(MIN(xmouse, xslack), area.x);
-		} else {
-			cc->geom.x = area.x;
-			cc->geom.w = area.w;
-		}
-		if (yslack >= area.y) {
-			cc->geom.y = MAX(MIN(ymouse, yslack), area.y);
-		} else {
-			cc->geom.y = area.y;
-			cc->geom.h = area.h;
+		point = (struct geom){ xmouse, ymouse, 0, 0 };
+		cc->rc = region_find(sc, point);
+		area = client_region_area(cc, CWM_GAP);
+
+		if (!(cc->hint.flags & (USSize | PSize))) {
+			cc->geom.w = MIN(cc->geom.w, area.w - cc->bwidth * 2);
+			cc->geom.h = MIN(cc->geom.h, area.h - cc->bwidth * 2);
+			client_applysizehints(cc);
 		}
+		cc->geom.x = xmouse - cc->geom.w / 2 - cc->bwidth;
+		cc->geom.y = ymouse - cc->geom.h / 2 - cc->bwidth;
 	}
+	xslack = area.x + area.w - cc->geom.w - cc->bwidth * 2;
+	yslack = area.y + area.h - cc->geom.h - cc->bwidth * 2;
+	cc->geom.x = MAX(MIN(cc->geom.x, xslack), area.x);
+	cc->geom.y = MAX(MIN(cc->geom.y, yslack), area.y);
 }
 
 static void
@@ -974,7 +984,6 @@
 {
 	struct client_ctx	*ci;
 	struct group_ctx 	*gc = cc->gc;
-	struct screen_ctx 	*sc = cc->sc;
 	struct geom 		 area;
 	int 			 i, n, mh, x, h, w;
 
@@ -983,17 +992,15 @@
 	i = n = 0;
 
 	TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
-		if (ci->flags & CLIENT_HIDDEN ||
-		    ci->flags & CLIENT_IGNORE || (ci == cc))
+		if ((ci->flags & (CLIENT_HIDDEN | CLIENT_IGNORE)) ||
+		    (ci == cc) || (ci->rc != cc->rc))
 			continue;
 		n++;
 	}
 	if (n == 0)
 		return;
 
-	area = screen_area(sc,
-	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	area = client_region_area(cc, CWM_GAP);
 
 	if (cc->flags & CLIENT_VMAXIMIZED ||
 	    cc->geom.h + (cc->bwidth * 2) >= area.h)
@@ -1011,8 +1018,8 @@
 	w = area.w / n;
 	h = area.h - mh;
 	TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
-		if (ci->flags & CLIENT_HIDDEN ||
-		    ci->flags & CLIENT_IGNORE || (ci == cc))
+		if ((ci->flags & (CLIENT_HIDDEN | CLIENT_IGNORE)) ||
+		    (ci == cc) || (ci->rc != cc->rc))
 			continue;
 		ci->bwidth = Conf.bwidth;
 		ci->geom.y = area.y + mh;
@@ -1033,7 +1040,6 @@
 {
 	struct client_ctx	*ci;
 	struct group_ctx 	*gc = cc->gc;
-	struct screen_ctx 	*sc = cc->sc;
 	struct geom 		 area;
 	int 			 i, n, mw, y, h, w;
 
@@ -1042,17 +1048,15 @@
 	i = n = 0;
 
 	TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
-		if (ci->flags & CLIENT_HIDDEN ||
-		    ci->flags & CLIENT_IGNORE || (ci == cc))
+		if ((ci->flags & (CLIENT_HIDDEN | CLIENT_IGNORE)) ||
+		    (ci == cc) || (ci->rc != cc->rc))
 			continue;
 		n++;
 	}
 	if (n == 0)
 		return;
 
-	area = screen_area(sc,
-	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	area = client_region_area(cc, CWM_GAP);
 
 	if (cc->flags & CLIENT_HMAXIMIZED ||
 	    cc->geom.w + (cc->bwidth * 2) >= area.w)
@@ -1070,8 +1074,8 @@
 	h = area.h / n;
 	w = area.w - mw;
 	TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
-		if (ci->flags & CLIENT_HIDDEN ||
-		    ci->flags & CLIENT_IGNORE || (ci == cc))
+		if ((ci->flags & (CLIENT_HIDDEN | CLIENT_IGNORE)) ||
+		    (ci == cc) || (ci->rc != cc->rc))
 			continue;
 		ci->bwidth = Conf.bwidth;
 		ci->geom.y = y;
@@ -1087,6 +1091,51 @@
 	}
 }
 
+static void
+client_migrate_area(struct geom *geom, struct geom *newarea,
+    struct geom *oldarea)
+{
+	int	 oldslack, newslack;
+
+	oldslack = oldarea->w - geom->w - (Conf.bwidth * 2);
+	newslack = newarea->w - geom->w - (Conf.bwidth * 2);
+	geom->x -= oldarea->x;
+	if (oldslack != 0 && newslack != oldslack)
+		geom->x = (long long)geom->x * newslack / oldslack;
+	geom->x += newarea->x;
+
+	oldslack = oldarea->h - geom->h - (Conf.bwidth * 2);
+	newslack = newarea->h - geom->h - (Conf.bwidth * 2);
+	geom->y -= oldarea->y;
+	if (oldslack != 0 && newslack != oldslack)
+		geom->y = (long long)geom->y * newslack / oldslack;
+	geom->y += newarea->y;
+}
+
+void
+client_migrate_region(struct client_ctx *cc, struct region_ctx *rc)
+{
+	struct screen_ctx	*sc = cc->sc;
+	struct geom		 oldarea, newarea;
+
+	oldarea = screen_apply_gap(sc, cc->rc->view);
+	newarea = screen_apply_gap(sc, rc->view);
+	client_migrate_area(&cc->savegeom, &newarea, &oldarea);
+	client_migrate_area(&cc->fullgeom, &newarea, &oldarea);
+	if (cc->flags & CLIENT_FULLSCREEN)
+		cc->geom = rc->view;
+	else {
+		client_migrate_area(&cc->geom, &newarea, &oldarea);
+		if (cc->flags & CLIENT_HMAXIMIZED)
+			cc->geom.w = newarea.w - (cc->bwidth * 2);
+		if (cc->flags & CLIENT_VMAXIMIZED)
+			cc->geom.h = newarea.h - (cc->bwidth * 2);
+		client_keep_visible(cc);
+	}
+	cc->rc = rc;
+	client_resize(cc, 0);
+}
+
 long
 client_get_wm_state(struct client_ctx *cc)
 {
@@ -1108,4 +1157,3 @@
 	XChangeProperty(X_Dpy, cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
 	    PropModeReplace, (unsigned char *)data, 2);
 }
-
Index: conf.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/conf.c,v
retrieving revision 1.204
diff -u -r1.204 conf.c
--- conf.c	13 Aug 2016 09:59:48 -0000      1.204
+++ conf.c	18 Jul 2016 20:40:12 -0000
@@ -406,6 +406,10 @@
 	    {.i = (CWM_CLIENT_CYCLE | CWM_CLIENT_CYCLE_INGRP)} },
 	{ "rcycleingroup", kbfunc_client_cycle, CWM_CONTEXT_CLIENT,
 	    {.i = (CWM_CLIENT_RCYCLE | CWM_CLIENT_CYCLE_INGRP)} },
+	{ "migrateregion", kbfunc_client_migrateregion, CWM_CONTEXT_CLIENT,
+	    {.i = CWM_CLIENT_CYCLE} },
+	{ "rmigrateregion", kbfunc_client_migrateregion, CWM_CONTEXT_CLIENT,
+	    {.i = CWM_CLIENT_RCYCLE} },
 	{ "grouptoggle", kbfunc_client_grouptoggle, CWM_CONTEXT_CLIENT,
 	    {.i = CWM_KBD}},
 	{ "sticky", kbfunc_client_toggle_sticky, CWM_CONTEXT_CLIENT, {0} },
Index: cwmrc.5
===================================================================
RCS file: /cvs/xenocara/app/cwm/cwmrc.5,v
retrieving revision 1.61
diff -u -r1.61 cwmrc.5
--- cwmrc.5	12 Jul 2015 14:31:47 -0000	1.61
+++ cwmrc.5	18 Jul 2016 20:40:12 -0000
@@ -289,6 +289,10 @@
 Forward cycle through windows in current group.
 .It rcycleingroup
 Reverse cycle through windows in current group.
+.It migrateregion
+Migrate current window to next region (XRandR monitor).
+.It rmigrateregion
+Migrate current window to previous region (XRandR monitor).
 .It delete
 Delete current window.
 .It hide
Index: kbfunc.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/kbfunc.c,v
retrieving revision 1.126
diff -u -r1.126 kbfunc.c
--- kbfunc.c	17 Nov 2015 14:32:38 -0000	1.126
+++ kbfunc.c	18 Jul 2016 20:40:12 -0000
@@ -104,19 +104,10 @@
 	kbfunc_amount(arg->i, Conf.mamount, &mx, &my);
 
 	cc->geom.x += mx;
-	if (cc->geom.x + cc->geom.w < 0)
-		cc->geom.x = -cc->geom.w;
-	if (cc->geom.x > sc->view.w - 1)
-		cc->geom.x = sc->view.w - 1;
 	cc->geom.y += my;
-	if (cc->geom.y + cc->geom.h < 0)
-		cc->geom.y = -cc->geom.h;
-	if (cc->geom.y > sc->view.h - 1)
-		cc->geom.y = sc->view.h - 1;
-
-	area = screen_area(sc,
-	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	client_keep_visible(cc);
+
+	area = client_region_area(cc, CWM_GAP);
 	cc->geom.x += client_snapcalc(cc->geom.x,
 	    cc->geom.x + cc->geom.w + (cc->bwidth * 2),
 	    area.x, area.x + area.w, sc->snapdist);
@@ -149,6 +140,7 @@
 		cc->geom.w = cc->hint.minw;
 	if ((cc->geom.h += my * cc->hint.inch) < cc->hint.minh)
 		cc->geom.h = cc->hint.minh;
+	client_keep_visible(cc);
 	client_resize(cc, 1);
 
 	/* Make sure the pointer stays within the window. */
@@ -491,6 +483,24 @@
 kbfunc_client_movetogroup(struct client_ctx *cc, union arg *arg)
 {
 	group_movetogroup(cc, arg->i);
+}
+
+void
+kbfunc_client_migrateregion(struct client_ctx *cc, union arg *arg)
+{
+	struct screen_ctx	*sc = cc->sc;
+	struct region_ctx	*rc;
+
+	if (arg->i == CWM_CLIENT_RCYCLE) {
+		if ((rc = TAILQ_PREV(cc->rc, region_ctx_q, entry)) == NULL)
+			rc = TAILQ_LAST(&sc->regionq, region_ctx_q);
+	} else {
+		if ((rc = TAILQ_NEXT(cc->rc, entry)) == NULL)
+			rc = TAILQ_FIRST(&sc->regionq);
+	}
+	client_ptrsave(cc);
+	client_migrate_region(cc, rc);
+	client_ptrwarp(cc);
 }
 
 void
Index: mousefunc.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/mousefunc.c,v
retrieving revision 1.103
diff -u -r1.103 mousefunc.c
--- mousefunc.c	17 Nov 2015 14:31:28 -0000	1.103
+++ mousefunc.c	18 Jul 2016 20:40:12 -0000
@@ -140,10 +140,14 @@
 
 			cc->geom.x = ev.xmotion.x_root - px - cc->bwidth;
 			cc->geom.y = ev.xmotion.y_root - py - cc->bwidth;
+			if (client_keep_visible(cc)) {
+				px = ev.xmotion.x_root - cc->geom.x -
+				    cc->bwidth;
+				py = ev.xmotion.y_root - cc->geom.y -
+				    cc->bwidth;
+			}
 
-			area = screen_area(sc,
-			    cc->geom.x + cc->geom.w / 2,
-			    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+			area = client_region_area(cc, CWM_GAP);
 			cc->geom.x += client_snapcalc(cc->geom.x,
 			    cc->geom.x + cc->geom.w + (cc->bwidth * 2),
 			    area.x, area.x + area.w, sc->snapdist);
Index: screen.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/screen.c,v
retrieving revision 1.79
diff -u -r1.79 screen.c
--- screen.c	11 Nov 2015 14:22:01 -0000	1.79
+++ screen.c	18 Jul 2016 20:40:12 -0000
@@ -125,32 +125,67 @@
 }
 
 struct region_ctx *
-region_find(struct screen_ctx *sc, int x, int y)
+region_find(struct screen_ctx *sc, struct geom area)
 {
 	struct region_ctx	*rc;
+	struct region_ctx	*best = NULL;
+	long long		 overlap = 0;
+	int			 cx, cy, distance = -1;
 
+	cx = area.x + area.w / 2;
+	cy = area.y + area.h / 2;
 	TAILQ_FOREACH(rc, &sc->regionq, entry) {
-		if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) &&
-		    (y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) {
-			break;
+		if ((cx >= rc->view.x) && (cx < (rc->view.x + rc->view.w)) &&
+		    (cy >= rc->view.y) && (cy < (rc->view.y + rc->view.h))) {
+			/* Centre of area is within region, we're done */
+			return(rc);
+		}
+		if ((area.x < (rc->view.x + rc->view.w)) &&
+		    (rc->view.x < (area.x + area.w)) &&
+		    (area.y < (rc->view.y + rc->view.h)) &&
+		    (rc->view.y < (area.y + area.h))) {
+			/* Region overlaps with area; find largest overlap */
+			int	w, h;
+
+			w = MIN(area.x + area.w, rc->view.x + rc->view.w) -
+			    MAX(area.x, rc->view.x);
+			h = MIN(area.y + area.h, rc->view.y + rc->view.h) -
+			    MAX(area.y, rc->view.y);
+			if ((long long)w * h > overlap) {
+				overlap = (long long)w * h;
+				best = rc;
+			}
+		} else if (!overlap) {
+			/* Lacking overlaps, find region with shortest
+			 * Manhattan distance from area */
+			int	d = 0;
+
+			if ((area.x + area.w) <= rc->view.x)
+				d += rc->view.x - (area.x + area.w - 1);
+			else if ((rc->view.x + rc->view.w) <= area.x)
+				d += area.x - (rc->view.x + rc->view.w - 1);
+			if ((area.y + area.h) <= rc->view.y)
+				d += rc->view.y - (area.y + area.h - 1);
+			else if ((rc->view.y + rc->view.h) <= area.y)
+				d += area.y - (rc->view.y + rc->view.h - 1);
+
+			if (distance == -1 || d < distance) {
+				distance = d;
+				best = rc;
+			}
 		}
 	}
-	return(rc);
+	return(best);
 }
 
 struct geom
 screen_area(struct screen_ctx *sc, int x, int y, int flags)
 {
 	struct region_ctx	*rc;
-	struct geom		 area = sc->work;
+	struct geom		 area, point = { x, y, 0, 0 };
 
-	TAILQ_FOREACH(rc, &sc->regionq, entry) {
-		if ((x >= rc->area.x) && (x < (rc->area.x + rc->area.w)) &&
-		    (y >= rc->area.y) && (y < (rc->area.y + rc->area.h))) {
-			area = rc->area;
-			break;
-		}
-	}
+	rc = region_find(sc, point);
+	area = rc->view;
 	if (flags & CWM_GAP)
 		area = screen_apply_gap(sc, area);
 	return(area);
@@ -159,7 +194,9 @@
 void
 screen_update_geometry(struct screen_ctx *sc)
 {
-	struct region_ctx	*rc;
+	struct region_ctx_q	 oldregionq;
+	struct region_ctx	*oldrc, *rc;
+	struct client_ctx	*cc;
 
 	sc->view.x = 0;
 	sc->view.y = 0;
@@ -167,9 +204,10 @@
 	sc->view.h = DisplayHeight(X_Dpy, sc->which);
 	sc->work = screen_apply_gap(sc, sc->view);
 
+	TAILQ_INIT(&oldregionq);
 	while ((rc = TAILQ_FIRST(&sc->regionq)) != NULL) {
 		TAILQ_REMOVE(&sc->regionq, rc, entry);
-		free(rc);
+		TAILQ_INSERT_TAIL(&oldregionq, rc, entry);
 	}
 
 	if (HasRandr) {
@@ -189,15 +227,10 @@
 
 			rc = xmalloc(sizeof(*rc));
 			rc->num = i;
-			rc->area.x = ci->x;
-			rc->area.y = ci->y;
-			rc->area.w = ci->width;
-			rc->area.h = ci->height;
 			rc->view.x = ci->x;
 			rc->view.y = ci->y;
 			rc->view.w = ci->width;
 			rc->view.h = ci->height;
-			rc->work = screen_apply_gap(sc, rc->view);
 			TAILQ_INSERT_TAIL(&sc->regionq, rc, entry);
 
 			XRRFreeCrtcInfo(ci);
@@ -210,8 +243,18 @@
 		rc->view.y = 0;
 		rc->view.w = DisplayWidth(X_Dpy, sc->which);
 		rc->view.h = DisplayHeight(X_Dpy, sc->which);
-		rc->work = screen_apply_gap(sc, rc->view);
 		TAILQ_INSERT_TAIL(&sc->regionq, rc, entry);
+	}
+
+	while ((oldrc = TAILQ_FIRST(&oldregionq)) != NULL) {
+		rc = region_find(sc, oldrc->view);
+		TAILQ_FOREACH(cc, &sc->clientq, entry) {
+			if (cc->rc != oldrc)
+				continue;
+			client_migrate_region(cc, rc);
+		}
+		TAILQ_REMOVE(&oldregionq, oldrc, entry);
+		free(oldrc);
 	}
 
 	xu_ewmh_net_desktop_geometry(sc);
Index: xevents.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/xevents.c,v
retrieving revision 1.120
diff -u -r1.120 xevents.c
--- xevents.c	10 Nov 2015 20:05:33 -0000	1.120
+++ xevents.c	18 Jul 2016 20:40:12 -0000
@@ -139,6 +139,8 @@
 		if (e->value_mask & CWStackMode)
 			wc.stack_mode = e->detail;
 
+		client_keep_visible(cc);
+
 		if (cc->geom.x == 0 && cc->geom.w >= sc->view.w)
 			cc->geom.x -= cc->bwidth;
 

Reply via email to