cwm(1) without warping the pointer

Walter Alejandro Iglesias <[email protected]> Mon, 29 Jun 2026 19:12:11 +0200
Newsgroups gmane.os.openbsd.misc
Message-ID <[email protected]>
Years ago, discussing in private with Okan, he told me he didn't like
the warping pointer behavior.  Most modern window managers don't do this
with Mouse or Sloppy focus.  At that time, I had been using fvwm2 for
decades, which also uses this trick, so it didn't bother me.  But now, I
starting to see that adopting the no warping modern behavior it's not a
bad idea; among other inconveniences, it avoids many of the bugs I've
recently posted on this list.

So, the diff below is in testing state.  Makes cwm behave like most
modern window manager, no warping pointer.

Tests and feedback welcome.


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	29 Jun 2026 16:46:58 -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	29 Jun 2026 16:46:58 -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	29 Jun 2026 16:46:59 -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: 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	29 Jun 2026 16:46:59 -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