From 4f6bdd3ea1fcc83abd962e9a2dc7737519164a6b Mon Sep 17 00:00:00 2001
From: Rutherther <rutherther@proton.me>
Date: Fri, 19 Jul 2024 16:25:46 +0200
Subject: [PATCH] singletagset-pertag

---
 config.def.h | 12 +++++--
 dwl.c        | 90 ++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/config.def.h b/config.def.h
index 22d2171..7feb04d 100644
--- a/config.def.h
+++ b/config.def.h
@@ -43,12 +43,18 @@ static const Layout layouts[] = {
 */
 /* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */
 static const MonitorRule monrules[] = {
-	/* name       mfact  nmaster scale layout       rotate/reflect                x    y */
+	/* name   scale  rotate/reflect                x    y */
 	/* example of a HiDPI laptop monitor:
-	{ "eDP-1",    0.5f,  1,      2,    &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL,   -1,  -1 },
+	{ "eDP-1",    2, WL_OUTPUT_TRANSFORM_NORMAL,   -1,  -1 },
 	*/
 	/* defaults */
-	{ NULL,       0.55f, 1,      1,    &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL,   -1,  -1 },
+	{ NULL,       1, WL_OUTPUT_TRANSFORM_NORMAL,   -1,  -1 },
+};
+
+static const TagRule tagrules[] = {
+    /* tag     mfact      nmaster   layout */
+    /* defaults */
+    { 0,       0.55,      1,        &layouts[0] }
 };
 
 /* keyboard */
diff --git a/dwl.c b/dwl.c
index 40a6e48..465d9dd 100644
--- a/dwl.c
+++ b/dwl.c
@@ -104,6 +104,7 @@ typedef struct {
 	const Arg arg;
 } Button;
 
+typedef struct Pertag Pertag;
 typedef struct Monitor Monitor;
 typedef struct {
 	/* Must keep these three elements in this order */
@@ -209,6 +210,7 @@ struct Monitor {
 	int nmaster;
 	char ltsymbol[16];
 	int asleep;
+	unsigned int pertag[2]; /* the tag used for layout via pertag */
 };
 
 typedef struct {
@@ -226,6 +228,13 @@ typedef struct {
 	struct wl_listener destroy;
 } PointerConstraint;
 
+typedef struct {
+	unsigned int tag;
+	float mfact;
+	int nmaster;
+	const Layout *lt;
+} TagRule;
+
 typedef struct {
 	const char *id;
 	const char *title;
@@ -245,6 +254,7 @@ typedef struct {
 
 /* function declarations */
 static void applybounds(Client *c, struct wlr_box *bbox);
+static void applypertag(Monitor *m);
 static void applyrules(Client *c);
 static void arrange(Monitor *m);
 static void arrangelayer(Monitor *m, struct wl_list *list,
@@ -293,6 +303,7 @@ static void focusstack(const Arg *arg);
 static Client *focustop(Monitor *m);
 static void fullscreennotify(struct wl_listener *listener, void *data);
 static void gpureset(struct wl_listener *listener, void *data);
+static unsigned int getpertagtag(unsigned int curtagset);
 static size_t getunusedtag(void);
 static void handlesig(int signo);
 static void incnmaster(const Arg *arg);
@@ -415,6 +426,7 @@ static struct wlr_output_layout *output_layout;
 static struct wlr_box sgeom;
 static struct wl_list mons;
 static Monitor *selmon;
+static Pertag pertag;
 
 #ifdef XWAYLAND
 static void activatex11(struct wl_listener *listener, void *data);
@@ -435,6 +447,13 @@ static xcb_atom_t netatom[NetLast];
 /* attempt to encapsulate suck into one file */
 #include "client.h"
 
+struct Pertag {
+	int nmasters[TAGCOUNT + 1]; /* number of windows in master area */
+	float mfacts[TAGCOUNT + 1]; /* mfacts per tag */
+	unsigned int sellts[TAGCOUNT + 1]; /* selected layouts */
+	const Layout *ltidxs[TAGCOUNT + 1][2]; /* matrix of tags and layouts indexes  */
+};
+
 /* function implementations */
 void
 applybounds(Client *c, struct wlr_box *bbox)
@@ -1023,6 +1042,33 @@ createlocksurface(struct wl_listener *listener, void *data)
 		client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat));
 }
 
+unsigned int
+getpertagtag(unsigned int curtagset)
+{
+	size_t i;
+
+	if (curtagset == TAGMASK) {
+		return 0;
+	}
+
+	if ((curtagset & TAGMASK) == 0) {
+		return 0; // What to do in this case?
+	}
+
+	for (i = 0; !(curtagset & 1 << i); i++) ;
+	return i + 1;
+}
+
+void
+applypertag(Monitor *m)
+{
+	m->nmaster = pertag.nmasters[m->pertag[m->seltags]];
+	m->mfact = pertag.mfacts[m->pertag[m->seltags]];
+	m->sellt = pertag.sellts[m->pertag[m->seltags]];
+	m->lt[m->sellt] = pertag.ltidxs[m->pertag[m->seltags]][m->sellt];
+	m->lt[m->sellt^1] = pertag.ltidxs[m->pertag[m->seltags]][m->sellt^1];
+}
+
 void
 createmon(struct wl_listener *listener, void *data)
 {
@@ -1046,14 +1092,12 @@ createmon(struct wl_listener *listener, void *data)
 	wlr_output_state_init(&state);
 	/* Initialize monitor state using configured rules */
 	m->tagset[0] = m->tagset[1] = (1<<getunusedtag()) & TAGMASK;
+	m->pertag[0] = m->pertag[1] = getpertagtag(m->tagset[0]);
+	applypertag(m);
 	for (r = monrules; r < END(monrules); r++) {
 		if (!r->name || strstr(wlr_output->name, r->name)) {
 			m->m.x = r->x;
 			m->m.y = r->y;
-			m->mfact = r->mfact;
-			m->nmaster = r->nmaster;
-			m->lt[0] = r->lt;
-			m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]];
 			strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
 			wlr_output_state_set_scale(&state, r->scale);
 			wlr_output_state_set_transform(&state, r->rr);
@@ -1597,7 +1641,7 @@ incnmaster(const Arg *arg)
 {
 	if (!arg || !selmon)
 		return;
-	selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+	selmon->nmaster = pertag.nmasters[selmon->pertag[selmon->seltags]] = MAX(selmon->nmaster + arg->i, 0);
 	arrange(selmon);
 }
 
@@ -2433,9 +2477,9 @@ setlayout(const Arg *arg)
 	if (!selmon)
 		return;
 	if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
-		selmon->sellt ^= 1;
+		selmon->sellt = pertag.sellts[selmon->pertag[selmon->seltags]] ^= 1;
 	if (arg && arg->v)
-		selmon->lt[selmon->sellt] = (Layout *)arg->v;
+		selmon->lt[selmon->sellt] = pertag.ltidxs[selmon->pertag[selmon->seltags]][selmon->sellt] = (Layout *)arg->v;
 	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol));
 	arrange(selmon);
 	printstatus();
@@ -2452,7 +2496,7 @@ setmfact(const Arg *arg)
 	f = arg->f < 1.0f ? arg->f + selmon->mfact : arg->f - 1.0f;
 	if (f < 0.1 || f > 0.9)
 		return;
-	selmon->mfact = f;
+	selmon->mfact = pertag.mfacts[selmon->pertag[selmon->seltags]] = f;
 	arrange(selmon);
 }
 
@@ -2504,6 +2548,10 @@ setsel(struct wl_listener *listener, void *data)
 void
 setup(void)
 {
+	const TagRule *r;
+	struct xkb_context *context;
+	struct xkb_keymap *keymap;
+
 	int i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE};
 	struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig};
 	sigemptyset(&sa.sa_mask);
@@ -2596,6 +2644,19 @@ setup(void)
 	LISTEN_STATIC(&output_layout->events.change, updatemons);
 	wlr_xdg_output_manager_v1_create(dpy, output_layout);
 
+	for (i = 0; i <= TAGCOUNT; i++) {
+		for (r = tagrules; r < END(tagrules); r++) {
+			if (!r->tag || r->tag == i) {
+				pertag.mfacts[i] = r->mfact;
+				pertag.nmasters[i] = r->nmaster;
+				pertag.sellts[i] = 0;
+				pertag.ltidxs[i][0] = r->lt;
+				pertag.ltidxs[i][1] = r->lt;
+				break;
+			}
+		}
+	}
+
 	/* Configure a listener to be notified when new outputs are available on the
 	 * backend. */
 	wl_list_init(&mons);
@@ -2857,6 +2918,12 @@ toggleview(const Arg *arg)
 		if (m !=selmon && newtagset & m->tagset[m->seltags])
 			return;
 
+	// set new pertag tag only if the tag we were at was removed, or if all tags are shown.
+	if (!(newtagset & 1 << (selmon->pertag[selmon->seltags] - 1)) || newtagset == TAGMASK) {
+		selmon->pertag[selmon->seltags] = getpertagtag(newtagset);
+	}
+
+	applypertag(selmon);
 	selmon->tagset[selmon->seltags] = newtagset;
 	attachclients(selmon);
 	focusclient(focustop(selmon), 1);
@@ -3072,6 +3139,8 @@ view(const Arg *arg)
 			}
 			m->seltags ^= 1;
 			m->tagset[m->seltags] = origm->tagset[origm->seltags];
+			m->pertag[m->seltags] = origm->pertag[origm->seltags];
+			applypertag(m);
 			attachclients(m);
 			/* Beware: this changes selmon */
 			focusclient(focustop(m), 1);
@@ -3081,11 +3150,14 @@ view(const Arg *arg)
 	}
 
 	origm->seltags ^= 1; /* toggle sel tagset */
-	if (arg->ui & TAGMASK)
+	if (arg->ui & TAGMASK) {
 		origm->tagset[origm->seltags] = arg->ui & TAGMASK;
+		origm->pertag[origm->seltags] = getpertagtag(arg->ui & TAGMASK);
+	}
 
 	/* Change selmon back to orig mon */
 	selmon = origm;
+	applypertag(origm);
 	attachclients(origm);
 	focusclient(focustop(origm), 1);
 	arrange(origm);
-- 
2.45.2

