dwm-status2d-barpadding-systray-20241014-b663875.diff (29414B)
1 From b663875b9f4ed4357403ae340b312d38ce071f00 Mon Sep 17 00:00:00 2001 2 From: elbachir-one <bachiralfa@gmail.com> 3 Date: Mon, 14 Oct 2024 17:39:18 +0100 4 Subject: [PATCH] (Status2d + Barpadding + Systray) all patched together 5 6 --- 7 config.def.h | 8 +- 8 dwm.c | 561 +++++++++++++++++++++++++++++++++++++++++++++++---- 9 2 files changed, 527 insertions(+), 42 deletions(-) 10 11 diff --git a/config.def.h b/config.def.h 12 index 9efa774..b3265b4 100644 13 --- a/config.def.h 14 +++ b/config.def.h 15 @@ -3,6 +3,13 @@ 16 /* appearance */ 17 static const unsigned int borderpx = 1; /* border pixel of windows */ 18 static const unsigned int snap = 32; /* snap pixel */ 19 +static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ 20 +static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */ 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 vertpad = 10; /* vertical padding of bar */ 25 +static const int sidepad = 10; /* horizontal padding of bar */ 26 static const int showbar = 1; /* 0 means no bar */ 27 static const int topbar = 1; /* 0 means bottom bar */ 28 static const char *fonts[] = { "monospace:size=10" }; 29 @@ -113,4 +120,3 @@ static const Button buttons[] = { 30 { ClkTagBar, MODKEY, Button1, tag, {0} }, 31 { ClkTagBar, MODKEY, Button3, toggletag, {0} }, 32 }; 33 - 34 diff --git a/dwm.c b/dwm.c 35 index 1443802..8802de0 100644 36 --- a/dwm.c 37 +++ b/dwm.c 38 @@ -56,12 +56,27 @@ 39 #define TAGMASK ((1 << LENGTH(tags)) - 1) 40 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 41 42 +#define SYSTEM_TRAY_REQUEST_DOCK 0 43 +/* XEMBED messages */ 44 +#define XEMBED_EMBEDDED_NOTIFY 0 45 +#define XEMBED_WINDOW_ACTIVATE 1 46 +#define XEMBED_FOCUS_IN 4 47 +#define XEMBED_MODALITY_ON 10 48 +#define XEMBED_MAPPED (1 << 0) 49 +#define XEMBED_WINDOW_ACTIVATE 1 50 +#define XEMBED_WINDOW_DEACTIVATE 2 51 +#define VERSION_MAJOR 0 52 +#define VERSION_MINOR 0 53 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR 54 + 55 /* enums */ 56 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 57 enum { SchemeNorm, SchemeSel }; /* color schemes */ 58 enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 59 + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, 60 NetWMFullscreen, NetActiveWindow, NetWMWindowType, 61 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 62 +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ 63 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 64 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 65 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 66 @@ -140,6 +155,12 @@ typedef struct { 67 int monitor; 68 } Rule; 69 70 +typedef struct Systray Systray; 71 +struct Systray { 72 + Window win; 73 + Client *icons; 74 +}; 75 + 76 /* function declarations */ 77 static void applyrules(Client *c); 78 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 79 @@ -162,6 +183,7 @@ static void detachstack(Client *c); 80 static Monitor *dirtomon(int dir); 81 static void drawbar(Monitor *m); 82 static void drawbars(void); 83 +static int drawstatusbar(Monitor *m, int bh, char* text); 84 static void enternotify(XEvent *e); 85 static void expose(XEvent *e); 86 static void focus(Client *c); 87 @@ -171,6 +193,7 @@ static void focusstack(const Arg *arg); 88 static Atom getatomprop(Client *c, Atom prop); 89 static int getrootptr(int *x, int *y); 90 static long getstate(Window w); 91 +static unsigned int getsystraywidth(); 92 static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 93 static void grabbuttons(Client *c, int focused); 94 static void grabkeys(void); 95 @@ -188,13 +211,16 @@ static void pop(Client *c); 96 static void propertynotify(XEvent *e); 97 static void quit(const Arg *arg); 98 static Monitor *recttomon(int x, int y, int w, int h); 99 +static void removesystrayicon(Client *i); 100 static void resize(Client *c, int x, int y, int w, int h, int interact); 101 +static void resizebarwin(Monitor *m); 102 static void resizeclient(Client *c, int x, int y, int w, int h); 103 static void resizemouse(const Arg *arg); 104 +static void resizerequest(XEvent *e); 105 static void restack(Monitor *m); 106 static void run(void); 107 static void scan(void); 108 -static int sendevent(Client *c, Atom proto); 109 +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); 110 static void sendmon(Client *c, Monitor *m); 111 static void setclientstate(Client *c, long state); 112 static void setfocus(Client *c); 113 @@ -205,6 +231,7 @@ static void setup(void); 114 static void seturgent(Client *c, int urg); 115 static void showhide(Client *c); 116 static void spawn(const Arg *arg); 117 +static Monitor *systraytomon(Monitor *m); 118 static void tag(const Arg *arg); 119 static void tagmon(const Arg *arg); 120 static void tile(Monitor *m); 121 @@ -222,24 +249,31 @@ static int updategeom(void); 122 static void updatenumlockmask(void); 123 static void updatesizehints(Client *c); 124 static void updatestatus(void); 125 +static void updatesystray(void); 126 +static void updatesystrayicongeom(Client *i, int w, int h); 127 +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); 128 static void updatetitle(Client *c); 129 static void updatewindowtype(Client *c); 130 static void updatewmhints(Client *c); 131 static void view(const Arg *arg); 132 static Client *wintoclient(Window w); 133 static Monitor *wintomon(Window w); 134 +static Client *wintosystrayicon(Window w); 135 static int xerror(Display *dpy, XErrorEvent *ee); 136 static int xerrordummy(Display *dpy, XErrorEvent *ee); 137 static int xerrorstart(Display *dpy, XErrorEvent *ee); 138 static void zoom(const Arg *arg); 139 140 /* variables */ 141 +static Systray *systray = NULL; 142 static const char broken[] = "broken"; 143 -static char stext[256]; 144 +static char stext[1024]; 145 static int screen; 146 static int sw, sh; /* X display screen geometry width, height */ 147 static int bh; /* bar height */ 148 static int lrpad; /* sum of left and right padding for text */ 149 +static int vp; /* vertical padding for bar */ 150 +static int sp; /* side padding for bar */ 151 static int (*xerrorxlib)(Display *, XErrorEvent *); 152 static unsigned int numlockmask = 0; 153 static void (*handler[LASTEvent]) (XEvent *) = { 154 @@ -256,9 +290,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { 155 [MapRequest] = maprequest, 156 [MotionNotify] = motionnotify, 157 [PropertyNotify] = propertynotify, 158 + [ResizeRequest] = resizerequest, 159 [UnmapNotify] = unmapnotify 160 }; 161 -static Atom wmatom[WMLast], netatom[NetLast]; 162 +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; 163 static int running = 1; 164 static Cur *cursor[CurLast]; 165 static Clr **scheme; 166 @@ -440,7 +475,7 @@ buttonpress(XEvent *e) 167 arg.ui = 1 << i; 168 } else if (ev->x < x + TEXTW(selmon->ltsymbol)) 169 click = ClkLtSymbol; 170 - else if (ev->x > selmon->ww - (int)TEXTW(stext)) 171 + else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) 172 click = ClkStatusText; 173 else 174 click = ClkWinTitle; 175 @@ -483,9 +518,16 @@ cleanup(void) 176 XUngrabKey(dpy, AnyKey, AnyModifier, root); 177 while (mons) 178 cleanupmon(mons); 179 - for (i = 0; i < CurLast; i++) 180 + 181 + if (showsystray) { 182 + XUnmapWindow(dpy, systray->win); 183 + XDestroyWindow(dpy, systray->win); 184 + free(systray); 185 + } 186 + 187 + for (i = 0; i < CurLast; i++) 188 drw_cur_free(drw, cursor[i]); 189 - for (i = 0; i < LENGTH(colors); i++) 190 + for (i = 0; i < LENGTH(colors) + 1; i++) 191 free(scheme[i]); 192 free(scheme); 193 XDestroyWindow(dpy, wmcheckwin); 194 @@ -514,9 +556,58 @@ cleanupmon(Monitor *mon) 195 void 196 clientmessage(XEvent *e) 197 { 198 + XWindowAttributes wa; 199 + XSetWindowAttributes swa; 200 XClientMessageEvent *cme = &e->xclient; 201 Client *c = wintoclient(cme->window); 202 203 + if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { 204 + /* add systray icons */ 205 + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { 206 + if (!(c = (Client *)calloc(1, sizeof(Client)))) 207 + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 208 + if (!(c->win = cme->data.l[2])) { 209 + free(c); 210 + return; 211 + } 212 + c->mon = selmon; 213 + c->next = systray->icons; 214 + systray->icons = c; 215 + if (!XGetWindowAttributes(dpy, c->win, &wa)) { 216 + /* use sane defaults */ 217 + wa.width = bh; 218 + wa.height = bh; 219 + wa.border_width = 0; 220 + } 221 + c->x = c->oldx = c->y = c->oldy = 0; 222 + c->w = c->oldw = wa.width; 223 + c->h = c->oldh = wa.height; 224 + c->oldbw = wa.border_width; 225 + c->bw = 0; 226 + c->isfloating = True; 227 + /* reuse tags field as mapped status */ 228 + c->tags = 1; 229 + updatesizehints(c); 230 + updatesystrayicongeom(c, wa.width, wa.height); 231 + XAddToSaveSet(dpy, c->win); 232 + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); 233 + XReparentWindow(dpy, c->win, systray->win, 0, 0); 234 + /* use parents background color */ 235 + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 236 + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); 237 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 238 + /* FIXME not sure if I have to send these events, too */ 239 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 240 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 241 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 242 + XSync(dpy, False); 243 + resizebarwin(selmon); 244 + updatesystray(); 245 + setclientstate(c, NormalState); 246 + } 247 + return; 248 + } 249 + 250 if (!c) 251 return; 252 if (cme->message_type == netatom[NetWMState]) { 253 @@ -569,7 +660,7 @@ configurenotify(XEvent *e) 254 for (c = m->clients; c; c = c->next) 255 if (c->isfullscreen) 256 resizeclient(c, m->mx, m->my, m->mw, m->mh); 257 - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 258 + resizebarwin(m); 259 } 260 focus(NULL); 261 arrange(NULL); 262 @@ -654,6 +745,11 @@ destroynotify(XEvent *e) 263 264 if ((c = wintoclient(ev->window))) 265 unmanage(c, 1); 266 + else if ((c = wintosystrayicon(ev->window))) { 267 + removesystrayicon(c); 268 + resizebarwin(selmon); 269 + updatesystray(); 270 + } 271 } 272 273 void 274 @@ -694,10 +790,119 @@ dirtomon(int dir) 275 return m; 276 } 277 278 +int 279 +drawstatusbar(Monitor *m, int bh, char* stext) { 280 + int ret, i, w, x, len; 281 + short isCode = 0; 282 + char *text; 283 + char *p; 284 + 285 + len = strlen(stext) + 1 ; 286 + if (!(text = (char*) malloc(sizeof(char)*len))) 287 + die("malloc"); 288 + p = text; 289 + memcpy(text, stext, len); 290 + 291 + /* compute width of the status text */ 292 + w = 0; 293 + i = -1; 294 + while (text[++i]) { 295 + if (text[i] == '^') { 296 + if (!isCode) { 297 + isCode = 1; 298 + text[i] = '\0'; 299 + w += TEXTW(text) - lrpad; 300 + text[i] = '^'; 301 + if (text[++i] == 'f') 302 + w += atoi(text + ++i); 303 + } else { 304 + isCode = 0; 305 + text = text + i + 1; 306 + i = -1; 307 + } 308 + } 309 + } 310 + if (!isCode) 311 + w += TEXTW(text) - lrpad; 312 + else 313 + isCode = 0; 314 + text = p; 315 + 316 + w += lrpad; /* 1px padding on both sides */ 317 + ret = m->ww - w; 318 + x = m->ww - w - getsystraywidth(); 319 + 320 + drw_setscheme(drw, scheme[LENGTH(colors)]); 321 + drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; 322 + drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; 323 + drw_rect(drw, x - 2 * sp, 0, w, bh, 1, 1); 324 + x += lrpad / 2; 325 + 326 + /* process status text */ 327 + i = -1; 328 + while (text[++i]) { 329 + if (text[i] == '^' && !isCode) { 330 + isCode = 1; 331 + 332 + text[i] = '\0'; 333 + w = TEXTW(text) - lrpad; 334 + drw_text(drw, x - 2 * sp, vp / 2, w, bh - vp, 0, text, 0); 335 + 336 + x += w; 337 + 338 + /* process code */ 339 + while (text[++i] != '^') { 340 + if (text[i] == 'c') { 341 + char buf[8]; 342 + memcpy(buf, (char*)text+i+1, 7); 343 + buf[7] = '\0'; 344 + drw_clr_create(drw, &drw->scheme[ColFg], buf); 345 + i += 7; 346 + } else if (text[i] == 'b') { 347 + char buf[8]; 348 + memcpy(buf, (char*)text+i+1, 7); 349 + buf[7] = '\0'; 350 + drw_clr_create(drw, &drw->scheme[ColBg], buf); 351 + i += 7; 352 + } else if (text[i] == 'd') { 353 + drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; 354 + drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; 355 + } else if (text[i] == 'r') { 356 + int rx = atoi(text + ++i); 357 + while (text[++i] != ','); 358 + int ry = atoi(text + ++i); 359 + while (text[++i] != ','); 360 + int rw = atoi(text + ++i); 361 + while (text[++i] != ','); 362 + int rh = atoi(text + ++i); 363 + 364 + drw_rect(drw, rx +x - 2 * sp, ry + vp / 2, rw, MIN(rh, bh - vp), 1, 0); 365 + } else if (text[i] == 'f') { 366 + x += atoi(text + ++i); 367 + } 368 + } 369 + 370 + text = text + i + 1; 371 + i=-1; 372 + isCode = 0; 373 + } 374 + } 375 + 376 + if (!isCode) { 377 + w = TEXTW(text) - lrpad; 378 + drw_text(drw, x - 2 * sp, 0, w, bh, 0, text, 0); 379 + } 380 + 381 + drw_setscheme(drw, scheme[SchemeNorm]); 382 + free(p); 383 + 384 + return ret; 385 +} 386 + 387 void 388 drawbar(Monitor *m) 389 { 390 - int x, w, tw = 0; 391 + int x, w, tw = 0, stw = 0; 392 int boxs = drw->fonts->h / 9; 393 int boxw = drw->fonts->h / 6 + 2; 394 unsigned int i, occ = 0, urg = 0; 395 @@ -706,13 +911,15 @@ drawbar(Monitor *m) 396 if (!m->showbar) 397 return; 398 399 + if(showsystray && m == systraytomon(m) && !systrayonleft) 400 + stw = getsystraywidth(); 401 + 402 /* draw status first so it can be overdrawn by tags later */ 403 if (m == selmon) { /* status is only drawn on selected monitor */ 404 - drw_setscheme(drw, scheme[SchemeNorm]); 405 - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 406 - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); 407 + tw = m->ww - drawstatusbar(m, bh, stext); 408 } 409 410 + resizebarwin(m); 411 for (c = m->clients; c; c = c->next) { 412 occ |= c->tags; 413 if (c->isurgent) 414 @@ -733,18 +940,18 @@ drawbar(Monitor *m) 415 drw_setscheme(drw, scheme[SchemeNorm]); 416 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 417 418 - if ((w = m->ww - tw - x) > bh) { 419 + if ((w = m->ww - tw - stw - x) > bh) { 420 if (m->sel) { 421 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); 422 - drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); 423 + drw_text(drw, x, 0, w - 2 * sp, bh, lrpad / 2, m->sel->name, 0); 424 if (m->sel->isfloating) 425 drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); 426 } else { 427 drw_setscheme(drw, scheme[SchemeNorm]); 428 - drw_rect(drw, x, 0, w, bh, 1, 1); 429 + drw_rect(drw, x, 0, w - 2 * sp, bh, 1, 1); 430 } 431 } 432 - drw_map(drw, m->barwin, 0, 0, m->ww, bh); 433 + drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); 434 } 435 436 void 437 @@ -781,8 +988,11 @@ expose(XEvent *e) 438 Monitor *m; 439 XExposeEvent *ev = &e->xexpose; 440 441 - if (ev->count == 0 && (m = wintomon(ev->window))) 442 + if (ev->count == 0 && (m = wintomon(ev->window))) { 443 drawbar(m); 444 + if (m == selmon) 445 + updatesystray(); 446 + } 447 } 448 449 void 450 @@ -868,9 +1078,17 @@ getatomprop(Client *c, Atom prop) 451 unsigned char *p = NULL; 452 Atom da, atom = None; 453 454 - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 455 + /* FIXME getatomprop should return the number of items and a pointer to 456 + * the stored data instead of this workaround */ 457 + Atom req = XA_ATOM; 458 + if (prop == xatom[XembedInfo]) 459 + req = xatom[XembedInfo]; 460 + 461 + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, 462 &da, &di, &dl, &dl, &p) == Success && p) { 463 atom = *(Atom *)p; 464 + if (da == xatom[XembedInfo] && dl == 2) 465 + atom = ((Atom *)p)[1]; 466 XFree(p); 467 } 468 return atom; 469 @@ -904,6 +1122,16 @@ getstate(Window w) 470 return result; 471 } 472 473 +unsigned int 474 +getsystraywidth() 475 +{ 476 + unsigned int w = 0; 477 + Client *i; 478 + if(showsystray) 479 + for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; 480 + return w ? w + systrayspacing : 1; 481 +} 482 + 483 int 484 gettextprop(Window w, Atom atom, char *text, unsigned int size) 485 { 486 @@ -1016,7 +1244,8 @@ killclient(const Arg *arg) 487 { 488 if (!selmon->sel) 489 return; 490 - if (!sendevent(selmon->sel, wmatom[WMDelete])) { 491 + 492 + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { 493 XGrabServer(dpy); 494 XSetErrorHandler(xerrordummy); 495 XSetCloseDownMode(dpy, DestroyAll); 496 @@ -1103,6 +1332,14 @@ maprequest(XEvent *e) 497 static XWindowAttributes wa; 498 XMapRequestEvent *ev = &e->xmaprequest; 499 500 + Client *i; 501 + if ((i = wintosystrayicon(ev->window))) { 502 + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); 503 + resizebarwin(selmon); 504 + updatesystray(); 505 + } 506 + 507 + 508 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) 509 return; 510 if (!wintoclient(ev->window)) 511 @@ -1224,7 +1461,18 @@ propertynotify(XEvent *e) 512 Window trans; 513 XPropertyEvent *ev = &e->xproperty; 514 515 - if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 516 + if ((c = wintosystrayicon(ev->window))) { 517 + if (ev->atom == XA_WM_NORMAL_HINTS) { 518 + updatesizehints(c); 519 + updatesystrayicongeom(c, c->w, c->h); 520 + } 521 + else 522 + updatesystrayiconstate(c, ev); 523 + resizebarwin(selmon); 524 + updatesystray(); 525 + } 526 + 527 + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 528 updatestatus(); 529 else if (ev->state == PropertyDelete) 530 return; /* ignore */ 531 @@ -1274,6 +1522,19 @@ recttomon(int x, int y, int w, int h) 532 return r; 533 } 534 535 +void 536 +removesystrayicon(Client *i) 537 +{ 538 + Client **ii; 539 + 540 + if (!showsystray || !i) 541 + return; 542 + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); 543 + if (ii) 544 + *ii = i->next; 545 + free(i); 546 +} 547 + 548 void 549 resize(Client *c, int x, int y, int w, int h, int interact) 550 { 551 @@ -1281,6 +1542,15 @@ resize(Client *c, int x, int y, int w, int h, int interact) 552 resizeclient(c, x, y, w, h); 553 } 554 555 +void 556 +resizebarwin(Monitor *m) { 557 + unsigned int w = m->ww; 558 + if (showsystray && m == systraytomon(m) && !systrayonleft) 559 + w -= getsystraywidth(); 560 + XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh); 561 + 562 +} 563 + 564 void 565 resizeclient(Client *c, int x, int y, int w, int h) 566 { 567 @@ -1353,6 +1623,19 @@ resizemouse(const Arg *arg) 568 } 569 } 570 571 +void 572 +resizerequest(XEvent *e) 573 +{ 574 + XResizeRequestEvent *ev = &e->xresizerequest; 575 + Client *i; 576 + 577 + if ((i = wintosystrayicon(ev->window))) { 578 + updatesystrayicongeom(i, ev->width, ev->height); 579 + resizebarwin(selmon); 580 + updatesystray(); 581 + } 582 +} 583 + 584 void 585 restack(Monitor *m) 586 { 587 @@ -1442,26 +1725,37 @@ setclientstate(Client *c, long state) 588 } 589 590 int 591 -sendevent(Client *c, Atom proto) 592 +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) 593 { 594 int n; 595 - Atom *protocols; 596 + Atom *protocols, mt; 597 int exists = 0; 598 XEvent ev; 599 600 - if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { 601 - while (!exists && n--) 602 - exists = protocols[n] == proto; 603 - XFree(protocols); 604 + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { 605 + mt = wmatom[WMProtocols]; 606 + if (XGetWMProtocols(dpy, w, &protocols, &n)) { 607 + while (!exists && n--) 608 + exists = protocols[n] == proto; 609 + XFree(protocols); 610 + } 611 } 612 + else { 613 + exists = True; 614 + mt = proto; 615 + } 616 + 617 if (exists) { 618 ev.type = ClientMessage; 619 - ev.xclient.window = c->win; 620 - ev.xclient.message_type = wmatom[WMProtocols]; 621 + ev.xclient.window = w; 622 + ev.xclient.message_type = mt; 623 ev.xclient.format = 32; 624 - ev.xclient.data.l[0] = proto; 625 - ev.xclient.data.l[1] = CurrentTime; 626 - XSendEvent(dpy, c->win, False, NoEventMask, &ev); 627 + ev.xclient.data.l[0] = d0; 628 + ev.xclient.data.l[1] = d1; 629 + ev.xclient.data.l[2] = d2; 630 + ev.xclient.data.l[3] = d3; 631 + ev.xclient.data.l[4] = d4; 632 + XSendEvent(dpy, w, False, mask, &ev); 633 } 634 return exists; 635 } 636 @@ -1475,7 +1769,7 @@ setfocus(Client *c) 637 XA_WINDOW, 32, PropModeReplace, 638 (unsigned char *) &(c->win), 1); 639 } 640 - sendevent(c, wmatom[WMTakeFocus]); 641 + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); 642 } 643 644 void 645 @@ -1562,7 +1856,10 @@ setup(void) 646 die("no fonts could be loaded."); 647 lrpad = drw->fonts->h; 648 bh = drw->fonts->h + 2; 649 + sp = sidepad; 650 + vp = (topbar == 1) ? vertpad : - vertpad; 651 updategeom(); 652 + 653 /* init atoms */ 654 utf8string = XInternAtom(dpy, "UTF8_STRING", False); 655 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 656 @@ -1570,22 +1867,32 @@ setup(void) 657 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 658 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 659 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 660 - netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 661 - netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 662 + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 663 + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); 664 + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); 665 + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); 666 + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); 667 + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 668 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 669 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 670 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 671 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 672 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 673 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 674 - /* init cursors */ 675 + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); 676 + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); 677 + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); 678 + /* init cursors */ 679 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 680 cursor[CurResize] = drw_cur_create(drw, XC_sizing); 681 cursor[CurMove] = drw_cur_create(drw, XC_fleur); 682 /* init appearance */ 683 - scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); 684 + scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *)); 685 + scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], 3); 686 for (i = 0; i < LENGTH(colors); i++) 687 scheme[i] = drw_scm_create(drw, colors[i], 3); 688 + /* init system tray */ 689 + updatesystray(); 690 /* init bars */ 691 updatebars(); 692 updatestatus(); 693 @@ -1716,7 +2023,19 @@ togglebar(const Arg *arg) 694 { 695 selmon->showbar = !selmon->showbar; 696 updatebarpos(selmon); 697 - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 698 + resizebarwin(selmon); 699 + if (showsystray) { 700 + XWindowChanges wc; 701 + if (!selmon->showbar) 702 + wc.y = -bh; 703 + else if (selmon->showbar) { 704 + wc.y = 0; 705 + if (!selmon->topbar) 706 + wc.y = selmon->mh - bh; 707 + } 708 + XConfigureWindow(dpy, systray->win, CWY, &wc); 709 + } 710 + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, bh); 711 arrange(selmon); 712 } 713 714 @@ -1812,11 +2131,18 @@ unmapnotify(XEvent *e) 715 else 716 unmanage(c, 0); 717 } 718 + else if ((c = wintosystrayicon(ev->window))) { 719 + /* KLUDGE! sometimes icons occasionally unmap their windows, but do 720 + * _not_ destroy them. We map those windows back */ 721 + XMapRaised(dpy, c->win); 722 + updatesystray(); 723 + } 724 } 725 726 void 727 updatebars(void) 728 { 729 + unsigned int w; 730 Monitor *m; 731 XSetWindowAttributes wa = { 732 .override_redirect = True, 733 @@ -1827,10 +2153,15 @@ updatebars(void) 734 for (m = mons; m; m = m->next) { 735 if (m->barwin) 736 continue; 737 - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), 738 + w = m->ww; 739 + if (showsystray && m == systraytomon(m)) 740 + w -= getsystraywidth(); 741 + m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh, 0, DefaultDepth(dpy, screen), 742 CopyFromParent, DefaultVisual(dpy, screen), 743 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 744 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 745 + if (showsystray && m == systraytomon(m)) 746 + XMapRaised(dpy, systray->win); 747 XMapRaised(dpy, m->barwin); 748 XSetClassHint(dpy, m->barwin, &ch); 749 } 750 @@ -1842,11 +2173,11 @@ updatebarpos(Monitor *m) 751 m->wy = m->my; 752 m->wh = m->mh; 753 if (m->showbar) { 754 - m->wh -= bh; 755 - m->by = m->topbar ? m->wy : m->wy + m->wh; 756 - m->wy = m->topbar ? m->wy + bh : m->wy; 757 + m->wh = m->wh - vertpad - bh; 758 + m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; 759 + m->wy = m->topbar ? m->wy + bh + vp : m->wy; 760 } else 761 - m->by = -bh; 762 + m->by = -bh - vp; 763 } 764 765 void 766 @@ -2007,6 +2338,128 @@ updatestatus(void) 767 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 768 strcpy(stext, "dwm-"VERSION); 769 drawbar(selmon); 770 + updatesystray(); 771 +} 772 + 773 + 774 +void 775 +updatesystrayicongeom(Client *i, int w, int h) 776 +{ 777 + if (i) { 778 + i->h = bh; 779 + if (w == h) 780 + i->w = bh; 781 + else if (h == bh) 782 + i->w = w; 783 + else 784 + i->w = (int) ((float)bh * ((float)w / (float)h)); 785 + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); 786 + /* force icons into the systray dimensions if they don't want to */ 787 + if (i->h > bh) { 788 + if (i->w == i->h) 789 + i->w = bh; 790 + else 791 + i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); 792 + i->h = bh; 793 + } 794 + } 795 +} 796 + 797 +void 798 +updatesystrayiconstate(Client *i, XPropertyEvent *ev) 799 +{ 800 + long flags; 801 + int code = 0; 802 + 803 + if (!showsystray || !i || ev->atom != xatom[XembedInfo] || 804 + !(flags = getatomprop(i, xatom[XembedInfo]))) 805 + return; 806 + 807 + if (flags & XEMBED_MAPPED && !i->tags) { 808 + i->tags = 1; 809 + code = XEMBED_WINDOW_ACTIVATE; 810 + XMapRaised(dpy, i->win); 811 + setclientstate(i, NormalState); 812 + } 813 + else if (!(flags & XEMBED_MAPPED) && i->tags) { 814 + i->tags = 0; 815 + code = XEMBED_WINDOW_DEACTIVATE; 816 + XUnmapWindow(dpy, i->win); 817 + setclientstate(i, WithdrawnState); 818 + } 819 + else 820 + return; 821 + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, 822 + systray->win, XEMBED_EMBEDDED_VERSION); 823 +} 824 + 825 +void 826 +updatesystray(void) 827 +{ 828 + XSetWindowAttributes wa; 829 + XWindowChanges wc; 830 + Client *i; 831 + Monitor *m = systraytomon(NULL); 832 + unsigned int x = m->mx + m->mw; 833 + unsigned int sw = TEXTW(stext) - lrpad + systrayspacing; 834 + unsigned int w = 1; 835 + 836 + if (!showsystray) 837 + return; 838 + if (systrayonleft) 839 + x -= sw + lrpad / 2; 840 + if (!systray) { 841 + /* init systray */ 842 + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) 843 + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); 844 + systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); 845 + wa.event_mask = ButtonPressMask | ExposureMask; 846 + wa.override_redirect = True; 847 + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 848 + XSelectInput(dpy, systray->win, SubstructureNotifyMask); 849 + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, 850 + PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); 851 + XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); 852 + XMapRaised(dpy, systray->win); 853 + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); 854 + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { 855 + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); 856 + XSync(dpy, False); 857 + } 858 + else { 859 + fprintf(stderr, "dwm: unable to obtain system tray.\n"); 860 + free(systray); 861 + systray = NULL; 862 + return; 863 + } 864 + } 865 + for (w = 0, i = systray->icons; i; i = i->next) { 866 + /* make sure the background color stays the same */ 867 + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 868 + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); 869 + XMapRaised(dpy, i->win); 870 + w += systrayspacing; 871 + i->x = w; 872 + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); 873 + w += i->w; 874 + if (i->mon != m) 875 + i->mon = m; 876 + } 877 + w = w ? w + systrayspacing : 1; 878 + x -= w; 879 + XMoveResizeWindow(dpy, systray->win, x - sp, m->by + vp, w, bh); 880 + wc.x = x - sp; 881 + wc.y = m->by + vp; 882 + wc.width = w; 883 + wc.height = bh; 884 + wc.stack_mode = Above; wc.sibling = m->barwin; 885 + XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); 886 + XMapWindow(dpy, systray->win); 887 + XMapSubwindows(dpy, systray->win); 888 + /* redraw background */ 889 + XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); 890 + XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); 891 + XSync(dpy, False); 892 } 893 894 void 895 @@ -2074,6 +2527,16 @@ wintoclient(Window w) 896 return NULL; 897 } 898 899 +Client * 900 +wintosystrayicon(Window w) { 901 + Client *i = NULL; 902 + 903 + if (!showsystray || !w) 904 + return i; 905 + for (i = systray->icons; i && i->win != w; i = i->next) ; 906 + return i; 907 +} 908 + 909 Monitor * 910 wintomon(Window w) 911 { 912 @@ -2127,6 +2590,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) 913 return -1; 914 } 915 916 +Monitor * 917 +systraytomon(Monitor *m) { 918 + Monitor *t; 919 + int i, n; 920 + if(!systraypinning) { 921 + if(!m) 922 + return selmon; 923 + return m == selmon ? m : NULL; 924 + } 925 + for(n = 1, t = mons; t && t->next; n++, t = t->next) ; 926 + for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; 927 + if(systraypinningfailfirst && n < systraypinning) 928 + return mons; 929 + return t; 930 +} 931 + 932 void 933 zoom(const Arg *arg) 934 { 935 -- 936 2.46.2 937