mirror of
https://codeberg.org/dwl/dwl-patches.git
synced 2026-03-24 01:41:32 +00:00
Add: shifttag
Shift to next/previous tag, with skipping occupied/unoccupied variants. This patch is an alternative to `shiftview` patch with more controls: - `shifttag`: shift to next/previous tags - `shifttag_occupied`: skipping unoccupied tags - `shifttag_unoccupied`: skipping occupied tags `shifttag-bar` is a variant to be applied on top of the bar patch.
This commit is contained in:
parent
ee75e70f79
commit
039be1a6cf
22
patches/shifttag/README.md
Normal file
22
patches/shifttag/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
### Description
|
||||
|
||||
Shift to next/previous tag, with skipping occupied/unoccupied variants.
|
||||
|
||||
This patch is an alternative to
|
||||
[shiftview](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/shiftview) with more controls:
|
||||
|
||||
| function | behavior |
|
||||
| :--- | :---|
|
||||
| shifttag | shift to next/previous tags |
|
||||
| shifttag_occupied | skipping unoccupied tags |
|
||||
| shifttag_unoccupied | skipping occupied tags |
|
||||
|
||||
`shifttag-bar` is a variant to be applied on top of the
|
||||
[bar](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/bar/) patch.
|
||||
|
||||
### Download
|
||||
- [shifttag-v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/shifttag/shifttag.patch)
|
||||
- [shifttag-bar-v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/shifttag/shifttag-bar.patch)
|
||||
|
||||
### Authors
|
||||
- [unixchad](https://codeberg.org/unixchad/)
|
||||
146
patches/shifttag/shifttag-bar.patch
Normal file
146
patches/shifttag/shifttag-bar.patch
Normal file
@ -0,0 +1,146 @@
|
||||
From 6d3a0506f45243cf11f0f7ba06048f453576567c Mon Sep 17 00:00:00 2001
|
||||
From: nate zhou <gnuunixchad@outlook.com>
|
||||
Date: Mon, 23 Mar 2026 01:16:06 +0800
|
||||
Subject: [PATCH] shifttag with filtering occupied/unoccupied tags support for
|
||||
bar patch
|
||||
|
||||
---
|
||||
config.def.h | 8 +++++
|
||||
shifttag.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 107 insertions(+)
|
||||
create mode 100644 shifttag.c
|
||||
|
||||
diff --git a/config.def.h b/config.def.h
|
||||
index 7da50d2..9fbcf2a 100644
|
||||
--- a/config.def.h
|
||||
+++ b/config.def.h
|
||||
@@ -124,6 +124,8 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA
|
||||
static const char *termcmd[] = { "foot", NULL };
|
||||
static const char *menucmd[] = { "wmenu-run", NULL };
|
||||
|
||||
+#include "shifttag.c"
|
||||
+
|
||||
static const Key keys[] = {
|
||||
/* Note that Shift changes certain key codes: 2 -> at, etc. */
|
||||
/* modifier key function argument */
|
||||
@@ -138,6 +140,12 @@ static const Key keys[] = {
|
||||
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
|
||||
{ MODKEY, XKB_KEY_Return, zoom, {0} },
|
||||
{ MODKEY, XKB_KEY_Tab, view, {0} },
|
||||
+ { MODKEY, XKB_KEY_apostrophe, shifttag_occupied, { .i = 1 } },
|
||||
+ { MODKEY, XKB_KEY_semicolon, shifttag_occupied, { .i = -1 } },
|
||||
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_quotedbl, shifttag, { .i = 1 } },
|
||||
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_colon, shifttag, { .i = -1 } },
|
||||
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_apostrophe, shifttag_unoccupied, { .i = 1 } },
|
||||
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_semicolon, shifttag_unoccupied, { .i = -1 } },
|
||||
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} },
|
||||
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
|
||||
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
|
||||
diff --git a/shifttag.c b/shifttag.c
|
||||
new file mode 100644
|
||||
index 0000000..2615f4c
|
||||
--- /dev/null
|
||||
+++ b/shifttag.c
|
||||
@@ -0,0 +1,99 @@
|
||||
+// "arg->i" stores the number of tags to shift right (positive value)
|
||||
+// or left (negative value)
|
||||
+
|
||||
+static uint32_t
|
||||
+get_occupied_tags(Monitor *m)
|
||||
+{
|
||||
+ uint32_t occupied = 0;
|
||||
+ Client *c;
|
||||
+ wl_list_for_each(c, &clients, link) {
|
||||
+ if (c->mon == m)
|
||||
+ occupied |= c->tags;
|
||||
+ }
|
||||
+ return occupied & TAGMASK;
|
||||
+}
|
||||
+
|
||||
+static uint32_t
|
||||
+find_next_tag(uint32_t current_tag, int direction, bool skip_unoccupied, bool skip_occupied)
|
||||
+{
|
||||
+ uint32_t occupied = get_occupied_tags(selmon);
|
||||
+ uint32_t start = current_tag;
|
||||
+ uint32_t test_tag = current_tag;
|
||||
+ uint32_t new_tag = current_tag;
|
||||
+ int count = 0;
|
||||
+
|
||||
+ if (!skip_unoccupied && !skip_occupied) {
|
||||
+ if (direction > 0) {
|
||||
+ new_tag = (current_tag << 1) | (current_tag >> (LENGTH(tags) - 1));
|
||||
+ } else {
|
||||
+ new_tag = (current_tag >> 1) | (current_tag << (LENGTH(tags) - 1));
|
||||
+ }
|
||||
+ return new_tag & TAGMASK;
|
||||
+ }
|
||||
+
|
||||
+ do {
|
||||
+ if (direction > 0) {
|
||||
+ if (test_tag << 1 && (test_tag << 1) <= TAGMASK)
|
||||
+ test_tag = test_tag << 1;
|
||||
+ else
|
||||
+ test_tag = 1;
|
||||
+ } else {
|
||||
+ if (test_tag >> 1)
|
||||
+ test_tag = test_tag >> 1;
|
||||
+ else
|
||||
+ test_tag = 1 << (LENGTH(tags) - 1);
|
||||
+ }
|
||||
+
|
||||
+ int is_occupied = (occupied & test_tag) != 0;
|
||||
+ int should_select = (skip_unoccupied && is_occupied) ||
|
||||
+ (skip_occupied && !is_occupied);
|
||||
+
|
||||
+ if (should_select) {
|
||||
+ new_tag = test_tag;
|
||||
+ break;
|
||||
+ }
|
||||
+ count++;
|
||||
+ } while (test_tag != start && count < LENGTH(tags));
|
||||
+
|
||||
+ return new_tag;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+shifttag_with_filter(const Arg *arg, bool skip_unoccupied, bool skip_occupied)
|
||||
+{
|
||||
+ Arg a;
|
||||
+ if (!selmon)
|
||||
+ return;
|
||||
+
|
||||
+ uint32_t occupied = get_occupied_tags(selmon);
|
||||
+ if (skip_unoccupied && !skip_occupied && occupied == 0)
|
||||
+ return;
|
||||
+ if (skip_occupied && !skip_unoccupied && (occupied == TAGMASK))
|
||||
+ return;
|
||||
+
|
||||
+ uint32_t curseltags = selmon->tagset[selmon->seltags];
|
||||
+ uint32_t nextseltags = find_next_tag(curseltags, arg->i, skip_unoccupied, skip_occupied);
|
||||
+
|
||||
+ if (nextseltags != curseltags) {
|
||||
+ a.i = nextseltags;
|
||||
+ view(&a);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+shifttag(const Arg *arg)
|
||||
+{
|
||||
+ shifttag_with_filter(arg, false, false);
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+shifttag_occupied(const Arg *arg)
|
||||
+{
|
||||
+ shifttag_with_filter(arg, true, false);
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+shifttag_unoccupied(const Arg *arg)
|
||||
+{
|
||||
+ shifttag_with_filter(arg, false, true);
|
||||
+}
|
||||
--
|
||||
2.53.0
|
||||
|
||||
145
patches/shifttag/shifttag.patch
Normal file
145
patches/shifttag/shifttag.patch
Normal file
@ -0,0 +1,145 @@
|
||||
From 49b0da41f1f28a60cf216f5bffecc0ce1ea1ec9c Mon Sep 17 00:00:00 2001
|
||||
From: nate zhou <gnuunixchad@outlook.com>
|
||||
Date: Mon, 23 Mar 2026 00:50:54 +0800
|
||||
Subject: [PATCH] shifttag with filtering occupied/unoccupied tags support
|
||||
|
||||
---
|
||||
config.def.h | 8 +++++
|
||||
shifttag.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 107 insertions(+)
|
||||
create mode 100644 shifttag.c
|
||||
|
||||
diff --git a/config.def.h b/config.def.h
|
||||
index 8a6eda0..2377fa5 100644
|
||||
--- a/config.def.h
|
||||
+++ b/config.def.h
|
||||
@@ -118,6 +118,8 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA
|
||||
static const char *termcmd[] = { "foot", NULL };
|
||||
static const char *menucmd[] = { "wmenu-run", NULL };
|
||||
|
||||
+#include "shifttag.c"
|
||||
+
|
||||
static const Key keys[] = {
|
||||
/* Note that Shift changes certain key codes: 2 -> at, etc. */
|
||||
/* modifier key function argument */
|
||||
@@ -131,6 +133,12 @@ static const Key keys[] = {
|
||||
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
|
||||
{ MODKEY, XKB_KEY_Return, zoom, {0} },
|
||||
{ MODKEY, XKB_KEY_Tab, view, {0} },
|
||||
+ { MODKEY, XKB_KEY_apostrophe, shifttag_occupied, { .i = 1 } },
|
||||
+ { MODKEY, XKB_KEY_semicolon, shifttag_occupied, { .i = -1 } },
|
||||
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_quotedbl, shifttag, { .i = 1 } },
|
||||
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_colon, shifttag, { .i = -1 } },
|
||||
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_apostrophe, shifttag_unoccupied, { .i = 1 } },
|
||||
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_semicolon, shifttag_unoccupied, { .i = -1 } },
|
||||
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} },
|
||||
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
|
||||
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
|
||||
diff --git a/shifttag.c b/shifttag.c
|
||||
new file mode 100644
|
||||
index 0000000..5aeee00
|
||||
--- /dev/null
|
||||
+++ b/shifttag.c
|
||||
@@ -0,0 +1,99 @@
|
||||
+// "arg->i" stores the number of tags to shift right (positive value)
|
||||
+// or left (negative value)
|
||||
+
|
||||
+static uint32_t
|
||||
+get_occupied_tags(Monitor *m)
|
||||
+{
|
||||
+ uint32_t occupied = 0;
|
||||
+ Client *c;
|
||||
+ wl_list_for_each(c, &clients, link) {
|
||||
+ if (c->mon == m)
|
||||
+ occupied |= c->tags;
|
||||
+ }
|
||||
+ return occupied & TAGMASK;
|
||||
+}
|
||||
+
|
||||
+static uint32_t
|
||||
+find_next_tag(uint32_t current_tag, int direction, bool skip_unoccupied, bool skip_occupied)
|
||||
+{
|
||||
+ uint32_t occupied = get_occupied_tags(selmon);
|
||||
+ uint32_t start = current_tag;
|
||||
+ uint32_t test_tag = current_tag;
|
||||
+ uint32_t new_tag = current_tag;
|
||||
+ int count = 0;
|
||||
+
|
||||
+ if (!skip_unoccupied && !skip_occupied) {
|
||||
+ if (direction > 0) {
|
||||
+ new_tag = (current_tag << 1) | (current_tag >> (TAGCOUNT - 1));
|
||||
+ } else {
|
||||
+ new_tag = (current_tag >> 1) | (current_tag << (TAGCOUNT - 1));
|
||||
+ }
|
||||
+ return new_tag & TAGMASK;
|
||||
+ }
|
||||
+
|
||||
+ do {
|
||||
+ if (direction > 0) {
|
||||
+ if (test_tag << 1 && (test_tag << 1) <= TAGMASK)
|
||||
+ test_tag = test_tag << 1;
|
||||
+ else
|
||||
+ test_tag = 1;
|
||||
+ } else {
|
||||
+ if (test_tag >> 1)
|
||||
+ test_tag = test_tag >> 1;
|
||||
+ else
|
||||
+ test_tag = 1 << (TAGCOUNT - 1);
|
||||
+ }
|
||||
+
|
||||
+ int is_occupied = (occupied & test_tag) != 0;
|
||||
+ int should_select = (skip_unoccupied && is_occupied) ||
|
||||
+ (skip_occupied && !is_occupied);
|
||||
+
|
||||
+ if (should_select) {
|
||||
+ new_tag = test_tag;
|
||||
+ break;
|
||||
+ }
|
||||
+ count++;
|
||||
+ } while (test_tag != start && count < TAGCOUNT);
|
||||
+
|
||||
+ return new_tag;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+shifttag_with_filter(const Arg *arg, bool skip_unoccupied, bool skip_occupied)
|
||||
+{
|
||||
+ Arg a;
|
||||
+ if (!selmon)
|
||||
+ return;
|
||||
+
|
||||
+ uint32_t occupied = get_occupied_tags(selmon);
|
||||
+ if (skip_unoccupied && !skip_occupied && occupied == 0)
|
||||
+ return;
|
||||
+ if (skip_occupied && !skip_unoccupied && (occupied == TAGMASK))
|
||||
+ return;
|
||||
+
|
||||
+ uint32_t curseltags = selmon->tagset[selmon->seltags];
|
||||
+ uint32_t nextseltags = find_next_tag(curseltags, arg->i, skip_unoccupied, skip_occupied);
|
||||
+
|
||||
+ if (nextseltags != curseltags) {
|
||||
+ a.i = nextseltags;
|
||||
+ view(&a);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+shifttag(const Arg *arg)
|
||||
+{
|
||||
+ shifttag_with_filter(arg, false, false);
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+shifttag_occupied(const Arg *arg)
|
||||
+{
|
||||
+ shifttag_with_filter(arg, true, false);
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+shifttag_unoccupied(const Arg *arg)
|
||||
+{
|
||||
+ shifttag_with_filter(arg, false, true);
|
||||
+}
|
||||
--
|
||||
2.53.0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user