From fd8382a82ca9bb51265e0338a8b8cc977bfe6f45 Mon Sep 17 00:00:00 2001
From: Jon H <>
Date: Wed, 18 Mar 2026 06:17:07 -0700
Subject: [PATCH 1/5] Adds stack bounded stack navigation functions
---
src/config/parse_config.h | 10 ++++++--
src/dispatch/bind_declare.h | 2 ++
src/dispatch/bind_define.h | 48 +++++++++++++++++++++++++++++++++++++
3 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/src/config/parse_config.h b/src/config/parse_config.h
index fda401d9..d2d7cf83 100644
--- a/src/config/parse_config.h
+++ b/src/config/parse_config.h
@@ -938,7 +938,10 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
if (strcmp(func_name, "focusstack") == 0) {
func = focusstack;
(*arg).i = parse_circle_direction(arg_value);
- } else if (strcmp(func_name, "focusdir") == 0) {
+ } else if (strcmp(func_name, "focusstack_bounded") == 0) {
+ func = focusstack_bounded;
+ (*arg).i = parse_circle_direction(arg_value);
+ } else if (strcmp(func_name, "focusdir") == 0) {
func = focusdir;
(*arg).i = parse_direction(arg_value);
} else if (strcmp(func_name, "incnmaster") == 0) {
@@ -955,7 +958,10 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
} else if (strcmp(func_name, "exchange_stack_client") == 0) {
func = exchange_stack_client;
(*arg).i = parse_circle_direction(arg_value);
- } else if (strcmp(func_name, "toggleglobal") == 0) {
+ } else if (strcmp(func_name, "exchange_stack_client_bounded") == 0) {
+ func = exchange_stack_client_bounded;
+ (*arg).i = parse_circle_direction(arg_value);
+ } else if (strcmp(func_name, "toggleglobal") == 0) {
func = toggleglobal;
} else if (strcmp(func_name, "toggleoverview") == 0) {
func = toggleoverview;
diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h
index dbeebd33..fcfb9520 100644
--- a/src/dispatch/bind_declare.h
+++ b/src/dispatch/bind_declare.h
@@ -32,11 +32,13 @@ int32_t quit(const Arg *arg);
int32_t moveresize(const Arg *arg);
int32_t exchange_client(const Arg *arg);
int32_t exchange_stack_client(const Arg *arg);
+int32_t exchange_stack_client_bounded(const Arg *arg);
int32_t killclient(const Arg *arg);
int32_t toggleglobal(const Arg *arg);
int32_t incnmaster(const Arg *arg);
int32_t focusmon(const Arg *arg);
int32_t focusstack(const Arg *arg);
+int32_t focusstack_bounded(const Arg *arg);
int32_t chvt(const Arg *arg);
int32_t reload_config(const Arg *arg);
int32_t smartmovewin(const Arg *arg);
diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h
index 68309356..6c9059dc 100644
--- a/src/dispatch/bind_define.h
+++ b/src/dispatch/bind_define.h
@@ -134,6 +134,27 @@ int32_t exchange_stack_client(const Arg *arg) {
return 0;
}
+int32_t exchange_stack_client_bounded(const Arg *arg) {
+ if (!selmon)
+ return 0;
+
+ Client *c = selmon->sel;
+ Client *tc = NULL;
+ if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen)
+ return 0;
+ /* again, lazy we just search once more if we found master */
+ if (arg->i == NEXT) {
+ tc = get_next_stack_client(c, false);
+ if (tc && tc->ismaster) tc = get_next_stack_client(tc, false);
+ } else {
+ tc = get_next_stack_client(c, true);
+ if (tc && tc->ismaster) tc = get_next_stack_client(tc, true);
+ }
+ if (tc)
+ exchange_two_client(c, tc);
+ return 0;
+}
+
int32_t focusdir(const Arg *arg) {
Client *c = NULL;
c = direction_select(arg);
@@ -263,6 +284,33 @@ int32_t focusstack(const Arg *arg) {
return 0;
}
+int32_t focusstack_bounded(const Arg *arg) {
+ /* Focus the next or previous client (in tiling order) on selmon */
+ /* but skipping past master */
+ Client *sel = focustop(selmon);
+ Client *tc = NULL;
+
+ if (!sel)
+ return 0;
+ /* lazy fix we just search twice, don't make new deeper functions */
+ if (arg->i == NEXT) {
+ tc = get_next_stack_client(sel, false);
+ if (tc && tc->ismaster) tc = get_next_stack_client(tc, false);
+ } else {
+ tc = get_next_stack_client(sel, true);
+ if (tc && tc->ismaster) tc = get_next_stack_client(tc, true);
+ }
+ /* If only one client is visible on selmon, then c == sel */
+
+ if (!tc)
+ return 0;
+
+ focusclient(tc, 1);
+ if (config.warpcursor)
+ warp_cursor(tc);
+ return 0;
+}
+
int32_t incnmaster(const Arg *arg) {
if (!arg || !selmon)
return 0;
From e9ad5943574c986ad145cb605d886cf4cb9e9a40 Mon Sep 17 00:00:00 2001
From: Jon H <>
Date: Thu, 19 Mar 2026 11:16:32 -0700
Subject: [PATCH 2/5] Add "ismaster" ipc protocol extension
---
mmsg/mmsg.c | 24 ++++++++++++++++++++++--
protocols/dwl-ipc-unstable-v2.xml | 9 ++++++++-
src/ext-protocol/dwl-ipc.h | 5 +++++
3 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/mmsg/mmsg.c b/mmsg/mmsg.c
index 4e0e1d8c..2ad9d074 100644
--- a/mmsg/mmsg.c
+++ b/mmsg/mmsg.c
@@ -35,6 +35,7 @@ static int32_t cflag;
static int32_t vflag;
static int32_t mflag;
static int32_t fflag;
+static int32_t Mflag;
static int32_t qflag;
static int32_t dflag;
static int32_t xflag;
@@ -310,6 +311,17 @@ static void dwl_ipc_output_floating(void *data,
printf("floating %u\n", is_floating);
}
+static void dwl_ipc_output_master(void *data,
+ struct zdwl_ipc_output_v2 *dwl_ipc_output,
+ uint32_t is_master) {
+ if (!Mflag)
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("master: %u\n", is_master);
+}
+
static void dwl_ipc_output_frame(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output) {
if (mode & SET) {
@@ -419,6 +431,7 @@ static const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = {
.layout_symbol = dwl_ipc_output_layout_symbol,
.fullscreen = dwl_ipc_output_fullscreen,
.floating = dwl_ipc_output_floating,
+ .master = dwl_ipc_output_master,
.x = dwl_ipc_output_x,
.y = dwl_ipc_output_y,
.width = dwl_ipc_output_width,
@@ -529,6 +542,7 @@ static void usage(void) {
"\t-v Get visibility of statusbar\n"
"\t-m Get fullscreen status\n"
"\t-f Get floating status\n"
+ "\t-M Get master status\n"
"\t-x Get focused client geometry\n"
"\t-e Get name of last focused layer\n"
"\t-k Get current keyboard layout\n"
@@ -758,6 +772,12 @@ int32_t main(int32_t argc, char *argv[]) {
usage();
mode |= GET;
break;
+ case 'M':
+ Mflag = 1;
+ if (mode == SET)
+ usage();
+ mode |= GET;
+ break;
case 'x':
xflag = 1;
if (mode == SET)
@@ -797,9 +817,9 @@ int32_t main(int32_t argc, char *argv[]) {
usage();
if (mode & GET && !output_name &&
!(oflag || tflag || lflag || Oflag || Tflag || Lflag || cflag ||
- vflag || mflag || fflag || xflag || eflag || kflag || bflag ||
+ vflag || mflag || fflag || Mflag || xflag || eflag || kflag || bflag ||
Aflag || dflag))
- oflag = tflag = lflag = cflag = vflag = mflag = fflag = xflag = eflag =
+ oflag = tflag = lflag = cflag = vflag = mflag = fflag = Mflag = xflag = eflag =
kflag = bflag = Aflag = 1;
display = wl_display_connect(NULL);
diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml
index 5852107e..608981f7 100644
--- a/protocols/dwl-ipc-unstable-v2.xml
+++ b/protocols/dwl-ipc-unstable-v2.xml
@@ -190,7 +190,14 @@ I would probably just submit raphi's patchset but I don't think that would be po
Indicates if the selected client on this output is floating.
-
+
+
+
+
+ Indicates if the selected client on this output is master.
+
+
+
diff --git a/src/ext-protocol/dwl-ipc.h b/src/ext-protocol/dwl-ipc.h
index ab0bdb8d..22cd1fda 100644
--- a/src/ext-protocol/dwl-ipc.h
+++ b/src/ext-protocol/dwl-ipc.h
@@ -169,6 +169,11 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
zdwl_ipc_output_v2_send_floating(ipc_output->resource,
focused ? focused->isfloating : 0);
}
+ if (wl_resource_get_version(ipc_output->resource) >=
+ ZDWL_IPC_OUTPUT_V2_MASTER_SINCE_VERSION) {
+ zdwl_ipc_output_v2_send_master(ipc_output->resource,
+ focused ? focused->ismaster : 0);
+ }
if (wl_resource_get_version(ipc_output->resource) >=
ZDWL_IPC_OUTPUT_V2_X_SINCE_VERSION) {
zdwl_ipc_output_v2_send_x(ipc_output->resource,
From a66b248ea998b466ff8246529127be4655320ed7 Mon Sep 17 00:00:00 2001
From: Jon H <>
Date: Thu, 19 Mar 2026 11:45:11 -0700
Subject: [PATCH 3/5] Loop past master in bounded
---
src/dispatch/bind_define.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h
index 6c9059dc..e051da0c 100644
--- a/src/dispatch/bind_define.h
+++ b/src/dispatch/bind_define.h
@@ -145,10 +145,10 @@ int32_t exchange_stack_client_bounded(const Arg *arg) {
/* again, lazy we just search once more if we found master */
if (arg->i == NEXT) {
tc = get_next_stack_client(c, false);
- if (tc && tc->ismaster) tc = get_next_stack_client(tc, false);
+ while (tc && tc != c && tc->ismaster) tc = get_next_stack_client(tc, false);
} else {
tc = get_next_stack_client(c, true);
- if (tc && tc->ismaster) tc = get_next_stack_client(tc, true);
+ while (tc && tc != c && tc->ismaster) tc = get_next_stack_client(tc, true);
}
if (tc)
exchange_two_client(c, tc);
@@ -295,10 +295,10 @@ int32_t focusstack_bounded(const Arg *arg) {
/* lazy fix we just search twice, don't make new deeper functions */
if (arg->i == NEXT) {
tc = get_next_stack_client(sel, false);
- if (tc && tc->ismaster) tc = get_next_stack_client(tc, false);
+ while (tc && tc != sel && tc->ismaster) tc = get_next_stack_client(tc, false);
} else {
tc = get_next_stack_client(sel, true);
- if (tc && tc->ismaster) tc = get_next_stack_client(tc, true);
+ while (tc && tc != sel && tc->ismaster) tc = get_next_stack_client(tc, true);
}
/* If only one client is visible on selmon, then c == sel */
From f3336eae6833eb2cbd82222e115908a700283c1b Mon Sep 17 00:00:00 2001
From: Jon H <>
Date: Thu, 19 Mar 2026 15:23:19 -0700
Subject: [PATCH 4/5] Implements ipc protocol for retrieving current count of
masters
---
mmsg/mmsg.c | 25 ++++++++++++++++++++++---
protocols/dwl-ipc-unstable-v2.xml | 7 +++++++
src/ext-protocol/dwl-ipc.h | 5 +++++
3 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/mmsg/mmsg.c b/mmsg/mmsg.c
index 2ad9d074..b20e45c4 100644
--- a/mmsg/mmsg.c
+++ b/mmsg/mmsg.c
@@ -36,6 +36,7 @@ static int32_t vflag;
static int32_t mflag;
static int32_t fflag;
static int32_t Mflag;
+static int32_t nflag;
static int32_t qflag;
static int32_t dflag;
static int32_t xflag;
@@ -178,6 +179,16 @@ static void dwl_ipc_output_layout_symbol(
printf("layout %s\n", layout);
}
+static void dwl_ipc_output_nmasters(
+ void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, uint32_t count) {
+ if (!(nflag && mode & GET))
+ return;
+ char *output_name = data;
+ if (output_name)
+ printf("%s ", output_name);
+ printf("nmasters %d\n", count);
+}
+
static void dwl_ipc_output_title(void *data,
struct zdwl_ipc_output_v2 *dwl_ipc_output,
const char *title) {
@@ -432,6 +443,7 @@ static const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = {
.fullscreen = dwl_ipc_output_fullscreen,
.floating = dwl_ipc_output_floating,
.master = dwl_ipc_output_master,
+ .nmasters = dwl_ipc_output_nmasters,
.x = dwl_ipc_output_x,
.y = dwl_ipc_output_y,
.width = dwl_ipc_output_width,
@@ -543,6 +555,7 @@ static void usage(void) {
"\t-m Get fullscreen status\n"
"\t-f Get floating status\n"
"\t-M Get master status\n"
+ "\t-n Get current masters count\n"
"\t-x Get focused client geometry\n"
"\t-e Get name of last focused layer\n"
"\t-k Get current keyboard layout\n"
@@ -778,6 +791,12 @@ int32_t main(int32_t argc, char *argv[]) {
usage();
mode |= GET;
break;
+ case 'n':
+ nflag = 1;
+ if (mode == SET)
+ usage();
+ mode |= GET;
+ break;
case 'x':
xflag = 1;
if (mode == SET)
@@ -816,11 +835,11 @@ int32_t main(int32_t argc, char *argv[]) {
if (mode == NONE)
usage();
if (mode & GET && !output_name &&
- !(oflag || tflag || lflag || Oflag || Tflag || Lflag || cflag ||
+ !(oflag || tflag || lflag || Oflag || nflag || Tflag || Lflag || cflag ||
vflag || mflag || fflag || Mflag || xflag || eflag || kflag || bflag ||
Aflag || dflag))
- oflag = tflag = lflag = cflag = vflag = mflag = fflag = Mflag = xflag = eflag =
- kflag = bflag = Aflag = 1;
+ oflag = tflag = lflag = cflag = vflag = mflag =
+ nflag = fflag = Mflag = xflag = eflag = kflag = bflag = Aflag = 1;
display = wl_display_connect(NULL);
if (!display)
diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml
index 608981f7..41b88153 100644
--- a/protocols/dwl-ipc-unstable-v2.xml
+++ b/protocols/dwl-ipc-unstable-v2.xml
@@ -136,6 +136,13 @@ I would probably just submit raphi's patchset but I don't think that would be po
+
+
+
+ Indicates the count of masters has changed.
+
+
+
diff --git a/src/ext-protocol/dwl-ipc.h b/src/ext-protocol/dwl-ipc.h
index 22cd1fda..4f7bfa26 100644
--- a/src/ext-protocol/dwl-ipc.h
+++ b/src/ext-protocol/dwl-ipc.h
@@ -114,6 +114,7 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
xkb_layout_index_t current;
int32_t tagmask, state, numclients, focused_client, tag;
const char *title, *appid, *symbol;
+ uint32_t nmasters;
char kb_layout[32];
focused = focustop(monitor);
zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon);
@@ -143,8 +144,10 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
if (monitor->isoverview) {
symbol = overviewlayout.symbol;
+ nmasters = 0;
} else {
symbol = monitor->pertag->ltidxs[monitor->pertag->curtag]->symbol;
+ nmasters = monitor->pertag->nmasters[monitor->pertag->curtag];
}
keyboard = &kb_group->wlr_group->keyboard;
@@ -159,6 +162,8 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
zdwl_ipc_output_v2_send_title(ipc_output->resource, title ? title : broken);
zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid ? appid : broken);
zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, symbol);
+ zdwl_ipc_output_v2_send_nmasters(ipc_output->resource, nmasters);
+
if (wl_resource_get_version(ipc_output->resource) >=
ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) {
zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource,
From df1d62264973d48c0094703760f8df7ec4402f0f Mon Sep 17 00:00:00 2001
From: Jon H <>
Date: Fri, 20 Mar 2026 13:29:22 -0700
Subject: [PATCH 5/5] Adds killclient_byappid to internal commands
---
src/config/parse_config.h | 5 ++++-
src/dispatch/bind_declare.h | 1 +
src/dispatch/bind_define.h | 15 +++++++++++++++
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/src/config/parse_config.h b/src/config/parse_config.h
index d2d7cf83..4c9ebc27 100644
--- a/src/config/parse_config.h
+++ b/src/config/parse_config.h
@@ -989,7 +989,10 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
(*arg).i = atoi(arg_value);
} else if (strcmp(func_name, "killclient") == 0) {
func = killclient;
- } else if (strcmp(func_name, "centerwin") == 0) {
+ } else if (strcmp(func_name, "killclient_byappid") == 0) {
+ func = killclient_byappid;
+ (*arg).v = strdup(arg_value);
+ } else if (strcmp(func_name, "centerwin") == 0) {
func = centerwin;
} else if (strcmp(func_name, "focuslast") == 0) {
func = focuslast;
diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h
index fcfb9520..d0b3e538 100644
--- a/src/dispatch/bind_declare.h
+++ b/src/dispatch/bind_declare.h
@@ -34,6 +34,7 @@ int32_t exchange_client(const Arg *arg);
int32_t exchange_stack_client(const Arg *arg);
int32_t exchange_stack_client_bounded(const Arg *arg);
int32_t killclient(const Arg *arg);
+int32_t killclient_byappid(const Arg *arg);
int32_t toggleglobal(const Arg *arg);
int32_t incnmaster(const Arg *arg);
int32_t focusmon(const Arg *arg);
diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h
index e051da0c..e1f12fc7 100644
--- a/src/dispatch/bind_define.h
+++ b/src/dispatch/bind_define.h
@@ -409,6 +409,21 @@ int32_t killclient(const Arg *arg) {
return 0;
}
+int32_t killclient_byappid(const Arg *arg) {
+ Client *c = NULL;
+ /* No Arg? No Kill. */
+ if (!arg || !arg->v)
+ return 0;
+ /* for some reason its warning me about this indent? */
+
+ wl_list_for_each(c, &clients, link) {
+ if (strcmp((*arg).v, client_get_appid(c)) == 0) {
+ pending_kill_client(c);
+ }
+ }
+ return 0;
+}
+
int32_t moveresize(const Arg *arg) {
const char *cursors[] = {"nw-resize", "ne-resize", "sw-resize",
"se-resize"};