From 79de70e29ef57b62983a026d1602b0c073829f39 Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 21:25:48 +0100 Subject: [PATCH] swallow: update to 0.8 --- patches/swallow/README.md | 3 +- .../{swallow.patch => swallow-0.7.patch} | 0 patches/swallow/swallow-0.8.patch | 350 ++++++++++++++++++ 3 files changed, 352 insertions(+), 1 deletion(-) rename patches/swallow/{swallow.patch => swallow-0.7.patch} (100%) create mode 100644 patches/swallow/swallow-0.8.patch diff --git a/patches/swallow/README.md b/patches/swallow/README.md index 6c16162..f03fa79 100644 --- a/patches/swallow/README.md +++ b/patches/swallow/README.md @@ -22,7 +22,8 @@ scratch to make it more robust and add a few more features: #### swallow.patch -- [v0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swallow/swallow.patch) +- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swallow/swallow-0.8.patch) +- [v0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swallow/swallow-0.7.patch) - [2025-03-03 v0.7](https://codeberg.org/dwl/dwl-patches/raw/commit/2e5748edfe1129f95c7bb1bf9dd590a897f55f57/patches/swallow/swallow.patch) (added "dynamic swallowing" support) - [2024-07-13](https://codeberg.org/dwl/dwl-patches/raw/commit/f1ed83eaeba46108f4ee8164094cb431d64a3e68/patches/swallow/swallow.patch) - [2024-07-13](https://codeberg.org/dwl/dwl-patches/raw/commit/f64d701bab2f9f52d3637edd091684f920407d87/patches/swallow/swallow.patch) diff --git a/patches/swallow/swallow.patch b/patches/swallow/swallow-0.7.patch similarity index 100% rename from patches/swallow/swallow.patch rename to patches/swallow/swallow-0.7.patch diff --git a/patches/swallow/swallow-0.8.patch b/patches/swallow/swallow-0.8.patch new file mode 100644 index 0000000..5204467 --- /dev/null +++ b/patches/swallow/swallow-0.8.patch @@ -0,0 +1,350 @@ +From 66efeab90dad4b6702a17b771cb08aeef159506a Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Wed, 5 Feb 2025 02:34:39 +0100 +Subject: [PATCH] swallow: hide the terminal when it spawns a client + +--- + client.h | 12 ++++ + config.def.h | 11 +++- + dwl.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 168 insertions(+), 7 deletions(-) + +diff --git a/client.h b/client.h +index d9f90bb..83e1c57 100644 +--- a/client.h ++++ b/client.h +@@ -131,6 +131,18 @@ client_get_appid(Client *c) + return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken"; + } + ++static inline int ++client_get_pid(Client *c) ++{ ++ pid_t pid; ++#ifdef XWAYLAND ++ if (client_is_x11(c)) ++ return c->surface.xwayland->pid; ++#endif ++ wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); ++ return pid; ++} ++ + static inline void + client_get_clip(Client *c, struct wlr_box *clip) + { +diff --git a/config.def.h b/config.def.h +index 8a6eda0..1a82020 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -13,6 +13,8 @@ static const float focuscolor[] = COLOR(0x005577ff); + static const float urgentcolor[] = COLOR(0xff0000ff); + /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ + static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ ++static int enableautoswallow = 1; /* enables autoswallowing newly spawned clients */ ++static float swallowborder = 1.0f; /* add this multiplied by borderpx to border when a client is swallowed */ + + /* tagging - TAGCOUNT must be no greater than 31 */ + #define TAGCOUNT (9) +@@ -21,9 +23,10 @@ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You ca + static int log_level = WLR_ERROR; + + static const Rule rules[] = { +- /* app_id title tags mask isfloating monitor */ +- { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ +- { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */ ++ /* app_id title tags mask isfloating isterm noswallow monitor */ ++ { "foot", NULL, 0, 0, 1, 1, -1 }, ++ { "Gimp_EXAMPLE", NULL, 0, 1, 0, 0, -1 }, /* Start on currently visible tags floating, not tiled */ ++ { "firefox_EXAMPLE", NULL, 1 << 8, 0, 0, 0, -1 }, /* Start on ONLY tag "9" */ + /* default/example rule: can be changed but cannot be eliminated; at least one rule must exist */ + }; + +@@ -138,6 +141,8 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, ++ { MODKEY, XKB_KEY_a, toggleswallow, {0} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_A, toggleautoswallow,{0} }, + { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, + { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, +diff --git a/dwl.c b/dwl.c +index 44f3ad9..066b705 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -74,12 +74,13 @@ + #define MAX(A, B) ((A) > (B) ? (A) : (B)) + #define MIN(A, B) ((A) < (B) ? (A) : (B)) + #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) +-#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) ++#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]) && !(C)->swallowedby) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define END(A) ((A) + LENGTH(A)) + #define TAGMASK ((1u << TAGCOUNT) - 1) + #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) + #define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0) ++#define BORDERPX(C) (borderpx + ((C)->swallowing ? (int)ceilf(swallowborder * (C)->swallowing->bw) : 0)) + + /* enums */ + enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ +@@ -101,7 +102,8 @@ typedef struct { + } Button; + + typedef struct Monitor Monitor; +-typedef struct { ++typedef struct Client Client; ++struct Client { + /* Must keep this field first */ + unsigned int type; /* XDGShell or X11* */ + +@@ -138,8 +140,12 @@ typedef struct { + unsigned int bw; + uint32_t tags; + int isfloating, isurgent, isfullscreen; ++ int isterm, noswallow; + uint32_t resize; /* configure serial of a pending resize */ +-} Client; ++ pid_t pid; ++ Client *swallowing; /* client being hidden */ ++ Client *swallowedby; ++}; + + typedef struct { + uint32_t mod; +@@ -227,6 +233,8 @@ typedef struct { + const char *title; + uint32_t tags; + int isfloating; ++ int isterm; ++ int noswallow; + int monitor; + } Rule; + +@@ -308,6 +316,7 @@ static void moveresize(const Arg *arg); + static void outputmgrapply(struct wl_listener *listener, void *data); + static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test); + static void outputmgrtest(struct wl_listener *listener, void *data); ++static pid_t parentpid(pid_t pid); + static void pointerfocus(Client *c, struct wlr_surface *surface, + double sx, double sy, uint32_t time); + static void printstatus(void); +@@ -331,11 +340,15 @@ static void setsel(struct wl_listener *listener, void *data); + static void setup(void); + static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); ++static void swallow(Client *c, Client *toswallow); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); ++static Client *termforwin(Client *c); + static void tile(Monitor *m); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); ++static void toggleswallow(const Arg *arg); ++static void toggleautoswallow(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unlocksession(struct wl_listener *listener, void *data); +@@ -486,11 +499,15 @@ applyrules(Client *c) + appid = client_get_appid(c); + title = client_get_title(c); + ++ c->pid = client_get_pid(c); ++ + for (r = rules; r < END(rules); r++) { + if ((!r->title || strstr(title, r->title)) + && (!r->id || strstr(appid, r->id))) { + c->isfloating = r->isfloating; + newtags |= r->tags; ++ c->isterm = r->isterm; ++ c->noswallow = r->noswallow; + i = 0; + wl_list_for_each(m, &mons, link) { + if (r->monitor == i++) +@@ -500,6 +517,12 @@ applyrules(Client *c) + } + + c->isfloating |= client_is_float_type(c); ++ if (enableautoswallow && !c->noswallow && !c->isfloating && ++ !c->surface.xdg->initial_commit) { ++ Client *p = termforwin(c); ++ if (p) ++ swallow(c, p); ++ } + setmon(c, mon, newtags); + } + +@@ -2057,6 +2080,20 @@ outputmgrtest(struct wl_listener *listener, void *data) + outputmgrapplyortest(config, 1); + } + ++pid_t ++parentpid(pid_t pid) ++{ ++ unsigned int v = 0; ++ FILE *f; ++ char buf[256]; ++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)pid); ++ if (!(f = fopen(buf, "r"))) ++ return 0; ++ fscanf(f, "%*u %*s %*c %u", &v); ++ fclose(f); ++ return (pid_t)v; ++} ++ + void + pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, + uint32_t time) +@@ -2351,7 +2388,7 @@ setfullscreen(Client *c, int fullscreen) + c->isfullscreen = fullscreen; + if (!c->mon || !client_surface(c)->mapped) + return; +- c->bw = fullscreen ? 0 : borderpx; ++ c->bw = fullscreen ? 0 : BORDERPX(c); + client_set_fullscreen(c, fullscreen); + wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen + ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); +@@ -2418,6 +2455,9 @@ setmon(Client *c, Monitor *m, uint32_t newtags) + setfloating(c, c->isfloating); + } + focusclient(focustop(selmon), 1); ++ ++ if (c->swallowing) ++ setmon(c->swallowing, m, newtags); + } + + void +@@ -2688,6 +2728,44 @@ startdrag(struct wl_listener *listener, void *data) + LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); + } + ++void ++swallow(Client *c, Client *toswallow) ++{ ++ /* Do not allow a client to swallow itself */ ++ if (c == toswallow) ++ return; ++ ++ /* Swallow */ ++ if (toswallow && !c->swallowing) { ++ c->swallowing = toswallow; ++ toswallow->swallowedby = c; ++ toswallow->mon = c->mon; ++ toswallow->mon = c->mon; ++ wl_list_remove(&c->link); ++ wl_list_insert(&c->swallowing->link, &c->link); ++ wl_list_remove(&c->flink); ++ wl_list_insert(&c->swallowing->flink, &c->flink); ++ c->bw = BORDERPX(c); ++ c->tags = toswallow->tags; ++ c->isfloating = toswallow->isfloating; ++ c->geom = toswallow->geom; ++ setfullscreen(toswallow, 0); ++ } ++ ++ /* Unswallow */ ++ else if (c->swallowing) { ++ wl_list_remove(&c->swallowing->link); ++ wl_list_insert(&c->link, &c->swallowing->link); ++ wl_list_remove(&c->swallowing->flink); ++ wl_list_insert(&c->flink, &c->swallowing->flink); ++ c->swallowing->tags = c->tags; ++ c->swallowing->swallowedby = NULL; ++ c->swallowing = NULL; ++ c->bw = BORDERPX(c); ++ setfullscreen(c, 0); ++ } ++} ++ + void + tag(const Arg *arg) + { +@@ -2709,6 +2787,40 @@ tagmon(const Arg *arg) + setmon(sel, dirtomon(arg->i), 0); + } + ++Client * ++termforwin(Client *c) ++{ ++ Client *p; ++ pid_t pid; ++ pid_t pids[32]; ++ size_t i, pids_len; ++ ++ if (!c->pid || c->isterm) ++ return NULL; ++ ++ /* Get all parent pids */ ++ pids_len = 0; ++ pid = c->pid; ++ while (pids_len < LENGTH(pids)) { ++ pid = parentpid(pid); ++ if (!pid) ++ break; ++ pids[pids_len++] = pid; ++ } ++ ++ /* Find closest parent */ ++ for (i = 0; i < pids_len; i++) { ++ wl_list_for_each(p, &clients, link) { ++ if (!p->pid || !p->isterm || p->swallowedby) ++ continue; ++ if (pids[i] == p->pid) ++ return p; ++ } ++ } ++ ++ return NULL; ++} ++ + void + tile(Monitor *m) + { +@@ -2760,6 +2872,32 @@ togglefullscreen(const Arg *arg) + setfullscreen(sel, !sel->isfullscreen); + } + ++void ++toggleswallow(const Arg *arg) ++{ ++ Client *c, *sel = focustop(selmon); ++ if (!sel) ++ return; ++ ++ if (sel->swallowing) { ++ swallow(sel, NULL); ++ } else { ++ wl_list_for_each(c, &sel->flink, flink) { ++ if (&c->flink == &fstack) ++ continue; /* wrap past the sentinel node */ ++ if (VISIBLEON(c, selmon)) ++ break; /* found it */ ++ } ++ swallow(sel, c); ++ } ++} ++ ++void ++toggleautoswallow(const Arg *arg) ++{ ++ enableautoswallow = !enableautoswallow; ++} ++ + void + toggletag(const Arg *arg) + { +@@ -2820,6 +2958,12 @@ unmapnotify(struct wl_listener *listener, void *data) + grabc = NULL; + } + ++ if (c->swallowing) { ++ swallow(c, NULL); ++ } else if (c->swallowedby) { ++ swallow(c->swallowedby, NULL); ++ } ++ + if (client_is_unmanaged(c)) { + if (c == exclusive_focus) { + exclusive_focus = NULL; +-- +2.53.0 +