Re: cwm(1) without warping the pointer

Walter Alejandro Iglesias <[email protected]> Wed, 1 Jul 2026 12:11:15 +0200
Newsgroups gmane.os.openbsd.misc
Message-ID <[email protected]>
In this proposal, I included a behaviour I'd proposed with a previous
diff: when a window is closed pass the focus to the previous one in the
stack.  Now more than before it's necessary to raise that window.  If we
don't, the focused window can end totally obscured behind a bigger one.


Last update:


Index: calmwm.h
===================================================================
RCS file: /cvs/xenocara/app/cwm/calmwm.h,v
diff -u -p -u -p -r1.380 calmwm.h
--- calmwm.h	20 Aug 2025 23:44:06 -0000	1.380
+++ calmwm.h	1 Jul 2026 08:53:35 -0000
@@ -405,6 +405,7 @@ void			 client_config(struct client_ctx 
 struct client_ctx	*client_current(struct screen_ctx *);
 void			 client_draw_border(struct client_ctx *);
 struct client_ctx	*client_find(Window);
+void			 client_flush_enter_events(void);
 void			 client_get_sizehints(struct client_ctx *);
 void			 client_hide(struct client_ctx *);
 void 			 client_htile(struct client_ctx *);
Index: client.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/client.c,v
diff -u -p -u -p -r1.267 client.c
--- client.c	22 Mar 2023 08:27:36 -0000	1.267
+++ client.c	1 Jul 2026 08:53:35 -0000
@@ -210,6 +210,7 @@ client_remove(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = cc->sc;
 	struct winname		*wn;
+	struct client_ctx	*prevcc;
 
 	TAILQ_REMOVE(&sc->clientq, cc, entry);
 
@@ -230,6 +231,20 @@ client_remove(struct client_ctx *cc)
 	free(cc->res_class);
 	free(cc->res_name);
 	free(cc);
+
+	if (TAILQ_EMPTY(&sc->clientq))
+		return;
+
+	prevcc = TAILQ_FIRST(&sc->clientq);
+
+	/* Avoid windows with skip client flag or from other groups */
+	if ((prevcc->flags & (CLIENT_SKIP_CYCLE)) &&
+	    ! (prevcc->flags & CWM_CYCLE_INGROUP))
+		return;
+
+	client_raise(prevcc);
+	client_set_active(prevcc);
+	client_flush_enter_events();
 }
 
 void
@@ -336,7 +351,7 @@ client_toggle_fullscreen(struct client_c
 resize:
 	client_resize(cc, 0);
 	xu_ewmh_set_net_wm_state(cc);
-	client_ptr_inbound(cc, 1);
+	client_flush_enter_events();
 }
 
 void
@@ -377,7 +392,7 @@ client_toggle_maximize(struct client_ctx
 resize:
 	client_resize(cc, 0);
 	xu_ewmh_set_net_wm_state(cc);
-	client_ptr_inbound(cc, 1);
+	client_flush_enter_events();
 }
 
 void
@@ -410,7 +425,7 @@ client_toggle_vmaximize(struct client_ct
 resize:
 	client_resize(cc, 0);
 	xu_ewmh_set_net_wm_state(cc);
-	client_ptr_inbound(cc, 1);
+	client_flush_enter_events();
 }
 
 void
@@ -443,7 +458,7 @@ client_toggle_hmaximize(struct client_ct
 resize:
 	client_resize(cc, 0);
 	xu_ewmh_set_net_wm_state(cc);
-	client_ptr_inbound(cc, 1);
+	client_flush_enter_events();
 }
 
 void
@@ -516,8 +531,6 @@ client_ptr_inbound(struct client_ctx *cc
 		cc->ptr.y = 0;
 	else if (cc->ptr.y > cc->geom.h - 1)
 		cc->ptr.y = cc->geom.h - 1;
-
-	client_ptr_warp(cc);
 }
 
 void
@@ -949,7 +962,6 @@ client_htile(struct client_ctx *cc)
 	if (Conf.htile > 0)
 		cc->geom.h = ((area.h - (cc->bwidth * 2)) * Conf.htile) / 100;
 	client_resize(cc, 1);
-	client_ptr_warp(cc);
 
 	mh = cc->geom.h + (cc->bwidth * 2);
 	x = area.x;
@@ -1018,7 +1030,6 @@ client_vtile(struct client_ctx *cc)
 		cc->geom.w = ((area.w - (cc->bwidth * 2)) * Conf.vtile) / 100;
 	cc->geom.h = area.h - (cc->bwidth * 2);
 	client_resize(cc, 1);
-	client_ptr_warp(cc);
 
 	mw = cc->geom.w + (cc->bwidth * 2);
 	y = area.y;
@@ -1046,4 +1057,14 @@ client_vtile(struct client_ctx *cc)
 		i++;
 		client_resize(ci, 1);
 	}
+}
+
+void
+client_flush_enter_events(void)
+{
+	XEvent ev;
+
+	XSync(X_Dpy, False);
+	while (XCheckMaskEvent(X_Dpy, EnterWindowMask, &ev))
+		LOG_DEBUG3("Discarding stale EnterNotify event");
 }
Index: kbfunc.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/kbfunc.c,v
diff -u -p -u -p -r1.176 kbfunc.c
--- kbfunc.c	20 Aug 2025 23:44:06 -0000	1.176
+++ kbfunc.c	1 Jul 2026 08:53:36 -0000
@@ -154,6 +154,11 @@ kbfunc_client_move_mb(void *ctx, struct 
 	struct screen_ctx	*sc = cc->sc;
 	struct geom		 area;
 	int			 move = 1;
+	int			 x, y;
+
+	xu_ptr_get(cc->win, &x, &y);
+	if (!client_inbound(cc, x, y))
+		return;
 
 	client_raise(cc);
 
@@ -242,6 +247,11 @@ kbfunc_client_resize_mb(void *ctx, struc
 	Time			 ltime = 0;
 	struct screen_ctx	*sc = cc->sc;
 	int			 resize = 1;
+	int			 x, y;
+
+	xu_ptr_get(cc->win, &x, &y);
+	if (!client_inbound(cc, x, y))
+		return;
 
 	if (cc->flags & CLIENT_FREEZE)
 		return;
@@ -329,7 +339,7 @@ kbfunc_client_snap(void *ctx, struct car
 		}
 	}
 	client_move(cc);
-	client_ptr_inbound(cc, 1);
+	client_flush_enter_events();
 }
 
 void
@@ -409,7 +419,7 @@ void
 kbfunc_client_cycle(void *ctx, struct cargs *cargs)
 {
 	struct screen_ctx	*sc = ctx;
-	struct client_ctx	*newcc, *oldcc, *prevcc;
+	struct client_ctx	*newcc, *oldcc;
 	int			 again = 1, flags = cargs->flag;
 
 	/* For X apps that ignore/steal events. */
@@ -420,7 +430,6 @@ kbfunc_client_cycle(void *ctx, struct ca
 	if (TAILQ_EMPTY(&sc->clientq))
 		return;
 
-	prevcc = TAILQ_FIRST(&sc->clientq);
 	oldcc = client_current(sc);
 	if (oldcc == NULL)
 		oldcc = (flags & CWM_CYCLE_REVERSE) ?
@@ -450,24 +459,17 @@ kbfunc_client_cycle(void *ctx, struct ca
 		}
 	}
 
-	/* Reset when cycling mod is released. XXX I hate this hack */
+	/* Reset when cycling mod is released */
 	sc->cycling = 1;
-	client_ptr_save(oldcc);
-	client_raise(prevcc);
-	client_raise(newcc);
-	if (!client_inbound(newcc, newcc->ptr.x, newcc->ptr.y)) {
-		newcc->ptr.x = newcc->geom.w / 2;
-		newcc->ptr.y = newcc->geom.h / 2;
-	}
 
-	/* When no client is active, warp pointer to last active. */
-	if (oldcc->flags & (CLIENT_ACTIVE))
-		client_ptr_warp(newcc);
-	else if (oldcc->flags & (CLIENT_SKIP_CYCLE))
-		client_ptr_warp(newcc);
-	else {
+	/* When no client is active, activate last active. */
+	if (oldcc->flags & (CLIENT_ACTIVE) ||
+	    oldcc->flags & (CLIENT_SKIP_CYCLE)) {
+		client_raise(newcc);
+		client_set_active(newcc);
+	} else {
 		client_raise(oldcc);
-		client_ptr_warp(oldcc);
+		client_set_active(oldcc);
 	}
 }
 
@@ -553,7 +555,7 @@ kbfunc_menu_client(void *ctx, struct car
 		client_show(cc);
 		if ((old_cc = client_current(sc)) != NULL)
 			client_ptr_save(old_cc);
-		client_ptr_warp(cc);
+		client_set_active(cc);
 	}
 
 	menuq_clear(&menuq);
Index: screen.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/screen.c,v
diff -u -p -u -p -r1.98 screen.c
--- screen.c	27 Jan 2022 18:45:10 -0000	1.98
+++ screen.c	1 Jul 2026 08:53:36 -0000
@@ -87,22 +87,52 @@ screen_init(int which)
 static void
 screen_scan(struct screen_ctx *sc)
 {
-	struct client_ctx	 *cc, *active = NULL;
-	Window			*wins, w0, w1, rwin, cwin;
-	unsigned int		 nwins, i, mask;
-	int			 rx, ry, wx, wy;
+	struct client_ctx	*active = NULL, *cc;
+	Atom			 actual_type;
+	Atom			 net_active_win;
+	int			 actual_format, rx, ry, wx, wy;
+	unsigned int		 i, mask, nwins;
+	unsigned long		 bytes_after, nitems;
+	unsigned char		*prop_return = NULL;
+	Window			*wins, cwin, ewmh_active_win = None;
+	Window			 rwin, w0, w1;
 
+	/* Try to get the window active before the restart via EWMH */
+	net_active_win = XInternAtom(X_Dpy, "_NET_ACTIVE_WINDOW", False);
+
+	if (XGetWindowProperty(X_Dpy, sc->rootwin, net_active_win, 0, 1,
+	    False, XA_WINDOW, &actual_type, &actual_format, &nitems,
+	    &bytes_after, &prop_return) == Success && prop_return != NULL) {
+		if (nitems > 0)
+			ewmh_active_win = *(Window *)prop_return;
+		XFree(prop_return);
+	}
+
+	/* Query the current pointer position as a fallback */
 	XQueryPointer(X_Dpy, sc->rootwin, &rwin, &cwin,
 	    &rx, &ry, &wx, &wy, &mask);
 
+	/* Scan the window tree and initialize clients */
 	if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
 		for (i = 0; i < nwins; i++) {
-			if ((cc = client_init(wins[i], sc)) != NULL)
-				if (cc->win == cwin)
-					active = cc;
+			if ((cc = client_init(wins[i], sc)) != NULL) {
+				/*
+				 * If EWMH told us which window was active,
+				 * match against that.  Otherwise, fall back
+				 * to the window under the pointer.
+				 */
+				if (ewmh_active_win != None) {
+					if (cc->win == ewmh_active_win)
+						active = cc;
+				} else {
+					if (cc->win == cwin)
+						active = cc;
+				}
+			}
 		}
 		XFree(wins);
 	}
+
 	if (active)
 		client_set_active(active);
 }
Index: xevents.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/xevents.c,v
diff -u -p -u -p -r1.151 xevents.c
--- xevents.c	5 Jan 2026 14:46:59 -0000	1.151
+++ xevents.c	1 Jul 2026 08:53:36 -0000
@@ -93,7 +93,7 @@ xev_handle_maprequest(XEvent *ee)
 		cc = client_init(e->window, NULL);
 
 	if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
-		client_ptr_warp(cc);
+		client_set_active(cc);
 }
 
 static void
@@ -330,8 +330,8 @@ xev_handle_keypress(XEvent *ee)
 	kb->cargs->xev = CWM_XEV_KEY;
 	switch (kb->context) {
 	case CWM_CONTEXT_CC:
-		if (((cc = client_find(e->subwindow)) == NULL) &&
-		    ((cc = client_current(sc)) == NULL))
+		if (((cc = client_current(sc)) == NULL) &&
+		    ((cc = client_find(e->subwindow)) == NULL))
 			return;
 		(*kb->callback)(cc, kb->cargs);
 		break;
@@ -404,7 +404,6 @@ xev_handle_clientmessage(XEvent *ee)
 			if ((old_cc = client_current(NULL)) != NULL)
 				client_ptr_save(old_cc);
 			client_show(cc);
-			client_ptr_warp(cc);
 		}
 	} else if (e->message_type == ewmh[_NET_WM_DESKTOP]) {
 		if ((cc = client_find(e->window)) != NULL) {


-- 
Walter