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