commit 9694fe8634c3038702bf9ebde0cfab3a0f6d1732
parent a4cf6a171b4b510fade8d5b7ec857ddcc3db49ec
Author: Christoph Lohmann <20h@r-36.net>
Date: Tue, 3 Nov 2015 19:58:08 +0100
Merge branch 'master' of git://git.suckless.org/sites because I suck.
Diffstat:
18 files changed, 2179 insertions(+), 1995 deletions(-)
diff --git a/dwm.suckless.org/patches/dwm-14343e69cc59-systray.diff b/dwm.suckless.org/patches/dwm-14343e69cc59-systray.diff
@@ -1,687 +0,0 @@
-diff --git a/config.def.h b/config.def.h
-index eaae8f3..42f4165 100644
---- a/config.def.h
-+++ b/config.def.h
-@@ -15,6 +15,10 @@ static const char selbgcolor[] = "#005577";
- static const char selfgcolor[] = "#eeeeee";
- static const unsigned int borderpx = 1; /* border pixel of windows */
- static const unsigned int snap = 32; /* snap pixel */
-+static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
-+static const unsigned int systrayspacing = 2; /* systray spacing */
-+static const Bool systraypinningfailfirst = True; /* True: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
-+static const Bool showsystray = True; /* False means no systray */
- static const Bool showbar = True; /* False means no bar */
- static const Bool topbar = True; /* False means bottom bar */
-
-diff --git a/dwm.c b/dwm.c
-index 169adcb..859143e 100644
---- a/dwm.c
-+++ b/dwm.c
-@@ -57,12 +57,30 @@
- #define TAGMASK ((1 << LENGTH(tags)) - 1)
- #define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
-
-+#define SYSTEM_TRAY_REQUEST_DOCK 0
-+#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
-+
-+/* XEMBED messages */
-+#define XEMBED_EMBEDDED_NOTIFY 0
-+#define XEMBED_WINDOW_ACTIVATE 1
-+#define XEMBED_FOCUS_IN 4
-+#define XEMBED_MODALITY_ON 10
-+
-+#define XEMBED_MAPPED (1 << 0)
-+#define XEMBED_WINDOW_ACTIVATE 1
-+#define XEMBED_WINDOW_DEACTIVATE 2
-+
-+#define VERSION_MAJOR 0
-+#define VERSION_MINOR 0
-+#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
-+
- /* enums */
- enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
- enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */
--enum { NetSupported, NetWMName, NetWMState,
-- NetWMFullscreen, NetActiveWindow, NetWMWindowType,
-- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
-+enum { NetSupported, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation,
-+ NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow, NetWMWindowType,
-+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
-+enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
- enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
- enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
- ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
-@@ -141,6 +159,12 @@ typedef struct {
- int monitor;
- } Rule;
-
-+typedef struct Systray Systray;
-+struct Systray {
-+ Window win;
-+ Client *icons;
-+};
-+
- /* function declarations */
- static void applyrules(Client *c);
- static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact);
-@@ -170,8 +194,10 @@ static void focus(Client *c);
- static void focusin(XEvent *e);
- static void focusmon(const Arg *arg);
- static void focusstack(const Arg *arg);
-+static Atom getatomprop(Client *c, Atom prop);
- static Bool getrootptr(int *x, int *y);
- static long getstate(Window w);
-+static unsigned int getsystraywidth();
- static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
- static void grabbuttons(Client *c, Bool focused);
- static void grabkeys(void);
-@@ -189,13 +215,16 @@ static void pop(Client *);
- static void propertynotify(XEvent *e);
- static void quit(const Arg *arg);
- static Monitor *recttomon(int x, int y, int w, int h);
-+static void removesystrayicon(Client *i);
- static void resize(Client *c, int x, int y, int w, int h, Bool interact);
-+static void resizebarwin(Monitor *m);
- static void resizeclient(Client *c, int x, int y, int w, int h);
- static void resizemouse(const Arg *arg);
-+static void resizerequest(XEvent *e);
- static void restack(Monitor *m);
- static void run(void);
- static void scan(void);
--static Bool sendevent(Client *c, Atom proto);
-+static Bool sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
- static void sendmon(Client *c, Monitor *m);
- static void setclientstate(Client *c, long state);
- static void setfocus(Client *c);
-@@ -206,6 +235,7 @@ static void setup(void);
- static void showhide(Client *c);
- static void sigchld(int unused);
- static void spawn(const Arg *arg);
-+static Monitor *systraytomon(Monitor *m);
- static void tag(const Arg *arg);
- static void tagmon(const Arg *arg);
- static void tile(Monitor *);
-@@ -223,18 +253,24 @@ static void updateclientlist(void);
- static void updatenumlockmask(void);
- static void updatesizehints(Client *c);
- static void updatestatus(void);
-+static void updatesystray(void);
-+static void updatesystrayicongeom(Client *i, int w, int h);
-+static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
- static void updatewindowtype(Client *c);
- static void updatetitle(Client *c);
- static void updatewmhints(Client *c);
- static void view(const Arg *arg);
- static Client *wintoclient(Window w);
- static Monitor *wintomon(Window w);
-+static Client *wintosystrayicon(Window w);
- static int xerror(Display *dpy, XErrorEvent *ee);
- static int xerrordummy(Display *dpy, XErrorEvent *ee);
- static int xerrorstart(Display *dpy, XErrorEvent *ee);
- static void zoom(const Arg *arg);
-
- /* variables */
-+static Systray *systray = NULL;
-+static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
- static const char broken[] = "broken";
- static char stext[256];
- static int screen;
-@@ -256,9 +292,10 @@ static void (*handler[LASTEvent]) (XEvent *) = {
- [MapRequest] = maprequest,
- [MotionNotify] = motionnotify,
- [PropertyNotify] = propertynotify,
-+ [ResizeRequest] = resizerequest,
- [UnmapNotify] = unmapnotify
- };
--static Atom wmatom[WMLast], netatom[NetLast];
-+static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
- static Bool running = True;
- static Cur *cursor[CurLast];
- static ClrScheme scheme[SchemeLast];
-@@ -471,6 +508,11 @@ cleanup(void) {
- XUngrabKey(dpy, AnyKey, AnyModifier, root);
- while(mons)
- cleanupmon(mons);
-+ if(showsystray) {
-+ XUnmapWindow(dpy, systray->win);
-+ XDestroyWindow(dpy, systray->win);
-+ free(systray);
-+ }
- drw_cur_free(drw, cursor[CurNormal]);
- drw_cur_free(drw, cursor[CurResize]);
- drw_cur_free(drw, cursor[CurMove]);
-@@ -515,9 +557,49 @@ clearurgent(Client *c) {
-
- void
- clientmessage(XEvent *e) {
-+ XWindowAttributes wa;
-+ XSetWindowAttributes swa;
- XClientMessageEvent *cme = &e->xclient;
- Client *c = wintoclient(cme->window);
-
-+ if(showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
-+ /* add systray icons */
-+ if(cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
-+ if(!(c = (Client *)calloc(1, sizeof(Client))))
-+ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
-+ c->win = cme->data.l[2];
-+ c->mon = selmon;
-+ c->next = systray->icons;
-+ systray->icons = c;
-+ XGetWindowAttributes(dpy, c->win, &wa);
-+ c->x = c->oldx = c->y = c->oldy = 0;
-+ c->w = c->oldw = wa.width;
-+ c->h = c->oldh = wa.height;
-+ c->oldbw = wa.border_width;
-+ c->bw = 0;
-+ c->isfloating = True;
-+ /* reuse tags field as mapped status */
-+ c->tags = 1;
-+ updatesizehints(c);
-+ updatesystrayicongeom(c, wa.width, wa.height);
-+ XAddToSaveSet(dpy, c->win);
-+ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
-+ XReparentWindow(dpy, c->win, systray->win, 0, 0);
-+ /* use parents background color */
-+ swa.background_pixel = scheme[SchemeNorm].bg->pix;
-+ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
-+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
-+ /* FIXME not sure if I have to send these events, too */
-+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
-+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
-+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
-+ XSync(dpy, False);
-+ resizebarwin(selmon);
-+ updatesystray();
-+ setclientstate(c, NormalState);
-+ }
-+ return;
-+ }
- if(!c)
- return;
- if(cme->message_type == netatom[NetWMState]) {
-@@ -567,7 +649,7 @@ configurenotify(XEvent *e) {
- drw_resize(drw, sw, bh);
- updatebars();
- for(m = mons; m; m = m->next)
-- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
-+ resizebarwin(m);
- focus(NULL);
- arrange(NULL);
- }
-@@ -651,6 +733,11 @@ destroynotify(XEvent *e) {
-
- if((c = wintoclient(ev->window)))
- unmanage(c, True);
-+ else if((c = wintosystrayicon(ev->window))) {
-+ removesystrayicon(c);
-+ resizebarwin(selmon);
-+ updatesystray();
-+ }
- }
-
- void
-@@ -695,6 +782,7 @@ drawbar(Monitor *m) {
- unsigned int i, occ = 0, urg = 0;
- Client *c;
-
-+ resizebarwin(m);
- for(c = m->clients; c; c = c->next) {
- occ |= c->tags;
- if(c->isurgent)
-@@ -717,6 +805,9 @@ drawbar(Monitor *m) {
- if(m == selmon) { /* status is only drawn on selected monitor */
- w = TEXTW(stext);
- x = m->ww - w;
-+ if(showsystray && m == systraytomon(m)) {
-+ x -= getsystraywidth();
-+ }
- if(x < xx) {
- x = xx;
- w = m->ww - xx;
-@@ -746,6 +837,7 @@ drawbars(void) {
-
- for(m = mons; m; m = m->next)
- drawbar(m);
-+ updatesystray();
- }
-
- void
-@@ -772,8 +864,11 @@ expose(XEvent *e) {
- Monitor *m;
- XExposeEvent *ev = &e->xexpose;
-
-- if(ev->count == 0 && (m = wintomon(ev->window)))
-+ if(ev->count == 0 && (m = wintomon(ev->window))) {
- drawbar(m);
-+ if(m == selmon)
-+ updatesystray();
-+ }
- }
-
- void
-@@ -856,10 +951,17 @@ getatomprop(Client *c, Atom prop) {
- unsigned long dl;
- unsigned char *p = NULL;
- Atom da, atom = None;
-+ /* FIXME getatomprop should return the number of items and a pointer to
-+ * the stored data instead of this workaround */
-+ Atom req = XA_ATOM;
-+ if(prop == xatom[XembedInfo])
-+ req = xatom[XembedInfo];
-
-- if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
-+ if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
- &da, &di, &dl, &dl, &p) == Success && p) {
- atom = *(Atom *)p;
-+ if(da == xatom[XembedInfo] && dl == 2)
-+ atom = ((Atom *)p)[1];
- XFree(p);
- }
- return atom;
-@@ -891,6 +993,15 @@ getstate(Window w) {
- return result;
- }
-
-+unsigned int
-+getsystraywidth() {
-+ unsigned int w = 0;
-+ Client *i;
-+ if(showsystray)
-+ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
-+ return w ? w + systrayspacing : 1;
-+}
-+
- Bool
- gettextprop(Window w, Atom atom, char *text, unsigned int size) {
- char **list = NULL;
-@@ -991,7 +1102,7 @@ void
- killclient(const Arg *arg) {
- if(!selmon->sel)
- return;
-- if(!sendevent(selmon->sel, wmatom[WMDelete])) {
-+ if(!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
- XGrabServer(dpy);
- XSetErrorHandler(xerrordummy);
- XSetCloseDownMode(dpy, DestroyAll);
-@@ -1077,6 +1188,12 @@ void
- maprequest(XEvent *e) {
- static XWindowAttributes wa;
- XMapRequestEvent *ev = &e->xmaprequest;
-+ Client *i;
-+ if((i = wintosystrayicon(ev->window))) {
-+ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
-+ resizebarwin(selmon);
-+ updatesystray();
-+ }
-
- if(!XGetWindowAttributes(dpy, ev->window, &wa))
- return;
-@@ -1198,6 +1315,16 @@ propertynotify(XEvent *e) {
- Window trans;
- XPropertyEvent *ev = &e->xproperty;
-
-+ if((c = wintosystrayicon(ev->window))) {
-+ if(ev->atom == XA_WM_NORMAL_HINTS) {
-+ updatesizehints(c);
-+ updatesystrayicongeom(c, c->w, c->h);
-+ }
-+ else
-+ updatesystrayiconstate(c, ev);
-+ resizebarwin(selmon);
-+ updatesystray();
-+ }
- if((ev->window == root) && (ev->atom == XA_WM_NAME))
- updatestatus();
- else if(ev->state == PropertyDelete)
-@@ -1247,12 +1374,33 @@ recttomon(int x, int y, int w, int h) {
- }
-
- void
-+removesystrayicon(Client *i) {
-+ Client **ii;
-+
-+ if(!showsystray || !i)
-+ return;
-+ for(ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
-+ if(ii)
-+ *ii = i->next;
-+ free(i);
-+}
-+
-+
-+void
- resize(Client *c, int x, int y, int w, int h, Bool interact) {
- if(applysizehints(c, &x, &y, &w, &h, interact))
- resizeclient(c, x, y, w, h);
- }
-
- void
-+resizebarwin(Monitor *m) {
-+ unsigned int w = m->ww;
-+ if(showsystray && m == systraytomon(m))
-+ w -= getsystraywidth();
-+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
-+}
-+
-+void
- resizeclient(Client *c, int x, int y, int w, int h) {
- XWindowChanges wc;
-
-@@ -1323,6 +1471,18 @@ resizemouse(const Arg *arg) {
- }
-
- void
-+resizerequest(XEvent *e) {
-+ XResizeRequestEvent *ev = &e->xresizerequest;
-+ Client *i;
-+
-+ if((i = wintosystrayicon(ev->window))) {
-+ updatesystrayicongeom(i, ev->width, ev->height);
-+ resizebarwin(selmon);
-+ updatesystray();
-+ }
-+}
-+
-+void
- restack(Monitor *m) {
- Client *c;
- XEvent ev;
-@@ -1406,25 +1566,35 @@ setclientstate(Client *c, long state) {
- }
-
- Bool
--sendevent(Client *c, Atom proto) {
-+sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) {
- int n;
-- Atom *protocols;
-+ Atom *protocols, mt;
- Bool exists = False;
- XEvent ev;
-
-- if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
-- while(!exists && n--)
-- exists = protocols[n] == proto;
-- XFree(protocols);
-+ if(proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
-+ mt = wmatom[WMProtocols];
-+ if(XGetWMProtocols(dpy, w, &protocols, &n)) {
-+ while(!exists && n--)
-+ exists = protocols[n] == proto;
-+ XFree(protocols);
-+ }
-+ }
-+ else {
-+ exists = True;
-+ mt = proto;
- }
- if(exists) {
- ev.type = ClientMessage;
-- ev.xclient.window = c->win;
-- ev.xclient.message_type = wmatom[WMProtocols];
-+ ev.xclient.window = w;
-+ ev.xclient.message_type = mt;
- ev.xclient.format = 32;
-- ev.xclient.data.l[0] = proto;
-- ev.xclient.data.l[1] = CurrentTime;
-- XSendEvent(dpy, c->win, False, NoEventMask, &ev);
-+ ev.xclient.data.l[0] = d0;
-+ ev.xclient.data.l[1] = d1;
-+ ev.xclient.data.l[2] = d2;
-+ ev.xclient.data.l[3] = d3;
-+ ev.xclient.data.l[4] = d4;
-+ XSendEvent(dpy, w, False, mask, &ev);
- }
- return exists;
- }
-@@ -1437,7 +1607,7 @@ setfocus(Client *c) {
- XA_WINDOW, 32, PropModeReplace,
- (unsigned char *) &(c->win), 1);
- }
-- sendevent(c, wmatom[WMTakeFocus]);
-+ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
- }
-
- void
-@@ -1520,12 +1690,18 @@ setup(void) {
- wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
- netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
- netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
-+ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
-+ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
-+ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
- netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
- netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
- netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
- netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
- netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
- netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
-+ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
-+ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
-+ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
- /* init cursors */
- cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
- cursor[CurResize] = drw_cur_create(drw, XC_sizing);
-@@ -1537,6 +1713,8 @@ setup(void) {
- scheme[SchemeSel].border = drw_clr_create(drw, selbordercolor);
- scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor);
- scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor);
-+ /* init system tray */
-+ updatesystray();
- /* init bars */
- updatebars();
- updatestatus();
-@@ -1592,6 +1770,22 @@ spawn(const Arg *arg) {
- }
- }
-
-+Monitor *
-+systraytomon(Monitor *m) {
-+ Monitor *t;
-+ int i, n;
-+ if(!systraypinning) {
-+ if(!m)
-+ return selmon;
-+ return m == selmon ? m : NULL;
-+ }
-+ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
-+ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
-+ if(systraypinningfailfirst && n < systraypinning)
-+ return mons;
-+ return t;
-+}
-+
- void
- tag(const Arg *arg) {
- if(selmon->sel && arg->ui & TAGMASK) {
-@@ -1638,7 +1832,18 @@ void
- togglebar(const Arg *arg) {
- selmon->showbar = !selmon->showbar;
- updatebarpos(selmon);
-- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
-+ resizebarwin(selmon);
-+ if(showsystray) {
-+ XWindowChanges wc;
-+ if(!selmon->showbar)
-+ wc.y = -bh;
-+ else if(selmon->showbar) {
-+ wc.y = 0;
-+ if(!selmon->topbar)
-+ wc.y = selmon->mh - bh;
-+ }
-+ XConfigureWindow(dpy, systray->win, CWY, &wc);
-+ }
- arrange(selmon);
- }
-
-@@ -1728,11 +1933,18 @@ unmapnotify(XEvent *e) {
- else
- unmanage(c, False);
- }
-+ else if((c = wintosystrayicon(ev->window))) {
-+ removesystrayicon(c);
-+ resizebarwin(selmon);
-+ updatesystray();
-+ }
- }
-
- void
- updatebars(void) {
-+ unsigned int w;
- Monitor *m;
-+
- XSetWindowAttributes wa = {
- .override_redirect = True,
- .background_pixmap = ParentRelative,
-@@ -1741,10 +1953,15 @@ updatebars(void) {
- for(m = mons; m; m = m->next) {
- if (m->barwin)
- continue;
-- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
-+ w = m->ww;
-+ if(showsystray && m == systraytomon(m))
-+ w -= getsystraywidth();
-+ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
- CopyFromParent, DefaultVisual(dpy, screen),
- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
-+ if(showsystray && m == systraytomon(m))
-+ XMapRaised(dpy, systray->win);
- XMapRaised(dpy, m->barwin);
- }
- }
-@@ -1938,6 +2155,117 @@ updatestatus(void) {
- }
-
- void
-+updatesystrayicongeom(Client *i, int w, int h) {
-+ if(i) {
-+ i->h = bh;
-+ if(w == h)
-+ i->w = bh;
-+ else if(h == bh)
-+ i->w = w;
-+ else
-+ i->w = (int) ((float)bh * ((float)w / (float)h));
-+ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
-+ /* force icons into the systray dimenons if they don't want to */
-+ if(i->h > bh) {
-+ if(i->w == i->h)
-+ i->w = bh;
-+ else
-+ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
-+ i->h = bh;
-+ }
-+ }
-+}
-+
-+void
-+updatesystrayiconstate(Client *i, XPropertyEvent *ev) {
-+ long flags;
-+ int code = 0;
-+
-+ if(!showsystray || !i || ev->atom != xatom[XembedInfo] ||
-+ !(flags = getatomprop(i, xatom[XembedInfo])))
-+ return;
-+
-+ if(flags & XEMBED_MAPPED && !i->tags) {
-+ i->tags = 1;
-+ code = XEMBED_WINDOW_ACTIVATE;
-+ XMapRaised(dpy, i->win);
-+ setclientstate(i, NormalState);
-+ }
-+ else if(!(flags & XEMBED_MAPPED) && i->tags) {
-+ i->tags = 0;
-+ code = XEMBED_WINDOW_DEACTIVATE;
-+ XUnmapWindow(dpy, i->win);
-+ setclientstate(i, WithdrawnState);
-+ }
-+ else
-+ return;
-+ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
-+ systray->win, XEMBED_EMBEDDED_VERSION);
-+}
-+
-+void
-+updatesystray(void) {
-+ XSetWindowAttributes wa;
-+ XWindowChanges wc;
-+ Client *i;
-+ Monitor *m = systraytomon(NULL);
-+ unsigned int x = m->mx + m->mw;
-+ unsigned int w = 1;
-+
-+ if(!showsystray)
-+ return;
-+ if(!systray) {
-+ /* init systray */
-+ if(!(systray = (Systray *)calloc(1, sizeof(Systray))))
-+ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
-+ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel].bg->pix);
-+ wa.event_mask = ButtonPressMask | ExposureMask;
-+ wa.override_redirect = True;
-+ wa.background_pixel = scheme[SchemeNorm].bg->pix;
-+ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
-+ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
-+ PropModeReplace, (unsigned char *)&systrayorientation, 1);
-+ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
-+ XMapRaised(dpy, systray->win);
-+ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
-+ if(XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
-+ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
-+ XSync(dpy, False);
-+ }
-+ else {
-+ fprintf(stderr, "dwm: unable to obtain system tray.\n");
-+ free(systray);
-+ systray = NULL;
-+ return;
-+ }
-+ }
-+ for(w = 0, i = systray->icons; i; i = i->next) {
-+ /* make sure the background color stays the same */
-+ wa.background_pixel = scheme[SchemeNorm].bg->pix;
-+ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
-+ XMapRaised(dpy, i->win);
-+ w += systrayspacing;
-+ i->x = w;
-+ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
-+ w += i->w;
-+ if(i->mon != m)
-+ i->mon = m;
-+ }
-+ w = w ? w + systrayspacing : 1;
-+ x -= w;
-+ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
-+ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
-+ wc.stack_mode = Above; wc.sibling = m->barwin;
-+ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
-+ XMapWindow(dpy, systray->win);
-+ XMapSubwindows(dpy, systray->win);
-+ /* redraw background */
-+ XSetForeground(dpy, drw->gc, scheme[SchemeNorm].bg->pix);
-+ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
-+ XSync(dpy, False);
-+}
-+
-+void
- updatewindowtype(Client *c) {
- Atom state = getatomprop(c, netatom[NetWMState]);
- Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
-@@ -2006,6 +2334,16 @@ wintomon(Window w) {
- return selmon;
- }
-
-+Client *
-+wintosystrayicon(Window w) {
-+ Client *i = NULL;
-+
-+ if(!showsystray || !w)
-+ return i;
-+ for(i = systray->icons; i && i->win != w; i = i->next) ;
-+ return i;
-+}
-+
- /* There's no way to check accesses to destroyed windows, thus those cases are
- * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
- * default error handler, which may call exit. */
diff --git a/dwm.suckless.org/patches/dwm-6.1-pertag.diff b/dwm.suckless.org/patches/dwm-6.1-pertag.diff
@@ -1,15 +1,8 @@
-Author: Jan Christoph Ebersbach <jceb@e-jc.de>
-URL: http://dwm.suckless.org/patches/pertag
-This patch keeps layout, mwfact, barpos and nmaster per tag.
-
-Contributors:
-- Carlos Pita, thanks for debugging NetActiveWindow issues and sending a patch
-
-Index: dwm/dwm.c
-===================================================================
---- dwm/dwm.c.orig 2014-02-09 15:24:18.232117136 +0100
-+++ dwm/dwm.c 2014-02-09 15:24:18.224117135 +0100
-@@ -110,6 +110,7 @@
+diff --git a/dwm.c b/dwm.c
+index 96b43f7..cc16ba1 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -111,6 +111,7 @@ typedef struct {
void (*arrange)(Monitor *);
} Layout;
@@ -17,7 +10,7 @@ Index: dwm/dwm.c
struct Monitor {
char ltsymbol[16];
float mfact;
-@@ -129,6 +130,7 @@
+@@ -130,6 +131,7 @@ struct Monitor {
Monitor *next;
Window barwin;
const Layout *lt[2];
@@ -25,7 +18,7 @@ Index: dwm/dwm.c
};
typedef struct {
-@@ -270,6 +272,16 @@
+@@ -270,6 +272,16 @@ static Window root;
/* configuration, allows nested code to access above variables */
#include "config.h"
@@ -42,7 +35,7 @@ Index: dwm/dwm.c
/* compile-time check if all tags fit into an unsigned int bit array. */
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
-@@ -518,6 +530,7 @@
+@@ -516,6 +528,7 @@ void
clientmessage(XEvent *e) {
XClientMessageEvent *cme = &e->xclient;
Client *c = wintoclient(cme->window);
@@ -50,7 +43,7 @@ Index: dwm/dwm.c
if(!c)
return;
-@@ -530,6 +543,8 @@
+@@ -528,6 +541,8 @@ clientmessage(XEvent *e) {
if(!ISVISIBLE(c)) {
c->mon->seltags ^= 1;
c->mon->tagset[c->mon->seltags] = c->tags;
@@ -59,15 +52,15 @@ Index: dwm/dwm.c
}
pop(c);
}
-@@ -631,6 +646,7 @@
+@@ -629,6 +644,7 @@ configurerequest(XEvent *e) {
Monitor *
createmon(void) {
Monitor *m;
+ int i;
- if(!(m = (Monitor *)calloc(1, sizeof(Monitor))))
- die("fatal: could not malloc() %u bytes\n", sizeof(Monitor));
-@@ -642,6 +658,27 @@
+ m = ecalloc(1, sizeof(Monitor));
+ m->tagset[0] = m->tagset[1] = 1;
+@@ -639,6 +655,27 @@ createmon(void) {
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
@@ -95,7 +88,7 @@ Index: dwm/dwm.c
return m;
}
-@@ -958,7 +995,7 @@
+@@ -957,7 +994,7 @@ grabkeys(void) {
void
incnmaster(const Arg *arg) {
@@ -104,7 +97,7 @@ Index: dwm/dwm.c
arrange(selmon);
}
-@@ -1462,10 +1499,13 @@
+@@ -1469,10 +1506,13 @@ setfullscreen(Client *c, Bool fullscreen) {
void
setlayout(const Arg *arg) {
@@ -121,7 +114,7 @@ Index: dwm/dwm.c
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
if(selmon->sel)
arrange(selmon);
-@@ -1483,7 +1523,7 @@
+@@ -1490,7 +1530,7 @@ setmfact(const Arg *arg) {
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
if(f < 0.1 || f > 0.9)
return;
@@ -130,7 +123,7 @@ Index: dwm/dwm.c
arrange(selmon);
}
-@@ -1627,7 +1667,7 @@
+@@ -1635,7 +1675,7 @@ tile(Monitor *m) {
void
togglebar(const Arg *arg) {
@@ -139,7 +132,7 @@ Index: dwm/dwm.c
updatebarpos(selmon);
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
arrange(selmon);
-@@ -1663,9 +1703,29 @@
+@@ -1671,9 +1711,29 @@ toggletag(const Arg *arg) {
void
toggleview(const Arg *arg) {
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
@@ -169,7 +162,7 @@ Index: dwm/dwm.c
focus(NULL);
arrange(selmon);
}
-@@ -1960,11 +2020,33 @@
+@@ -1967,11 +2027,33 @@ updatewmhints(Client *c) {
void
view(const Arg *arg) {
diff --git a/dwm.suckless.org/patches/dwm-6.1-pertag_without_bar.diff b/dwm.suckless.org/patches/dwm-6.1-pertag_without_bar.diff
@@ -0,0 +1,161 @@
+diff --git a/dwm.c b/dwm.c
+index 96b43f7..5e11a07 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -111,6 +111,7 @@ typedef struct {
+ void (*arrange)(Monitor *);
+ } Layout;
+
++typedef struct Pertag Pertag;
+ struct Monitor {
+ char ltsymbol[16];
+ float mfact;
+@@ -130,6 +131,7 @@ struct Monitor {
+ Monitor *next;
+ Window barwin;
+ const Layout *lt[2];
++ Pertag *pertag;
+ };
+
+ typedef struct {
+@@ -270,6 +272,14 @@ static Window root;
+ /* configuration, allows nested code to access above variables */
+ #include "config.h"
+
++struct Pertag {
++ unsigned int curtag, prevtag; /* current and previous tag */
++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
++};
++
+ /* compile-time check if all tags fit into an unsigned int bit array. */
+ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
+@@ -629,6 +639,7 @@ configurerequest(XEvent *e) {
+ Monitor *
+ createmon(void) {
+ Monitor *m;
++ int i;
+
+ m = ecalloc(1, sizeof(Monitor));
+ m->tagset[0] = m->tagset[1] = 1;
+@@ -639,6 +650,21 @@ createmon(void) {
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
++ if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag))))
++ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag));
++ m->pertag->curtag = m->pertag->prevtag = 1;
++ for(i=0; i <= LENGTH(tags); i++) {
++ /* init nmaster */
++ m->pertag->nmasters[i] = m->nmaster;
++
++ /* init mfacts */
++ m->pertag->mfacts[i] = m->mfact;
++
++ /* init layouts */
++ m->pertag->ltidxs[i][0] = m->lt[0];
++ m->pertag->ltidxs[i][1] = m->lt[1];
++ m->pertag->sellts[i] = m->sellt;
++ }
+ return m;
+ }
+
+@@ -957,7 +983,7 @@ grabkeys(void) {
+
+ void
+ incnmaster(const Arg *arg) {
+- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+ }
+
+@@ -1469,10 +1495,13 @@ setfullscreen(Client *c, Bool fullscreen) {
+
+ void
+ setlayout(const Arg *arg) {
+- if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
+- selmon->sellt ^= 1;
++ if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
++ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
++ }
+ if(arg && arg->v)
+- selmon->lt[selmon->sellt] = (Layout *)arg->v;
++ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
+ if(selmon->sel)
+ arrange(selmon);
+@@ -1490,7 +1519,7 @@ setmfact(const Arg *arg) {
+ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
+ if(f < 0.1 || f > 0.9)
+ return;
+- selmon->mfact = f;
++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
+ arrange(selmon);
+ }
+
+@@ -1671,9 +1700,27 @@ toggletag(const Arg *arg) {
+ void
+ toggleview(const Arg *arg) {
+ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
++ int i;
+
+ if(newtagset) {
++ if(newtagset == ~0) {
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++ selmon->pertag->curtag = 0;
++ }
++ /* test if the user did not select the same tag */
++ if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++ for (i=0; !(newtagset & 1 << i); i++) ;
++ selmon->pertag->curtag = i + 1;
++ }
+ selmon->tagset[selmon->seltags] = newtagset;
++
++ /* apply settings for this view */
++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
+ focus(NULL);
+ arrange(selmon);
+ }
+@@ -1967,11 +2014,31 @@ updatewmhints(Client *c) {
+
+ void
+ view(const Arg *arg) {
++ int i;
++ unsigned int tmptag;
++
+ if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
+ return;
+ selmon->seltags ^= 1; /* toggle sel tagset */
+- if(arg->ui & TAGMASK)
++ if(arg->ui & TAGMASK) {
++ selmon->pertag->prevtag = selmon->pertag->curtag;
+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
++ if(arg->ui == ~0)
++ selmon->pertag->curtag = 0;
++ else {
++ for (i=0; !(arg->ui & 1 << i); i++) ;
++ selmon->pertag->curtag = i + 1;
++ }
++ } else {
++ tmptag = selmon->pertag->prevtag;
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++ selmon->pertag->curtag = tmptag;
++ }
++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
+ focus(NULL);
+ arrange(selmon);
+ }
diff --git a/dwm.suckless.org/patches/dwm-7e1182c-pertag-tab.diff b/dwm.suckless.org/patches/dwm-7e1182c-pertag-tab.diff
@@ -0,0 +1,692 @@
+diff --git a/config.def.h b/config.def.h
+index 3fde3cf..ef6d4d3 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -15,10 +15,22 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
+ static const unsigned int snap = 32; /* snap pixel */
+ static const Bool showbar = True; /* False means no bar */
+ static const Bool topbar = True; /* False means bottom bar */
++/* Display modes of the tab bar: never shown, always shown, shown only in */
++/* monocle mode in presence of several windows. */
++/* Modes after showtab_nmodes are disabled */
++enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
++static const int showtab = showtab_auto; /* Default tab bar show mode */
++static const Bool toptab = False; /* False means bottom tab bar */
++
+
+ /* tagging */
+ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+
++/* default layout per tags */
++/* The first element is for all-tag view, following i-th element corresponds to */
++/* tags[i]. Layout is referred using the layouts array index.*/
++static int def_layouts[1 + LENGTH(tags)] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
++
+ static const Rule rules[] = {
+ /* xprop(1):
+ * WM_CLASS(STRING) = instance, class
+@@ -32,7 +44,7 @@ static const Rule rules[] = {
+ /* layout(s) */
+ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
+ static const int nmaster = 1; /* number of clients in master area */
+-static const Bool resizehints = True; /* True means respect size hints in tiled resizals */
++static const Bool resizehints = False; /* True means respect size hints in tiled resizals */
+
+ static const Layout layouts[] = {
+ /* symbol arrange function */
+@@ -62,6 +74,7 @@ static Key keys[] = {
+ { MODKEY, XK_p, spawn, {.v = dmenucmd } },
+ { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
+ { MODKEY, XK_b, togglebar, {0} },
++ { MODKEY, XK_w, tabmode, {-1} },
+ { MODKEY, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY, XK_i, incnmaster, {.i = +1 } },
+@@ -109,5 +122,6 @@ static Button buttons[] = {
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
++ { ClkTabBar, 0, Button1, focuswin, {0} },
+ };
+
+diff --git a/dwm.1 b/dwm.1
+index 6687011..077d92b 100644
+--- a/dwm.1
++++ b/dwm.1
+@@ -19,14 +19,22 @@ layout applied.
+ Windows are grouped by tags. Each window can be tagged with one or multiple
+ tags. Selecting certain tags displays all windows with these tags.
+ .P
+-Each screen contains a small status bar which displays all available tags, the
+-layout, the title of the focused window, and the text read from the root window
+-name property, if the screen is focused. A floating window is indicated with an
+-empty square and a maximised floating window is indicated with a filled square
+-before the windows title. The selected tags are indicated with a different
+-color. The tags of the focused window are indicated with a filled square in the
+-top left corner. The tags which are applied to one or more windows are
+-indicated with an empty square in the top left corner.
++Each screen contains two small status bars.
++.P
++One bar displays all available tags, the layout, the title of the focused
++window, and the text read from the root window name property, if the screen is
++focused. A floating window is indicated with an empty square and a maximised
++floating window is indicated with a filled square before the windows title. The
++selected tags are indicated with a different color. The tags of the focused
++window are indicated with a filled square in the top left corner. The tags
++which are applied to one or more windows are indicated with an empty square in
++the top left corner.
++.P
++Another bar contains a tab for each window of the current view and allows
++navigation between windows, especially in the monocle mode. The different
++display modes of this bar are described under the Mod1\-w Keybord command
++section. When a single tag is selected, that tag is indicated in the left corner
++of the tab bar.
+ .P
+ dwm draws a small border around windows to indicate the focus state.
+ .SH OPTIONS
+@@ -43,7 +51,8 @@ command.
+ .TP
+ .B Button1
+ click on a tag label to display all windows with that tag, click on the layout
+-label toggles between tiled and floating layout.
++label toggles between tiled and floating layout, click on a window name in the
++tab bar brings focus to that window.
+ .TP
+ .B Button3
+ click on a tag label adds/removes all windows with that tag to/from the view.
+@@ -104,6 +113,12 @@ Increase master area size.
+ .B Mod1\-h
+ Decrease master area size.
+ .TP
++.B Mod1\-w
++Cycle over the tab bar display modes: never displayed, always displayed,
++displayed only in monocle mode when the view contains than one window (auto
++mode). Some display modes can be disabled in the configuration, config.h. In
++the default configuration only "never" and "auto" display modes are enabled.
++.TP
+ .B Mod1\-Return
+ Zooms/cycles focused window to/from master area (tiled layouts only).
+ .TP
+diff --git a/dwm.c b/dwm.c
+index 96b43f7..c0aab5d 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -64,7 +64,7 @@ enum { NetSupported, NetWMName, NetWMState,
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+-enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
++enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+
+ typedef union {
+@@ -111,25 +111,35 @@ typedef struct {
+ void (*arrange)(Monitor *);
+ } Layout;
+
++#define MAXTABS 50
++
++typedef struct Pertag Pertag;
+ struct Monitor {
+ char ltsymbol[16];
+ float mfact;
+ int nmaster;
+ int num;
+ int by; /* bar geometry */
++ int ty; /* tab bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
+ unsigned int seltags;
+ unsigned int sellt;
+ unsigned int tagset[2];
+ Bool showbar;
++ Bool showtab;
+ Bool topbar;
++ Bool toptab;
+ Client *clients;
+ Client *sel;
+ Client *stack;
+ Monitor *next;
+ Window barwin;
++ Window tabwin;
++ int ntabs;
++ int tab_widths[MAXTABS];
+ const Layout *lt[2];
++ Pertag *pertag;
+ };
+
+ typedef struct {
+@@ -164,12 +174,15 @@ static void detachstack(Client *c);
+ static Monitor *dirtomon(int dir);
+ static void drawbar(Monitor *m);
+ static void drawbars(void);
++static void drawtab(Monitor *m);
++static void drawtabs(void);
+ static void enternotify(XEvent *e);
+ static void expose(XEvent *e);
+ static void focus(Client *c);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
++static void focuswin(const Arg* arg);
+ static Bool getrootptr(int *x, int *y);
+ static long getstate(Window w);
+ static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
+@@ -206,6 +219,7 @@ static void setup(void);
+ static void showhide(Client *c);
+ static void sigchld(int unused);
+ static void spawn(const Arg *arg);
++static void tabmode(const Arg *arg);
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
+ static void tile(Monitor *);
+@@ -240,6 +254,7 @@ static char stext[256];
+ static int screen;
+ static int sw, sh; /* X display screen geometry width, height */
+ static int bh, blw = 0; /* bar geometry */
++static int th = 0; /* tab bar geometry */
+ static int (*xerrorxlib)(Display *, XErrorEvent *);
+ static unsigned int numlockmask = 0;
+ static void (*handler[LASTEvent]) (XEvent *) = {
+@@ -270,6 +285,16 @@ static Window root;
+ /* configuration, allows nested code to access above variables */
+ #include "config.h"
+
++struct Pertag {
++ unsigned int curtag, prevtag; /* current and previous tag */
++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
++ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
++ Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */
++};
++
+ /* compile-time check if all tags fit into an unsigned int bit array. */
+ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
+@@ -389,6 +414,9 @@ arrange(Monitor *m) {
+
+ void
+ arrangemon(Monitor *m) {
++ updatebarpos(m);
++ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
++
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
+ if(m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+@@ -437,14 +465,32 @@ buttonpress(XEvent *e) {
+ else
+ click = ClkWinTitle;
+ }
++ if(ev->window == selmon->tabwin) {
++ i = 0; x = 0;
++ for(c = selmon->clients; c; c = c->next){
++ if(!ISVISIBLE(c)) continue;
++ x += selmon->tab_widths[i];
++ if (ev->x > x)
++ ++i;
++ else
++ break;
++ if(i >= m->ntabs) break;
++ }
++ if(c) {
++ click = ClkTabBar;
++ arg.ui = i;
++ }
++ }
+ else if((c = wintoclient(ev->window))) {
+ focus(c);
+ click = ClkClientWin;
+ }
+ for(i = 0; i < LENGTH(buttons); i++)
+ if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
+- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
+- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
++ buttons[i].func(((click == ClkTagBar || click == ClkTabBar)
++ && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
++ }
+ }
+
+ void
+@@ -497,6 +543,8 @@ cleanupmon(Monitor *mon) {
+ }
+ XUnmapWindow(dpy, mon->barwin);
+ XDestroyWindow(dpy, mon->barwin);
++ XUnmapWindow(dpy, mon->tabwin);
++ XDestroyWindow(dpy, mon->tabwin);
+ free(mon);
+ }
+
+@@ -516,6 +564,7 @@ void
+ clientmessage(XEvent *e) {
+ XClientMessageEvent *cme = &e->xclient;
+ Client *c = wintoclient(cme->window);
++ int i;
+
+ if(!c)
+ return;
+@@ -528,6 +577,8 @@ clientmessage(XEvent *e) {
+ if(!ISVISIBLE(c)) {
+ c->mon->seltags ^= 1;
+ c->mon->tagset[c->mon->seltags] = c->tags;
++ for(i=0; !(c->tags & 1 << i); i++);
++ view(&(Arg){.ui = 1 << i});
+ }
+ pop(c);
+ }
+@@ -565,8 +616,11 @@ configurenotify(XEvent *e) {
+ if(updategeom() || dirty) {
+ drw_resize(drw, sw, bh);
+ updatebars();
+- for(m = mons; m; m = m->next)
++ //refreshing display of status bar. The tab bar is handled by the arrange()
++ //method, which is called below
++ for(m = mons; m; m = m->next){
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
++ }
+ focus(NULL);
+ arrange(NULL);
+ }
+@@ -629,16 +683,41 @@ configurerequest(XEvent *e) {
+ Monitor *
+ createmon(void) {
+ Monitor *m;
++ int i;
+
+ m = ecalloc(1, sizeof(Monitor));
+ m->tagset[0] = m->tagset[1] = 1;
+ m->mfact = mfact;
+ m->nmaster = nmaster;
+ m->showbar = showbar;
++ m->showtab = showtab;
+ m->topbar = topbar;
+- m->lt[0] = &layouts[0];
++ m->toptab = toptab;
++ m->ntabs = 0;
++ m->lt[0] = &layouts[def_layouts[1] % LENGTH(layouts)];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
++ if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag))))
++ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag));
++ m->pertag->curtag = m->pertag->prevtag = 1;
++ for(i=0; i <= LENGTH(tags); i++) {
++ /* init nmaster */
++ m->pertag->nmasters[i] = m->nmaster;
++
++ /* init mfacts */
++ m->pertag->mfacts[i] = m->mfact;
++
++ /* init layouts */
++ m->pertag->ltidxs[i][0] = &layouts[def_layouts[i % LENGTH(def_layouts)] % LENGTH(layouts)];
++ m->pertag->ltidxs[i][1] = m->lt[1];
++ m->pertag->sellts[i] = m->sellt;
++
++ /* init showbar */
++ m->pertag->showbars[i] = m->showbar;
++
++ /* swap focus and zoomswap*/
++ m->pertag->prevzooms[i] = NULL;
++ }
+ return m;
+ }
+
+@@ -749,6 +828,104 @@ drawbars(void) {
+ }
+
+ void
++drawtabs(void) {
++ Monitor *m;
++
++ for(m = mons; m; m = m->next)
++ drawtab(m);
++}
++
++static int
++cmpint(const void *p1, const void *p2) {
++ /* The actual arguments to this function are "pointers to
++ pointers to char", but strcmp(3) arguments are "pointers
++ to char", hence the following cast plus dereference */
++ return *((int*) p1) > * (int*) p2;
++}
++
++
++void
++drawtab(Monitor *m) {
++ Client *c;
++ int i;
++ int itag = -1;
++ char view_info[50];
++ int view_info_w = 0;
++ int sorted_label_widths[MAXTABS];
++ int tot_width;
++ int maxsize = bh;
++ int x = 0;
++ int w = 0;
++
++ //view_info: indicate the tag which is displayed in the view
++ for(i = 0; i < LENGTH(tags); ++i){
++ if((selmon->tagset[selmon->seltags] >> i) & 1) {
++ if(itag >=0){ //more than one tag selected
++ itag = -1;
++ break;
++ }
++ itag = i;
++ }
++ }
++ if(0 <= itag && itag < LENGTH(tags)){
++ snprintf(view_info, sizeof view_info, "[%s]", tags[itag]);
++ } else {
++ strncpy(view_info, "[...]", sizeof view_info);
++ }
++ view_info[sizeof(view_info) - 1 ] = 0;
++ view_info_w = TEXTW(view_info);
++ tot_width = view_info_w;
++
++ /* Calculates number of labels and their width */
++ m->ntabs = 0;
++ for(c = m->clients; c; c = c->next){
++ if(!ISVISIBLE(c)) continue;
++ m->tab_widths[m->ntabs] = TEXTW(c->name);
++ tot_width += m->tab_widths[m->ntabs];
++ ++m->ntabs;
++ if(m->ntabs >= MAXTABS) break;
++ }
++
++ if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated
++ memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs);
++ qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint);
++ tot_width = view_info_w;
++ for(i = 0; i < m->ntabs; ++i){
++ if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww)
++ break;
++ tot_width += sorted_label_widths[i];
++ }
++ maxsize = (m->ww - tot_width) / (m->ntabs - i);
++ } else{
++ maxsize = m->ww;
++ }
++ i = 0;
++ for(c = m->clients; c; c = c->next){
++ if(!ISVISIBLE(c)) continue;
++ if(i >= m->ntabs) break;
++ if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize;
++ w = m->tab_widths[i];
++ drw_setscheme(drw, (c == m->sel) ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
++ drw_text(drw, x, 0, w, th, c->name, 0);
++ x += w;
++ ++i;
++ }
++
++ drw_setscheme(drw, &scheme[SchemeNorm]);
++
++ /* cleans interspace between window names and current viewed tag label */
++ w = m->ww - view_info_w - x;
++ drw_text(drw, x, 0, w, th, NULL, 0);
++
++ /* view info */
++ x += w;
++ w = view_info_w;
++ drw_text(drw, x, 0, w, th, view_info, 0);
++
++ drw_map(drw, m->tabwin, 0, 0, m->ww, th);
++}
++
++void
+ enternotify(XEvent *e) {
+ Client *c;
+ Monitor *m;
+@@ -772,8 +949,10 @@ expose(XEvent *e) {
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+- if(ev->count == 0 && (m = wintomon(ev->window)))
++ if(ev->count == 0 && (m = wintomon(ev->window))){
+ drawbar(m);
++ drawtab(m);
++ }
+ }
+
+ void
+@@ -800,6 +979,7 @@ focus(Client *c) {
+ }
+ selmon->sel = c;
+ drawbars();
++ drawtabs();
+ }
+
+ void
+@@ -850,6 +1030,19 @@ focusstack(const Arg *arg) {
+ }
+ }
+
++void
++focuswin(const Arg* arg){
++ int iwin = arg->i;
++ Client* c = NULL;
++ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
++ if(ISVISIBLE(c)) --iwin;
++ };
++ if(c) {
++ focus(c);
++ restack(selmon);
++ }
++}
++
+ Atom
+ getatomprop(Client *c, Atom prop) {
+ int di;
+@@ -957,7 +1150,7 @@ grabkeys(void) {
+
+ void
+ incnmaster(const Arg *arg) {
+- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+ }
+
+@@ -1215,12 +1408,14 @@ propertynotify(XEvent *e) {
+ case XA_WM_HINTS:
+ updatewmhints(c);
+ drawbars();
++ drawtabs();
+ break;
+ }
+ if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
+ updatetitle(c);
+ if(c == c->mon->sel)
+ drawbar(c->mon);
++ drawtab(c->mon);
+ }
+ if(ev->atom == netatom[NetWMWindowType])
+ updatewindowtype(c);
+@@ -1328,6 +1523,7 @@ restack(Monitor *m) {
+ XWindowChanges wc;
+
+ drawbar(m);
++ drawtab(m);
+ if(!m->sel)
+ return;
+ if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
+@@ -1469,10 +1665,13 @@ setfullscreen(Client *c, Bool fullscreen) {
+
+ void
+ setlayout(const Arg *arg) {
+- if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
+- selmon->sellt ^= 1;
++ if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
++ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
++ }
+ if(arg && arg->v)
+- selmon->lt[selmon->sellt] = (Layout *)arg->v;
++ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
+ if(selmon->sel)
+ arrange(selmon);
+@@ -1490,7 +1689,7 @@ setmfact(const Arg *arg) {
+ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
+ if(f < 0.1 || f > 0.9)
+ return;
+- selmon->mfact = f;
++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
+ arrange(selmon);
+ }
+
+@@ -1511,6 +1710,8 @@ setup(void) {
+ if (!drw->fontcount)
+ die("no fonts could be loaded.\n");
+ bh = drw->fonts[0]->h + 2;
++ th = bh;
++
+ updategeom();
+ /* init atoms */
+ wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
+@@ -1635,13 +1836,23 @@ tile(Monitor *m) {
+
+ void
+ togglebar(const Arg *arg) {
+- selmon->showbar = !selmon->showbar;
++ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
+ updatebarpos(selmon);
+ XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
+ arrange(selmon);
+ }
+
+ void
++tabmode(const Arg *arg) {
++ if(arg && arg->i >= 0)
++ selmon->showtab = arg->ui % showtab_nmodes;
++ else
++ selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
++ arrange(selmon);
++}
++
++
++void
+ togglefloating(const Arg *arg) {
+ if(!selmon->sel)
+ return;
+@@ -1671,9 +1882,29 @@ toggletag(const Arg *arg) {
+ void
+ toggleview(const Arg *arg) {
+ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
++ int i;
+
+ if(newtagset) {
++ if(newtagset == ~0) {
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++ selmon->pertag->curtag = 0;
++ }
++ /* test if the user did not select the same tag */
++ if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++ for (i=0; !(newtagset & 1 << i); i++) ;
++ selmon->pertag->curtag = i + 1;
++ }
+ selmon->tagset[selmon->seltags] = newtagset;
++
++ /* apply settings for this view */
++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
++ togglebar(NULL);
+ focus(NULL);
+ arrange(selmon);
+ }
+@@ -1745,20 +1976,43 @@ updatebars(void) {
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
+ XMapRaised(dpy, m->barwin);
++ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
++ CopyFromParent, DefaultVisual(dpy, screen),
++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
++ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
++ XMapRaised(dpy, m->tabwin);
+ }
+ }
+
+ void
+ updatebarpos(Monitor *m) {
++ Client *c;
++ int nvis = 0;
++
+ m->wy = m->my;
+ m->wh = m->mh;
+ if(m->showbar) {
+ m->wh -= bh;
+ m->by = m->topbar ? m->wy : m->wy + m->wh;
+- m->wy = m->topbar ? m->wy + bh : m->wy;
+- }
+- else
++ if ( m->topbar )
++ m->wy += bh;
++ } else {
+ m->by = -bh;
++ }
++
++ for(c = m->clients; c; c = c->next){
++ if(ISVISIBLE(c)) ++nvis;
++ }
++
++ if(m->showtab == showtab_always
++ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){
++ m->wh -= th;
++ m->ty = m->toptab ? m->wy : m->wy + m->wh;
++ if ( m->toptab )
++ m->wy += th;
++ } else {
++ m->ty = -th;
++ }
+ }
+
+ void
+@@ -1967,11 +2221,33 @@ updatewmhints(Client *c) {
+
+ void
+ view(const Arg *arg) {
++ int i;
++ unsigned int tmptag;
++
+ if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
+ return;
+ selmon->seltags ^= 1; /* toggle sel tagset */
+- if(arg->ui & TAGMASK)
++ if(arg->ui & TAGMASK) {
++ selmon->pertag->prevtag = selmon->pertag->curtag;
+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
++ if(arg->ui == ~0)
++ selmon->pertag->curtag = 0;
++ else {
++ for (i=0; !(arg->ui & 1 << i); i++) ;
++ selmon->pertag->curtag = i + 1;
++ }
++ } else {
++ tmptag = selmon->pertag->prevtag;
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++ selmon->pertag->curtag = tmptag;
++ }
++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
++ togglebar(NULL);
+ focus(NULL);
+ arrange(selmon);
+ }
+@@ -1997,7 +2273,7 @@ wintomon(Window w) {
+ if(w == root && getrootptr(&x, &y))
+ return recttomon(x, y, 1, 1);
+ for(m = mons; m; m = m->next)
+- if(w == m->barwin)
++ if(w == m->barwin || w == m->tabwin)
+ return m;
+ if((c = wintoclient(w)))
+ return c->mon;
diff --git a/dwm.suckless.org/patches/dwm-7e1182c-systray.diff b/dwm.suckless.org/patches/dwm-7e1182c-systray.diff
@@ -0,0 +1,687 @@
+diff --git a/config.def.h b/config.def.h
+index 3fde3cf..bd0e848 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -13,6 +13,10 @@ static const char selbgcolor[] = "#005577";
+ static const char selfgcolor[] = "#eeeeee";
+ static const unsigned int borderpx = 1; /* border pixel of windows */
+ static const unsigned int snap = 32; /* snap pixel */
++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
++static const unsigned int systrayspacing = 2; /* systray spacing */
++static const Bool systraypinningfailfirst = True; /* True: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
++static const Bool showsystray = True; /* False means no systray */
+ static const Bool showbar = True; /* False means no bar */
+ static const Bool topbar = True; /* False means bottom bar */
+
+diff --git a/dwm.c b/dwm.c
+index 96b43f7..37af33e 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -57,12 +57,30 @@
+ #define TAGMASK ((1 << LENGTH(tags)) - 1)
+ #define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
+
++#define SYSTEM_TRAY_REQUEST_DOCK 0
++#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
++
++/* XEMBED messages */
++#define XEMBED_EMBEDDED_NOTIFY 0
++#define XEMBED_WINDOW_ACTIVATE 1
++#define XEMBED_FOCUS_IN 4
++#define XEMBED_MODALITY_ON 10
++
++#define XEMBED_MAPPED (1 << 0)
++#define XEMBED_WINDOW_ACTIVATE 1
++#define XEMBED_WINDOW_DEACTIVATE 2
++
++#define VERSION_MAJOR 0
++#define VERSION_MINOR 0
++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
++
+ /* enums */
+ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
+ enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */
+-enum { NetSupported, NetWMName, NetWMState,
+- NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
++enum { NetSupported, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation,
++ NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow, NetWMWindowType,
++ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
+ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+ enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+@@ -141,6 +159,12 @@ typedef struct {
+ int monitor;
+ } Rule;
+
++typedef struct Systray Systray;
++struct Systray {
++ Window win;
++ Client *icons;
++};
++
+ /* function declarations */
+ static void applyrules(Client *c);
+ static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact);
+@@ -170,8 +194,10 @@ static void focus(Client *c);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
++static Atom getatomprop(Client *c, Atom prop);
+ static Bool getrootptr(int *x, int *y);
+ static long getstate(Window w);
++static unsigned int getsystraywidth();
+ static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
+ static void grabbuttons(Client *c, Bool focused);
+ static void grabkeys(void);
+@@ -189,13 +215,16 @@ static void pop(Client *);
+ static void propertynotify(XEvent *e);
+ static void quit(const Arg *arg);
+ static Monitor *recttomon(int x, int y, int w, int h);
++static void removesystrayicon(Client *i);
+ static void resize(Client *c, int x, int y, int w, int h, Bool interact);
++static void resizebarwin(Monitor *m);
+ static void resizeclient(Client *c, int x, int y, int w, int h);
+ static void resizemouse(const Arg *arg);
++static void resizerequest(XEvent *e);
+ static void restack(Monitor *m);
+ static void run(void);
+ static void scan(void);
+-static Bool sendevent(Client *c, Atom proto);
++static Bool sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
+ static void sendmon(Client *c, Monitor *m);
+ static void setclientstate(Client *c, long state);
+ static void setfocus(Client *c);
+@@ -206,6 +235,7 @@ static void setup(void);
+ static void showhide(Client *c);
+ static void sigchld(int unused);
+ static void spawn(const Arg *arg);
++static Monitor *systraytomon(Monitor *m);
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
+ static void tile(Monitor *);
+@@ -223,18 +253,24 @@ static void updateclientlist(void);
+ static void updatenumlockmask(void);
+ static void updatesizehints(Client *c);
+ static void updatestatus(void);
++static void updatesystray(void);
++static void updatesystrayicongeom(Client *i, int w, int h);
++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
+ static void updatewindowtype(Client *c);
+ static void updatetitle(Client *c);
+ static void updatewmhints(Client *c);
+ static void view(const Arg *arg);
+ static Client *wintoclient(Window w);
+ static Monitor *wintomon(Window w);
++static Client *wintosystrayicon(Window w);
+ static int xerror(Display *dpy, XErrorEvent *ee);
+ static int xerrordummy(Display *dpy, XErrorEvent *ee);
+ static int xerrorstart(Display *dpy, XErrorEvent *ee);
+ static void zoom(const Arg *arg);
+
+ /* variables */
++static Systray *systray = NULL;
++static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
+ static const char broken[] = "broken";
+ static char stext[256];
+ static int screen;
+@@ -256,9 +292,10 @@ static void (*handler[LASTEvent]) (XEvent *) = {
+ [MapRequest] = maprequest,
+ [MotionNotify] = motionnotify,
+ [PropertyNotify] = propertynotify,
++ [ResizeRequest] = resizerequest,
+ [UnmapNotify] = unmapnotify
+ };
+-static Atom wmatom[WMLast], netatom[NetLast];
++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
+ static Bool running = True;
+ static Cur *cursor[CurLast];
+ static ClrScheme scheme[SchemeLast];
+@@ -472,6 +509,11 @@ cleanup(void) {
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ while(mons)
+ cleanupmon(mons);
++ if(showsystray) {
++ XUnmapWindow(dpy, systray->win);
++ XDestroyWindow(dpy, systray->win);
++ free(systray);
++ }
+ for(i = 0; i < CurLast; i++)
+ drw_cur_free(drw, cursor[i]);
+ for(i = 0; i < SchemeLast; i++) {
+@@ -514,9 +556,49 @@ clearurgent(Client *c) {
+
+ void
+ clientmessage(XEvent *e) {
++ XWindowAttributes wa;
++ XSetWindowAttributes swa;
+ XClientMessageEvent *cme = &e->xclient;
+ Client *c = wintoclient(cme->window);
+
++ if(showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
++ /* add systray icons */
++ if(cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
++ if(!(c = (Client *)calloc(1, sizeof(Client))))
++ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
++ c->win = cme->data.l[2];
++ c->mon = selmon;
++ c->next = systray->icons;
++ systray->icons = c;
++ XGetWindowAttributes(dpy, c->win, &wa);
++ c->x = c->oldx = c->y = c->oldy = 0;
++ c->w = c->oldw = wa.width;
++ c->h = c->oldh = wa.height;
++ c->oldbw = wa.border_width;
++ c->bw = 0;
++ c->isfloating = True;
++ /* reuse tags field as mapped status */
++ c->tags = 1;
++ updatesizehints(c);
++ updatesystrayicongeom(c, wa.width, wa.height);
++ XAddToSaveSet(dpy, c->win);
++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
++ XReparentWindow(dpy, c->win, systray->win, 0, 0);
++ /* use parents background color */
++ swa.background_pixel = scheme[SchemeNorm].bg->pix;
++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ /* FIXME not sure if I have to send these events, too */
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ XSync(dpy, False);
++ resizebarwin(selmon);
++ updatesystray();
++ setclientstate(c, NormalState);
++ }
++ return;
++ }
+ if(!c)
+ return;
+ if(cme->message_type == netatom[NetWMState]) {
+@@ -566,7 +648,7 @@ configurenotify(XEvent *e) {
+ drw_resize(drw, sw, bh);
+ updatebars();
+ for(m = mons; m; m = m->next)
+- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
++ resizebarwin(m);
+ focus(NULL);
+ arrange(NULL);
+ }
+@@ -649,6 +731,11 @@ destroynotify(XEvent *e) {
+
+ if((c = wintoclient(ev->window)))
+ unmanage(c, True);
++ else if((c = wintosystrayicon(ev->window))) {
++ removesystrayicon(c);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+ }
+
+ void
+@@ -695,6 +782,7 @@ drawbar(Monitor *m) {
+
+ dx = (drw->fonts[0]->ascent + drw->fonts[0]->descent + 2) / 4;
+
++ resizebarwin(m);
+ for(c = m->clients; c; c = c->next) {
+ occ |= c->tags;
+ if(c->isurgent)
+@@ -717,6 +805,9 @@ drawbar(Monitor *m) {
+ if(m == selmon) { /* status is only drawn on selected monitor */
+ w = TEXTW(stext);
+ x = m->ww - w;
++ if(showsystray && m == systraytomon(m)) {
++ x -= getsystraywidth();
++ }
+ if(x < xx) {
+ x = xx;
+ w = m->ww - xx;
+@@ -746,6 +837,7 @@ drawbars(void) {
+
+ for(m = mons; m; m = m->next)
+ drawbar(m);
++ updatesystray();
+ }
+
+ void
+@@ -772,8 +864,11 @@ expose(XEvent *e) {
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+- if(ev->count == 0 && (m = wintomon(ev->window)))
++ if(ev->count == 0 && (m = wintomon(ev->window))) {
+ drawbar(m);
++ if(m == selmon)
++ updatesystray();
++ }
+ }
+
+ void
+@@ -856,10 +951,17 @@ getatomprop(Client *c, Atom prop) {
+ unsigned long dl;
+ unsigned char *p = NULL;
+ Atom da, atom = None;
++ /* FIXME getatomprop should return the number of items and a pointer to
++ * the stored data instead of this workaround */
++ Atom req = XA_ATOM;
++ if(prop == xatom[XembedInfo])
++ req = xatom[XembedInfo];
+
+- if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
++ if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
+ &da, &di, &dl, &dl, &p) == Success && p) {
+ atom = *(Atom *)p;
++ if(da == xatom[XembedInfo] && dl == 2)
++ atom = ((Atom *)p)[1];
+ XFree(p);
+ }
+ return atom;
+@@ -891,6 +993,15 @@ getstate(Window w) {
+ return result;
+ }
+
++unsigned int
++getsystraywidth() {
++ unsigned int w = 0;
++ Client *i;
++ if(showsystray)
++ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
++ return w ? w + systrayspacing : 1;
++}
++
+ Bool
+ gettextprop(Window w, Atom atom, char *text, unsigned int size) {
+ char **list = NULL;
+@@ -991,7 +1102,7 @@ void
+ killclient(const Arg *arg) {
+ if(!selmon->sel)
+ return;
+- if(!sendevent(selmon->sel, wmatom[WMDelete])) {
++ if(!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
+ XGrabServer(dpy);
+ XSetErrorHandler(xerrordummy);
+ XSetCloseDownMode(dpy, DestroyAll);
+@@ -1076,6 +1187,12 @@ void
+ maprequest(XEvent *e) {
+ static XWindowAttributes wa;
+ XMapRequestEvent *ev = &e->xmaprequest;
++ Client *i;
++ if((i = wintosystrayicon(ev->window))) {
++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+
+ if(!XGetWindowAttributes(dpy, ev->window, &wa))
+ return;
+@@ -1197,6 +1314,16 @@ propertynotify(XEvent *e) {
+ Window trans;
+ XPropertyEvent *ev = &e->xproperty;
+
++ if((c = wintosystrayicon(ev->window))) {
++ if(ev->atom == XA_WM_NORMAL_HINTS) {
++ updatesizehints(c);
++ updatesystrayicongeom(c, c->w, c->h);
++ }
++ else
++ updatesystrayiconstate(c, ev);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+ if((ev->window == root) && (ev->atom == XA_WM_NAME))
+ updatestatus();
+ else if(ev->state == PropertyDelete)
+@@ -1246,12 +1373,33 @@ recttomon(int x, int y, int w, int h) {
+ }
+
+ void
++removesystrayicon(Client *i) {
++ Client **ii;
++
++ if(!showsystray || !i)
++ return;
++ for(ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
++ if(ii)
++ *ii = i->next;
++ free(i);
++}
++
++
++void
+ resize(Client *c, int x, int y, int w, int h, Bool interact) {
+ if(applysizehints(c, &x, &y, &w, &h, interact))
+ resizeclient(c, x, y, w, h);
+ }
+
+ void
++resizebarwin(Monitor *m) {
++ unsigned int w = m->ww;
++ if(showsystray && m == systraytomon(m))
++ w -= getsystraywidth();
++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
++}
++
++void
+ resizeclient(Client *c, int x, int y, int w, int h) {
+ XWindowChanges wc;
+
+@@ -1322,6 +1470,18 @@ resizemouse(const Arg *arg) {
+ }
+
+ void
++resizerequest(XEvent *e) {
++ XResizeRequestEvent *ev = &e->xresizerequest;
++ Client *i;
++
++ if((i = wintosystrayicon(ev->window))) {
++ updatesystrayicongeom(i, ev->width, ev->height);
++ resizebarwin(selmon);
++ updatesystray();
++ }
++}
++
++void
+ restack(Monitor *m) {
+ Client *c;
+ XEvent ev;
+@@ -1405,25 +1565,35 @@ setclientstate(Client *c, long state) {
+ }
+
+ Bool
+-sendevent(Client *c, Atom proto) {
++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) {
+ int n;
+- Atom *protocols;
++ Atom *protocols, mt;
+ Bool exists = False;
+ XEvent ev;
+
+- if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
+- while(!exists && n--)
+- exists = protocols[n] == proto;
+- XFree(protocols);
++ if(proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
++ mt = wmatom[WMProtocols];
++ if(XGetWMProtocols(dpy, w, &protocols, &n)) {
++ while(!exists && n--)
++ exists = protocols[n] == proto;
++ XFree(protocols);
++ }
++ }
++ else {
++ exists = True;
++ mt = proto;
+ }
+ if(exists) {
+ ev.type = ClientMessage;
+- ev.xclient.window = c->win;
+- ev.xclient.message_type = wmatom[WMProtocols];
++ ev.xclient.window = w;
++ ev.xclient.message_type = mt;
+ ev.xclient.format = 32;
+- ev.xclient.data.l[0] = proto;
+- ev.xclient.data.l[1] = CurrentTime;
+- XSendEvent(dpy, c->win, False, NoEventMask, &ev);
++ ev.xclient.data.l[0] = d0;
++ ev.xclient.data.l[1] = d1;
++ ev.xclient.data.l[2] = d2;
++ ev.xclient.data.l[3] = d3;
++ ev.xclient.data.l[4] = d4;
++ XSendEvent(dpy, w, False, mask, &ev);
+ }
+ return exists;
+ }
+@@ -1436,7 +1606,7 @@ setfocus(Client *c) {
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &(c->win), 1);
+ }
+- sendevent(c, wmatom[WMTakeFocus]);
++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
+ }
+
+ void
+@@ -1519,12 +1689,18 @@ setup(void) {
+ wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
+ netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
+ netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
+ netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
+ netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+ netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+ netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
+ /* init cursors */
+ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
+ cursor[CurResize] = drw_cur_create(drw, XC_sizing);
+@@ -1536,6 +1712,8 @@ setup(void) {
+ scheme[SchemeSel].border = drw_clr_create(drw, selbordercolor);
+ scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor);
+ scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor);
++ /* init system tray */
++ updatesystray();
+ /* init bars */
+ updatebars();
+ updatestatus();
+@@ -1591,6 +1769,22 @@ spawn(const Arg *arg) {
+ }
+ }
+
++Monitor *
++systraytomon(Monitor *m) {
++ Monitor *t;
++ int i, n;
++ if(!systraypinning) {
++ if(!m)
++ return selmon;
++ return m == selmon ? m : NULL;
++ }
++ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
++ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
++ if(systraypinningfailfirst && n < systraypinning)
++ return mons;
++ return t;
++}
++
+ void
+ tag(const Arg *arg) {
+ if(selmon->sel && arg->ui & TAGMASK) {
+@@ -1637,7 +1831,18 @@ void
+ togglebar(const Arg *arg) {
+ selmon->showbar = !selmon->showbar;
+ updatebarpos(selmon);
+- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
++ resizebarwin(selmon);
++ if(showsystray) {
++ XWindowChanges wc;
++ if(!selmon->showbar)
++ wc.y = -bh;
++ else if(selmon->showbar) {
++ wc.y = 0;
++ if(!selmon->topbar)
++ wc.y = selmon->mh - bh;
++ }
++ XConfigureWindow(dpy, systray->win, CWY, &wc);
++ }
+ arrange(selmon);
+ }
+
+@@ -1727,11 +1932,18 @@ unmapnotify(XEvent *e) {
+ else
+ unmanage(c, False);
+ }
++ else if((c = wintosystrayicon(ev->window))) {
++ removesystrayicon(c);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+ }
+
+ void
+ updatebars(void) {
++ unsigned int w;
+ Monitor *m;
++
+ XSetWindowAttributes wa = {
+ .override_redirect = True,
+ .background_pixmap = ParentRelative,
+@@ -1740,10 +1952,15 @@ updatebars(void) {
+ for(m = mons; m; m = m->next) {
+ if (m->barwin)
+ continue;
+- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
++ w = m->ww;
++ if(showsystray && m == systraytomon(m))
++ w -= getsystraywidth();
++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
+ CopyFromParent, DefaultVisual(dpy, screen),
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
++ if(showsystray && m == systraytomon(m))
++ XMapRaised(dpy, systray->win);
+ XMapRaised(dpy, m->barwin);
+ }
+ }
+@@ -1936,6 +2153,117 @@ updatestatus(void) {
+ }
+
+ void
++updatesystrayicongeom(Client *i, int w, int h) {
++ if(i) {
++ i->h = bh;
++ if(w == h)
++ i->w = bh;
++ else if(h == bh)
++ i->w = w;
++ else
++ i->w = (int) ((float)bh * ((float)w / (float)h));
++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
++ /* force icons into the systray dimenons if they don't want to */
++ if(i->h > bh) {
++ if(i->w == i->h)
++ i->w = bh;
++ else
++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
++ i->h = bh;
++ }
++ }
++}
++
++void
++updatesystrayiconstate(Client *i, XPropertyEvent *ev) {
++ long flags;
++ int code = 0;
++
++ if(!showsystray || !i || ev->atom != xatom[XembedInfo] ||
++ !(flags = getatomprop(i, xatom[XembedInfo])))
++ return;
++
++ if(flags & XEMBED_MAPPED && !i->tags) {
++ i->tags = 1;
++ code = XEMBED_WINDOW_ACTIVATE;
++ XMapRaised(dpy, i->win);
++ setclientstate(i, NormalState);
++ }
++ else if(!(flags & XEMBED_MAPPED) && i->tags) {
++ i->tags = 0;
++ code = XEMBED_WINDOW_DEACTIVATE;
++ XUnmapWindow(dpy, i->win);
++ setclientstate(i, WithdrawnState);
++ }
++ else
++ return;
++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
++ systray->win, XEMBED_EMBEDDED_VERSION);
++}
++
++void
++updatesystray(void) {
++ XSetWindowAttributes wa;
++ XWindowChanges wc;
++ Client *i;
++ Monitor *m = systraytomon(NULL);
++ unsigned int x = m->mx + m->mw;
++ unsigned int w = 1;
++
++ if(!showsystray)
++ return;
++ if(!systray) {
++ /* init systray */
++ if(!(systray = (Systray *)calloc(1, sizeof(Systray))))
++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
++ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel].bg->pix);
++ wa.event_mask = ButtonPressMask | ExposureMask;
++ wa.override_redirect = True;
++ wa.background_pixel = scheme[SchemeNorm].bg->pix;
++ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
++ PropModeReplace, (unsigned char *)&systrayorientation, 1);
++ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
++ XMapRaised(dpy, systray->win);
++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
++ if(XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
++ XSync(dpy, False);
++ }
++ else {
++ fprintf(stderr, "dwm: unable to obtain system tray.\n");
++ free(systray);
++ systray = NULL;
++ return;
++ }
++ }
++ for(w = 0, i = systray->icons; i; i = i->next) {
++ /* make sure the background color stays the same */
++ wa.background_pixel = scheme[SchemeNorm].bg->pix;
++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
++ XMapRaised(dpy, i->win);
++ w += systrayspacing;
++ i->x = w;
++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
++ w += i->w;
++ if(i->mon != m)
++ i->mon = m;
++ }
++ w = w ? w + systrayspacing : 1;
++ x -= w;
++ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
++ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
++ wc.stack_mode = Above; wc.sibling = m->barwin;
++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
++ XMapWindow(dpy, systray->win);
++ XMapSubwindows(dpy, systray->win);
++ /* redraw background */
++ XSetForeground(dpy, drw->gc, scheme[SchemeNorm].bg->pix);
++ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
++ XSync(dpy, False);
++}
++
++void
+ updatewindowtype(Client *c) {
+ Atom state = getatomprop(c, netatom[NetWMState]);
+ Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
+@@ -2004,6 +2332,16 @@ wintomon(Window w) {
+ return selmon;
+ }
+
++Client *
++wintosystrayicon(Window w) {
++ Client *i = NULL;
++
++ if(!showsystray || !w)
++ return i;
++ for(i = systray->icons; i && i->win != w; i = i->next) ;
++ return i;
++}
++
+ /* There's no way to check accesses to destroyed windows, thus those cases are
+ * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
+ * default error handler, which may call exit. */
diff --git a/dwm.suckless.org/patches/dwm-7e1182c-tab.diff b/dwm.suckless.org/patches/dwm-7e1182c-tab.diff
@@ -0,0 +1,506 @@
+diff --git a/config.def.h b/config.def.h
+index 3fde3cf..6a7ad0f 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -15,6 +15,13 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
+ static const unsigned int snap = 32; /* snap pixel */
+ static const Bool showbar = True; /* False means no bar */
+ static const Bool topbar = True; /* False means bottom bar */
++/* Display modes of the tab bar: never shown, always shown, shown only in */
++/* monocle mode in presence of several windows. */
++/* Modes after showtab_nmodes are disabled */
++enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
++static const int showtab = showtab_auto; /* Default tab bar show mode */
++static const Bool toptab = False; /* False means bottom tab bar */
++
+
+ /* tagging */
+ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+@@ -32,7 +39,7 @@ static const Rule rules[] = {
+ /* layout(s) */
+ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
+ static const int nmaster = 1; /* number of clients in master area */
+-static const Bool resizehints = True; /* True means respect size hints in tiled resizals */
++static const Bool resizehints = False; /* True means respect size hints in tiled resizals */
+
+ static const Layout layouts[] = {
+ /* symbol arrange function */
+@@ -62,6 +69,7 @@ static Key keys[] = {
+ { MODKEY, XK_p, spawn, {.v = dmenucmd } },
+ { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
+ { MODKEY, XK_b, togglebar, {0} },
++ { MODKEY, XK_w, tabmode, {-1} },
+ { MODKEY, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY, XK_i, incnmaster, {.i = +1 } },
+@@ -109,5 +117,6 @@ static Button buttons[] = {
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
++ { ClkTabBar, 0, Button1, focuswin, {0} },
+ };
+
+diff --git a/dwm.1 b/dwm.1
+index 6687011..f736591 100644
+--- a/dwm.1
++++ b/dwm.1
+@@ -19,14 +19,22 @@ layout applied.
+ Windows are grouped by tags. Each window can be tagged with one or multiple
+ tags. Selecting certain tags displays all windows with these tags.
+ .P
+-Each screen contains a small status bar which displays all available tags, the
+-layout, the title of the focused window, and the text read from the root window
+-name property, if the screen is focused. A floating window is indicated with an
+-empty square and a maximised floating window is indicated with a filled square
+-before the windows title. The selected tags are indicated with a different
+-color. The tags of the focused window are indicated with a filled square in the
+-top left corner. The tags which are applied to one or more windows are
+-indicated with an empty square in the top left corner.
++Each screen contains two small status bars.
++.P
++One bar displays all available tags, the layout, the title of the focused
++window, and the text read from the root window name property, if the screen is
++focused. A floating window is indicated with an empty square and a maximised
++floating window is indicated with a filled square before the windows title.
++The selected tags are indicated with a different color. The tags of the focused
++window are indicated with a filled square in the top left corner. The tags
++which are applied to one or more windows are indicated with an empty square in
++the top left corner.
++.P
++Another bar contains a tab for each window of the current view and allows
++navigation between windows, especially in the monocle mode. The different
++display modes of this bar are described under the Mod1\-w Keybord command
++section. When a single tag is selected, that tag is indicated in the left
++corner of the tab bar.
+ .P
+ dwm draws a small border around windows to indicate the focus state.
+ .SH OPTIONS
+@@ -43,7 +51,8 @@ command.
+ .TP
+ .B Button1
+ click on a tag label to display all windows with that tag, click on the layout
+-label toggles between tiled and floating layout.
++label toggles between tiled and floating layout, click on a window name in the
++tab bar brings focus to that window.
+ .TP
+ .B Button3
+ click on a tag label adds/removes all windows with that tag to/from the view.
+@@ -104,6 +113,12 @@ Increase master area size.
+ .B Mod1\-h
+ Decrease master area size.
+ .TP
++.B Mod1\-w
++Cycle over the tab bar display modes: never displayed, always displayed,
++displayed only in monocle mode when the view contains than one window (auto
++mode). Some display modes can be disabled in the configuration, config.h. In
++the default configuration only "never" and "auto" display modes are enabled.
++.TP
+ .B Mod1\-Return
+ Zooms/cycles focused window to/from master area (tiled layouts only).
+ .TP
+diff --git a/dwm.c b/dwm.c
+index 96b43f7..585dd7b 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -64,7 +64,7 @@ enum { NetSupported, NetWMName, NetWMState,
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+-enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
++enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+
+ typedef union {
+@@ -111,24 +111,32 @@ typedef struct {
+ void (*arrange)(Monitor *);
+ } Layout;
+
++#define MAXTABS 50
++
+ struct Monitor {
+ char ltsymbol[16];
+ float mfact;
+ int nmaster;
+ int num;
+ int by; /* bar geometry */
++ int ty; /* tab bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
+ unsigned int seltags;
+ unsigned int sellt;
+ unsigned int tagset[2];
+ Bool showbar;
++ Bool showtab;
+ Bool topbar;
++ Bool toptab;
+ Client *clients;
+ Client *sel;
+ Client *stack;
+ Monitor *next;
+ Window barwin;
++ Window tabwin;
++ int ntabs;
++ int tab_widths[MAXTABS];
+ const Layout *lt[2];
+ };
+
+@@ -164,12 +172,15 @@ static void detachstack(Client *c);
+ static Monitor *dirtomon(int dir);
+ static void drawbar(Monitor *m);
+ static void drawbars(void);
++static void drawtab(Monitor *m);
++static void drawtabs(void);
+ static void enternotify(XEvent *e);
+ static void expose(XEvent *e);
+ static void focus(Client *c);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
++static void focuswin(const Arg* arg);
+ static Bool getrootptr(int *x, int *y);
+ static long getstate(Window w);
+ static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
+@@ -206,6 +217,7 @@ static void setup(void);
+ static void showhide(Client *c);
+ static void sigchld(int unused);
+ static void spawn(const Arg *arg);
++static void tabmode(const Arg *arg);
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
+ static void tile(Monitor *);
+@@ -240,6 +252,7 @@ static char stext[256];
+ static int screen;
+ static int sw, sh; /* X display screen geometry width, height */
+ static int bh, blw = 0; /* bar geometry */
++static int th = 0; /* tab bar geometry */
+ static int (*xerrorxlib)(Display *, XErrorEvent *);
+ static unsigned int numlockmask = 0;
+ static void (*handler[LASTEvent]) (XEvent *) = {
+@@ -389,6 +402,9 @@ arrange(Monitor *m) {
+
+ void
+ arrangemon(Monitor *m) {
++ updatebarpos(m);
++ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
++
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
+ if(m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+@@ -437,14 +453,32 @@ buttonpress(XEvent *e) {
+ else
+ click = ClkWinTitle;
+ }
++ if(ev->window == selmon->tabwin) {
++ i = 0; x = 0;
++ for(c = selmon->clients; c; c = c->next){
++ if(!ISVISIBLE(c)) continue;
++ x += selmon->tab_widths[i];
++ if (ev->x > x)
++ ++i;
++ else
++ break;
++ if(i >= m->ntabs) break;
++ }
++ if(c) {
++ click = ClkTabBar;
++ arg.ui = i;
++ }
++ }
+ else if((c = wintoclient(ev->window))) {
+ focus(c);
+ click = ClkClientWin;
+ }
+ for(i = 0; i < LENGTH(buttons); i++)
+ if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
+- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
+- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
++ buttons[i].func(((click == ClkTagBar || click == ClkTabBar)
++ && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
++ }
+ }
+
+ void
+@@ -497,6 +531,8 @@ cleanupmon(Monitor *mon) {
+ }
+ XUnmapWindow(dpy, mon->barwin);
+ XDestroyWindow(dpy, mon->barwin);
++ XUnmapWindow(dpy, mon->tabwin);
++ XDestroyWindow(dpy, mon->tabwin);
+ free(mon);
+ }
+
+@@ -565,8 +601,11 @@ configurenotify(XEvent *e) {
+ if(updategeom() || dirty) {
+ drw_resize(drw, sw, bh);
+ updatebars();
+- for(m = mons; m; m = m->next)
++ //refreshing display of status bar. The tab bar is handled by the arrange()
++ //method, which is called below
++ for(m = mons; m; m = m->next){
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
++ }
+ focus(NULL);
+ arrange(NULL);
+ }
+@@ -635,7 +674,10 @@ createmon(void) {
+ m->mfact = mfact;
+ m->nmaster = nmaster;
+ m->showbar = showbar;
++ m->showtab = showtab;
+ m->topbar = topbar;
++ m->toptab = toptab;
++ m->ntabs = 0;
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+@@ -749,6 +791,104 @@ drawbars(void) {
+ }
+
+ void
++drawtabs(void) {
++ Monitor *m;
++
++ for(m = mons; m; m = m->next)
++ drawtab(m);
++}
++
++static int
++cmpint(const void *p1, const void *p2) {
++ /* The actual arguments to this function are "pointers to
++ pointers to char", but strcmp(3) arguments are "pointers
++ to char", hence the following cast plus dereference */
++ return *((int*) p1) > * (int*) p2;
++}
++
++
++void
++drawtab(Monitor *m) {
++ Client *c;
++ int i;
++ int itag = -1;
++ char view_info[50];
++ int view_info_w = 0;
++ int sorted_label_widths[MAXTABS];
++ int tot_width;
++ int maxsize = bh;
++ int x = 0;
++ int w = 0;
++
++ //view_info: indicate the tag which is displayed in the view
++ for(i = 0; i < LENGTH(tags); ++i){
++ if((selmon->tagset[selmon->seltags] >> i) & 1) {
++ if(itag >=0){ //more than one tag selected
++ itag = -1;
++ break;
++ }
++ itag = i;
++ }
++ }
++ if(0 <= itag && itag < LENGTH(tags)){
++ snprintf(view_info, sizeof view_info, "[%s]", tags[itag]);
++ } else {
++ strncpy(view_info, "[...]", sizeof view_info);
++ }
++ view_info[sizeof(view_info) - 1 ] = 0;
++ view_info_w = TEXTW(view_info);
++ tot_width = view_info_w;
++
++ /* Calculates number of labels and their width */
++ m->ntabs = 0;
++ for(c = m->clients; c; c = c->next){
++ if(!ISVISIBLE(c)) continue;
++ m->tab_widths[m->ntabs] = TEXTW(c->name);
++ tot_width += m->tab_widths[m->ntabs];
++ ++m->ntabs;
++ if(m->ntabs >= MAXTABS) break;
++ }
++
++ if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated
++ memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs);
++ qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint);
++ tot_width = view_info_w;
++ for(i = 0; i < m->ntabs; ++i){
++ if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww)
++ break;
++ tot_width += sorted_label_widths[i];
++ }
++ maxsize = (m->ww - tot_width) / (m->ntabs - i);
++ } else{
++ maxsize = m->ww;
++ }
++ i = 0;
++ for(c = m->clients; c; c = c->next){
++ if(!ISVISIBLE(c)) continue;
++ if(i >= m->ntabs) break;
++ if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize;
++ w = m->tab_widths[i];
++ drw_setscheme(drw, (c == m->sel) ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
++ drw_text(drw, x, 0, w, th, c->name, 0);
++ x += w;
++ ++i;
++ }
++
++ drw_setscheme(drw, &scheme[SchemeNorm]);
++
++ /* cleans interspace between window names and current viewed tag label */
++ w = m->ww - view_info_w - x;
++ drw_text(drw, x, 0, w, th, NULL, 0);
++
++ /* view info */
++ x += w;
++ w = view_info_w;
++ drw_text(drw, x, 0, w, th, view_info, 0);
++
++ drw_map(drw, m->tabwin, 0, 0, m->ww, th);
++}
++
++void
+ enternotify(XEvent *e) {
+ Client *c;
+ Monitor *m;
+@@ -772,8 +912,10 @@ expose(XEvent *e) {
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+- if(ev->count == 0 && (m = wintomon(ev->window)))
++ if(ev->count == 0 && (m = wintomon(ev->window))){
+ drawbar(m);
++ drawtab(m);
++ }
+ }
+
+ void
+@@ -800,6 +942,7 @@ focus(Client *c) {
+ }
+ selmon->sel = c;
+ drawbars();
++ drawtabs();
+ }
+
+ void
+@@ -850,6 +993,19 @@ focusstack(const Arg *arg) {
+ }
+ }
+
++void
++focuswin(const Arg* arg){
++ int iwin = arg->i;
++ Client* c = NULL;
++ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
++ if(ISVISIBLE(c)) --iwin;
++ };
++ if(c) {
++ focus(c);
++ restack(selmon);
++ }
++}
++
+ Atom
+ getatomprop(Client *c, Atom prop) {
+ int di;
+@@ -1215,12 +1371,14 @@ propertynotify(XEvent *e) {
+ case XA_WM_HINTS:
+ updatewmhints(c);
+ drawbars();
++ drawtabs();
+ break;
+ }
+ if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
+ updatetitle(c);
+ if(c == c->mon->sel)
+ drawbar(c->mon);
++ drawtab(c->mon);
+ }
+ if(ev->atom == netatom[NetWMWindowType])
+ updatewindowtype(c);
+@@ -1328,6 +1486,7 @@ restack(Monitor *m) {
+ XWindowChanges wc;
+
+ drawbar(m);
++ drawtab(m);
+ if(!m->sel)
+ return;
+ if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
+@@ -1511,6 +1670,8 @@ setup(void) {
+ if (!drw->fontcount)
+ die("no fonts could be loaded.\n");
+ bh = drw->fonts[0]->h + 2;
++ th = bh;
++
+ updategeom();
+ /* init atoms */
+ wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
+@@ -1642,6 +1803,16 @@ togglebar(const Arg *arg) {
+ }
+
+ void
++tabmode(const Arg *arg) {
++ if(arg && arg->i >= 0)
++ selmon->showtab = arg->ui % showtab_nmodes;
++ else
++ selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
++ arrange(selmon);
++}
++
++
++void
+ togglefloating(const Arg *arg) {
+ if(!selmon->sel)
+ return;
+@@ -1745,20 +1916,43 @@ updatebars(void) {
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
+ XMapRaised(dpy, m->barwin);
++ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
++ CopyFromParent, DefaultVisual(dpy, screen),
++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
++ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
++ XMapRaised(dpy, m->tabwin);
+ }
+ }
+
+ void
+ updatebarpos(Monitor *m) {
++ Client *c;
++ int nvis = 0;
++
+ m->wy = m->my;
+ m->wh = m->mh;
+ if(m->showbar) {
+ m->wh -= bh;
+ m->by = m->topbar ? m->wy : m->wy + m->wh;
+- m->wy = m->topbar ? m->wy + bh : m->wy;
+- }
+- else
++ if ( m->topbar )
++ m->wy += bh;
++ } else {
+ m->by = -bh;
++ }
++
++ for(c = m->clients; c; c = c->next){
++ if(ISVISIBLE(c)) ++nvis;
++ }
++
++ if(m->showtab == showtab_always
++ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){
++ m->wh -= th;
++ m->ty = m->toptab ? m->wy : m->wy + m->wh;
++ if ( m->toptab )
++ m->wy += th;
++ } else {
++ m->ty = -th;
++ }
+ }
+
+ void
+@@ -1997,7 +2191,7 @@ wintomon(Window w) {
+ if(w == root && getrootptr(&x, &y))
+ return recttomon(x, y, 1, 1);
+ for(m = mons; m; m = m->next)
+- if(w == m->barwin)
++ if(w == m->barwin || w == m->tabwin)
+ return m;
+ if((c = wintoclient(w)))
+ return c->mon;
diff --git a/dwm.suckless.org/patches/dwm-master_2015-03-05_14343e-pertag-tab-v2b.diff b/dwm.suckless.org/patches/dwm-master_2015-03-05_14343e-pertag-tab-v2b.diff
@@ -1,693 +0,0 @@
-diff --git a/config.def.h b/config.def.h
-index eaae8f3..a07814b 100644
---- a/config.def.h
-+++ b/config.def.h
-@@ -17,10 +17,22 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
- static const unsigned int snap = 32; /* snap pixel */
- static const Bool showbar = True; /* False means no bar */
- static const Bool topbar = True; /* False means bottom bar */
-+/* Display modes of the tab bar: never shown, always shown, shown only in */
-+/* monocle mode in presence of several windows. */
-+/* Modes after showtab_nmodes are disabled */
-+enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
-+static const int showtab = showtab_auto; /* Default tab bar show mode */
-+static const Bool toptab = False; /* False means bottom tab bar */
-+
-
- /* tagging */
- static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
-
-+/* default layout per tags */
-+/* The first element is for all-tag view, following i-th element corresponds to */
-+/* tags[i]. Layout is referred using the layouts array index.*/
-+static int def_layouts[1 + LENGTH(tags)] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-+
- static const Rule rules[] = {
- /* xprop(1):
- * WM_CLASS(STRING) = instance, class
-@@ -34,7 +46,7 @@ static const Rule rules[] = {
- /* layout(s) */
- static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
- static const int nmaster = 1; /* number of clients in master area */
--static const Bool resizehints = True; /* True means respect size hints in tiled resizals */
-+static const Bool resizehints = False; /* True means respect size hints in tiled resizals */
-
- static const Layout layouts[] = {
- /* symbol arrange function */
-@@ -64,6 +76,7 @@ static Key keys[] = {
- { MODKEY, XK_p, spawn, {.v = dmenucmd } },
- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
- { MODKEY, XK_b, togglebar, {0} },
-+ { MODKEY, XK_w, tabmode, {-1} },
- { MODKEY, XK_j, focusstack, {.i = +1 } },
- { MODKEY, XK_k, focusstack, {.i = -1 } },
- { MODKEY, XK_i, incnmaster, {.i = +1 } },
-@@ -111,5 +124,6 @@ static Button buttons[] = {
- { ClkTagBar, 0, Button3, toggleview, {0} },
- { ClkTagBar, MODKEY, Button1, tag, {0} },
- { ClkTagBar, MODKEY, Button3, toggletag, {0} },
-+ { ClkTabBar, 0, Button1, focuswin, {0} },
- };
-
-diff --git a/dwm.1 b/dwm.1
-index 6687011..077d92b 100644
---- a/dwm.1
-+++ b/dwm.1
-@@ -19,14 +19,22 @@ layout applied.
- Windows are grouped by tags. Each window can be tagged with one or multiple
- tags. Selecting certain tags displays all windows with these tags.
- .P
--Each screen contains a small status bar which displays all available tags, the
--layout, the title of the focused window, and the text read from the root window
--name property, if the screen is focused. A floating window is indicated with an
--empty square and a maximised floating window is indicated with a filled square
--before the windows title. The selected tags are indicated with a different
--color. The tags of the focused window are indicated with a filled square in the
--top left corner. The tags which are applied to one or more windows are
--indicated with an empty square in the top left corner.
-+Each screen contains two small status bars.
-+.P
-+One bar displays all available tags, the layout, the title of the focused
-+window, and the text read from the root window name property, if the screen is
-+focused. A floating window is indicated with an empty square and a maximised
-+floating window is indicated with a filled square before the windows title. The
-+selected tags are indicated with a different color. The tags of the focused
-+window are indicated with a filled square in the top left corner. The tags
-+which are applied to one or more windows are indicated with an empty square in
-+the top left corner.
-+.P
-+Another bar contains a tab for each window of the current view and allows
-+navigation between windows, especially in the monocle mode. The different
-+display modes of this bar are described under the Mod1\-w Keybord command
-+section. When a single tag is selected, that tag is indicated in the left corner
-+of the tab bar.
- .P
- dwm draws a small border around windows to indicate the focus state.
- .SH OPTIONS
-@@ -43,7 +51,8 @@ command.
- .TP
- .B Button1
- click on a tag label to display all windows with that tag, click on the layout
--label toggles between tiled and floating layout.
-+label toggles between tiled and floating layout, click on a window name in the
-+tab bar brings focus to that window.
- .TP
- .B Button3
- click on a tag label adds/removes all windows with that tag to/from the view.
-@@ -104,6 +113,12 @@ Increase master area size.
- .B Mod1\-h
- Decrease master area size.
- .TP
-+.B Mod1\-w
-+Cycle over the tab bar display modes: never displayed, always displayed,
-+displayed only in monocle mode when the view contains than one window (auto
-+mode). Some display modes can be disabled in the configuration, config.h. In
-+the default configuration only "never" and "auto" display modes are enabled.
-+.TP
- .B Mod1\-Return
- Zooms/cycles focused window to/from master area (tiled layouts only).
- .TP
-diff --git a/dwm.c b/dwm.c
-index 169adcb..515a30f 100644
---- a/dwm.c
-+++ b/dwm.c
-@@ -64,7 +64,7 @@ enum { NetSupported, NetWMName, NetWMState,
- NetWMFullscreen, NetActiveWindow, NetWMWindowType,
- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
- enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
--enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
-+enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
- ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
-
- typedef union {
-@@ -111,25 +111,35 @@ typedef struct {
- void (*arrange)(Monitor *);
- } Layout;
-
-+#define MAXTABS 50
-+
-+typedef struct Pertag Pertag;
- struct Monitor {
- char ltsymbol[16];
- float mfact;
- int nmaster;
- int num;
- int by; /* bar geometry */
-+ int ty; /* tab bar geometry */
- int mx, my, mw, mh; /* screen size */
- int wx, wy, ww, wh; /* window area */
- unsigned int seltags;
- unsigned int sellt;
- unsigned int tagset[2];
- Bool showbar;
-+ Bool showtab;
- Bool topbar;
-+ Bool toptab;
- Client *clients;
- Client *sel;
- Client *stack;
- Monitor *next;
- Window barwin;
-+ Window tabwin;
-+ int ntabs;
-+ int tab_widths[MAXTABS];
- const Layout *lt[2];
-+ Pertag *pertag;
- };
-
- typedef struct {
-@@ -164,12 +174,15 @@ static void detachstack(Client *c);
- static Monitor *dirtomon(int dir);
- static void drawbar(Monitor *m);
- static void drawbars(void);
-+static void drawtab(Monitor *m);
-+static void drawtabs(void);
- static void enternotify(XEvent *e);
- static void expose(XEvent *e);
- static void focus(Client *c);
- static void focusin(XEvent *e);
- static void focusmon(const Arg *arg);
- static void focusstack(const Arg *arg);
-+static void focuswin(const Arg* arg);
- static Bool getrootptr(int *x, int *y);
- static long getstate(Window w);
- static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
-@@ -206,6 +219,7 @@ static void setup(void);
- static void showhide(Client *c);
- static void sigchld(int unused);
- static void spawn(const Arg *arg);
-+static void tabmode(const Arg *arg);
- static void tag(const Arg *arg);
- static void tagmon(const Arg *arg);
- static void tile(Monitor *);
-@@ -240,6 +254,7 @@ static char stext[256];
- static int screen;
- static int sw, sh; /* X display screen geometry width, height */
- static int bh, blw = 0; /* bar geometry */
-+static int th = 0; /* tab bar geometry */
- static int (*xerrorxlib)(Display *, XErrorEvent *);
- static unsigned int numlockmask = 0;
- static void (*handler[LASTEvent]) (XEvent *) = {
-@@ -270,6 +285,16 @@ static Window root;
- /* configuration, allows nested code to access above variables */
- #include "config.h"
-
-+struct Pertag {
-+ unsigned int curtag, prevtag; /* current and previous tag */
-+ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
-+ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
-+ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
-+ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
-+ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
-+ Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */
-+};
-+
- /* compile-time check if all tags fit into an unsigned int bit array. */
- struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
-
-@@ -389,6 +414,9 @@ arrange(Monitor *m) {
-
- void
- arrangemon(Monitor *m) {
-+ updatebarpos(m);
-+ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
-+
- strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
- if(m->lt[m->sellt]->arrange)
- m->lt[m->sellt]->arrange(m);
-@@ -437,14 +465,32 @@ buttonpress(XEvent *e) {
- else
- click = ClkWinTitle;
- }
-+ if(ev->window == selmon->tabwin) {
-+ i = 0; x = 0;
-+ for(c = selmon->clients; c; c = c->next){
-+ if(!ISVISIBLE(c)) continue;
-+ x += selmon->tab_widths[i];
-+ if (ev->x > x)
-+ ++i;
-+ else
-+ break;
-+ if(i >= m->ntabs) break;
-+ }
-+ if(c) {
-+ click = ClkTabBar;
-+ arg.ui = i;
-+ }
-+ }
- else if((c = wintoclient(ev->window))) {
- focus(c);
- click = ClkClientWin;
- }
- for(i = 0; i < LENGTH(buttons); i++)
- if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
-- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
-- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
-+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
-+ buttons[i].func(((click == ClkTagBar || click == ClkTabBar)
-+ && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
-+ }
- }
-
- void
-@@ -498,6 +544,8 @@ cleanupmon(Monitor *mon) {
- }
- XUnmapWindow(dpy, mon->barwin);
- XDestroyWindow(dpy, mon->barwin);
-+ XUnmapWindow(dpy, mon->tabwin);
-+ XDestroyWindow(dpy, mon->tabwin);
- free(mon);
- }
-
-@@ -517,6 +565,7 @@ void
- clientmessage(XEvent *e) {
- XClientMessageEvent *cme = &e->xclient;
- Client *c = wintoclient(cme->window);
-+ int i;
-
- if(!c)
- return;
-@@ -529,6 +578,8 @@ clientmessage(XEvent *e) {
- if(!ISVISIBLE(c)) {
- c->mon->seltags ^= 1;
- c->mon->tagset[c->mon->seltags] = c->tags;
-+ for(i=0; !(c->tags & 1 << i); i++);
-+ view(&(Arg){.ui = 1 << i});
- }
- pop(c);
- }
-@@ -566,8 +617,11 @@ configurenotify(XEvent *e) {
- if(updategeom() || dirty) {
- drw_resize(drw, sw, bh);
- updatebars();
-- for(m = mons; m; m = m->next)
-+ //refreshing display of status bar. The tab bar is handled by the arrange()
-+ //method, which is called below
-+ for(m = mons; m; m = m->next){
- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
-+ }
- focus(NULL);
- arrange(NULL);
- }
-@@ -630,6 +684,7 @@ configurerequest(XEvent *e) {
- Monitor *
- createmon(void) {
- Monitor *m;
-+ int i;
-
- if(!(m = (Monitor *)calloc(1, sizeof(Monitor))))
- die("fatal: could not malloc() %u bytes\n", sizeof(Monitor));
-@@ -637,10 +692,34 @@ createmon(void) {
- m->mfact = mfact;
- m->nmaster = nmaster;
- m->showbar = showbar;
-+ m->showtab = showtab;
- m->topbar = topbar;
-- m->lt[0] = &layouts[0];
-+ m->toptab = toptab;
-+ m->ntabs = 0;
-+ m->lt[0] = &layouts[def_layouts[1] % LENGTH(layouts)];
- m->lt[1] = &layouts[1 % LENGTH(layouts)];
- strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
-+ if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag))))
-+ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag));
-+ m->pertag->curtag = m->pertag->prevtag = 1;
-+ for(i=0; i <= LENGTH(tags); i++) {
-+ /* init nmaster */
-+ m->pertag->nmasters[i] = m->nmaster;
-+
-+ /* init mfacts */
-+ m->pertag->mfacts[i] = m->mfact;
-+
-+ /* init layouts */
-+ m->pertag->ltidxs[i][0] = &layouts[def_layouts[i % LENGTH(def_layouts)] % LENGTH(layouts)];
-+ m->pertag->ltidxs[i][1] = m->lt[1];
-+ m->pertag->sellts[i] = m->sellt;
-+
-+ /* init showbar */
-+ m->pertag->showbars[i] = m->showbar;
-+
-+ /* swap focus and zoomswap*/
-+ m->pertag->prevzooms[i] = NULL;
-+ }
- return m;
- }
-
-@@ -749,6 +828,104 @@ drawbars(void) {
- }
-
- void
-+drawtabs(void) {
-+ Monitor *m;
-+
-+ for(m = mons; m; m = m->next)
-+ drawtab(m);
-+}
-+
-+static int
-+cmpint(const void *p1, const void *p2) {
-+ /* The actual arguments to this function are "pointers to
-+ pointers to char", but strcmp(3) arguments are "pointers
-+ to char", hence the following cast plus dereference */
-+ return *((int*) p1) > * (int*) p2;
-+}
-+
-+
-+void
-+drawtab(Monitor *m) {
-+ Client *c;
-+ int i;
-+ int itag = -1;
-+ char view_info[50];
-+ int view_info_w = 0;
-+ int sorted_label_widths[MAXTABS];
-+ int tot_width;
-+ int maxsize = bh;
-+ int x = 0;
-+ int w = 0;
-+
-+ //view_info: indicate the tag which is displayed in the view
-+ for(i = 0; i < LENGTH(tags); ++i){
-+ if((selmon->tagset[selmon->seltags] >> i) & 1) {
-+ if(itag >=0){ //more than one tag selected
-+ itag = -1;
-+ break;
-+ }
-+ itag = i;
-+ }
-+ }
-+ if(0 <= itag && itag < LENGTH(tags)){
-+ snprintf(view_info, sizeof view_info, "[%s]", tags[itag]);
-+ } else {
-+ strncpy(view_info, "[...]", sizeof view_info);
-+ }
-+ view_info[sizeof(view_info) - 1 ] = 0;
-+ view_info_w = TEXTW(view_info);
-+ tot_width = view_info_w;
-+
-+ /* Calculates number of labels and their width */
-+ m->ntabs = 0;
-+ for(c = m->clients; c; c = c->next){
-+ if(!ISVISIBLE(c)) continue;
-+ m->tab_widths[m->ntabs] = TEXTW(c->name);
-+ tot_width += m->tab_widths[m->ntabs];
-+ ++m->ntabs;
-+ if(m->ntabs >= MAXTABS) break;
-+ }
-+
-+ if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated
-+ memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs);
-+ qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint);
-+ tot_width = view_info_w;
-+ for(i = 0; i < m->ntabs; ++i){
-+ if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww)
-+ break;
-+ tot_width += sorted_label_widths[i];
-+ }
-+ maxsize = (m->ww - tot_width) / (m->ntabs - i);
-+ } else{
-+ maxsize = m->ww;
-+ }
-+ i = 0;
-+ for(c = m->clients; c; c = c->next){
-+ if(!ISVISIBLE(c)) continue;
-+ if(i >= m->ntabs) break;
-+ if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize;
-+ w = m->tab_widths[i];
-+ drw_setscheme(drw, (c == m->sel) ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
-+ drw_text(drw, x, 0, w, th, c->name, 0);
-+ x += w;
-+ ++i;
-+ }
-+
-+ drw_setscheme(drw, &scheme[SchemeNorm]);
-+
-+ /* cleans interspace between window names and current viewed tag label */
-+ w = m->ww - view_info_w - x;
-+ drw_text(drw, x, 0, w, th, NULL, 0);
-+
-+ /* view info */
-+ x += w;
-+ w = view_info_w;
-+ drw_text(drw, x, 0, w, th, view_info, 0);
-+
-+ drw_map(drw, m->tabwin, 0, 0, m->ww, th);
-+}
-+
-+void
- enternotify(XEvent *e) {
- Client *c;
- Monitor *m;
-@@ -772,8 +949,10 @@ expose(XEvent *e) {
- Monitor *m;
- XExposeEvent *ev = &e->xexpose;
-
-- if(ev->count == 0 && (m = wintomon(ev->window)))
-+ if(ev->count == 0 && (m = wintomon(ev->window))){
- drawbar(m);
-+ drawtab(m);
-+ }
- }
-
- void
-@@ -800,6 +979,7 @@ focus(Client *c) {
- }
- selmon->sel = c;
- drawbars();
-+ drawtabs();
- }
-
- void
-@@ -850,6 +1030,19 @@ focusstack(const Arg *arg) {
- }
- }
-
-+void
-+focuswin(const Arg* arg){
-+ int iwin = arg->i;
-+ Client* c = NULL;
-+ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
-+ if(ISVISIBLE(c)) --iwin;
-+ };
-+ if(c) {
-+ focus(c);
-+ restack(selmon);
-+ }
-+}
-+
- Atom
- getatomprop(Client *c, Atom prop) {
- int di;
-@@ -957,7 +1150,7 @@ grabkeys(void) {
-
- void
- incnmaster(const Arg *arg) {
-- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
-+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
- arrange(selmon);
- }
-
-@@ -1216,12 +1409,14 @@ propertynotify(XEvent *e) {
- case XA_WM_HINTS:
- updatewmhints(c);
- drawbars();
-+ drawtabs();
- break;
- }
- if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
- updatetitle(c);
- if(c == c->mon->sel)
- drawbar(c->mon);
-+ drawtab(c->mon);
- }
- if(ev->atom == netatom[NetWMWindowType])
- updatewindowtype(c);
-@@ -1329,6 +1524,7 @@ restack(Monitor *m) {
- XWindowChanges wc;
-
- drawbar(m);
-+ drawtab(m);
- if(!m->sel)
- return;
- if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
-@@ -1470,10 +1666,13 @@ setfullscreen(Client *c, Bool fullscreen) {
-
- void
- setlayout(const Arg *arg) {
-- if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
-- selmon->sellt ^= 1;
-+ if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
-+ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
-+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
-+ }
- if(arg && arg->v)
-- selmon->lt[selmon->sellt] = (Layout *)arg->v;
-+ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
-+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
- strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
- if(selmon->sel)
- arrange(selmon);
-@@ -1491,7 +1690,7 @@ setmfact(const Arg *arg) {
- f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
- if(f < 0.1 || f > 0.9)
- return;
-- selmon->mfact = f;
-+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
- arrange(selmon);
- }
-
-@@ -1512,6 +1711,8 @@ setup(void) {
- if (!drw->fontcount)
- die("No fonts could be loaded.\n");
- bh = drw->fonts[0]->h + 2;
-+ th = bh;
-+
- updategeom();
- /* init atoms */
- wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
-@@ -1636,13 +1837,23 @@ tile(Monitor *m) {
-
- void
- togglebar(const Arg *arg) {
-- selmon->showbar = !selmon->showbar;
-+ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
- updatebarpos(selmon);
- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
- arrange(selmon);
- }
-
- void
-+tabmode(const Arg *arg) {
-+ if(arg && arg->i >= 0)
-+ selmon->showtab = arg->ui % showtab_nmodes;
-+ else
-+ selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
-+ arrange(selmon);
-+}
-+
-+
-+void
- togglefloating(const Arg *arg) {
- if(!selmon->sel)
- return;
-@@ -1672,9 +1883,29 @@ toggletag(const Arg *arg) {
- void
- toggleview(const Arg *arg) {
- unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
-+ int i;
-
- if(newtagset) {
-+ if(newtagset == ~0) {
-+ selmon->pertag->prevtag = selmon->pertag->curtag;
-+ selmon->pertag->curtag = 0;
-+ }
-+ /* test if the user did not select the same tag */
-+ if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
-+ selmon->pertag->prevtag = selmon->pertag->curtag;
-+ for (i=0; !(newtagset & 1 << i); i++) ;
-+ selmon->pertag->curtag = i + 1;
-+ }
- selmon->tagset[selmon->seltags] = newtagset;
-+
-+ /* apply settings for this view */
-+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
-+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
-+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
-+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
-+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
-+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
-+ togglebar(NULL);
- focus(NULL);
- arrange(selmon);
- }
-@@ -1746,20 +1977,43 @@ updatebars(void) {
- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
- XMapRaised(dpy, m->barwin);
-+ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
-+ CopyFromParent, DefaultVisual(dpy, screen),
-+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
-+ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
-+ XMapRaised(dpy, m->tabwin);
- }
- }
-
- void
- updatebarpos(Monitor *m) {
-+ Client *c;
-+ int nvis = 0;
-+
- m->wy = m->my;
- m->wh = m->mh;
- if(m->showbar) {
- m->wh -= bh;
- m->by = m->topbar ? m->wy : m->wy + m->wh;
-- m->wy = m->topbar ? m->wy + bh : m->wy;
-- }
-- else
-+ if ( m->topbar )
-+ m->wy += bh;
-+ } else {
- m->by = -bh;
-+ }
-+
-+ for(c = m->clients; c; c = c->next){
-+ if(ISVISIBLE(c)) ++nvis;
-+ }
-+
-+ if(m->showtab == showtab_always
-+ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){
-+ m->wh -= th;
-+ m->ty = m->toptab ? m->wy : m->wy + m->wh;
-+ if ( m->toptab )
-+ m->wy += th;
-+ } else {
-+ m->ty = -th;
-+ }
- }
-
- void
-@@ -1969,11 +2223,33 @@ updatewmhints(Client *c) {
-
- void
- view(const Arg *arg) {
-+ int i;
-+ unsigned int tmptag;
-+
- if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
- return;
- selmon->seltags ^= 1; /* toggle sel tagset */
-- if(arg->ui & TAGMASK)
-+ if(arg->ui & TAGMASK) {
-+ selmon->pertag->prevtag = selmon->pertag->curtag;
- selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
-+ if(arg->ui == ~0)
-+ selmon->pertag->curtag = 0;
-+ else {
-+ for (i=0; !(arg->ui & 1 << i); i++) ;
-+ selmon->pertag->curtag = i + 1;
-+ }
-+ } else {
-+ tmptag = selmon->pertag->prevtag;
-+ selmon->pertag->prevtag = selmon->pertag->curtag;
-+ selmon->pertag->curtag = tmptag;
-+ }
-+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
-+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
-+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
-+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
-+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
-+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
-+ togglebar(NULL);
- focus(NULL);
- arrange(selmon);
- }
-@@ -1999,7 +2275,7 @@ wintomon(Window w) {
- if(w == root && getrootptr(&x, &y))
- return recttomon(x, y, 1, 1);
- for(m = mons; m; m = m->next)
-- if(w == m->barwin)
-+ if(w == m->barwin || w == m->tabwin)
- return m;
- if((c = wintoclient(w)))
- return c->mon;
diff --git a/dwm.suckless.org/patches/dwm-master_2015-03-05_14343e-tab-v2b.diff b/dwm.suckless.org/patches/dwm-master_2015-03-05_14343e-tab-v2b.diff
@@ -1,506 +0,0 @@
-diff --git a/config.def.h b/config.def.h
-index eaae8f3..9161486 100644
---- a/config.def.h
-+++ b/config.def.h
-@@ -17,6 +17,13 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
- static const unsigned int snap = 32; /* snap pixel */
- static const Bool showbar = True; /* False means no bar */
- static const Bool topbar = True; /* False means bottom bar */
-+/* Display modes of the tab bar: never shown, always shown, shown only in */
-+/* monocle mode in presence of several windows. */
-+/* Modes after showtab_nmodes are disabled */
-+enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
-+static const int showtab = showtab_auto; /* Default tab bar show mode */
-+static const Bool toptab = False; /* False means bottom tab bar */
-+
-
- /* tagging */
- static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
-@@ -34,7 +41,7 @@ static const Rule rules[] = {
- /* layout(s) */
- static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
- static const int nmaster = 1; /* number of clients in master area */
--static const Bool resizehints = True; /* True means respect size hints in tiled resizals */
-+static const Bool resizehints = False; /* True means respect size hints in tiled resizals */
-
- static const Layout layouts[] = {
- /* symbol arrange function */
-@@ -64,6 +71,7 @@ static Key keys[] = {
- { MODKEY, XK_p, spawn, {.v = dmenucmd } },
- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
- { MODKEY, XK_b, togglebar, {0} },
-+ { MODKEY, XK_w, tabmode, {-1} },
- { MODKEY, XK_j, focusstack, {.i = +1 } },
- { MODKEY, XK_k, focusstack, {.i = -1 } },
- { MODKEY, XK_i, incnmaster, {.i = +1 } },
-@@ -111,5 +119,6 @@ static Button buttons[] = {
- { ClkTagBar, 0, Button3, toggleview, {0} },
- { ClkTagBar, MODKEY, Button1, tag, {0} },
- { ClkTagBar, MODKEY, Button3, toggletag, {0} },
-+ { ClkTabBar, 0, Button1, focuswin, {0} },
- };
-
-diff --git a/dwm.1 b/dwm.1
-index 6687011..9f5c274 100644
---- a/dwm.1
-+++ b/dwm.1
-@@ -19,14 +19,22 @@ layout applied.
- Windows are grouped by tags. Each window can be tagged with one or multiple
- tags. Selecting certain tags displays all windows with these tags.
- .P
--Each screen contains a small status bar which displays all available tags, the
--layout, the title of the focused window, and the text read from the root window
--name property, if the screen is focused. A floating window is indicated with an
--empty square and a maximised floating window is indicated with a filled square
--before the windows title. The selected tags are indicated with a different
--color. The tags of the focused window are indicated with a filled square in the
--top left corner. The tags which are applied to one or more windows are
--indicated with an empty square in the top left corner.
-+Each screen contains two small status bars.
-+.P
-+One bar displays all available tags, the layout, the title of the focused
-+window, and the text read from the root window name property, if the screen is
-+focused. A floating window is indicated with an empty square and a maximised
-+floating window is indicated with a filled square before the windows title. The
-+selected tags are indicated with a different color. The tags of the focused
-+window are indicated with a filled square in the top left corner. The tags
-+which are applied to one or more windows are indicated with an empty square in
-+the top left corner.
-+.P
-+Another bar contains a tab for each window of the current view and allows
-+navigation between windows, especially in the monocle mode. The different
-+display modes of this bar are described under the Mod1\-w Keybord command
-+section. When a single tag is selected, that tag is indicated in the left corner
-+of the tab bar.
- .P
- dwm draws a small border around windows to indicate the focus state.
- .SH OPTIONS
-@@ -43,7 +51,8 @@ command.
- .TP
- .B Button1
- click on a tag label to display all windows with that tag, click on the layout
--label toggles between tiled and floating layout.
-+label toggles between tiled and floating layout, click on a window name in the
-+tab bar brings focus to that window.
- .TP
- .B Button3
- click on a tag label adds/removes all windows with that tag to/from the view.
-@@ -104,6 +113,12 @@ Increase master area size.
- .B Mod1\-h
- Decrease master area size.
- .TP
-+.B Mod1\-w
-+Cycle over the tab bar display modes: never displayed, always displayed,
-+displayed only in monocle mode when the view contains than one window (auto
-+mode). Some display modes can be disabled in the configuration, config.h. In
-+the default configuration only "never" and "auto" display modes are enabled.
-+.TP
- .B Mod1\-Return
- Zooms/cycles focused window to/from master area (tiled layouts only).
- .TP
-diff --git a/dwm.c b/dwm.c
-index 169adcb..4ec61fb 100644
---- a/dwm.c
-+++ b/dwm.c
-@@ -64,7 +64,7 @@ enum { NetSupported, NetWMName, NetWMState,
- NetWMFullscreen, NetActiveWindow, NetWMWindowType,
- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
- enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
--enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
-+enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
- ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
-
- typedef union {
-@@ -111,24 +111,32 @@ typedef struct {
- void (*arrange)(Monitor *);
- } Layout;
-
-+#define MAXTABS 50
-+
- struct Monitor {
- char ltsymbol[16];
- float mfact;
- int nmaster;
- int num;
- int by; /* bar geometry */
-+ int ty; /* tab bar geometry */
- int mx, my, mw, mh; /* screen size */
- int wx, wy, ww, wh; /* window area */
- unsigned int seltags;
- unsigned int sellt;
- unsigned int tagset[2];
- Bool showbar;
-+ Bool showtab;
- Bool topbar;
-+ Bool toptab;
- Client *clients;
- Client *sel;
- Client *stack;
- Monitor *next;
- Window barwin;
-+ Window tabwin;
-+ int ntabs;
-+ int tab_widths[MAXTABS];
- const Layout *lt[2];
- };
-
-@@ -164,12 +172,15 @@ static void detachstack(Client *c);
- static Monitor *dirtomon(int dir);
- static void drawbar(Monitor *m);
- static void drawbars(void);
-+static void drawtab(Monitor *m);
-+static void drawtabs(void);
- static void enternotify(XEvent *e);
- static void expose(XEvent *e);
- static void focus(Client *c);
- static void focusin(XEvent *e);
- static void focusmon(const Arg *arg);
- static void focusstack(const Arg *arg);
-+static void focuswin(const Arg* arg);
- static Bool getrootptr(int *x, int *y);
- static long getstate(Window w);
- static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
-@@ -206,6 +217,7 @@ static void setup(void);
- static void showhide(Client *c);
- static void sigchld(int unused);
- static void spawn(const Arg *arg);
-+static void tabmode(const Arg *arg);
- static void tag(const Arg *arg);
- static void tagmon(const Arg *arg);
- static void tile(Monitor *);
-@@ -240,6 +252,7 @@ static char stext[256];
- static int screen;
- static int sw, sh; /* X display screen geometry width, height */
- static int bh, blw = 0; /* bar geometry */
-+static int th = 0; /* tab bar geometry */
- static int (*xerrorxlib)(Display *, XErrorEvent *);
- static unsigned int numlockmask = 0;
- static void (*handler[LASTEvent]) (XEvent *) = {
-@@ -389,6 +402,9 @@ arrange(Monitor *m) {
-
- void
- arrangemon(Monitor *m) {
-+ updatebarpos(m);
-+ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
-+
- strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
- if(m->lt[m->sellt]->arrange)
- m->lt[m->sellt]->arrange(m);
-@@ -437,14 +453,32 @@ buttonpress(XEvent *e) {
- else
- click = ClkWinTitle;
- }
-+ if(ev->window == selmon->tabwin) {
-+ i = 0; x = 0;
-+ for(c = selmon->clients; c; c = c->next){
-+ if(!ISVISIBLE(c)) continue;
-+ x += selmon->tab_widths[i];
-+ if (ev->x > x)
-+ ++i;
-+ else
-+ break;
-+ if(i >= m->ntabs) break;
-+ }
-+ if(c) {
-+ click = ClkTabBar;
-+ arg.ui = i;
-+ }
-+ }
- else if((c = wintoclient(ev->window))) {
- focus(c);
- click = ClkClientWin;
- }
- for(i = 0; i < LENGTH(buttons); i++)
- if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
-- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
-- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
-+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
-+ buttons[i].func(((click == ClkTagBar || click == ClkTabBar)
-+ && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
-+ }
- }
-
- void
-@@ -498,6 +532,8 @@ cleanupmon(Monitor *mon) {
- }
- XUnmapWindow(dpy, mon->barwin);
- XDestroyWindow(dpy, mon->barwin);
-+ XUnmapWindow(dpy, mon->tabwin);
-+ XDestroyWindow(dpy, mon->tabwin);
- free(mon);
- }
-
-@@ -566,8 +602,11 @@ configurenotify(XEvent *e) {
- if(updategeom() || dirty) {
- drw_resize(drw, sw, bh);
- updatebars();
-- for(m = mons; m; m = m->next)
-+ //refreshing display of status bar. The tab bar is handled by the arrange()
-+ //method, which is called below
-+ for(m = mons; m; m = m->next){
- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
-+ }
- focus(NULL);
- arrange(NULL);
- }
-@@ -637,7 +676,10 @@ createmon(void) {
- m->mfact = mfact;
- m->nmaster = nmaster;
- m->showbar = showbar;
-+ m->showtab = showtab;
- m->topbar = topbar;
-+ m->toptab = toptab;
-+ m->ntabs = 0;
- m->lt[0] = &layouts[0];
- m->lt[1] = &layouts[1 % LENGTH(layouts)];
- strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
-@@ -749,6 +791,104 @@ drawbars(void) {
- }
-
- void
-+drawtabs(void) {
-+ Monitor *m;
-+
-+ for(m = mons; m; m = m->next)
-+ drawtab(m);
-+}
-+
-+static int
-+cmpint(const void *p1, const void *p2) {
-+ /* The actual arguments to this function are "pointers to
-+ pointers to char", but strcmp(3) arguments are "pointers
-+ to char", hence the following cast plus dereference */
-+ return *((int*) p1) > * (int*) p2;
-+}
-+
-+
-+void
-+drawtab(Monitor *m) {
-+ Client *c;
-+ int i;
-+ int itag = -1;
-+ char view_info[50];
-+ int view_info_w = 0;
-+ int sorted_label_widths[MAXTABS];
-+ int tot_width;
-+ int maxsize = bh;
-+ int x = 0;
-+ int w = 0;
-+
-+ //view_info: indicate the tag which is displayed in the view
-+ for(i = 0; i < LENGTH(tags); ++i){
-+ if((selmon->tagset[selmon->seltags] >> i) & 1) {
-+ if(itag >=0){ //more than one tag selected
-+ itag = -1;
-+ break;
-+ }
-+ itag = i;
-+ }
-+ }
-+ if(0 <= itag && itag < LENGTH(tags)){
-+ snprintf(view_info, sizeof view_info, "[%s]", tags[itag]);
-+ } else {
-+ strncpy(view_info, "[...]", sizeof view_info);
-+ }
-+ view_info[sizeof(view_info) - 1 ] = 0;
-+ view_info_w = TEXTW(view_info);
-+ tot_width = view_info_w;
-+
-+ /* Calculates number of labels and their width */
-+ m->ntabs = 0;
-+ for(c = m->clients; c; c = c->next){
-+ if(!ISVISIBLE(c)) continue;
-+ m->tab_widths[m->ntabs] = TEXTW(c->name);
-+ tot_width += m->tab_widths[m->ntabs];
-+ ++m->ntabs;
-+ if(m->ntabs >= MAXTABS) break;
-+ }
-+
-+ if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated
-+ memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs);
-+ qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint);
-+ tot_width = view_info_w;
-+ for(i = 0; i < m->ntabs; ++i){
-+ if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww)
-+ break;
-+ tot_width += sorted_label_widths[i];
-+ }
-+ maxsize = (m->ww - tot_width) / (m->ntabs - i);
-+ } else{
-+ maxsize = m->ww;
-+ }
-+ i = 0;
-+ for(c = m->clients; c; c = c->next){
-+ if(!ISVISIBLE(c)) continue;
-+ if(i >= m->ntabs) break;
-+ if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize;
-+ w = m->tab_widths[i];
-+ drw_setscheme(drw, (c == m->sel) ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
-+ drw_text(drw, x, 0, w, th, c->name, 0);
-+ x += w;
-+ ++i;
-+ }
-+
-+ drw_setscheme(drw, &scheme[SchemeNorm]);
-+
-+ /* cleans interspace between window names and current viewed tag label */
-+ w = m->ww - view_info_w - x;
-+ drw_text(drw, x, 0, w, th, NULL, 0);
-+
-+ /* view info */
-+ x += w;
-+ w = view_info_w;
-+ drw_text(drw, x, 0, w, th, view_info, 0);
-+
-+ drw_map(drw, m->tabwin, 0, 0, m->ww, th);
-+}
-+
-+void
- enternotify(XEvent *e) {
- Client *c;
- Monitor *m;
-@@ -772,8 +912,10 @@ expose(XEvent *e) {
- Monitor *m;
- XExposeEvent *ev = &e->xexpose;
-
-- if(ev->count == 0 && (m = wintomon(ev->window)))
-+ if(ev->count == 0 && (m = wintomon(ev->window))){
- drawbar(m);
-+ drawtab(m);
-+ }
- }
-
- void
-@@ -800,6 +942,7 @@ focus(Client *c) {
- }
- selmon->sel = c;
- drawbars();
-+ drawtabs();
- }
-
- void
-@@ -850,6 +993,19 @@ focusstack(const Arg *arg) {
- }
- }
-
-+void
-+focuswin(const Arg* arg){
-+ int iwin = arg->i;
-+ Client* c = NULL;
-+ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
-+ if(ISVISIBLE(c)) --iwin;
-+ };
-+ if(c) {
-+ focus(c);
-+ restack(selmon);
-+ }
-+}
-+
- Atom
- getatomprop(Client *c, Atom prop) {
- int di;
-@@ -1216,12 +1372,14 @@ propertynotify(XEvent *e) {
- case XA_WM_HINTS:
- updatewmhints(c);
- drawbars();
-+ drawtabs();
- break;
- }
- if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
- updatetitle(c);
- if(c == c->mon->sel)
- drawbar(c->mon);
-+ drawtab(c->mon);
- }
- if(ev->atom == netatom[NetWMWindowType])
- updatewindowtype(c);
-@@ -1329,6 +1487,7 @@ restack(Monitor *m) {
- XWindowChanges wc;
-
- drawbar(m);
-+ drawtab(m);
- if(!m->sel)
- return;
- if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
-@@ -1512,6 +1671,8 @@ setup(void) {
- if (!drw->fontcount)
- die("No fonts could be loaded.\n");
- bh = drw->fonts[0]->h + 2;
-+ th = bh;
-+
- updategeom();
- /* init atoms */
- wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
-@@ -1643,6 +1804,16 @@ togglebar(const Arg *arg) {
- }
-
- void
-+tabmode(const Arg *arg) {
-+ if(arg && arg->i >= 0)
-+ selmon->showtab = arg->ui % showtab_nmodes;
-+ else
-+ selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
-+ arrange(selmon);
-+}
-+
-+
-+void
- togglefloating(const Arg *arg) {
- if(!selmon->sel)
- return;
-@@ -1746,20 +1917,43 @@ updatebars(void) {
- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
- XMapRaised(dpy, m->barwin);
-+ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
-+ CopyFromParent, DefaultVisual(dpy, screen),
-+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
-+ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
-+ XMapRaised(dpy, m->tabwin);
- }
- }
-
- void
- updatebarpos(Monitor *m) {
-+ Client *c;
-+ int nvis = 0;
-+
- m->wy = m->my;
- m->wh = m->mh;
- if(m->showbar) {
- m->wh -= bh;
- m->by = m->topbar ? m->wy : m->wy + m->wh;
-- m->wy = m->topbar ? m->wy + bh : m->wy;
-- }
-- else
-+ if ( m->topbar )
-+ m->wy += bh;
-+ } else {
- m->by = -bh;
-+ }
-+
-+ for(c = m->clients; c; c = c->next){
-+ if(ISVISIBLE(c)) ++nvis;
-+ }
-+
-+ if(m->showtab == showtab_always
-+ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){
-+ m->wh -= th;
-+ m->ty = m->toptab ? m->wy : m->wy + m->wh;
-+ if ( m->toptab )
-+ m->wy += th;
-+ } else {
-+ m->ty = -th;
-+ }
- }
-
- void
-@@ -1999,7 +2193,7 @@ wintomon(Window w) {
- if(w == root && getrootptr(&x, &y))
- return recttomon(x, y, 1, 1);
- for(m = mons; m; m = m->next)
-- if(w == m->barwin)
-+ if(w == m->barwin || w == m->tabwin)
- return m;
- if((c = wintoclient(w)))
- return c->mon;
diff --git a/dwm.suckless.org/patches/pertag.md b/dwm.suckless.org/patches/pertag.md
@@ -11,12 +11,10 @@ Download
Patches against different versions of dwm are available at
[dwm-clean-patches](https://github.com/jceb/dwm-clean-patches).
- * [dwm-6.1-pertag.diff](dwm-6.1-pertag.diff) (6630b) (20140209)
+ * [dwm-6.1-pertag.diff](dwm-6.1-pertag.diff) (6630b) (20151031)
* [dwm-10e232f9ace7-pertag.diff](dwm-10e232f9ace7-pertag.diff) (5955b) (20120406)
- * [dwm-6.0-pertag_without_bar.diff](dwm-6.0-pertag_without_bar.diff) (5578b) (20140530)
* [dwm-6.0-pertag.diff](dwm-6.0-pertag.diff) (5955b) (20120406)
* [dwm-r1578-pertag.diff][9] (nmaster included in mainline)
- * [dwm-5.8.2-pertag\_without\_bar.diff][8]
* [dwm-5.8.2-pertag.diff][7]
* [dwm-5.7.2-pertag.diff][6]
* [dwm-pertag-5.6.1.diff][5]
@@ -24,6 +22,11 @@ Patches against different versions of dwm are available at
* [dwm-5.2-pertag.diff][3]
* [dwm-5.1-pertag.diff][2]
+ * Using pertag with the same barpos
+ * [dwm-6.1-pertag_without_bar.diff](dwm-6.1-pertag_without_bar.diff) (5578b) (20151031)
+ * [dwm-6.0-pertag_without_bar.diff](dwm-6.0-pertag_without_bar.diff) (5578b) (20140530)
+ * [dwm-5.8.2-pertag\_without\_bar.diff][8]
+
Authors
-------
* Jan Christoph Ebersbach - <jceb@e-jc.de>
@@ -32,6 +35,7 @@ Authors
* Updated by Sidney Amani - `<seed at uffs dot org>`
* Updated by William Light - `<wrl at illest dot net>`
* Updated by termac - `<terror.macbeth.I at gmail dot com>`
+ * Updated by Ivan Tham - `pickfire at riseup dot net`
[1]: historical/taglayouts
[2]: http://v4hn.de/patches/dwm-5.1-pertag.diff
diff --git a/dwm.suckless.org/patches/systray.md b/dwm.suckless.org/patches/systray.md
@@ -11,7 +11,7 @@ Download
Patches against different versions of dwm are available at
[dwm-clean-patches](https://github.com/jceb/dwm-clean-patches).
- * [dwm-14343e69cc59-systray.diff](dwm-14343e69cc59-systray.diff) () (20150808)
+ * [dwm-7e1182c-systray.diff](dwm-7e1182c-systray.diff) () (20151031)
* [dwm-6.1-systray.diff](dwm-6.1-systray.diff) (21630b) (20140209)
* [dwm-c794a9f5ae5e-systray.diff](dwm-c794a9f5ae5e-systray.diff) (19946b) (20130119)
* [dwm-6.0-systray.diff](dwm-6.0-systray.diff) (19788b) (20130119)
diff --git a/dwm.suckless.org/patches/tab.md b/dwm.suckless.org/patches/tab.md
@@ -125,12 +125,12 @@ Download
* Tab patch alone
* For dwm 6.0: [dwm-6.0-tab-v2b.diff](dwm-6.0-tab-v2b.diff)
- * For dwm from the git master branch: [dwm-master\_2015-03-05\_14343e-tab-v2b.diff](dwm-master\_2015-03-05\_14343e-tab-v2b.diff)
+ * For dwm from the git master branch: [dwm-7e1182c-tab.diff](dwm-7e1182c-tab.diff)
* Combined patch of tab and the [pertag](pertag) patch from Jan Christoph Ebersbach.
* Follow the [link](pertag) for the description of this patch and the credits. The possibility to define the default layout per view has been added.
* For dwm 6.0: [dwm-6.0-pertag-tab-v2b.diff](dwm-6.0-pertag-tab-v2b.diff)
- * For dwm from the git master branch: [dwm-master\_2015-03-05\_14343e-pertag-tab-v2b.diff](dwm-master\_2015-03-05\_14343e-pertag-tab-v2b.diff)
+ * For dwm from the git master branch: [dwm-7e1182c-pertag-tab.diff](dwm-7e1182c-pertag-tab.diff)
Old versions
diff --git a/dwm.suckless.org/tutorial.md b/dwm.suckless.org/tutorial.md
@@ -40,7 +40,7 @@ To **move to another terminal**, press `[Alt]+[j]` or `[Alt]+[k]`.
To **move a terminal to another _tag_**, hover to the terminal and press `[Shift]+[Alt]+[2]`.
-To **focus on another _tag_**, press `[Shift]+[2]`.
+To **focus on another _tag_**, press `[Alt]+[tag number]`.
To **move a terminal to master or stack**, press `[Alt]+[d]` or `[Alt]+[i]`.
diff --git a/suckless.org/conference/index.md b/suckless.org/conference/index.md
@@ -5,7 +5,7 @@ slcon2 will be held in Budapest on 2015-10-(30-31).
This time it'll be a conference for invited participants *only*.
-Preliminary schedule (as of 2015-09-01)
+Preliminary Schedule (as of 2015-10-30)
=======================================
Friday, 2015-10-30
@@ -14,17 +14,11 @@ Friday, 2015-10-30
Talks
-----
-(10:30-10:45) *Welcome*, Anselm R Garbe
+(11:00-11:15) *Welcome*, Anselm R Garbe
Anselm will open slcon2 and give an overview on the final conference schedule.
-(10:45-11.00) Coffee/tea break
-
-(11:00-12:00) *stali 2015*, Anselm R Garbe
-
-(12:00-13:00) Lunch break
-
-(13:00-14:00) *suckless core - A suckless userspace foundation*, Laslo Hunhold
+(11:15-12:00) *suckless core - A suckless userspace foundation*, Laslo Hunhold
This talk will focus on recent developments in the suckless core
programs, the design and motivation behind them and which issues were
@@ -32,32 +26,33 @@ Talks
suckless design principles and consistency.
Current issues and future plans will be discussed in the last part.
-(14:00-14:30) *new suckless tools / dwmutil/d\**, Anselm R Garbe
+(12.00-12:45) *stali 2015*, Anselm R Garbe
-(14:30-15:00) *An overview on what happened with suckless since 2013 (slcon1)*, Christoph Lohmann, presented by Anselm R Garbe
+(12:45-13:45) Lunch
- It has been a long time since 2013 and the last conference. This is a talk
- to show Christoph's perspective on suckless's historical documentation.
- What has changed? Who are the new persons involved? What has changed internally?
+(13:45-14:30) *new suckless tools/ideas*, Anselm R Garbe
-(15:00-15:30) Coffee/tea break
+(14:30-15:30) *Farbfeld - Rethinking image-formats*, Laslo Hunhold
-(15:30-16:30) *suckless.org e.V. foundation*, Anselm R Garbe
+ This talk will discuss the deficiencies of the RGBA color space and
+ present the Farbfeld format to store images in a device independent
+ way using the Lab color space.
+ Additionally, an alternative to incorporated, namely imposed, image
+ compression is discussed, evaluating the advantages over other image
+ formats using different kinds of images.
- Anselm will present the idea of suckless.org e.V. and ask attendees
- to join, followed by a foundation ceremony.
+(15:30-16:00) Coffee/Tea break
-Hacking
--------
+(16:00-16:30) *suckless.org e.V.*, Anselm R Garbe
-(16:30-18:00) Hack session on suckless/stali tools with the brain power of all attendees
+ Anselm will present the idea of suckless.org e.V. and ask attendees
+ to join.
-(18:00-19:00) Refresh break / check your rooms
+(16:30-17:30) *suckless.org foundation*, All
-Social Event
-------------
+(17:30-19:00) Refresh break / check your rooms
-(19:00-) Late night social event in Budapest (nsz/et.al. will propose what to do/where to go)
+(19:00-) Late night social event in Budapest
Saturday, 2015-10-31
@@ -66,16 +61,9 @@ Saturday, 2015-10-31
Talks
-----
-(10:30-10:45) *The latest developments*, Anselm R Garbe
-
- Anselm will also charge the conference fees (presumably we can provide invoices
- as foundation on this day already)
-
-(10:45-11:00) Coffee/tea break
+(10:30-10:45) *Opening of day 2*, Anselm R Garbe
-(11:00-11:30) *dvtm - dynamic virtual terminal manager*, Marc André Tanner
-
-(11:30-12:00) *Simple Jabber - Divide And Conquer XMPP*, Jan Klemkow
+(10:45-11:30) *Simple Jabber - Divide And Conquer XMPP*, Jan Klemkow
The Extensible Messaging and Presence Protocol (XMPP)1 is like the web. It is
far too complex to be implemented in one program with the Unix philosophy in mind.
@@ -84,27 +72,18 @@ Talks
Its extensibility is the main reason that an implementation in a single
program is nearly impossible. This talk describes an approach to master this problem.
-(12:00-13:00) Lunch break
-
-(13:00-14:00) *Farbfeld - Rethinking image-formats*, Laslo Hunhold
-
- This talk will discuss the deficiencies of the RGBA color space and
- present the Farbfeld format to store images in a device independent
- way using the Lab color space.
- Additionally, an alternative to incorporated, namely imposed, image
- compression is discussed, evaluating the advantages over other image
- formats using different kinds of images.
-
-(14:00-14:30) *Finite state document processing*, Manu Raster
+(11:30-12:00) *Finite state document processing*, Manu Raster
Mainstream XML processing techniques wastefully consume time
and memory for example in file format conversions popularly
known as 'save as …'. In this talk we present a less
wasteful method based on finite-state transducers.
-(14:30-15:00) Coffee/tea break
+(12:00-13:00) Lunch
-(15:00-16:00) *UTF-8 everywhere? Writing Unicode compliant software that sucks less*, Laslo Hunhold
+(13:00-13:15) Conference fee collection
+
+(13:15-14:15) *UTF-8 everywhere? Writing Unicode compliant software that sucks less*, Laslo Hunhold
This talk will discuss UTF-8 and its history, how and when you have to
deal with it and which challenges are faced along the way, evaluating
@@ -117,21 +96,13 @@ Talks
and a midway drawn between total localization and minimalism,
presenting possible solutions.
-Hacking
--------
-
-(16:00-17:45) Working on the projects with all brain power
-
-End
----
-(17:45-18:00) *Closing*, Anselm R Garbe
+(14:15-14:45) Coffee/Tea break
- Anselm will close the conference and all attendees will decide on the next conference location.
+(14:45-15:00) *Formal conference talk closing*, Anselm R Garbe
+(15:00-17:00) Hacking
-Social Event 2
---------------
-(19:00-) Those who can stay until 2015-11-01 will celebrate another social hacking event
+(17:00-) TBD
Previous conferences
diff --git a/suckless.org/rocks.md b/suckless.org/rocks.md
@@ -46,10 +46,6 @@ for judging a programs as usable are:
This covers most console-based programs and programs from [plan9port][].
-Accounting
-----------
-* [Ledger](http://ledger-cli.org/)
-
Audio Players
-------------
* [C* Music Player](http://cmus.sourceforge.net/)
diff --git a/suckless.org/sucks/web.md b/suckless.org/sucks/web.md
@@ -4,7 +4,7 @@ It has enabled the global information exchange, mass surveillance, studies in
social control, allowed revolutions, made a fortune for many billionaires and
in the meanwhile ruined our climate: the web.
-[This is a moterfucking website.](http://motherfuckingwebsite.com/)
+[This is a motherfucking website.](http://motherfuckingwebsite.com/)
For short: There is an industry which is specialized on extending the resource
usage to display just some characters on your display. Millions of jobs are
diff --git a/surf.suckless.org/patches/surf-0.6-omnibar.diff b/surf.suckless.org/patches/surf-0.6-omnibar.diff
@@ -1,8 +1,8 @@
diff --git a/config.def.h b/config.def.h
-index 1eb9566..e1f142d 100644
+index 5245129..d8964bc 100644
--- a/config.def.h
+++ b/config.def.h
-@@ -63,6 +63,18 @@ static Bool allowgeolocation = TRUE;
+@@ -65,6 +65,18 @@ static Bool allowgeolocation = TRUE;
} \
}
@@ -21,20 +21,20 @@ index 1eb9566..e1f142d 100644
/* styles */
/*
* The iteration will stop at the first match, beginning at the beginning of
-@@ -110,7 +122,7 @@ static Key keys[] = {
+@@ -112,7 +124,7 @@ static Key keys[] = {
{ MODKEY, GDK_o, source, { 0 } },
{ MODKEY|GDK_SHIFT_MASK,GDK_o, inspector, { 0 } },
- { MODKEY, GDK_g, spawn, SETPROP("_SURF_URI", "_SURF_GO") },
-+ { MODKEY, GDK_KEY_g, spawn, GOTO },
++ { MODKEY, GDK_g, spawn, GOTO },
{ MODKEY, GDK_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND") },
{ MODKEY, GDK_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND") },
diff --git a/surf.c b/surf.c
-index 02656ec..1ccd821 100644
+index 28bffef..40ae97d 100644
--- a/surf.c
+++ b/surf.c
-@@ -712,11 +712,11 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
+@@ -777,11 +777,11 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c)
WebKitNetworkRequest *request;
WebKitWebSettings *set = webkit_web_view_get_settings(c->view);
SoupMessage *msg;
@@ -42,17 +42,17 @@ index 02656ec..1ccd821 100644
+ char *uri = geturi(c);
+ Arg arg = (Arg)ONLOAD(uri);
- switch(webkit_web_view_get_load_status (c->view)) {
+ switch (webkit_web_view_get_load_status (c->view)) {
case WEBKIT_LOAD_COMMITTED:
- uri = geturi(c);
- if(strstr(uri, "https://") == uri) {
+ if (strstr(uri, "https://") == uri) {
frame = webkit_web_view_get_main_frame(c->view);
src = webkit_web_frame_get_data_source(frame);
-@@ -733,6 +733,7 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
+@@ -798,6 +798,7 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c)
}
break;
case WEBKIT_LOAD_FINISHED:
+ spawn(NULL, &arg);
c->progress = 100;
updatetitle(c);
- if(diskcache) {
+ if (diskcache) {
diff --git a/tools.suckless.org/dmenu/scripts/index.md b/tools.suckless.org/dmenu/scripts/index.md
@@ -6,6 +6,7 @@ dmenu's user, feel free to add your own scripts, or comment existents.
Download
--------
+* [clipmenu](https://github.com/cdown/clipmenu): Clipboard management using dmenu
* [passmenu](http://git.zx2c4.com/password-store/tree/contrib/dmenu)
: get password from pass.
* [passmenu2](passmenu2): "pass" browser, vertical display and recursive "pass" folder support
@@ -19,5 +20,5 @@ Download
[run-recent](run-recent), but it uses atime to find recently executed commands
rather than a cache. As such, it also takes into account programs executed from
the terminal.
-* [browse](https://github.com/clamiax/scripts/blob/master/browse):
+* [browse](https://github.com/clamiax/scripts/blob/master/src/browse):
little files navigator
diff --git a/tools.suckless.org/sent.md b/tools.suckless.org/sent.md
@@ -0,0 +1,59 @@
+sent
+====
+
+Simple plaintext presentation tool.
+
+sent does not need latex, libreoffice or any other fancy file format, it uses
+plaintext files and png images. Currently every line represents a slide in the
+presentation. This may limit the use, but for presentations using the [Takahashi
+method](https://en.wikipedia.org/wiki/Takahashi_method) this is very nice and
+allows you to write down the presentation for a quick lightning talk within a
+few minutes.
+
+The presentation is displayed in a simple X11 window colored black on white for
+maximum contrast even if the sun shines directly onto the projected image. The
+content of each slide is automatically scaled to fit the window so you don't
+have to worry about alignment. Instead you can really concentrate on the
+content.
+
+Demo
+----
+
+To get a little demo, just type
+
+ make && ./sent example
+
+You can navigate with the arrow keys and quit with `q`.
+
+Usage
+-----
+
+Edit config.h to fit your needs then build again.
+
+ sent FILE1 [FILE2 ...]
+
+If one FILE equals `-`, stdin will be read. Use png images by prepending a `@`
+before the filename. Lines starting with `#` will be ignored. A presentation
+file could look like this:
+
+ sent
+ why?
+ @nyan.png
+ easy to use
+ depends on Xlib, libpng
+ no bloat
+ how?
+ sent FILENAME
+ one slide per line
+ # This is a comment and will not be part of the presentation
+ # The next line starts with a whitespace, it will not produce an image slide
+ @FILE.png
+ thanks / questions?
+
+Development
+-----------
+
+You can [browse](http://git.suckless.org/sent) its source code repository
+or get a copy using the following command:
+
+ git clone http://git.suckless.org/sent