|
|
|
From b21bc2db75d2dce254a6740fcca276eb7d815553 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Bakkeby <bakkeby@gmail.com>
|
|
|
|
Date: Mon, 12 Jun 2023 11:48:51 +0200
|
|
|
|
Subject: [PATCH] Master stacker patch
|
|
|
|
|
|
|
|
---
|
|
|
|
config.def.h | 16 ++-
|
|
|
|
dwm.c | 4 +
|
|
|
|
stacker.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
stacker.h | 35 +++++++
|
|
|
|
4 files changed, 322 insertions(+), 2 deletions(-)
|
|
|
|
create mode 100644 stacker.c
|
|
|
|
create mode 100644 stacker.h
|
|
|
|
|
|
|
|
diff --git a/config.def.h b/config.def.h
|
|
|
|
index 061ad66..78b2edc 100644
|
|
|
|
--- a/config.def.h
|
|
|
|
+++ b/config.def.h
|
|
|
|
@@ -52,6 +52,17 @@ static const Layout layouts[] = {
|
|
|
|
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
|
|
|
|
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
|
|
|
|
|
|
|
|
+#define STACKKEYS(MOD,ACTION) \
|
|
|
|
+ { MOD, XK_j, ACTION, {.i = INC(+1) } }, \
|
|
|
|
+ { MOD, XK_k, ACTION, {.i = INC(-1) } }, \
|
|
|
|
+ { MOD, XK_semicolon, ACTION, {.i = PREVSEL } }, \
|
|
|
|
+ { MOD, XK_y, ACTION, {.i = MASTER(1) } }, \
|
|
|
|
+ { MOD, XK_u, ACTION, {.i = MASTER(2) } }, \
|
|
|
|
+ { MOD, XK_n, ACTION, {.i = STACK(1) } }, \
|
|
|
|
+ { MOD, XK_o, ACTION, {.i = STACK(2) } }, \
|
|
|
|
+ { MOD, XK_g, ACTION, {.i = STACK(3) } }, \
|
|
|
|
+ { MOD, XK_slash, ACTION, {.i = LASTTILED } },
|
|
|
|
+
|
|
|
|
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
|
|
|
|
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
|
|
|
|
|
|
|
|
@@ -64,8 +75,6 @@ static const Key keys[] = {
|
|
|
|
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
|
|
|
|
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
|
|
|
|
{ MODKEY, XK_b, togglebar, {0} },
|
|
|
|
- { MODKEY, XK_j, focusstack, {.i = +1 } },
|
|
|
|
- { MODKEY, XK_k, focusstack, {.i = -1 } },
|
|
|
|
{ MODKEY, XK_i, incnmaster, {.i = +1 } },
|
|
|
|
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
|
|
|
|
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
|
|
|
|
@@ -84,6 +93,9 @@ static const Key keys[] = {
|
|
|
|
{ MODKEY, XK_period, focusmon, {.i = +1 } },
|
|
|
|
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
|
|
|
|
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
|
|
|
|
+ STACKKEYS(MODKEY, stackfocus) // focus on the nth client in the stack
|
|
|
|
+ STACKKEYS(MODKEY|ControlMask, stackpush) // move the currently focused client to the nth place in the stack
|
|
|
|
+ STACKKEYS(MODKEY|ShiftMask, stackswap) // swap the currently focused client with the nth client in the stack
|
|
|
|
TAGKEYS( XK_1, 0)
|
|
|
|
TAGKEYS( XK_2, 1)
|
|
|
|
TAGKEYS( XK_3, 2)
|
|
|
|
diff --git a/dwm.c b/dwm.c
|
|
|
|
index e5efb6a..90e6ead 100644
|
|
|
|
--- a/dwm.c
|
|
|
|
+++ b/dwm.c
|
|
|
|
@@ -270,7 +270,9 @@ static Monitor *mons, *selmon;
|
|
|
|
static Window root, wmcheckwin;
|
|
|
|
|
|
|
|
/* configuration, allows nested code to access above variables */
|
|
|
|
+#include "stacker.h"
|
|
|
|
#include "config.h"
|
|
|
|
+#include "stacker.c"
|
|
|
|
|
|
|
|
/* compile-time check if all tags fit into an unsigned int bit array. */
|
|
|
|
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
|
|
|
|
@@ -665,6 +667,7 @@ detach(Client *c)
|
|
|
|
|
|
|
|
for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
|
|
|
|
*tc = c->next;
|
|
|
|
+ c->next = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
@@ -674,6 +677,7 @@ detachstack(Client *c)
|
|
|
|
|
|
|
|
for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
|
|
|
|
*tc = c->snext;
|
|
|
|
+ c->snext = NULL;
|
|
|
|
|
|
|
|
if (c == c->mon->sel) {
|
|
|
|
for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
|
|
|
|
diff --git a/stacker.c b/stacker.c
|
|
|
|
new file mode 100644
|
|
|
|
index 0000000..9019345
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/stacker.c
|
|
|
|
@@ -0,0 +1,269 @@
|
|
|
|
+void
|
|
|
|
+attachabove(Client *c, Client *target)
|
|
|
|
+{
|
|
|
|
+ Client **tp;
|
|
|
|
+ Client *last;
|
|
|
|
+
|
|
|
|
+ if (target) {
|
|
|
|
+ last = lastclient(c);
|
|
|
|
+ last->next = target;
|
|
|
|
+ tp = clientptr(target);
|
|
|
|
+ *tp = c;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ attach(c);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client **
|
|
|
|
+clientptr(Client *c)
|
|
|
|
+{
|
|
|
|
+ Client **tc;
|
|
|
|
+ for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
|
|
|
|
+ return tc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+ismasterclient(Client *client)
|
|
|
|
+{
|
|
|
|
+ Monitor *m = client->mon;
|
|
|
|
+ Client *c;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0, c = nexttiled(m->clients); c && i < m->nmaster; c = nexttiled(c->next), ++i)
|
|
|
|
+ if (c == client)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client *
|
|
|
|
+lastclient(Client *c)
|
|
|
|
+{
|
|
|
|
+ Client *last;
|
|
|
|
+ for (last = c; last && last->next; last = last->next);
|
|
|
|
+
|
|
|
|
+ return last;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client *
|
|
|
|
+lasttiled(Client *c)
|
|
|
|
+{
|
|
|
|
+ Client *last = NULL;
|
|
|
|
+ for (; c; c = c->next)
|
|
|
|
+ if (ISVISIBLE(c) && ISTILED(c))
|
|
|
|
+ last = c;
|
|
|
|
+
|
|
|
|
+ return last;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client *
|
|
|
|
+nthmaster(Client *c, int n, int reduce)
|
|
|
|
+{
|
|
|
|
+ if (!c)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ return nthtiled(c, MIN(n, c->mon->nmaster), 1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client *
|
|
|
|
+nthstack(Client *c, int n, int reduce)
|
|
|
|
+{
|
|
|
|
+ if (!c)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ return nthtiled(c, n + c->mon->nmaster, 1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client *
|
|
|
|
+nthtiled(Client *c, int n, int reduce)
|
|
|
|
+{
|
|
|
|
+ Client *prev = NULL;
|
|
|
|
+ int i;
|
|
|
|
+ for (i = 1, c = nexttiled(c); c && (i++ < n); prev = c, c = nexttiled(c->next));
|
|
|
|
+
|
|
|
|
+ if (!c && reduce) {
|
|
|
|
+ c = prev;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return c;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client *
|
|
|
|
+inctiled(Client *c, int n)
|
|
|
|
+{
|
|
|
|
+ Client *f;
|
|
|
|
+
|
|
|
|
+ if (!c)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ if (n > 0) {
|
|
|
|
+ f = nexttiled(c->next);
|
|
|
|
+ if (!f) {
|
|
|
|
+ f = nexttiled(c->mon->clients);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ f = prevtiled(c);
|
|
|
|
+ if (!f) {
|
|
|
|
+ f = lasttiled(c->mon->clients);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return f;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client *
|
|
|
|
+prevtiled(Client *c)
|
|
|
|
+{
|
|
|
|
+ Client *p, *r;
|
|
|
|
+ for (p = nexttiled(c->mon->clients), r = NULL; p && p != c && (r = p); p = nexttiled(p->next));
|
|
|
|
+ return r;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client *
|
|
|
|
+prevsel(void)
|
|
|
|
+{
|
|
|
|
+ Monitor *m = selmon;
|
|
|
|
+ Client *c;
|
|
|
|
+
|
|
|
|
+ if (!m->clients)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ for (c = m->stack; c && (!ISVISIBLE(c) || c == m->sel); c = c->snext);
|
|
|
|
+ return c;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+swap(Client *a, Client *b)
|
|
|
|
+{
|
|
|
|
+ Client **ap = clientptr(a);
|
|
|
|
+ Client **bp = clientptr(b);
|
|
|
|
+ Client *an = a->next;
|
|
|
|
+ Client *bn = b->next;
|
|
|
|
+
|
|
|
|
+ if (bn == a) {
|
|
|
|
+ b->next = an;
|
|
|
|
+ a->next = b;
|
|
|
|
+ *bp = a;
|
|
|
|
+ } else if (an == b) {
|
|
|
|
+ b->next = a;
|
|
|
|
+ a->next = bn;
|
|
|
|
+ *ap = b;
|
|
|
|
+ } else {
|
|
|
|
+ b->next = an;
|
|
|
|
+ a->next = bn;
|
|
|
|
+ *ap = b;
|
|
|
|
+ *bp = a;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+stackfocus(const Arg *arg)
|
|
|
|
+{
|
|
|
|
+ Monitor *m = selmon;
|
|
|
|
+ Client *c = NULL;
|
|
|
|
+
|
|
|
|
+ if (ISINC(arg)) {
|
|
|
|
+ focusstack(&((Arg) { .i = GETINC(arg) }));
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!m->clients)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ c = stackposclient(arg);
|
|
|
|
+
|
|
|
|
+ if (!c)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (c == m->sel) {
|
|
|
|
+ if (arg->i != PREVSEL) {
|
|
|
|
+ stackfocus(&((Arg) { .i = PREVSEL }));
|
|
|
|
+ }
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ focus(c);
|
|
|
|
+ arrange(m);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+stackpush(const Arg *arg)
|
|
|
|
+{
|
|
|
|
+ Monitor *m = selmon;
|
|
|
|
+ Client *c = NULL, *sel = m->sel;
|
|
|
|
+
|
|
|
|
+ if (!m->clients)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (ISINC(arg)) {
|
|
|
|
+ stackswap(arg);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ c = stackposclient(arg);
|
|
|
|
+
|
|
|
|
+ if (!c)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (c == sel)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ detach(sel);
|
|
|
|
+ attachabove(sel, c);
|
|
|
|
+
|
|
|
|
+ arrange(m);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+stackswap(const Arg *arg)
|
|
|
|
+{
|
|
|
|
+ Monitor *m = selmon;
|
|
|
|
+ Client *c = NULL, *sel = m->sel;
|
|
|
|
+
|
|
|
|
+ if (!m->clients)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ c = stackposclient(arg);
|
|
|
|
+
|
|
|
|
+ if (!c)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (c == sel)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ swap(sel, c);
|
|
|
|
+
|
|
|
|
+ if (!ISINC(arg) && ismasterclient(c)) {
|
|
|
|
+ focus(c);
|
|
|
|
+ sel = c;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ arrange(m);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Client *
|
|
|
|
+stackposclient(const Arg *arg)
|
|
|
|
+{
|
|
|
|
+ Monitor *m = selmon;
|
|
|
|
+
|
|
|
|
+ if (!m->clients)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ if (ISINC(arg))
|
|
|
|
+ return inctiled(m->sel, GETINC(arg));
|
|
|
|
+
|
|
|
|
+ if (ISMASTER(arg))
|
|
|
|
+ return nthmaster(m->clients, GETMASTER(arg), 1);
|
|
|
|
+
|
|
|
|
+ if (ISSTACK(arg))
|
|
|
|
+ return nthstack(m->clients, GETSTACK(arg), 1);
|
|
|
|
+
|
|
|
|
+ if (ISLAST(arg))
|
|
|
|
+ return lasttiled(m->clients);
|
|
|
|
+
|
|
|
|
+ if (ISPREVSEL(arg))
|
|
|
|
+ return prevsel();
|
|
|
|
+
|
|
|
|
+ return nthtiled(m->clients, arg->i, 1);
|
|
|
|
+}
|
|
|
|
diff --git a/stacker.h b/stacker.h
|
|
|
|
new file mode 100644
|
|
|
|
index 0000000..0099db0
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/stacker.h
|
|
|
|
@@ -0,0 +1,35 @@
|
|
|
|
+#define INC(X) ((X) + 2000)
|
|
|
|
+#define MASTER(X) ((X) + 4000)
|
|
|
|
+#define STACK(X) ((X) + 5000)
|
|
|
|
+#define PREVSEL 3000
|
|
|
|
+#define LASTTILED -1
|
|
|
|
+#define ISINC(X) ((X)->i > 1000 && (X)->i < 3000)
|
|
|
|
+#define ISPREVSEL(X) ((X)->i == 3000)
|
|
|
|
+#define ISLAST(X) ((X)->i < 0)
|
|
|
|
+#define GETINC(X) ((X)->i - 2000)
|
|
|
|
+#define GETMASTER(X) ((X)->i - 4000)
|
|
|
|
+#define GETSTACK(X) ((X)->i - 5000)
|
|
|
|
+#define ISMASTER(X) ((X)->i >= 4000 && (X)->i < 5000)
|
|
|
|
+#define ISSTACK(X) ((X)->i >= 5000 && (X)->i < 6000)
|
|
|
|
+#define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M))
|
|
|
|
+#define TRUNC(X,A,B) (MAX((A), MIN((X), (B))))
|
|
|
|
+#define ISTILED(C) (C && C->win && !(C->isfloating))
|
|
|
|
+
|
|
|
|
+static void attachabove(Client *c, Client *target);
|
|
|
|
+static Client **clientptr(Client *c);
|
|
|
|
+static int ismasterclient(Client *c);
|
|
|
|
+static Client *lastclient(Client *c);
|
|
|
|
+static Client *lasttiled(Client *c);
|
|
|
|
+static Client *nexttiled(Client *c);
|
|
|
|
+static Client *nthmaster(Client *c, int n, int reduce);
|
|
|
|
+static Client *nthstack(Client *c, int n, int reduce);
|
|
|
|
+static Client *nthtiled(Client *c, int n, int reduce);
|
|
|
|
+static Client *inctiled(Client *c, int n);
|
|
|
|
+static Client *prevtiled(Client *c);
|
|
|
|
+static Client *prevsel(void);
|
|
|
|
+static void swap(Client *a, Client *b);
|
|
|
|
+
|
|
|
|
+static void stackfocus(const Arg *arg);
|
|
|
|
+static void stackpush(const Arg *arg);
|
|
|
|
+static void stackswap(const Arg *arg);
|
|
|
|
+static Client *stackposclient(const Arg *arg);
|
|
|
|
--
|
|
|
|
2.19.1
|
|
|
|
|