Hi,

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 keeps clients overlapping with the screen's
view area, handling these cases better:

- Moving client without border off screen (left or up)
- Moving client with a mouse, starting with the pointer outside
  the window
- Resizing partially off-screen client using a keyboard
- Client moving or resizing itself (ConfigureRequest event)

The following cases are still not handled:

- Not all screen's view area being viewable, i.e., covered with
  regions (e.g., XRandR with monitors of different sizes)
- Screen's view area changing size (XRandR monitors rotating or
  disconnecting)
- Non-rectangular clients (XShape)

So here are my thoughts, and your feedback is welcome.

We certainly want to deal with regions and not screens, here and
in some other places where screen_area() is called.  We could use
a function that returns the best region for a client, along the
lines of:

- find the region containing the centre of the window;
- failing that, find the region that has the biggest overlapping
  area with the window;
- if none overlap with it (client fully off screen), find the
  region closest to the window.

We may then cache the region inside client_ctx, if it worth
dealing with cache invalidation.

Regarding region changes, I'd like to see them handled the
following way:

- When a region geometry changes (screen rotating), all clients
  therein should retain the same relative position within the new
  region that they occupied in the old one
- When a region disappears, all its clients should move to
  another region, preferrably to the same one, likewise retaining
  their relative position
- A relative position is something like (in pseudocode):
    { (client.x - region.x) / (region.w - client.w),
      (client.y - region.y) / (region.h - client.h) }
  (whatever's in the corner or in the middle stays there, etc.)
- It's possible to keep hidden clients in place after an XRandR
  event until they're shown, but probably not worth it

To achieve this, screen_update_geometry() may be changed like so:

- initialize new regions;
- for every old region, find the best matching new region, along
  the lines of the algorithm above;
- for every client:
  - find the best matching old region;
  - note relative position within it;
  - move the client to the appropriate relative position in the
    new region found in the step above;
  - if the client is {h,v}maximized or fullscreen, resize;
  - ensure client is still visible (with the calculation above,
    clients that are partially off-screen may become fully so
    after moving);
- free old regions.

The simplest way to handle shaped clients I can think of is to
require them to be fully visible, i.e., that each of their four
corners (that they don't have) is inside a region.

Have I forgot any other cases that may move clients off screen?
My paranoia tells me to call client_keep_visible() from
client_cycle()...  Or maybe just call client_setactive() from
there.

What do you think?

Vadik.

-- 
An artist should be fit for the best society and keep out of it.
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	12 Dec 2015 17:39:11 -0000
@@ -394,6 +394,7 @@
 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_msg(struct client_ctx *, Atom, Time);
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	12 Dec 2015 17:39:12 -0000
@@ -442,6 +442,29 @@
 	client_config(cc);
 }
 
+int
+client_keep_visible(struct client_ctx *cc)
+{
+	struct screen_ctx	*sc = cc->sc;
+	int			 changed = 0;
+
+	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 >= sc->view.w) {
+		cc->geom.x = sc->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 >= sc->view.h) {
+		cc->geom.y = sc->view.h - 1;
+		changed = 1;
+	}
+	return(changed);
+}
+
 void
 client_move(struct client_ctx *cc)
 {
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	12 Dec 2015 17:39:12 -0000
@@ -104,15 +104,8 @@
 	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;
+	client_keep_visible(cc);
 
 	area = screen_area(sc,
 	    cc->geom.x + cc->geom.w / 2,
@@ -149,6 +142,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. */
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	12 Dec 2015 17:39:12 -0000
@@ -140,6 +140,12 @@
 
 			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,
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	12 Dec 2015 17:39: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