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