dwm
dynamic window manager
git clone https://9o.is/git/dwm.git
commit 3d7a1c10462943750c3f956f702f496ce95a3fa0 parent fe2016cebab48bf43e62b51a7136f65b045a477e Author: Jul <jul@9o.is> Date: Sun, 1 Feb 2026 05:59:26 -0500 shift windows between adjacent tags Diffstat:
| M | config.def.h | | | 9 | +++++++++ |
| M | config.h | | | 9 | +++++++++ |
| A | shift-tools.c | | | 114 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 132 insertions(+), 0 deletions(-)
diff --git a/config.def.h b/config.def.h @@ -52,6 +52,11 @@ static const Layout layouts[] = { { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, +#define SHIFTKEYS(KEY,MOVE) \ + { MODKEY, KEY, shiftview, { .i = MOVE } }, \ + { MODKEY|Mod1Mask, KEY, shifttag, { .i = MOVE } }, \ + { MODKEY|ShiftMask, KEY, shiftboth, { .i = MOVE } }, \ + { MODKEY|Mod1Mask|ControlMask, KEY, shiftswaptags, { .i = MOVE } }, /* helper for spawning shell commands in the pre dwm-5.0 fashion */ #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } @@ -61,6 +66,8 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; static const char *termcmd[] = { "st", NULL }; +#include "shift-tools.c" + static const Key keys[] = { /* modifier key function argument */ { MODKEY, XK_p, spawn, {.v = dmenucmd } }, @@ -95,6 +102,8 @@ static const Key keys[] = { TAGKEYS( XK_7, 6) TAGKEYS( XK_8, 7) TAGKEYS( XK_9, 8) + SHIFTKEYS( XK_Left, -1) + SHIFTKEYS( XK_Right, +1) { MODKEY|ShiftMask, XK_q, quit, {0} }, { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, }; diff --git a/config.h b/config.h @@ -46,6 +46,11 @@ static const Layout layouts[] = { { MODKEY|Mod1Mask, KEY, toggleview, {.ui = 1 << TAG} }, \ { MODKEY|ControlMask, KEY, tag, {.ui = 1 << TAG} }, \ { MODKEY|Mod1Mask|ControlMask, KEY, toggletag, {.ui = 1 << TAG} }, +#define SHIFTKEYS(KEY,MOVE) \ + { MODKEY, KEY, shiftview, { .i = MOVE } }, \ + { MODKEY|Mod1Mask, KEY, shifttag, { .i = MOVE } }, \ + { MODKEY|ShiftMask, KEY, shiftboth, { .i = MOVE } }, \ + { MODKEY|Mod1Mask|ControlMask, KEY, shiftswaptags, { .i = MOVE } }, /* helper for spawning shell commands in the pre dwm-5.0 fashion */ #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } @@ -55,6 +60,8 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() static const char *dmenucmd[] = { "dmenu_run", "-b", "-m", dmenumon, "-fn", dmenufont, "-nb", col_black, "-nf", col_white, "-sb", col_white, "-sf", col_black, NULL }; static const char *termcmd[] = { "st", NULL }; +#include "shift-tools.c" + static const Key keys[] = { /* modifier key function argument */ { MODKEY, XK_p, spawn, {.v = dmenucmd } }, @@ -89,6 +96,8 @@ static const Key keys[] = { TAGKEYS( XK_7, 6) TAGKEYS( XK_8, 7) TAGKEYS( XK_9, 8) + SHIFTKEYS( XK_Left, -1) + SHIFTKEYS( XK_Right, +1) { MODKEY|Mod1Mask, XK_q, quit, {1} }, }; diff --git a/shift-tools.c b/shift-tools.c @@ -0,0 +1,114 @@ +void +shift(unsigned int *tag, int i) +{ + if (i > 0) /* left circular shift */ + *tag = ((*tag << i) | (*tag >> (LENGTH(tags) - i))); + else /* right circular shift */ + *tag = (*tag >> (- i) | *tag << (LENGTH(tags) + i)); +} + +/* send a window to the next/prev tag */ +void +shifttag(const Arg *arg) +{ + Arg shifted = { .ui = selmon->tagset[selmon->seltags] }; + + if (!selmon->clients) + return; + + shift(&shifted.ui, arg->i); + tag(&shifted); +} + +/* send a window to the next/prev tag that has a client, else it moves it to + * the next/prev one. */ +void +shifttagclients(const Arg *arg) +{ + Arg shifted = { .ui = selmon->tagset[selmon->seltags] }; + Client *c; + unsigned int tagmask = 0; + + for (c = selmon->clients; c; c = c->next) + tagmask = tagmask | c->tags; + + do + shift(&shifted.ui, arg->i); + while (tagmask && !(shifted.ui & tagmask)); + + tag(&shifted); +} + +/* view the next/prev tag */ +void +shiftview(const Arg *arg) +{ + Arg shifted = { .ui = selmon->tagset[selmon->seltags] }; + + shift(&shifted.ui, arg->i); + view(&shifted); +} + +/* view the next/prev tag that has a client, else view the next/prev tag */ +void +shiftviewclients(const Arg *arg) +{ + Arg shifted = { .ui = selmon->tagset[selmon->seltags] }; + Client *c; + unsigned int tagmask = 0; + + for (c = selmon->clients; c; c = c->next) + tagmask = tagmask | c->tags; + + do + shift(&shifted.ui, arg->i); + while (tagmask && !(shifted.ui & tagmask)); + + view(&shifted); +} + +/* move the active window to the next/prev tag and view it's new tag */ +void +shiftboth(const Arg *arg) +{ + Arg shifted = { .ui = selmon->tagset[selmon->seltags] }; + + shift(&shifted.ui, arg->i); + tag(&shifted); + view(&shifted); +} + +/* swaptags: https://dwm.suckless.org/patches/swaptags, used below */ +void +swaptags(const Arg *arg) +{ + Client *c; + unsigned int newtag = arg->ui & TAGMASK; + unsigned int curtag = selmon->tagset[selmon->seltags]; + + if (newtag == curtag || !curtag || (curtag & (curtag-1))) + return; + + for (c = selmon->clients; c != NULL; c = c->next) { + if ((c->tags & newtag) || (c->tags & curtag)) + c->tags ^= curtag ^ newtag; + if (!c->tags) + c->tags = newtag; + } + + //uncomment to 'view' the new swaped tag + //selmon->tagset[selmon->seltags] = newtag; + + focus(NULL); + arrange(selmon); +} + +/* swaps "tags" (all the clients on it) with the next/prev tag */ +void +shiftswaptags(const Arg *arg) +{ + Arg shifted = { .ui = selmon->tagset[selmon->seltags] }; + + shift(&shifted.ui, arg->i); + swaptags(&shifted); +}