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