Re: cwm(1) without warping the pointer

Walter Alejandro Iglesias <[email protected]> Tue, 30 Jun 2026 11:39:08 +0200
Newsgroups gmane.os.openbsd.misc
Message-ID <[email protected]>
A final tweak.  When you restart cwm the focus still automatically goes
to the window under the pointer.  In the update below I modified
screen_scan() to first check _NET_ACTIVE_WINDOW.


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	30 Jun 2026 09:37:13 -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	30 Jun 2026 09:37:13 -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,19 @@ 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_set_active(prevcc);
+	client_flush_enter_events();
 }
 
 void
@@ -336,7 +350,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 +391,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 +424,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 +457,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 +530,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 +961,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 +1029,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 +1056,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	30 Jun 2026 09:37:13 -0000
@@ -409,7 +409,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 +420,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 +449,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 +545,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	30 Jun 2026 09:37:13 -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	30 Jun 2026 09:37:13 -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