sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

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:
Ddwm.suckless.org/patches/dwm-14343e69cc59-systray.diff | 687-------------------------------------------------------------------------------
Mdwm.suckless.org/patches/dwm-6.1-pertag.diff | 45+++++++++++++++++++--------------------------
Adwm.suckless.org/patches/dwm-6.1-pertag_without_bar.diff | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adwm.suckless.org/patches/dwm-7e1182c-pertag-tab.diff | 692+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adwm.suckless.org/patches/dwm-7e1182c-systray.diff | 687+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adwm.suckless.org/patches/dwm-7e1182c-tab.diff | 506+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddwm.suckless.org/patches/dwm-master_2015-03-05_14343e-pertag-tab-v2b.diff | 693-------------------------------------------------------------------------------
Ddwm.suckless.org/patches/dwm-master_2015-03-05_14343e-tab-v2b.diff | 506-------------------------------------------------------------------------------
Mdwm.suckless.org/patches/pertag.md | 10+++++++---
Mdwm.suckless.org/patches/systray.md | 2+-
Mdwm.suckless.org/patches/tab.md | 4++--
Mdwm.suckless.org/tutorial.md | 2+-
Msuckless.org/conference/index.md | 91+++++++++++++++++++++++++++----------------------------------------------------
Msuckless.org/rocks.md | 4----
Msuckless.org/sucks/web.md | 2+-
Msurf.suckless.org/patches/surf-0.6-omnibar.diff | 20++++++++++----------
Mtools.suckless.org/dmenu/scripts/index.md | 3++-
Atools.suckless.org/sent.md | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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