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