dwm-statusbar.diff (27546B)
1 diff --git a/dwm.1 b/dwm.1 2 index ddc8321..53cabd8 100644 3 --- a/dwm.1 4 +++ b/dwm.1 5 @@ -30,6 +30,14 @@ top left corner. The tags which are applied to one or more windows are 6 indicated with an empty square in the top left corner. 7 .P 8 dwm draws a small border around windows to indicate the focus state. 9 +.P 10 +On start, dwm can start additional programs that may be specified in two special 11 +shell scripts (see the FILES section below), autostart_blocking.sh and 12 +autostart.sh. The former is executed first and dwm will wait for its 13 +termination before starting. The latter is executed in the background before 14 +dwm enters its handler loop. 15 +.P 16 +Either of these files may be omitted. 17 .SH OPTIONS 18 .TP 19 .B \-v 20 @@ -92,6 +100,12 @@ Sets monocle layout. 21 .B Mod1\-space 22 Toggles between current and previous layout. 23 .TP 24 +.B Mod1\-Control\-, 25 +Cycles backwards in layout list. 26 +.TP 27 +.B Mod1\-Control\-. 28 +Cycles forwards in layout list. 29 +.TP 30 .B Mod1\-j 31 Focus next window. 32 .TP 33 @@ -152,6 +166,21 @@ Toggles focused window between floating and tiled state. 34 .TP 35 .B Mod1\-Button3 36 Resize focused window while dragging. Tiled windows will be toggled to the floating state. 37 +.SH FILES 38 +The files containing programs to be started along with dwm are searched for in 39 +the following directories: 40 +.IP "1. $XDG_DATA_HOME/dwm" 41 +.IP "2. $HOME/.local/share/dwm" 42 +.IP "3. $HOME/.dwm" 43 +.P 44 +The first existing directory is scanned for any of the autostart files below. 45 +.TP 15 46 +autostart.sh 47 +This file is started as a shell background process before dwm enters its handler 48 +loop. 49 +.TP 15 50 +autostart_blocking.sh 51 +This file is started before any autostart.sh; dwm waits for its termination. 52 .SH CUSTOMIZATION 53 dwm is customized by creating a custom config.h and (re)compiling the source 54 code. This keeps it fast, secure and simple. 55 diff --git a/dwm.c b/dwm.c 56 index f1d86b2..07d2672 100644 57 --- a/dwm.c 58 +++ b/dwm.c 59 @@ -29,6 +29,7 @@ 60 #include <string.h> 61 #include <unistd.h> 62 #include <sys/types.h> 63 +#include <sys/stat.h> 64 #include <sys/wait.h> 65 #include <X11/cursorfont.h> 66 #include <X11/keysym.h> 67 @@ -57,12 +58,28 @@ 68 #define TAGMASK ((1 << LENGTH(tags)) - 1) 69 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 70 71 +#define SYSTEM_TRAY_REQUEST_DOCK 0 72 +/* XEMBED messages */ 73 +#define XEMBED_EMBEDDED_NOTIFY 0 74 +#define XEMBED_WINDOW_ACTIVATE 1 75 +#define XEMBED_FOCUS_IN 4 76 +#define XEMBED_MODALITY_ON 10 77 +#define XEMBED_MAPPED (1 << 0) 78 +#define XEMBED_WINDOW_ACTIVATE 1 79 +#define XEMBED_WINDOW_DEACTIVATE 2 80 +#define VERSION_MAJOR 0 81 +#define VERSION_MINOR 0 82 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR 83 + 84 + 85 /* enums */ 86 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 87 enum { SchemeNorm, SchemeSel }; /* color schemes */ 88 enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 89 + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, 90 NetWMFullscreen, NetActiveWindow, NetWMWindowType, 91 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 92 +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ 93 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 94 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 95 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 96 @@ -141,6 +158,12 @@ typedef struct { 97 int monitor; 98 } Rule; 99 100 +typedef struct Systray Systray; 101 +struct Systray { 102 + Window win; 103 + Client *icons; 104 +}; 105 + 106 /* function declarations */ 107 static void applyrules(Client *c); 108 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 109 @@ -157,6 +180,7 @@ static void configure(Client *c); 110 static void configurenotify(XEvent *e); 111 static void configurerequest(XEvent *e); 112 static Monitor *createmon(void); 113 +static void cyclelayout(const Arg *arg); 114 static void destroynotify(XEvent *e); 115 static void detach(Client *c); 116 static void detachstack(Client *c); 117 @@ -172,6 +196,7 @@ static void focusstack(const Arg *arg); 118 static Atom getatomprop(Client *c, Atom prop); 119 static int getrootptr(int *x, int *y); 120 static long getstate(Window w); 121 +static unsigned int getsystraywidth(); 122 static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 123 static void grabbuttons(Client *c, int focused); 124 static void grabkeys(void); 125 @@ -189,13 +214,17 @@ static void pop(Client *c); 126 static void propertynotify(XEvent *e); 127 static void quit(const Arg *arg); 128 static Monitor *recttomon(int x, int y, int w, int h); 129 +static void removesystrayicon(Client *i); 130 static void resize(Client *c, int x, int y, int w, int h, int interact); 131 +static void resizebarwin(Monitor *m); 132 static void resizeclient(Client *c, int x, int y, int w, int h); 133 static void resizemouse(const Arg *arg); 134 +static void resizerequest(XEvent *e); 135 static void restack(Monitor *m); 136 static void run(void); 137 +static void runautostart(void); 138 static void scan(void); 139 -static int sendevent(Client *c, Atom proto); 140 +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); 141 static void sendmon(Client *c, Monitor *m); 142 static void setclientstate(Client *c, long state); 143 static void setfocus(Client *c); 144 @@ -206,6 +235,8 @@ static void setup(void); 145 static void seturgent(Client *c, int urg); 146 static void showhide(Client *c); 147 static void spawn(const Arg *arg); 148 +static int statuswidth(void); 149 +static Monitor *systraytomon(Monitor *m); 150 static void tag(const Arg *arg); 151 static void tagmon(const Arg *arg); 152 static void tile(Monitor *m); 153 @@ -223,18 +254,23 @@ static int updategeom(void); 154 static void updatenumlockmask(void); 155 static void updatesizehints(Client *c); 156 static void updatestatus(void); 157 +static void updatesystray(void); 158 +static void updatesystrayicongeom(Client *i, int w, int h); 159 +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); 160 static void updatetitle(Client *c); 161 static void updatewindowtype(Client *c); 162 static void updatewmhints(Client *c); 163 static void view(const Arg *arg); 164 static Client *wintoclient(Window w); 165 static Monitor *wintomon(Window w); 166 +static Client *wintosystrayicon(Window w); 167 static int xerror(Display *dpy, XErrorEvent *ee); 168 static int xerrordummy(Display *dpy, XErrorEvent *ee); 169 static int xerrorstart(Display *dpy, XErrorEvent *ee); 170 static void zoom(const Arg *arg); 171 172 /* variables */ 173 +static Systray *systray = NULL; 174 static const char broken[] = "broken"; 175 static char stext[256]; 176 static int screen; 177 @@ -257,9 +293,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { 178 [MapRequest] = maprequest, 179 [MotionNotify] = motionnotify, 180 [PropertyNotify] = propertynotify, 181 + [ResizeRequest] = resizerequest, 182 [UnmapNotify] = unmapnotify 183 }; 184 -static Atom wmatom[WMLast], netatom[NetLast]; 185 +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; 186 static int running = 1; 187 static Cur *cursor[CurLast]; 188 static Clr **scheme; 189 @@ -441,7 +478,7 @@ buttonpress(XEvent *e) 190 arg.ui = 1 << i; 191 } else if (ev->x < x + TEXTW(selmon->ltsymbol)) 192 click = ClkLtSymbol; 193 - else if (ev->x > selmon->ww - (int)TEXTW(stext)) 194 + else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) 195 click = ClkStatusText; 196 else 197 click = ClkWinTitle; 198 @@ -484,6 +521,13 @@ cleanup(void) 199 XUngrabKey(dpy, AnyKey, AnyModifier, root); 200 while (mons) 201 cleanupmon(mons); 202 + 203 + if (showsystray) { 204 + XUnmapWindow(dpy, systray->win); 205 + XDestroyWindow(dpy, systray->win); 206 + free(systray); 207 + } 208 + 209 for (i = 0; i < CurLast; i++) 210 drw_cur_free(drw, cursor[i]); 211 for (i = 0; i < LENGTH(colors); i++) 212 @@ -515,9 +559,58 @@ cleanupmon(Monitor *mon) 213 void 214 clientmessage(XEvent *e) 215 { 216 + XWindowAttributes wa; 217 + XSetWindowAttributes swa; 218 XClientMessageEvent *cme = &e->xclient; 219 Client *c = wintoclient(cme->window); 220 221 + if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { 222 + /* add systray icons */ 223 + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { 224 + if (!(c = (Client *)calloc(1, sizeof(Client)))) 225 + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 226 + if (!(c->win = cme->data.l[2])) { 227 + free(c); 228 + return; 229 + } 230 + c->mon = selmon; 231 + c->next = systray->icons; 232 + systray->icons = c; 233 + if (!XGetWindowAttributes(dpy, c->win, &wa)) { 234 + /* use sane defaults */ 235 + wa.width = bh; 236 + wa.height = bh; 237 + wa.border_width = 0; 238 + } 239 + c->x = c->oldx = c->y = c->oldy = 0; 240 + c->w = c->oldw = wa.width; 241 + c->h = c->oldh = wa.height; 242 + c->oldbw = wa.border_width; 243 + c->bw = 0; 244 + c->isfloating = True; 245 + /* reuse tags field as mapped status */ 246 + c->tags = 1; 247 + updatesizehints(c); 248 + updatesystrayicongeom(c, wa.width, wa.height); 249 + XAddToSaveSet(dpy, c->win); 250 + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); 251 + XReparentWindow(dpy, c->win, systray->win, 0, 0); 252 + /* use parents background color */ 253 + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 254 + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); 255 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 256 + /* FIXME not sure if I have to send these events, too */ 257 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 258 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 259 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 260 + XSync(dpy, False); 261 + resizebarwin(selmon); 262 + updatesystray(); 263 + setclientstate(c, NormalState); 264 + } 265 + return; 266 + } 267 + 268 if (!c) 269 return; 270 if (cme->message_type == netatom[NetWMState]) { 271 @@ -570,7 +663,7 @@ configurenotify(XEvent *e) 272 for (c = m->clients; c; c = c->next) 273 if (c->isfullscreen) 274 resizeclient(c, m->mx, m->my, m->mw, m->mh); 275 - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 276 + resizebarwin(m); 277 } 278 focus(NULL); 279 arrange(NULL); 280 @@ -647,6 +740,23 @@ createmon(void) 281 return m; 282 } 283 284 +void 285 +cyclelayout(const Arg *arg) { 286 + Layout *l; 287 + for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++); 288 + if(arg->i > 0) { 289 + if(l->symbol && (l + 1)->symbol) 290 + setlayout(&((Arg) { .v = (l + 1) })); 291 + else 292 + setlayout(&((Arg) { .v = layouts })); 293 + } else { 294 + if(l != layouts && (l - 1)->symbol) 295 + setlayout(&((Arg) { .v = (l - 1) })); 296 + else 297 + setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] })); 298 + } 299 +} 300 + 301 void 302 destroynotify(XEvent *e) 303 { 304 @@ -655,6 +765,11 @@ destroynotify(XEvent *e) 305 306 if ((c = wintoclient(ev->window))) 307 unmanage(c, 1); 308 + else if ((c = wintosystrayicon(ev->window))) { 309 + removesystrayicon(c); 310 + resizebarwin(selmon); 311 + updatesystray(); 312 + } 313 } 314 315 void 316 @@ -695,13 +810,33 @@ dirtomon(int dir) 317 return m; 318 } 319 320 +int 321 +statuswidth(void) { 322 + char* ts; 323 + int tw = 0; 324 + 325 + tw = TEXTW(stext); 326 + for (ts = stext; *ts; ts++) { 327 + if ((unsigned int)*ts <= LENGTH(colors)) 328 + tw += lrpad; 329 + } 330 + 331 +// tw -= lrpad; 332 + 333 + return tw; 334 +} 335 + 336 void 337 drawbar(Monitor *m) 338 { 339 - int x, w, tw = 0; 340 + int x, w, tw = 0, stw = 0; 341 int boxs = drw->fonts->h / 9; 342 int boxw = drw->fonts->h / 6 + 2; 343 unsigned int i, occ = 0, urg = 0; 344 + char *ts = stext; 345 + char *tp = stext; 346 + int tx = 0; 347 + char ctmp; 348 Client *c; 349 350 if (!m->showbar) 351 @@ -710,8 +845,19 @@ drawbar(Monitor *m) 352 /* draw status first so it can be overdrawn by tags later */ 353 if (m == selmon) { /* status is only drawn on selected monitor */ 354 drw_setscheme(drw, scheme[SchemeNorm]); 355 - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 356 - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); 357 + tw = statuswidth(); 358 + ts = stext; 359 + while (1) { 360 + if ((unsigned int)*ts > LENGTH(colors)) { ts++; continue ; } 361 + ctmp = *ts; 362 + *ts = '\0'; 363 + drw_text(drw, m->ww - tw + tx - getsystraywidth(), 0, tw - tx, bh, lrpad / 2, tp, 0); 364 + tx += TEXTW(tp); 365 + if (ctmp == '\0') { break; } 366 + drw_setscheme(drw, scheme[(unsigned int)(ctmp-1)]); 367 + *ts = ctmp; 368 + tp = ++ts; 369 + } 370 } 371 372 for (c = m->clients; c; c = c->next) { 373 @@ -734,7 +880,7 @@ drawbar(Monitor *m) 374 drw_setscheme(drw, scheme[SchemeNorm]); 375 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 376 377 - if ((w = m->ww - tw - x) > bh) { 378 + if ((w = m->ww - tw - stw - x - getsystraywidth()) > bh) { 379 if (m->sel) { 380 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); 381 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); 382 @@ -745,7 +891,7 @@ drawbar(Monitor *m) 383 drw_rect(drw, x, 0, w, bh, 1, 1); 384 } 385 } 386 - drw_map(drw, m->barwin, 0, 0, m->ww, bh); 387 + drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); 388 } 389 390 void 391 @@ -782,8 +928,11 @@ expose(XEvent *e) 392 Monitor *m; 393 XExposeEvent *ev = &e->xexpose; 394 395 - if (ev->count == 0 && (m = wintomon(ev->window))) 396 + if (ev->count == 0 && (m = wintomon(ev->window))) { 397 drawbar(m); 398 + if (m == selmon) 399 + updatesystray(); 400 + } 401 } 402 403 void 404 @@ -869,14 +1018,32 @@ getatomprop(Client *c, Atom prop) 405 unsigned char *p = NULL; 406 Atom da, atom = None; 407 408 - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 409 + /* FIXME getatomprop should return the number of items and a pointer to 410 + * the stored data instead of this workaround */ 411 + Atom req = XA_ATOM; 412 + if (prop == xatom[XembedInfo]) 413 + req = xatom[XembedInfo]; 414 + 415 + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, 416 &da, &di, &dl, &dl, &p) == Success && p) { 417 atom = *(Atom *)p; 418 + if (da == xatom[XembedInfo] && dl == 2) 419 + atom = ((Atom *)p)[1]; 420 XFree(p); 421 } 422 return atom; 423 } 424 425 +unsigned int 426 +getsystraywidth() 427 +{ 428 + unsigned int w = 0; 429 + Client *i; 430 + if(showsystray) 431 + for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; 432 + return w ? w + systrayspacing : 1; 433 +} 434 + 435 int 436 getrootptr(int *x, int *y) 437 { 438 @@ -1017,7 +1184,8 @@ killclient(const Arg *arg) 439 { 440 if (!selmon->sel) 441 return; 442 - if (!sendevent(selmon->sel, wmatom[WMDelete])) { 443 + 444 + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { 445 XGrabServer(dpy); 446 XSetErrorHandler(xerrordummy); 447 XSetCloseDownMode(dpy, DestroyAll); 448 @@ -1104,6 +1272,13 @@ maprequest(XEvent *e) 449 static XWindowAttributes wa; 450 XMapRequestEvent *ev = &e->xmaprequest; 451 452 + Client *i; 453 + if ((i = wintosystrayicon(ev->window))) { 454 + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); 455 + resizebarwin(selmon); 456 + updatesystray(); 457 + } 458 + 459 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) 460 return; 461 if (!wintoclient(ev->window)) 462 @@ -1225,6 +1400,17 @@ propertynotify(XEvent *e) 463 Window trans; 464 XPropertyEvent *ev = &e->xproperty; 465 466 + if ((c = wintosystrayicon(ev->window))) { 467 + if (ev->atom == XA_WM_NORMAL_HINTS) { 468 + updatesizehints(c); 469 + updatesystrayicongeom(c, c->w, c->h); 470 + } 471 + else 472 + updatesystrayiconstate(c, ev); 473 + resizebarwin(selmon); 474 + updatesystray(); 475 + } 476 + 477 if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 478 updatestatus(); 479 else if (ev->state == PropertyDelete) 480 @@ -1275,6 +1461,19 @@ recttomon(int x, int y, int w, int h) 481 return r; 482 } 483 484 +void 485 +removesystrayicon(Client *i) 486 +{ 487 + Client **ii; 488 + 489 + if (!showsystray || !i) 490 + return; 491 + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); 492 + if (ii) 493 + *ii = i->next; 494 + free(i); 495 +} 496 + 497 void 498 resize(Client *c, int x, int y, int w, int h, int interact) 499 { 500 @@ -1282,6 +1481,14 @@ resize(Client *c, int x, int y, int w, int h, int interact) 501 resizeclient(c, x, y, w, h); 502 } 503 504 +void 505 +resizebarwin(Monitor *m) { 506 + unsigned int w = m->ww; 507 + if (showsystray && m == systraytomon(m) && !systrayonleft) 508 + w -= getsystraywidth(); 509 + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); 510 +} 511 + 512 void 513 resizeclient(Client *c, int x, int y, int w, int h) 514 { 515 @@ -1297,6 +1504,19 @@ resizeclient(Client *c, int x, int y, int w, int h) 516 XSync(dpy, False); 517 } 518 519 +void 520 +resizerequest(XEvent *e) 521 +{ 522 + XResizeRequestEvent *ev = &e->xresizerequest; 523 + Client *i; 524 + 525 + if ((i = wintosystrayicon(ev->window))) { 526 + updatesystrayicongeom(i, ev->width, ev->height); 527 + resizebarwin(selmon); 528 + updatesystray(); 529 + } 530 +} 531 + 532 void 533 resizemouse(const Arg *arg) 534 { 535 @@ -1390,6 +1610,39 @@ run(void) 536 handler[ev.type](&ev); /* call handler */ 537 } 538 539 +void 540 +runautostart(void) 541 +{ 542 + char rcpath[PATH_MAX]; 543 + char* home; 544 + pid_t pid; 545 + 546 + if ((home = getenv("HOME")) == NULL) { 547 + home = "/"; 548 + } 549 + 550 + snprintf(rcpath, sizeof(rcpath), "%s/%s", home, dwmrc); 551 + if (access(rcpath, X_OK) != 0) 552 + return; 553 + 554 +again: 555 + if ((pid = fork()) == -1) { 556 + fprintf(stderr, "error: unable to fork for autostart, retrying...\n"); 557 + sleep(3); 558 + goto again; 559 + } 560 + 561 + if (pid == 0) { // child 562 + 563 + execl(rcpath, rcpath, NULL); 564 + _exit(1); 565 + } 566 + 567 + if (waitpid(pid, NULL, 0) == -1) { 568 + fprintf(stderr, "warn: unable to wait for autostart, probably still alive\n"); 569 + } 570 +} 571 + 572 void 573 scan(void) 574 { 575 @@ -1443,26 +1696,37 @@ setclientstate(Client *c, long state) 576 } 577 578 int 579 -sendevent(Client *c, Atom proto) 580 +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) 581 { 582 int n; 583 - Atom *protocols; 584 + Atom *protocols, mt; 585 int exists = 0; 586 XEvent ev; 587 588 - if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { 589 - while (!exists && n--) 590 - exists = protocols[n] == proto; 591 - XFree(protocols); 592 + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { 593 + mt = wmatom[WMProtocols]; 594 + if (XGetWMProtocols(dpy, w, &protocols, &n)) { 595 + while (!exists && n--) 596 + exists = protocols[n] == proto; 597 + XFree(protocols); 598 + } 599 } 600 + else { 601 + exists = True; 602 + mt = proto; 603 + } 604 + 605 if (exists) { 606 ev.type = ClientMessage; 607 - ev.xclient.window = c->win; 608 - ev.xclient.message_type = wmatom[WMProtocols]; 609 + ev.xclient.window = w; 610 + ev.xclient.message_type = mt; 611 ev.xclient.format = 32; 612 - ev.xclient.data.l[0] = proto; 613 - ev.xclient.data.l[1] = CurrentTime; 614 - XSendEvent(dpy, c->win, False, NoEventMask, &ev); 615 + ev.xclient.data.l[0] = d0; 616 + ev.xclient.data.l[1] = d1; 617 + ev.xclient.data.l[2] = d2; 618 + ev.xclient.data.l[3] = d3; 619 + ev.xclient.data.l[4] = d4; 620 + XSendEvent(dpy, w, False, mask, &ev); 621 } 622 return exists; 623 } 624 @@ -1476,7 +1740,7 @@ setfocus(Client *c) 625 XA_WINDOW, 32, PropModeReplace, 626 (unsigned char *) &(c->win), 1); 627 } 628 - sendevent(c, wmatom[WMTakeFocus]); 629 + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); 630 } 631 632 void 633 @@ -1561,8 +1825,8 @@ setup(void) 634 drw = drw_create(dpy, screen, root, sw, sh); 635 if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 636 die("no fonts could be loaded."); 637 - lrpad = drw->fonts->h; 638 - bh = drw->fonts->h + 2; 639 + lrpad = drw->fonts->h + horizpadbar; 640 + bh = drw->fonts->h + vertpadbar; 641 updategeom(); 642 /* init atoms */ 643 utf8string = XInternAtom(dpy, "UTF8_STRING", False); 644 @@ -1572,6 +1836,10 @@ setup(void) 645 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 646 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 647 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 648 + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); 649 + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); 650 + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); 651 + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); 652 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 653 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 654 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 655 @@ -1579,6 +1847,9 @@ setup(void) 656 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 657 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 658 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 659 + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); 660 + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); 661 + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); 662 /* init cursors */ 663 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 664 cursor[CurResize] = drw_cur_create(drw, XC_sizing); 665 @@ -1587,6 +1858,8 @@ setup(void) 666 scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); 667 for (i = 0; i < LENGTH(colors); i++) 668 scheme[i] = drw_scm_create(drw, colors[i], 3); 669 + /* init system tray */ 670 + updatesystray(); 671 /* init bars */ 672 updatebars(); 673 updatestatus(); 674 @@ -1649,8 +1922,6 @@ spawn(const Arg *arg) 675 { 676 struct sigaction sa; 677 678 - if (arg->v == dmenucmd) 679 - dmenumon[0] = '0' + selmon->num; 680 if (fork() == 0) { 681 if (dpy) 682 close(ConnectionNumber(dpy)); 683 @@ -1717,7 +1988,18 @@ togglebar(const Arg *arg) 684 { 685 selmon->showbar = !selmon->showbar; 686 updatebarpos(selmon); 687 - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 688 + resizebarwin(selmon); 689 + if (showsystray) { 690 + XWindowChanges wc; 691 + if (!selmon->showbar) 692 + wc.y = -bh; 693 + else if (selmon->showbar) { 694 + wc.y = 0; 695 + if (!selmon->topbar) 696 + wc.y = selmon->mh - bh; 697 + } 698 + XConfigureWindow(dpy, systray->win, CWY, &wc); 699 + } 700 arrange(selmon); 701 } 702 703 @@ -1813,11 +2095,18 @@ unmapnotify(XEvent *e) 704 else 705 unmanage(c, 0); 706 } 707 + else if ((c = wintosystrayicon(ev->window))) { 708 + /* KLUDGE! sometimes icons occasionally unmap their windows, but do 709 + * _not_ destroy them. We map those windows back */ 710 + XMapRaised(dpy, c->win); 711 + updatesystray(); 712 + } 713 } 714 715 void 716 updatebars(void) 717 { 718 + unsigned int w; 719 Monitor *m; 720 XSetWindowAttributes wa = { 721 .override_redirect = True, 722 @@ -1828,10 +2117,15 @@ updatebars(void) 723 for (m = mons; m; m = m->next) { 724 if (m->barwin) 725 continue; 726 - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), 727 + w = m->ww; 728 + if (showsystray && m == systraytomon(m)) 729 + w -= getsystraywidth(); 730 + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), 731 CopyFromParent, DefaultVisual(dpy, screen), 732 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 733 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 734 + if (showsystray && m == systraytomon(m)) 735 + XMapRaised(dpy, systray->win); 736 XMapRaised(dpy, m->barwin); 737 XSetClassHint(dpy, m->barwin, &ch); 738 } 739 @@ -1851,7 +2145,7 @@ updatebarpos(Monitor *m) 740 } 741 742 void 743 -updateclientlist() 744 +updateclientlist(void) 745 { 746 Client *c; 747 Monitor *m; 748 @@ -2008,6 +2302,125 @@ updatestatus(void) 749 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 750 strcpy(stext, "dwm-"VERSION); 751 drawbar(selmon); 752 + updatesystray(); 753 +} 754 + 755 + 756 +void 757 +updatesystrayicongeom(Client *i, int w, int h) 758 +{ 759 + if (i) { 760 + i->h = bh; 761 + if (w == h) 762 + i->w = bh; 763 + else if (h == bh) 764 + i->w = w; 765 + else 766 + i->w = (int) ((float)bh * ((float)w / (float)h)); 767 + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); 768 + /* force icons into the systray dimensions if they don't want to */ 769 + if (i->h > bh) { 770 + if (i->w == i->h) 771 + i->w = bh; 772 + else 773 + i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); 774 + i->h = bh; 775 + } 776 + } 777 +} 778 + 779 +void 780 +updatesystrayiconstate(Client *i, XPropertyEvent *ev) 781 +{ 782 + long flags; 783 + int code = 0; 784 + 785 + if (!showsystray || !i || ev->atom != xatom[XembedInfo] || 786 + !(flags = getatomprop(i, xatom[XembedInfo]))) 787 + return; 788 + 789 + if (flags & XEMBED_MAPPED && !i->tags) { 790 + i->tags = 1; 791 + code = XEMBED_WINDOW_ACTIVATE; 792 + XMapRaised(dpy, i->win); 793 + setclientstate(i, NormalState); 794 + } 795 + else if (!(flags & XEMBED_MAPPED) && i->tags) { 796 + i->tags = 0; 797 + code = XEMBED_WINDOW_DEACTIVATE; 798 + XUnmapWindow(dpy, i->win); 799 + setclientstate(i, WithdrawnState); 800 + } 801 + else 802 + return; 803 + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, 804 + systray->win, XEMBED_EMBEDDED_VERSION); 805 +} 806 + 807 +void 808 +updatesystray(void) 809 +{ 810 + XSetWindowAttributes wa; 811 + XWindowChanges wc; 812 + Client *i; 813 + Monitor *m = systraytomon(NULL); 814 + unsigned int x = m->mx + m->mw; 815 + unsigned int sw = TEXTW(stext) - lrpad + systrayspacing; 816 + unsigned int w = 1; 817 + 818 + if (!showsystray) 819 + return; 820 + if (systrayonleft) 821 + x -= sw + lrpad / 2; 822 + if (!systray) { 823 + /* init systray */ 824 + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) 825 + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); 826 + systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); 827 + wa.event_mask = ButtonPressMask | ExposureMask; 828 + wa.override_redirect = True; 829 + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 830 + XSelectInput(dpy, systray->win, SubstructureNotifyMask); 831 + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, 832 + PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); 833 + XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); 834 + XMapRaised(dpy, systray->win); 835 + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); 836 + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { 837 + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); 838 + XSync(dpy, False); 839 + } 840 + else { 841 + fprintf(stderr, "dwm: unable to obtain system tray.\n"); 842 + free(systray); 843 + systray = NULL; 844 + return; 845 + } 846 + } 847 + for (w = 0, i = systray->icons; i; i = i->next) { 848 + /* make sure the background color stays the same */ 849 + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 850 + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); 851 + XMapRaised(dpy, i->win); 852 + w += systrayspacing; 853 + i->x = w; 854 + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); 855 + w += i->w; 856 + if (i->mon != m) 857 + i->mon = m; 858 + } 859 + w = w ? w + systrayspacing : 1; 860 + x -= w; 861 + XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); 862 + wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; 863 + wc.stack_mode = Above; wc.sibling = m->barwin; 864 + XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); 865 + XMapWindow(dpy, systray->win); 866 + XMapSubwindows(dpy, systray->win); 867 + /* redraw background */ 868 + XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); 869 + XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); 870 + XSync(dpy, False); 871 } 872 873 void 874 @@ -2075,6 +2488,16 @@ wintoclient(Window w) 875 return NULL; 876 } 877 878 +Client * 879 +wintosystrayicon(Window w) { 880 + Client *i = NULL; 881 + 882 + if (!showsystray || !w) 883 + return i; 884 + for (i = systray->icons; i && i->win != w; i = i->next) ; 885 + return i; 886 +} 887 + 888 Monitor * 889 wintomon(Window w) 890 { 891 @@ -2128,6 +2551,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) 892 return -1; 893 } 894 895 +Monitor * 896 +systraytomon(Monitor *m) { 897 + Monitor *t; 898 + int i, n; 899 + if(!systraypinning) { 900 + if(!m) 901 + return selmon; 902 + return m == selmon ? m : NULL; 903 + } 904 + for(n = 1, t = mons; t && t->next; n++, t = t->next) ; 905 + for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; 906 + if(systraypinningfailfirst && n < systraypinning) 907 + return mons; 908 + return t; 909 +} 910 + 911 void 912 zoom(const Arg *arg) 913 { 914 @@ -2158,6 +2597,7 @@ main(int argc, char *argv[]) 915 die("pledge"); 916 #endif /* __OpenBSD__ */ 917 scan(); 918 + runautostart(); 919 run(); 920 cleanup(); 921 XCloseDisplay(dpy);