mirror of
				https://codeberg.org/dwl/dwl-patches.git
				synced 2025-10-31 12:04:23 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			351 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			351 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 7255d7e2e1b87c0583a202ea20c83fa75466c0fc Mon Sep 17 00:00:00 2001
 | |
| From: Nikita Ivanov <nikita.vyach.ivanov@gmail.com>
 | |
| 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 |  10 +++-
 | |
|  dwl.c        | 152 +++++++++++++++++++++++++++++++++++++++++++++++++--
 | |
|  3 files changed, 167 insertions(+), 7 deletions(-)
 | |
| 
 | |
| diff --git a/client.h b/client.h
 | |
| index 42f225f..bc9cad2 100644
 | |
| --- a/client.h
 | |
| +++ b/client.h
 | |
| @@ -131,6 +131,18 @@ client_get_appid(Client *c)
 | |
|  	return c->surface.xdg->toplevel->app_id;
 | |
|  }
 | |
|  
 | |
| +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 22d2171..fb5f8fb 100644
 | |
| --- a/config.def.h
 | |
| +++ b/config.def.h
 | |
| @@ -13,6 +13,7 @@ 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.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
 | |
| +static int enableautoswallow = 1; /* enables autoswallowing newly spawned clients */
 | |
|  
 | |
|  /* tagging - TAGCOUNT must be no greater than 31 */
 | |
|  #define TAGCOUNT (9)
 | |
| @@ -22,10 +23,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) */
 | |
| @@ -142,6 +144,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 def2562..bbbbe6f 100644
 | |
| --- a/dwl.c
 | |
| +++ b/dwl.c
 | |
| @@ -73,12 +73,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 { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0)
 | |
| +#define BORDERPX(C)             (borderpx + ((C)->swallowing ? (C)->swallowing->bw : 0))
 | |
|  
 | |
|  /* enums */
 | |
|  enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
 | |
| @@ -104,7 +105,8 @@ typedef struct {
 | |
|  } Button;
 | |
|  
 | |
|  typedef struct Monitor Monitor;
 | |
| -typedef struct {
 | |
| +typedef struct Client Client;
 | |
| +struct Client {
 | |
|  	/* Must keep these three elements in this order */
 | |
|  	unsigned int type; /* XDGShell or X11* */
 | |
|  	struct wlr_box geom; /* layout-relative, includes border */
 | |
| @@ -140,8 +142,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;
 | |
| @@ -230,6 +236,8 @@ typedef struct {
 | |
|  	const char *title;
 | |
|  	uint32_t tags;
 | |
|  	int isfloating;
 | |
| +	int isterm;
 | |
| +	int noswallow;
 | |
|  	int monitor;
 | |
|  } Rule;
 | |
|  
 | |
| @@ -311,6 +319,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);
 | |
| @@ -335,11 +344,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);
 | |
| @@ -466,11 +479,15 @@ applyrules(Client *c)
 | |
|  	if (!(title = client_get_title(c)))
 | |
|  		title = broken;
 | |
|  
 | |
| +	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++)
 | |
| @@ -478,6 +495,12 @@ applyrules(Client *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);
 | |
|  }
 | |
|  
 | |
| @@ -2006,6 +2029,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)
 | |
| @@ -2326,7 +2363,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]);
 | |
| @@ -2404,6 +2441,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
 | |
| @@ -2669,6 +2709,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)
 | |
|  {
 | |
| @@ -2690,6 +2768,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)
 | |
|  {
 | |
| @@ -2741,6 +2853,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)
 | |
|  {
 | |
| @@ -2801,6 +2939,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.48.1
 | |
| 
 | 
