Index | Thread | Search

From:
akira.sato@keemail.me
Subject:
cwm tilling attempt
To:
Tech <tech@openbsd.org>
Cc:
Okan <okan@openbsd.org>
Date:
Sat, 4 Apr 2026 22:09:29 +0200

Download raw body.

Thread
  • akira.sato@keemail.me:

    cwm tilling attempt

Hello,

This is my attempt to bring a closer tilling experience to cwm, since it has some hybrid of it now, the diff attached adds persistent tiling mode with automatic reflow, my main "issue" with it is that the existing window-htile and window-vtile actions arrange windows once when invoked but do not maintain any layout state.  Opening a new window or closing one leaves the remaining windows where they are.

This change makes tiling persistent in two ways. 

 First, a new tilemode directive in cwmrc(5) accepts vtile, htile, or none.  When set, every group starts up with that tile mode active and new windows are automatically retiled into their group the moment they map.  The default is none, so existing configurations are unaffected.

example .cwmrc

vtile 50 
htile 50
tilemode vtile


Second, the window-htile and window-vtile keybind actions now record the tile direction on the group. A new window-tile-none action turns auto-tiling back off.  Closing a tiled window causes the survivors to reflow immediately and fill the available space.  If the last window in a group is closed the preceding window expands to fill the full screen.



--- calmwm.h	2026-04-04 14:03:42.992832109 +0000
+++ calmwm.h	2026-04-04 14:04:20.437783559 +0000
@@ -183,6 +183,7 @@
 	struct screen_ctx	*sc;
 	char			*name;
 	int			 num;
+	int			 tile_last;
 };
 TAILQ_HEAD(group_q, group_ctx);
 
@@ -296,6 +297,7 @@
 	int			 snapdist;
 	int			 htile;
 	int			 vtile;
+	int			 tilemode;
 	struct gap		 gap;
 	char			*color[CWM_COLOR_NITEMS];
 	char			*font;
@@ -437,6 +439,7 @@
 void			 client_transient(struct client_ctx *);
 void			 client_urgency(struct client_ctx *);
 void 			 client_vtile(struct client_ctx *);
+void			 client_retile_group(struct group_ctx *);
 void			 client_wm_hints(struct client_ctx *);
 
 void			 group_assign(struct group_ctx *, struct client_ctx *);
@@ -508,6 +511,7 @@
 void			 kbfunc_client_toggle_vmaximize(void *, struct cargs *);
 void 			 kbfunc_client_htile(void *, struct cargs *);
 void 			 kbfunc_client_vtile(void *, struct cargs *);
+void			 kbfunc_client_tile_none(void *, struct cargs *);
 void			 kbfunc_client_cycle(void *, struct cargs *);
 void			 kbfunc_client_toggle_group(void *, struct cargs *);
 void			 kbfunc_client_movetogroup(void *, struct cargs *);
--- client.c	2026-04-04 14:03:42.992832109 +0000
+++ client.c	2026-04-04 14:04:20.438783559 +0000
@@ -935,19 +935,23 @@
 			continue;
 		n++;
 	}
-	if (n == 0)
-		return;
-
-	if (cc->flags & CLIENT_VMAXIMIZED ||
-	    cc->geom.h + (cc->bwidth * 2) >= area.h)
-		return;
-
-	cc->flags &= ~CLIENT_HMAXIMIZED;
+	cc->flags &= ~(CLIENT_HMAXIMIZED | CLIENT_VMAXIMIZED);
 	cc->geom.x = area.x;
 	cc->geom.y = area.y;
 	cc->geom.w = area.w - (cc->bwidth * 2);
+	if (n == 0) {
+		cc->geom.h = area.h - (cc->bwidth * 2);
+		cc->gc->tile_last = 2;
+		Conf.tilemode = 2;
+		client_resize(cc, 1);
+		client_ptr_warp(cc);
+		return;
+	}
 	if (Conf.htile > 0)
 		cc->geom.h = ((area.h - (cc->bwidth * 2)) * Conf.htile) / 100;
+	if (cc->gc != NULL)
+		cc->gc->tile_last = 2;
+	Conf.tilemode = 2;
 	client_resize(cc, 1);
 	client_ptr_warp(cc);
 
@@ -1004,19 +1008,23 @@
 			continue;
 		n++;
 	}
-	if (n == 0)
-		return;
-
-	if (cc->flags & CLIENT_HMAXIMIZED ||
-	    cc->geom.w + (cc->bwidth * 2) >= area.w)
-		return;
-
-	cc->flags &= ~CLIENT_VMAXIMIZED;
+	cc->flags &= ~(CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED);
 	cc->geom.x = area.x;
 	cc->geom.y = area.y;
+	cc->geom.h = area.h - (cc->bwidth * 2);
+	if (n == 0) {
+		cc->geom.w = area.w - (cc->bwidth * 2);
+		cc->gc->tile_last = 1;
+		Conf.tilemode = 1;
+		client_resize(cc, 1);
+		client_ptr_warp(cc);
+		return;
+	}
 	if (Conf.vtile > 0)
 		cc->geom.w = ((area.w - (cc->bwidth * 2)) * Conf.vtile) / 100;
-	cc->geom.h = area.h - (cc->bwidth * 2);
+	if (cc->gc != NULL)
+		cc->gc->tile_last = 1;
+	Conf.tilemode = 1;
 	client_resize(cc, 1);
 	client_ptr_warp(cc);
 
@@ -1047,3 +1055,36 @@
 		client_resize(ci, 1);
 	}
 }
+
+void
+client_retile_group(struct group_ctx *gc)
+{
+	struct screen_ctx	*sc = gc->sc;
+	struct client_ctx	*cc;
+
+	if (gc->tile_last == 0)
+		return;
+
+	TAILQ_FOREACH(cc, &sc->clientq, entry) {
+		if (cc->gc != gc)
+			continue;
+		if (cc->flags & (CLIENT_HIDDEN | CLIENT_IGNORE))
+			continue;
+		break;
+	}
+	if (cc == NULL)
+		return;
+
+	if (gc->tile_last == 1)
+		client_vtile(cc);
+	else
+		client_htile(cc);
+}
+
+void
+client_tile_none(struct client_ctx *cc)
+{
+	Conf.tilemode = 0;
+	if (cc->gc != NULL)
+		cc->gc->tile_last = 0;
+}
--- group.c	2026-04-04 14:03:42.992832109 +0000
+++ group.c	2026-04-04 14:04:10.951783577 +0000
@@ -131,6 +131,7 @@
 	gc->sc = sc;
 	gc->name = xstrdup(name);
 	gc->num = num;
+	gc->tile_last = Conf.tilemode;
 	TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
 
 	if (num == 1)
--- xevents.c	2026-04-04 14:03:42.992832109 +0000
+++ xevents.c	2026-04-04 14:04:10.955783577 +0000
@@ -94,6 +94,9 @@
 
 	if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
 		client_ptr_warp(cc);
+
+	if (cc != NULL && cc->gc != NULL && cc->gc->tile_last != 0)
+		client_retile_group(cc->gc);
 }
 
 static void
@@ -101,6 +104,7 @@
 {
 	XUnmapEvent		*e = &ee->xunmap;
 	struct client_ctx	*cc;
+	struct group_ctx	*gc;
 
 	LOG_DEBUG3("window: 0x%lx", e->window);
 
@@ -108,8 +112,12 @@
 		if (e->send_event) {
 			xu_set_wm_state(cc->win, WithdrawnState);
 		} else {
-			if (!(cc->flags & CLIENT_HIDDEN))
+			if (!(cc->flags & CLIENT_HIDDEN)) {
+				gc = cc->gc;
 				client_remove(cc);
+				if (gc != NULL)
+					client_retile_group(gc);
+			}
 		}
 	}
 }
@@ -119,11 +127,16 @@
 {
 	XDestroyWindowEvent	*e = &ee->xdestroywindow;
 	struct client_ctx	*cc;
+	struct group_ctx	*gc;
 
 	LOG_DEBUG3("window: 0x%lx", e->window);
 
-	if ((cc = client_find(e->window)) != NULL)
+	if ((cc = client_find(e->window)) != NULL) {
+		gc = cc->gc;
 		client_remove(cc);
+		if (gc != NULL)
+			client_retile_group(gc);
+	}
 }
 
 static void
--- conf.c	2026-04-04 14:03:42.992832109 +0000
+++ conf.c	2026-04-04 14:04:10.960783577 +0000
@@ -86,6 +86,7 @@
 	{ FUNC_CC(window-delete, client_close, 0) },
 	{ FUNC_CC(window-htile, client_htile, 0) },
 	{ FUNC_CC(window-vtile, client_vtile, 0) },
+	{ FUNC_CC(window-tile-none, client_tile_none, 0) },
 	{ FUNC_CC(window-stick, client_toggle_sticky, 0) },
 	{ FUNC_CC(window-fullscreen, client_toggle_fullscreen, 0) },
 	{ FUNC_CC(window-maximize, client_toggle_maximize, 0) },
@@ -291,6 +292,7 @@
 	c->mamount = 1;
 	c->htile = 50;
 	c->vtile = 50;
+	c->tilemode = 0;
 	c->snapdist = 0;
 	c->ngroups = 0;
 	c->nameqlen = 5;
--- parse.y	2026-04-04 14:03:42.992832109 +0000
+++ parse.y	2026-04-04 14:04:10.965783577 +0000
@@ -71,7 +71,7 @@
 %token	BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE
 %token	FONTNAME STICKY GAP
 %token	AUTOGROUP COMMAND IGNORE WM
-%token	YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE
+%token	YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE TILEMODE
 %token	COLOR SNAPDIST
 %token	ACTIVEBORDER INACTIVEBORDER URGENCYBORDER
 %token	GROUPBORDER UNGROUPBORDER
@@ -147,6 +147,20 @@
 			}
 			conf->vtile = $2;
 		}
+		| TILEMODE STRING {
+			if (strcmp($2, "vtile") == 0)
+				conf->tilemode = 1;
+			else if (strcmp($2, "htile") == 0)
+				conf->tilemode = 2;
+			else if (strcmp($2, "none") == 0)
+				conf->tilemode = 0;
+			else {
+				yyerror("invalid tilemode");
+				free($2);
+				YYERROR;
+			}
+			free($2);
+		}
 		| MOVEAMOUNT NUMBER {
 			if ($2 < 0 || $2 > INT_MAX) {
 				yyerror("invalid movemount");
@@ -351,6 +365,7 @@
 		{ "selfont", 		FONTSELCOLOR},
 		{ "snapdist",		SNAPDIST},
 		{ "sticky",		STICKY},
+		{ "tilemode",		TILEMODE},
 		{ "unbind-key",		UNBINDKEY},
 		{ "unbind-mouse",	UNBINDMOUSE},
 		{ "ungroupborder",	UNGROUPBORDER},
--- kbfunc.c	2026-04-04 14:03:42.992832109 +0000
+++ kbfunc.c	2026-04-04 14:04:10.969783577 +0000
@@ -406,6 +406,12 @@
 }
 
 void
+kbfunc_client_tile_none(void *ctx, struct cargs *cargs)
+{
+	client_tile_none(ctx);
+}
+
+void
 kbfunc_client_cycle(void *ctx, struct cargs *cargs)
 {
 	struct screen_ctx	*sc = ctx;
--- cwmrc.5	2026-04-04 14:03:42.992832109 +0000
+++ cwmrc.5	2026-04-04 14:05:01.350783483 +0000
@@ -230,6 +230,28 @@
 If set to 0, the vertical size of the master window will
 remain unchanged.
 The default is 50.
+.It Ic tilemode Ar vtile No | Ar htile No | Ar none
+Set the automatic tile mode for all groups.
+When set to
+.Ar vtile ,
+each new window is placed on the left of the screen and existing
+windows share the remaining space, as with
+.Ic window-vtile .
+When set to
+.Ar htile ,
+each new window is placed at the top of the screen and existing
+windows share the remaining space, as with
+.Ic window-htile .
+Closing a window causes the remaining windows in the group to
+reflow and fill the screen.
+The tile mode may also be changed at runtime using the
+.Ic window-vtile ,
+.Ic window-htile ,
+and
+.Ic window-tile-none
+bind functions.
+The default is
+.Ar none .
 .It Ic wm Ar name path
 Every
 .Ar name
@@ -327,12 +349,16 @@
 .Ar htile
 (default half) of the vertical screen space.
 Other windows in its group share remaining screen space.
+Enables automatic tiling for the current group.
 .It window-vtile
 Current window is placed on the left of the screen, maximized vertically
 and resized to
 .Ar vtile
 (default half) of the horizontal screen space.
 Other windows in its group share remaining screen space.
+Enables automatic tiling for the current group.
+.It window-tile-none
+Disable automatic tiling for the current group.
 .It window-move
 Move current window.
 .It window-resize