dwm-swallow-20200522-7accbcf.diff (10221B)
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