dwm

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