Hi,

I received no comments to my previous e-mail on the subject from
13 Dec, so now that I have a machine with two monitors I just
implemented it.  Now, excuse me for ranting: I understand that
the people responsible for cwm are busy, but I'd like some
feedback on the patches I've sent to the list so far.  Even two
characters 'N' and 'o' would suffice.  I've maintained my own
window manager fork before, and I don't have a problem to do the
same with cwm, but I'd like to know whether to keep spamming you
with unsolicited code.  Meanwhile I put my patches on the web:

        http://www.vygo.net/vadik/cwm/

Rant over, now onto the patch.

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.

-- 
An artist is never ahead of his time, but most people are far
behind theirs.
                -- Varese
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	18 Jul 2016 20:40:12 -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) <= 0) {
+		cc->geom.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) <= 0) {
+		cc->geom.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.202
diff -u -r1.202 conf.c
--- conf.c	17 Nov 2015 15:19:19 -0000	1.202
+++ 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