diff options
| author | nic <ra@afu.re> | 2025-08-02 11:50:53 -0400 |
|---|---|---|
| committer | nic <ra@afu.re> | 2025-08-02 11:50:53 -0400 |
| commit | 1696d1f667098a2774d8b6af1454f408209b0764 (patch) | |
| tree | 9ad075fac93f873da34a4f275f4046b32afaf546 | |
| parent | 15bfffd87a6f9da0bc551db95c7c2a9a069b3708 (diff) | |
swallow.patch V0.7 applied
| -rw-r--r-- | client.h | 12 | ||||
| -rw-r--r-- | config.def.h | 16 | ||||
| -rw-r--r-- | dwl.c | 156 |
3 files changed, 173 insertions, 11 deletions
@@ -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 95c2afa..dad37df 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) @@ -22,10 +24,11 @@ static int log_level = WLR_ERROR; /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ static const Rule rules[] = { - /* app_id title tags mask isfloating monitor */ + /* app_id title tags mask isfloating isterm noswallow monitor */ /* examples: */ - { "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" */ + { "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" */ }; /* layout(s) */ @@ -125,7 +128,7 @@ static const char *menucmd[] = { "wmenu-run", NULL }; static const Key keys[] = { /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ /* modifier key function argument */ - { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, + { MODKEY, XKB_KEY_a, spawn, {.v = menucmd} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, @@ -135,13 +138,15 @@ 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|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} }, + { MODKEY, XKB_KEY_q, killclient, {0} }, { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, { 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} }, @@ -174,3 +179,4 @@ static const Button buttons[] = { { MODKEY, BTN_MIDDLE, togglefloating, {0} }, { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, }; + @@ -74,12 +74,14 @@ #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 +103,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 +141,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 +234,8 @@ typedef struct { const char *title; uint32_t tags; int isfloating; + int isterm; + int noswallow; int monitor; } Rule; @@ -308,6 +317,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 +341,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 +500,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++) @@ -498,8 +516,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); } @@ -2056,6 +2078,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) @@ -2350,7 +2386,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]); @@ -2417,6 +2453,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 +2727,44 @@ startdrag(struct wl_listener *listener, void *data) } 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) { Client *sel = focustop(selmon); @@ -2708,6 +2785,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 +2871,32 @@ togglefullscreen(const Arg *arg) } 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) { uint32_t newtags; @@ -2819,6 +2956,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; @@ -3212,3 +3355,4 @@ main(int argc, char *argv[]) usage: die("Usage: %s [-v] [-d] [-s startup command]", argv[0]); } + |
