dwm-systray-6.2.diff 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. From 4001ccae7b1a41bdcb247b0cf095a51af7b68c28 Mon Sep 17 00:00:00 2001
  2. From: Igor Gevka <igor.gevka@gmail.com>
  3. Date: Sun, 16 Feb 2020 15:03:10 -0800
  4. Subject: [PATCH] [PATCH] Implements a system tray for dwm.
  5. Original author: Jan Christoph Ebersbach <jceb@e-jc.de>, inspired by http://code.google.com/p/dwm-plus
  6. URL: http://dwm.suckless.org/patches/systray
  7. dwm 6.2 port by Igor Gevka <igor.gevka@gmail.com>
  8. ---
  9. config.def.h | 4 +
  10. dwm.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++----
  11. 2 files changed, 382 insertions(+), 26 deletions(-)
  12. diff --git a/config.def.h b/config.def.h
  13. index 1c0b587..2d824d1 100644
  14. --- a/config.def.h
  15. +++ b/config.def.h
  16. @@ -3,6 +3,10 @@
  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 unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
  21. +static const unsigned int systrayspacing = 2; /* systray spacing */
  22. +static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
  23. +static const int showsystray = 1; /* 0 means no systray */
  24. static const int showbar = 1; /* 0 means no bar */
  25. static const int topbar = 1; /* 0 means bottom bar */
  26. static const char *fonts[] = { "monospace:size=10" };
  27. diff --git a/dwm.c b/dwm.c
  28. index 4465af1..3e361fa 100644
  29. --- a/dwm.c
  30. +++ b/dwm.c
  31. @@ -57,12 +57,30 @@
  32. #define TAGMASK ((1 << LENGTH(tags)) - 1)
  33. #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
  34. +#define SYSTEM_TRAY_REQUEST_DOCK 0
  35. +
  36. +/* XEMBED messages */
  37. +#define XEMBED_EMBEDDED_NOTIFY 0
  38. +#define XEMBED_WINDOW_ACTIVATE 1
  39. +#define XEMBED_FOCUS_IN 4
  40. +#define XEMBED_MODALITY_ON 10
  41. +
  42. +#define XEMBED_MAPPED (1 << 0)
  43. +#define XEMBED_WINDOW_ACTIVATE 1
  44. +#define XEMBED_WINDOW_DEACTIVATE 2
  45. +
  46. +#define VERSION_MAJOR 0
  47. +#define VERSION_MINOR 0
  48. +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
  49. +
  50. /* enums */
  51. enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
  52. enum { SchemeNorm, SchemeSel }; /* color schemes */
  53. enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
  54. + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
  55. NetWMFullscreen, NetActiveWindow, NetWMWindowType,
  56. NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
  57. +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
  58. enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
  59. enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
  60. ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
  61. @@ -141,6 +159,12 @@ typedef struct {
  62. int monitor;
  63. } Rule;
  64. +typedef struct Systray Systray;
  65. +struct Systray {
  66. + Window win;
  67. + Client *icons;
  68. +};
  69. +
  70. /* function declarations */
  71. static void applyrules(Client *c);
  72. static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
  73. @@ -169,8 +193,10 @@ static void focus(Client *c);
  74. static void focusin(XEvent *e);
  75. static void focusmon(const Arg *arg);
  76. static void focusstack(const Arg *arg);
  77. +static Atom getatomprop(Client *c, Atom prop);
  78. static int getrootptr(int *x, int *y);
  79. static long getstate(Window w);
  80. +static unsigned int getsystraywidth();
  81. static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
  82. static void grabbuttons(Client *c, int focused);
  83. static void grabkeys(void);
  84. @@ -188,13 +214,16 @@ static void pop(Client *);
  85. static void propertynotify(XEvent *e);
  86. static void quit(const Arg *arg);
  87. static Monitor *recttomon(int x, int y, int w, int h);
  88. +static void removesystrayicon(Client *i);
  89. static void resize(Client *c, int x, int y, int w, int h, int interact);
  90. +static void resizebarwin(Monitor *m);
  91. static void resizeclient(Client *c, int x, int y, int w, int h);
  92. static void resizemouse(const Arg *arg);
  93. +static void resizerequest(XEvent *e);
  94. static void restack(Monitor *m);
  95. static void run(void);
  96. static void scan(void);
  97. -static int sendevent(Client *c, Atom proto);
  98. +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
  99. static void sendmon(Client *c, Monitor *m);
  100. static void setclientstate(Client *c, long state);
  101. static void setfocus(Client *c);
  102. @@ -206,6 +235,7 @@ static void seturgent(Client *c, int urg);
  103. static void showhide(Client *c);
  104. static void sigchld(int unused);
  105. static void spawn(const Arg *arg);
  106. +static Monitor *systraytomon(Monitor *m);
  107. static void tag(const Arg *arg);
  108. static void tagmon(const Arg *arg);
  109. static void tile(Monitor *);
  110. @@ -223,18 +253,23 @@ static int updategeom(void);
  111. static void updatenumlockmask(void);
  112. static void updatesizehints(Client *c);
  113. static void updatestatus(void);
  114. +static void updatesystray(void);
  115. +static void updatesystrayicongeom(Client *i, int w, int h);
  116. +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
  117. static void updatetitle(Client *c);
  118. static void updatewindowtype(Client *c);
  119. static void updatewmhints(Client *c);
  120. static void view(const Arg *arg);
  121. static Client *wintoclient(Window w);
  122. static Monitor *wintomon(Window w);
  123. +static Client *wintosystrayicon(Window w);
  124. static int xerror(Display *dpy, XErrorEvent *ee);
  125. static int xerrordummy(Display *dpy, XErrorEvent *ee);
  126. static int xerrorstart(Display *dpy, XErrorEvent *ee);
  127. static void zoom(const Arg *arg);
  128. /* variables */
  129. +static Systray *systray = NULL;
  130. static const char broken[] = "broken";
  131. static char stext[256];
  132. static int screen;
  133. @@ -257,9 +292,10 @@ static void (*handler[LASTEvent]) (XEvent *) = {
  134. [MapRequest] = maprequest,
  135. [MotionNotify] = motionnotify,
  136. [PropertyNotify] = propertynotify,
  137. + [ResizeRequest] = resizerequest,
  138. [UnmapNotify] = unmapnotify
  139. };
  140. -static Atom wmatom[WMLast], netatom[NetLast];
  141. +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
  142. static int running = 1;
  143. static Cur *cursor[CurLast];
  144. static Clr **scheme;
  145. @@ -439,7 +475,7 @@ buttonpress(XEvent *e)
  146. arg.ui = 1 << i;
  147. } else if (ev->x < x + blw)
  148. click = ClkLtSymbol;
  149. - else if (ev->x > selmon->ww - TEXTW(stext))
  150. + else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth())
  151. click = ClkStatusText;
  152. else
  153. click = ClkWinTitle;
  154. @@ -482,6 +518,11 @@ cleanup(void)
  155. XUngrabKey(dpy, AnyKey, AnyModifier, root);
  156. while (mons)
  157. cleanupmon(mons);
  158. + if (showsystray) {
  159. + XUnmapWindow(dpy, systray->win);
  160. + XDestroyWindow(dpy, systray->win);
  161. + free(systray);
  162. + }
  163. for (i = 0; i < CurLast; i++)
  164. drw_cur_free(drw, cursor[i]);
  165. for (i = 0; i < LENGTH(colors); i++)
  166. @@ -512,9 +553,57 @@ cleanupmon(Monitor *mon)
  167. void
  168. clientmessage(XEvent *e)
  169. {
  170. + XWindowAttributes wa;
  171. + XSetWindowAttributes swa;
  172. XClientMessageEvent *cme = &e->xclient;
  173. Client *c = wintoclient(cme->window);
  174. + if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
  175. + /* add systray icons */
  176. + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
  177. + if (!(c = (Client *)calloc(1, sizeof(Client))))
  178. + die("fatal: could not malloc() %u bytes\n", sizeof(Client));
  179. + if (!(c->win = cme->data.l[2])) {
  180. + free(c);
  181. + return;
  182. + }
  183. + c->mon = selmon;
  184. + c->next = systray->icons;
  185. + systray->icons = c;
  186. + if (!XGetWindowAttributes(dpy, c->win, &wa)) {
  187. + /* use sane defaults */
  188. + wa.width = bh;
  189. + wa.height = bh;
  190. + wa.border_width = 0;
  191. + }
  192. + c->x = c->oldx = c->y = c->oldy = 0;
  193. + c->w = c->oldw = wa.width;
  194. + c->h = c->oldh = wa.height;
  195. + c->oldbw = wa.border_width;
  196. + c->bw = 0;
  197. + c->isfloating = True;
  198. + /* reuse tags field as mapped status */
  199. + c->tags = 1;
  200. + updatesizehints(c);
  201. + updatesystrayicongeom(c, wa.width, wa.height);
  202. + XAddToSaveSet(dpy, c->win);
  203. + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
  204. + XReparentWindow(dpy, c->win, systray->win, 0, 0);
  205. + /* use parents background color */
  206. + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
  207. + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
  208. + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
  209. + /* FIXME not sure if I have to send these events, too */
  210. + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
  211. + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
  212. + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
  213. + XSync(dpy, False);
  214. + resizebarwin(selmon);
  215. + updatesystray();
  216. + setclientstate(c, NormalState);
  217. + }
  218. + return;
  219. + }
  220. if (!c)
  221. return;
  222. if (cme->message_type == netatom[NetWMState]) {
  223. @@ -567,7 +656,7 @@ configurenotify(XEvent *e)
  224. for (c = m->clients; c; c = c->next)
  225. if (c->isfullscreen)
  226. resizeclient(c, m->mx, m->my, m->mw, m->mh);
  227. - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
  228. + resizebarwin(m);
  229. }
  230. focus(NULL);
  231. arrange(NULL);
  232. @@ -652,6 +741,11 @@ destroynotify(XEvent *e)
  233. if ((c = wintoclient(ev->window)))
  234. unmanage(c, 1);
  235. + else if ((c = wintosystrayicon(ev->window))) {
  236. + removesystrayicon(c);
  237. + resizebarwin(selmon);
  238. + updatesystray();
  239. + }
  240. }
  241. void
  242. @@ -695,19 +789,23 @@ dirtomon(int dir)
  243. void
  244. drawbar(Monitor *m)
  245. {
  246. - int x, w, sw = 0;
  247. + int x, w, sw = 0, stw = 0;
  248. int boxs = drw->fonts->h / 9;
  249. int boxw = drw->fonts->h / 6 + 2;
  250. unsigned int i, occ = 0, urg = 0;
  251. Client *c;
  252. + if(showsystray && m == systraytomon(m))
  253. + stw = getsystraywidth();
  254. +
  255. /* draw status first so it can be overdrawn by tags later */
  256. if (m == selmon) { /* status is only drawn on selected monitor */
  257. drw_setscheme(drw, scheme[SchemeNorm]);
  258. - sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
  259. - drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0);
  260. + sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */
  261. + drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0);
  262. }
  263. + resizebarwin(m);
  264. for (c = m->clients; c; c = c->next) {
  265. occ |= c->tags;
  266. if (c->isurgent)
  267. @@ -728,7 +826,7 @@ drawbar(Monitor *m)
  268. drw_setscheme(drw, scheme[SchemeNorm]);
  269. x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
  270. - if ((w = m->ww - sw - x) > bh) {
  271. + if ((w = m->ww - sw - stw - x) > bh) {
  272. if (m->sel) {
  273. drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
  274. drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
  275. @@ -739,7 +837,7 @@ drawbar(Monitor *m)
  276. drw_rect(drw, x, 0, w, bh, 1, 1);
  277. }
  278. }
  279. - drw_map(drw, m->barwin, 0, 0, m->ww, bh);
  280. + drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
  281. }
  282. void
  283. @@ -776,8 +874,11 @@ expose(XEvent *e)
  284. Monitor *m;
  285. XExposeEvent *ev = &e->xexpose;
  286. - if (ev->count == 0 && (m = wintomon(ev->window)))
  287. + if (ev->count == 0 && (m = wintomon(ev->window))) {
  288. drawbar(m);
  289. + if (m == selmon)
  290. + updatesystray();
  291. + }
  292. }
  293. void
  294. @@ -862,10 +963,17 @@ getatomprop(Client *c, Atom prop)
  295. unsigned long dl;
  296. unsigned char *p = NULL;
  297. Atom da, atom = None;
  298. + /* FIXME getatomprop should return the number of items and a pointer to
  299. + * the stored data instead of this workaround */
  300. + Atom req = XA_ATOM;
  301. + if (prop == xatom[XembedInfo])
  302. + req = xatom[XembedInfo];
  303. - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
  304. + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
  305. &da, &di, &dl, &dl, &p) == Success && p) {
  306. atom = *(Atom *)p;
  307. + if (da == xatom[XembedInfo] && dl == 2)
  308. + atom = ((Atom *)p)[1];
  309. XFree(p);
  310. }
  311. return atom;
  312. @@ -899,6 +1007,16 @@ getstate(Window w)
  313. return result;
  314. }
  315. +unsigned int
  316. +getsystraywidth()
  317. +{
  318. + unsigned int w = 0;
  319. + Client *i;
  320. + if(showsystray)
  321. + for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
  322. + return w ? w + systrayspacing : 1;
  323. +}
  324. +
  325. int
  326. gettextprop(Window w, Atom atom, char *text, unsigned int size)
  327. {
  328. @@ -1003,7 +1121,7 @@ killclient(const Arg *arg)
  329. {
  330. if (!selmon->sel)
  331. return;
  332. - if (!sendevent(selmon->sel, wmatom[WMDelete])) {
  333. + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
  334. XGrabServer(dpy);
  335. XSetErrorHandler(xerrordummy);
  336. XSetCloseDownMode(dpy, DestroyAll);
  337. @@ -1091,6 +1209,12 @@ maprequest(XEvent *e)
  338. {
  339. static XWindowAttributes wa;
  340. XMapRequestEvent *ev = &e->xmaprequest;
  341. + Client *i;
  342. + if ((i = wintosystrayicon(ev->window))) {
  343. + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
  344. + resizebarwin(selmon);
  345. + updatesystray();
  346. + }
  347. if (!XGetWindowAttributes(dpy, ev->window, &wa))
  348. return;
  349. @@ -1215,6 +1339,16 @@ propertynotify(XEvent *e)
  350. Window trans;
  351. XPropertyEvent *ev = &e->xproperty;
  352. + if ((c = wintosystrayicon(ev->window))) {
  353. + if (ev->atom == XA_WM_NORMAL_HINTS) {
  354. + updatesizehints(c);
  355. + updatesystrayicongeom(c, c->w, c->h);
  356. + }
  357. + else
  358. + updatesystrayiconstate(c, ev);
  359. + resizebarwin(selmon);
  360. + updatesystray();
  361. + }
  362. if ((ev->window == root) && (ev->atom == XA_WM_NAME))
  363. updatestatus();
  364. else if (ev->state == PropertyDelete)
  365. @@ -1265,6 +1399,20 @@ recttomon(int x, int y, int w, int h)
  366. return r;
  367. }
  368. +void
  369. +removesystrayicon(Client *i)
  370. +{
  371. + Client **ii;
  372. +
  373. + if (!showsystray || !i)
  374. + return;
  375. + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
  376. + if (ii)
  377. + *ii = i->next;
  378. + free(i);
  379. +}
  380. +
  381. +
  382. void
  383. resize(Client *c, int x, int y, int w, int h, int interact)
  384. {
  385. @@ -1272,6 +1420,14 @@ resize(Client *c, int x, int y, int w, int h, int interact)
  386. resizeclient(c, x, y, w, h);
  387. }
  388. +void
  389. +resizebarwin(Monitor *m) {
  390. + unsigned int w = m->ww;
  391. + if (showsystray && m == systraytomon(m))
  392. + w -= getsystraywidth();
  393. + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
  394. +}
  395. +
  396. void
  397. resizeclient(Client *c, int x, int y, int w, int h)
  398. {
  399. @@ -1344,6 +1500,19 @@ resizemouse(const Arg *arg)
  400. }
  401. }
  402. +void
  403. +resizerequest(XEvent *e)
  404. +{
  405. + XResizeRequestEvent *ev = &e->xresizerequest;
  406. + Client *i;
  407. +
  408. + if ((i = wintosystrayicon(ev->window))) {
  409. + updatesystrayicongeom(i, ev->width, ev->height);
  410. + resizebarwin(selmon);
  411. + updatesystray();
  412. + }
  413. +}
  414. +
  415. void
  416. restack(Monitor *m)
  417. {
  418. @@ -1433,26 +1602,36 @@ setclientstate(Client *c, long state)
  419. }
  420. int
  421. -sendevent(Client *c, Atom proto)
  422. +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
  423. {
  424. int n;
  425. - Atom *protocols;
  426. + Atom *protocols, mt;
  427. int exists = 0;
  428. XEvent ev;
  429. - if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
  430. - while (!exists && n--)
  431. - exists = protocols[n] == proto;
  432. - XFree(protocols);
  433. + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
  434. + mt = wmatom[WMProtocols];
  435. + if (XGetWMProtocols(dpy, w, &protocols, &n)) {
  436. + while (!exists && n--)
  437. + exists = protocols[n] == proto;
  438. + XFree(protocols);
  439. + }
  440. + }
  441. + else {
  442. + exists = True;
  443. + mt = proto;
  444. }
  445. if (exists) {
  446. ev.type = ClientMessage;
  447. - ev.xclient.window = c->win;
  448. - ev.xclient.message_type = wmatom[WMProtocols];
  449. + ev.xclient.window = w;
  450. + ev.xclient.message_type = mt;
  451. ev.xclient.format = 32;
  452. - ev.xclient.data.l[0] = proto;
  453. - ev.xclient.data.l[1] = CurrentTime;
  454. - XSendEvent(dpy, c->win, False, NoEventMask, &ev);
  455. + ev.xclient.data.l[0] = d0;
  456. + ev.xclient.data.l[1] = d1;
  457. + ev.xclient.data.l[2] = d2;
  458. + ev.xclient.data.l[3] = d3;
  459. + ev.xclient.data.l[4] = d4;
  460. + XSendEvent(dpy, w, False, mask, &ev);
  461. }
  462. return exists;
  463. }
  464. @@ -1466,7 +1645,7 @@ setfocus(Client *c)
  465. XA_WINDOW, 32, PropModeReplace,
  466. (unsigned char *) &(c->win), 1);
  467. }
  468. - sendevent(c, wmatom[WMTakeFocus]);
  469. + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
  470. }
  471. void
  472. @@ -1555,6 +1734,10 @@ setup(void)
  473. wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
  474. netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
  475. netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
  476. + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
  477. + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
  478. + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
  479. + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
  480. netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
  481. netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
  482. netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
  483. @@ -1562,6 +1745,9 @@ setup(void)
  484. netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
  485. netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
  486. netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
  487. + xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
  488. + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
  489. + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
  490. /* init cursors */
  491. cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
  492. cursor[CurResize] = drw_cur_create(drw, XC_sizing);
  493. @@ -1570,6 +1756,8 @@ setup(void)
  494. scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
  495. for (i = 0; i < LENGTH(colors); i++)
  496. scheme[i] = drw_scm_create(drw, colors[i], 3);
  497. + /* init system tray */
  498. + updatesystray();
  499. /* init bars */
  500. updatebars();
  501. updatestatus();
  502. @@ -1701,7 +1889,18 @@ togglebar(const Arg *arg)
  503. {
  504. selmon->showbar = !selmon->showbar;
  505. updatebarpos(selmon);
  506. - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
  507. + resizebarwin(selmon);
  508. + if (showsystray) {
  509. + XWindowChanges wc;
  510. + if (!selmon->showbar)
  511. + wc.y = -bh;
  512. + else if (selmon->showbar) {
  513. + wc.y = 0;
  514. + if (!selmon->topbar)
  515. + wc.y = selmon->mh - bh;
  516. + }
  517. + XConfigureWindow(dpy, systray->win, CWY, &wc);
  518. + }
  519. arrange(selmon);
  520. }
  521. @@ -1796,11 +1995,18 @@ unmapnotify(XEvent *e)
  522. else
  523. unmanage(c, 0);
  524. }
  525. + else if ((c = wintosystrayicon(ev->window))) {
  526. + /* KLUDGE! sometimes icons occasionally unmap their windows, but do
  527. + * _not_ destroy them. We map those windows back */
  528. + XMapRaised(dpy, c->win);
  529. + updatesystray();
  530. + }
  531. }
  532. void
  533. updatebars(void)
  534. {
  535. + unsigned int w;
  536. Monitor *m;
  537. XSetWindowAttributes wa = {
  538. .override_redirect = True,
  539. @@ -1811,10 +2017,15 @@ updatebars(void)
  540. for (m = mons; m; m = m->next) {
  541. if (m->barwin)
  542. continue;
  543. - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
  544. + w = m->ww;
  545. + if (showsystray && m == systraytomon(m))
  546. + w -= getsystraywidth();
  547. + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
  548. CopyFromParent, DefaultVisual(dpy, screen),
  549. CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
  550. XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
  551. + if (showsystray && m == systraytomon(m))
  552. + XMapRaised(dpy, systray->win);
  553. XMapRaised(dpy, m->barwin);
  554. XSetClassHint(dpy, m->barwin, &ch);
  555. }
  556. @@ -1990,6 +2201,121 @@ updatestatus(void)
  557. if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
  558. strcpy(stext, "dwm-"VERSION);
  559. drawbar(selmon);
  560. + updatesystray();
  561. +}
  562. +
  563. +void
  564. +updatesystrayicongeom(Client *i, int w, int h)
  565. +{
  566. + if (i) {
  567. + i->h = bh;
  568. + if (w == h)
  569. + i->w = bh;
  570. + else if (h == bh)
  571. + i->w = w;
  572. + else
  573. + i->w = (int) ((float)bh * ((float)w / (float)h));
  574. + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
  575. + /* force icons into the systray dimensions if they don't want to */
  576. + if (i->h > bh) {
  577. + if (i->w == i->h)
  578. + i->w = bh;
  579. + else
  580. + i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
  581. + i->h = bh;
  582. + }
  583. + }
  584. +}
  585. +
  586. +void
  587. +updatesystrayiconstate(Client *i, XPropertyEvent *ev)
  588. +{
  589. + long flags;
  590. + int code = 0;
  591. +
  592. + if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
  593. + !(flags = getatomprop(i, xatom[XembedInfo])))
  594. + return;
  595. +
  596. + if (flags & XEMBED_MAPPED && !i->tags) {
  597. + i->tags = 1;
  598. + code = XEMBED_WINDOW_ACTIVATE;
  599. + XMapRaised(dpy, i->win);
  600. + setclientstate(i, NormalState);
  601. + }
  602. + else if (!(flags & XEMBED_MAPPED) && i->tags) {
  603. + i->tags = 0;
  604. + code = XEMBED_WINDOW_DEACTIVATE;
  605. + XUnmapWindow(dpy, i->win);
  606. + setclientstate(i, WithdrawnState);
  607. + }
  608. + else
  609. + return;
  610. + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
  611. + systray->win, XEMBED_EMBEDDED_VERSION);
  612. +}
  613. +
  614. +void
  615. +updatesystray(void)
  616. +{
  617. + XSetWindowAttributes wa;
  618. + XWindowChanges wc;
  619. + Client *i;
  620. + Monitor *m = systraytomon(NULL);
  621. + unsigned int x = m->mx + m->mw;
  622. + unsigned int w = 1;
  623. +
  624. + if (!showsystray)
  625. + return;
  626. + if (!systray) {
  627. + /* init systray */
  628. + if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
  629. + die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
  630. + systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
  631. + wa.event_mask = ButtonPressMask | ExposureMask;
  632. + wa.override_redirect = True;
  633. + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
  634. + XSelectInput(dpy, systray->win, SubstructureNotifyMask);
  635. + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
  636. + PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
  637. + XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
  638. + XMapRaised(dpy, systray->win);
  639. + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
  640. + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
  641. + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
  642. + XSync(dpy, False);
  643. + }
  644. + else {
  645. + fprintf(stderr, "dwm: unable to obtain system tray.\n");
  646. + free(systray);
  647. + systray = NULL;
  648. + return;
  649. + }
  650. + }
  651. + for (w = 0, i = systray->icons; i; i = i->next) {
  652. + /* make sure the background color stays the same */
  653. + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
  654. + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
  655. + XMapRaised(dpy, i->win);
  656. + w += systrayspacing;
  657. + i->x = w;
  658. + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
  659. + w += i->w;
  660. + if (i->mon != m)
  661. + i->mon = m;
  662. + }
  663. + w = w ? w + systrayspacing : 1;
  664. + x -= w;
  665. + XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
  666. + wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
  667. + wc.stack_mode = Above; wc.sibling = m->barwin;
  668. + XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
  669. + XMapWindow(dpy, systray->win);
  670. + XMapSubwindows(dpy, systray->win);
  671. + /* redraw background */
  672. + XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
  673. + XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
  674. + XSync(dpy, False);
  675. }
  676. void
  677. @@ -2057,6 +2383,16 @@ wintoclient(Window w)
  678. return NULL;
  679. }
  680. +Client *
  681. +wintosystrayicon(Window w) {
  682. + Client *i = NULL;
  683. +
  684. + if (!showsystray || !w)
  685. + return i;
  686. + for (i = systray->icons; i && i->win != w; i = i->next) ;
  687. + return i;
  688. +}
  689. +
  690. Monitor *
  691. wintomon(Window w)
  692. {
  693. @@ -2110,6 +2446,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee)
  694. return -1;
  695. }
  696. +Monitor *
  697. +systraytomon(Monitor *m) {
  698. + Monitor *t;
  699. + int i, n;
  700. + if(!systraypinning) {
  701. + if(!m)
  702. + return selmon;
  703. + return m == selmon ? m : NULL;
  704. + }
  705. + for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
  706. + for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
  707. + if(systraypinningfailfirst && n < systraypinning)
  708. + return mons;
  709. + return t;
  710. +}
  711. +
  712. void
  713. zoom(const Arg *arg)
  714. {
  715. --
  716. 2.17.1