dwm-swallow-20200522-7accbcf.diff 10.0 KB

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