commit 10e2c6e834051815915ebb85ed27f88e883e0e1b
parent f26b75e9b93d1e00a7bd450507af5f9fe6f37a14
Author: mike <mike@p16>
Date: Thu, 27 Aug 2026 23:28:30 -0500
submit autopage patch
Diffstat:
2 files changed, 292 insertions(+), 0 deletions(-)
diff --git a/dwm.suckless.org/patches/autopage/dwm-autopage-6.8.diff b/dwm.suckless.org/patches/autopage/dwm-autopage-6.8.diff
@@ -0,0 +1,233 @@
+diff -urN dwm-6.8/config.def.h dwm-autopage/config.def.h
+--- dwm-6.8/config.def.h 2026-08-07 00:23:50.492927886 -0500
++++ dwm-autopage/config.def.h 2026-08-12 19:37:15.854223459 -0500
+@@ -39,10 +39,12 @@
+ static const int refreshrate = 120; /* refresh rate (per second) for client move/resize */
+
+ static const Layout layouts[] = {
+- /* symbol arrange function */
+- { "[]=", tile }, /* first entry is default */
+- { "><>", NULL }, /* no layout function means floating behavior */
+- { "[M]", monocle },
++ /* symbol arrange page capacity follow*/
++ { "[T]=", tile, 0, 0 }, /* unlimited clients per tag */
++ { "[A]=", tile, 3, 0 }, /* 3 clients allowed per tag */
++ { "[AF]=", tile, 3, 1 }, /* 3 clients allowed per tag and follow autopaged clients */
++ { "><>", NULL, 0, 0 }, /* no layout function means floating behavior */
++ { "[M]", monocle, 0, 0 }, /* unlimited clients per tag */
+ };
+
+ /* key definitions */
+@@ -76,8 +78,10 @@
+ { MODKEY, XK_Tab, view, {0} },
+ { MODKEY|ShiftMask, XK_c, killclient, {0} },
+ { MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
+- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
+- { MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
++ { MODKEY, XK_s, setlayout, {.v = &layouts[1]} },
++ { MODKEY, XK_a, setlayout, {.v = &layouts[2]} },
++ { MODKEY, XK_f, setlayout, {.v = &layouts[3]} },
++ { MODKEY, XK_m, setlayout, {.v = &layouts[4]} },
+ { MODKEY, XK_space, setlayout, {0} },
+ { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
+ { MODKEY, XK_0, view, {.ui = ~0 } },
+diff -urN dwm-6.8/dwm.c dwm-autopage/dwm.c
+--- dwm-6.8/dwm.c 2026-08-07 00:23:50.493927886 -0500
++++ dwm-autopage/dwm.c 2026-08-12 14:05:36.940616192 -0500
+@@ -91,6 +91,7 @@
+ int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
+ int bw, oldbw;
+ unsigned int tags;
++ int isautopaged;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ Client *next;
+ Client *snext;
+@@ -108,6 +109,8 @@
+ typedef struct {
+ const char *symbol;
+ void (*arrange)(Monitor *);
++ unsigned int pagecapacity;
++ int followautopage;
+ } Layout;
+
+ struct Monitor {
+@@ -184,6 +187,8 @@
+ static void motionnotify(XEvent *e);
+ static void movemouse(const Arg *arg);
+ static Client *nexttiled(Client *c);
++static Client *oldestautopagedclient(Monitor *m, unsigned int tag);
++static void autopagemove(Client *c, unsigned int tag);
+ static void pop(Client *c);
+ static void propertynotify(XEvent *e);
+ static void quit(const Arg *arg);
+@@ -208,6 +213,7 @@
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
+ static void tile(Monitor *m);
++static int nclientstag(Monitor *m, unsigned int tag);
+ static void togglebar(const Arg *arg);
+ static void togglefloating(const Arg *arg);
+ static void toggletag(const Arg *arg);
+@@ -298,6 +304,11 @@
+ {
+ c->isfloating = r->isfloating;
+ c->tags |= r->tags;
++
++ if (r->tags) {
++ c->isautopaged = 0;
++ }
++
+ for (m = mons; m && m->num != r->monitor; m = m->next);
+ if (m)
+ c->mon = m;
+@@ -1028,15 +1039,43 @@
+ }
+ }
+
+-void
+-manage(Window w, XWindowAttributes *wa)
++static unsigned int
++nexttag(unsigned int tag)
++{
++ if (tag >= (1U << (LENGTH(tags) - 1)))
++ return 0;
++ return tag << 1;
++}
++
++static int
++nclientstag(Monitor *m, unsigned int tag)
++{
++ int n = 0;
++ Client *c;
++ if (!tag)
++ return 0;
++ for (c = m->clients; c; c = c->next)
++ if (!c->isfloating && (c->tags & tag))
++ n++;
++ return n;
++}
++
++static void
++autopagemove(Client *c, unsigned int tag)
+ {
++ c->tags = tag;
++ c->isautopaged = 1;
++}
++
++void
++manage(Window w, XWindowAttributes *wa) {
+ Client *c, *t = NULL;
+ Window trans = None;
+ XWindowChanges wc;
+
+ c = ecalloc(1, sizeof(Client));
+ c->win = w;
++ c->isautopaged = 1;
+ /* geometry */
+ c->x = c->oldx = wa->x;
+ c->y = c->oldy = wa->y;
+@@ -1051,6 +1090,19 @@
+ } else {
+ c->mon = selmon;
+ applyrules(c);
++
++ if (c->isfloating)
++ c->isautopaged = 0;
++ }
++
++ unsigned int cap = c->mon->lt[c->mon->sellt]->pagecapacity;
++ if (cap && c->isautopaged && nclientstag(c->mon, c->tags) >= (int)cap) {
++ unsigned int nt = nexttag(c->tags);
++ if (nt) {
++ autopagemove(c, nt);
++ if (c->mon->lt[c->mon->sellt]->followautopage)
++ view(&(Arg){ .ui = nt });
++ }
+ }
+
+ if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww)
+@@ -1671,6 +1723,7 @@
+ {
+ if (selmon->sel && arg->ui & TAGMASK) {
+ selmon->sel->tags = arg->ui & TAGMASK;
++ selmon->sel->isautopaged = 0;
+ focus(NULL);
+ arrange(selmon);
+ }
+@@ -1712,6 +1765,48 @@
+ }
+ }
+
++static Client *
++oldestautopagedclient(Monitor *m, unsigned int tag)
++{
++ Client *c, *oldest = NULL;
++
++ for (c = m->clients; c; c = c->next)
++ if (!c->isfloating &&
++ c->isautopaged &&
++ (c->tags & tag))
++ oldest = c;
++
++ return oldest;
++}
++
++static void
++collapse(Monitor *m, unsigned int tag)
++{
++ unsigned int cap, nt;
++ Client *c;
++
++ cap = m->lt[m->sellt]->pagecapacity;
++
++ if (!cap)
++ return;
++
++ while (nclientstag(m, tag) < (int)cap) {
++ nt = nexttag(tag);
++
++ if (!nt)
++ break;
++
++ c = oldestautopagedclient(m, nt);
++
++ if (!c)
++ break;
++
++ autopagemove(c, tag);
++
++ tag = nt;
++ }
++}
++
+ void
+ togglebar(const Arg *arg)
+ {
+@@ -1745,6 +1840,7 @@
+ newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
+ if (newtags) {
+ selmon->sel->tags = newtags;
++ selmon->sel->isautopaged = 0;
+ focus(NULL);
+ arrange(selmon);
+ }
+@@ -1780,6 +1876,8 @@
+ {
+ Monitor *m = c->mon;
+ XWindowChanges wc;
++ unsigned int tag = c->tags;
++ unsigned int t;
+
+ detach(c);
+ detachstack(c);
+@@ -1796,6 +1894,10 @@
+ XUngrabServer(dpy);
+ }
+ free(c);
++ for (t = 1; t <= TAGMASK; t <<= 1) {
++ if (tag & t)
++ collapse(m, t);
++ }
+ focus(NULL);
+ updateclientlist();
+ arrange(m);
diff --git a/dwm.suckless.org/patches/autopage/index.md b/dwm.suckless.org/patches/autopage/index.md
@@ -0,0 +1,59 @@
+autopage
+========
+
+Description
+-----------
+The `autopage` patch for dwm adds configurable per-layout client page
+capacity with automatic "paging" to overflow clients to the next tag.
+Rule-assigned and manually moved clients are protected from autopaging while
+empty slots are filled by eligible autopaged clients from the neighboring tag.
+
+The behavior of `autopage` can be configured in config.def.h
+```
+
+static const Layout layouts[] = {
+ /* symbol arrange page capacity follow*/
+ { "[T]=", tile, 0, 0 }, /* unlimited clients per tag */
+ { "[A]=", tile, 3, 0 }, /* 3 clients allowed per tag */
+ { "[AF]=", tile, 4, 1 }, /* 4 clients allowed per tag and follow autopagedclients */
+ { "[F]", NULL, 0, 0 }, /* no layout function means floating behavior */
+ { "[M]", monocle, 0, 0 }, /* unlimited clients per tag */
+};
+
+```
+The example only shows customization of the tiled layout, but the autopager
+is not layout dependent. It can work on just about any layout except for
+"floating" because I wrote it so that floating windows are not autopage
+candidates.
+
+
+As clients are closed, autopaged clients are pulled from subsequent tags to fill
+empty slots left by the closed clients. Rule-assigned and manually moved
+clients are still protected and will not change position.
+
+Behavior
+--------
+
+capacity = 3
+Tag 1: C1 C2 C3
+Tag 2: C4 C5 C6
+Tag 3: C7 C8 C9
+Tag 4: C10 C11 C12
+
+(close client 4)
+
+Tag 1: C1 C2 C3
+Tag 2: C5 C6 C7
+Tag 3: C8 C9 C10
+Tag 4: C11 C12
+
+Download
+--------
+* [dwm-autopage-6.8.diff](dwm-autopage-6.8.diff)
+ (2026-08-21)
+
+Author
+------
+* [Mike L.](mike868711@gmail.com)
+
+