dwm-swallow-20200522-7accbcf.diff (10221B) - raw


      1 From 7accbcf7db35995d4c26c5cd69338aafa6feb89a Mon Sep 17 00:00:00 2001
      2 From: wtl <wtl144000@gmail.com>
      3 Date: Fri, 22 May 2020 22:38:38 +0300
      4 Subject: [PATCH] swallow X windows from the terminal
      5 
      6 ---
      7  config.def.h |   9 ++-
      8  config.mk    |   2 +-
      9  dwm.c        | 218 +++++++++++++++++++++++++++++++++++++++++++++++++--
     10  3 files changed, 220 insertions(+), 9 deletions(-)
     11 
     12 diff --git a/config.def.h b/config.def.h
     13 index 1c0b587..4c0b25c 100644
     14 --- a/config.def.h
     15 +++ b/config.def.h
     16 @@ -3,6 +3,7 @@
     17  /* appearance */
     18  static const unsigned int borderpx  = 1;        /* border pixel of windows */
     19  static const unsigned int snap      = 32;       /* snap pixel */
     20 +static const int swallowfloating    = 0;        /* 1 means swallow floating windows by default */
     21  static const int showbar            = 1;        /* 0 means no bar */
     22  static const int topbar             = 1;        /* 0 means bottom bar */
     23  static const char *fonts[]          = { "monospace:size=10" };
     24 @@ -26,9 +27,11 @@ static const Rule rules[] = {
     25  	 *	WM_CLASS(STRING) = instance, class
     26  	 *	WM_NAME(STRING) = title
     27  	 */
     28 -	/* class      instance    title       tags mask     isfloating   monitor */
     29 -	{ "Gimp",     NULL,       NULL,       0,            1,           -1 },
     30 -	{ "Firefox",  NULL,       NULL,       1 << 8,       0,           -1 },
     31 +	/* class     instance  title           tags mask  isfloating  isterminal  noswallow  monitor */
     32 +	{ "Gimp",    NULL,     NULL,           0,         1,          0,           0,        -1 },
     33 +	{ "Firefox", NULL,     NULL,           1 << 8,    0,          0,          -1,        -1 },
     34 +	{ "st",      NULL,     NULL,           0,         0,          1,          -1,        -1 },
     35 +	{ NULL,      NULL,     "Event Tester", 0,         1,          0,           1,        -1 }, /* xev */
     36  };
     37  
     38  /* layout(s) */
     39 diff --git a/config.mk b/config.mk
     40 index 7084c33..b77641d 100644
     41 --- a/config.mk
     42 +++ b/config.mk
     43 @@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2
     44  
     45  # includes and libs
     46  INCS = -I${X11INC} -I${FREETYPEINC}
     47 -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
     48 +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res
     49  
     50  # flags
     51  CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
     52 diff --git a/dwm.c b/dwm.c
     53 index 9fd0286..1befee4 100644
     54 --- a/dwm.c
     55 +++ b/dwm.c
     56 @@ -40,6 +40,8 @@
     57  #include <X11/extensions/Xinerama.h>
     58  #endif /* XINERAMA */
     59  #include <X11/Xft/Xft.h>
     60 +#include <X11/Xlib-xcb.h>
     61 +#include <xcb/res.h>
     62  
     63  #include "drw.h"
     64  #include "util.h"
     65 @@ -92,9 +94,11 @@ struct Client {
     66  	int basew, baseh, incw, inch, maxw, maxh, minw, minh;
     67  	int bw, oldbw;
     68  	unsigned int tags;
     69 -	int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
     70 +	int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow;
     71 +	pid_t pid;
     72  	Client *next;
     73  	Client *snext;
     74 +	Client *swallowing;
     75  	Monitor *mon;
     76  	Window win;
     77  };
     78 @@ -138,6 +142,8 @@ typedef struct {
     79  	const char *title;
     80  	unsigned int tags;
     81  	int isfloating;
     82 +	int isterminal;
     83 +	int noswallow;
     84  	int monitor;
     85  } Rule;
     86  
     87 @@ -235,9 +241,16 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee);
     88  static int xerrorstart(Display *dpy, XErrorEvent *ee);
     89  static void zoom(const Arg *arg);
     90  
     91 +static pid_t getparentprocess(pid_t p);
     92 +static int isdescprocess(pid_t p, pid_t c);
     93 +static Client *swallowingclient(Window w);
     94 +static Client *termforwin(const Client *c);
     95 +static pid_t winpid(Window w);
     96 +
     97  /* variables */
     98  static const char broken[] = "broken";
     99  static char stext[256];
    100 +static int scanner;
    101  static int screen;
    102  static int sw, sh;           /* X display screen geometry width, height */
    103  static int bh, blw = 0;      /* bar geometry */
    104 @@ -269,6 +282,8 @@ static Drw *drw;
    105  static Monitor *mons, *selmon;
    106  static Window root, wmcheckwin;
    107  
    108 +static xcb_connection_t *xcon;
    109 +
    110  /* configuration, allows nested code to access above variables */
    111  #include "config.h"
    112  
    113 @@ -286,6 +301,7 @@ applyrules(Client *c)
    114  	XClassHint ch = { NULL, NULL };
    115  
    116  	/* rule matching */
    117 +	c->noswallow = -1;
    118  	c->isfloating = 0;
    119  	c->tags = 0;
    120  	XGetClassHint(dpy, c->win, &ch);
    121 @@ -298,6 +314,8 @@ applyrules(Client *c)
    122  		&& (!r->class || strstr(class, r->class))
    123  		&& (!r->instance || strstr(instance, r->instance)))
    124  		{
    125 +			c->isterminal = r->isterminal;
    126 +			c->noswallow  = r->noswallow;
    127  			c->isfloating = r->isfloating;
    128  			c->tags |= r->tags;
    129  			for (m = mons; m && m->num != r->monitor; m = m->next);
    130 @@ -414,6 +432,61 @@ attachstack(Client *c)
    131  	c->mon->stack = c;
    132  }
    133  
    134 +void
    135 +swallow(Client *p, Client *c)
    136 +{
    137 +	Client *s;
    138 +
    139 +	if (c->noswallow > 0 || c->isterminal)
    140 +		return;
    141 +	if (c->noswallow < 0 && !swallowfloating && c->isfloating)
    142 +		return;
    143 +
    144 +	detach(c);
    145 +	detachstack(c);
    146 +
    147 +	setclientstate(c, WithdrawnState);
    148 +	XUnmapWindow(dpy, p->win);
    149 +
    150 +	p->swallowing = c;
    151 +	c->mon = p->mon;
    152 +
    153 +	Window w = p->win;
    154 +	p->win = c->win;
    155 +	c->win = w;
    156 +
    157 +	XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace,
    158 +		(unsigned char *) &(p->win), 1);
    159 +
    160 +	updatetitle(p);
    161 +	s = scanner ? c : p;
    162 +	XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h);
    163 +	arrange(p->mon);
    164 +	configure(p);
    165 +	updateclientlist();
    166 +}
    167 +
    168 +void
    169 +unswallow(Client *c)
    170 +{
    171 +	c->win = c->swallowing->win;
    172 +
    173 +	free(c->swallowing);
    174 +	c->swallowing = NULL;
    175 +
    176 +	XDeleteProperty(dpy, c->win, netatom[NetClientList]);
    177 +
    178 +	/* unfullscreen the client */
    179 +	setfullscreen(c, 0);
    180 +	updatetitle(c);
    181 +	arrange(c->mon);
    182 +	XMapWindow(dpy, c->win);
    183 +	XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
    184 +	setclientstate(c, NormalState);
    185 +	focus(NULL);
    186 +	arrange(c->mon);
    187 +}
    188 +
    189  void
    190  buttonpress(XEvent *e)
    191  {
    192 @@ -653,6 +726,9 @@ destroynotify(XEvent *e)
    193  
    194  	if ((c = wintoclient(ev->window)))
    195  		unmanage(c, 1);
    196 +
    197 +	else if ((c = swallowingclient(ev->window)))
    198 +		unmanage(c->swallowing, 1);
    199  }
    200  
    201  void
    202 @@ -1018,12 +1094,13 @@ killclient(const Arg *arg)
    203  void
    204  manage(Window w, XWindowAttributes *wa)
    205  {
    206 -	Client *c, *t = NULL;
    207 +	Client *c, *t = NULL, *term = NULL;
    208  	Window trans = None;
    209  	XWindowChanges wc;
    210  
    211  	c = ecalloc(1, sizeof(Client));
    212  	c->win = w;
    213 +	c->pid = winpid(w);
    214  	/* geometry */
    215  	c->x = c->oldx = wa->x;
    216  	c->y = c->oldy = wa->y;
    217 @@ -1038,6 +1115,7 @@ manage(Window w, XWindowAttributes *wa)
    218  	} else {
    219  		c->mon = selmon;
    220  		applyrules(c);
    221 +		term = termforwin(c);
    222  	}
    223  
    224  	if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
    225 @@ -1074,6 +1152,8 @@ manage(Window w, XWindowAttributes *wa)
    226  	c->mon->sel = c;
    227  	arrange(c->mon);
    228  	XMapWindow(dpy, c->win);
    229 +	if (term)
    230 +		swallow(term, c);
    231  	focus(NULL);
    232  }
    233  
    234 @@ -1384,7 +1464,9 @@ run(void)
    235  void
    236  scan(void)
    237  {
    238 +	scanner = 1;
    239  	unsigned int i, num;
    240 +	char swin[256];
    241  	Window d1, d2, *wins = NULL;
    242  	XWindowAttributes wa;
    243  
    244 @@ -1395,6 +1477,8 @@ scan(void)
    245  				continue;
    246  			if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
    247  				manage(wins[i], &wa);
    248 +			else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin))
    249 +				manage(wins[i], &wa);
    250  		}
    251  		for (i = 0; i < num; i++) { /* now the transients */
    252  			if (!XGetWindowAttributes(dpy, wins[i], &wa))
    253 @@ -1406,6 +1490,7 @@ scan(void)
    254  		if (wins)
    255  			XFree(wins);
    256  	}
    257 +	scanner = 0;
    258  }
    259  
    260  void
    261 @@ -1768,6 +1853,20 @@ unmanage(Client *c, int destroyed)
    262  	Monitor *m = c->mon;
    263  	XWindowChanges wc;
    264  
    265 +	if (c->swallowing) {
    266 +		unswallow(c);
    267 +		return;
    268 +	}
    269 +
    270 +	Client *s = swallowingclient(c->win);
    271 +	if (s) {
    272 +		free(s->swallowing);
    273 +		s->swallowing = NULL;
    274 +		arrange(m);
    275 +		focus(NULL);
    276 +		return;
    277 +	}
    278 +
    279  	detach(c);
    280  	detachstack(c);
    281  	if (!destroyed) {
    282 @@ -1782,9 +1881,12 @@ unmanage(Client *c, int destroyed)
    283  		XUngrabServer(dpy);
    284  	}
    285  	free(c);
    286 -	focus(NULL);
    287 -	updateclientlist();
    288 -	arrange(m);
    289 +
    290 +	if (!s) {
    291 +		arrange(m);
    292 +		focus(NULL);
    293 +		updateclientlist();
    294 +	}
    295  }
    296  
    297  void
    298 @@ -2047,6 +2149,110 @@ view(const Arg *arg)
    299  	arrange(selmon);
    300  }
    301  
    302 +pid_t
    303 +winpid(Window w)
    304 +{
    305 +	pid_t result = 0;
    306 +
    307 +	xcb_res_client_id_spec_t spec = {0};
    308 +	spec.client = w;
    309 +	spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
    310 +
    311 +	xcb_generic_error_t *e = NULL;
    312 +	xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec);
    313 +	xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e);
    314 +
    315 +	if (!r)
    316 +		return (pid_t)0;
    317 +
    318 +	xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r);
    319 +	for (; i.rem; xcb_res_client_id_value_next(&i)) {
    320 +		spec = i.data->spec;
    321 +		if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
    322 +			uint32_t *t = xcb_res_client_id_value_value(i.data);
    323 +			result = *t;
    324 +			break;
    325 +		}
    326 +	}
    327 +
    328 +	free(r);
    329 +
    330 +	if (result == (pid_t)-1)
    331 +		result = 0;
    332 +	return result;
    333 +}
    334 +
    335 +pid_t
    336 +getparentprocess(pid_t p)
    337 +{
    338 +	unsigned int v = 0;
    339 +
    340 +#if defined(__linux__)
    341 +	FILE *f;
    342 +	char buf[256];
    343 +	snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
    344 +
    345 +	if (!(f = fopen(buf, "r")))
    346 +		return (pid_t)0;
    347 +
    348 +	if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1)
    349 +		v = (pid_t)0;
    350 +	fclose(f);
    351 +#elif defined(__FreeBSD__)
    352 +	struct kinfo_proc *proc = kinfo_getproc(p);
    353 +	if (!proc)
    354 +		return (pid_t)0;
    355 +
    356 +	v = proc->ki_ppid;
    357 +	free(proc);
    358 +#endif
    359 +	return (pid_t)v;
    360 +}
    361 +
    362 +int
    363 +isdescprocess(pid_t p, pid_t c)
    364 +{
    365 +	while (p != c && c != 0)
    366 +		c = getparentprocess(c);
    367 +
    368 +	return (int)c;
    369 +}
    370 +
    371 +Client *
    372 +termforwin(const Client *w)
    373 +{
    374 +	Client *c;
    375 +	Monitor *m;
    376 +
    377 +	if (!w->pid || w->isterminal)
    378 +		return NULL;
    379 +
    380 +	for (m = mons; m; m = m->next) {
    381 +		for (c = m->clients; c; c = c->next) {
    382 +			if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
    383 +				return c;
    384 +		}
    385 +	}
    386 +
    387 +	return NULL;
    388 +}
    389 +
    390 +Client *
    391 +swallowingclient(Window w)
    392 +{
    393 +	Client *c;
    394 +	Monitor *m;
    395 +
    396 +	for (m = mons; m; m = m->next) {
    397 +		for (c = m->clients; c; c = c->next) {
    398 +			if (c->swallowing && c->swallowing->win == w)
    399 +				return c;
    400 +		}
    401 +	}
    402 +
    403 +	return NULL;
    404 +}
    405 +
    406  Client *
    407  wintoclient(Window w)
    408  {
    409 @@ -2138,6 +2344,8 @@ main(int argc, char *argv[])
    410  		fputs("warning: no locale support\n", stderr);
    411  	if (!(dpy = XOpenDisplay(NULL)))
    412  		die("dwm: cannot open display");
    413 +	if (!(xcon = XGetXCBConnection(dpy)))
    414 +		die("dwm: cannot get xcb connection\n");
    415  	checkotherwm();
    416  	setup();
    417  #ifdef __OpenBSD__
    418 -- 
    419 2.26.2
    420