commit 21a3c8ad30ed1e8af33fe0015abadeceadfc7238
parent f2bfc78cd36c7206ed1bcbc87ec0e57016b421c9
Author: Philippe Gras <philippe.gras@free.fr>
Date:   Sat, 29 Jun 2013 20:18:32 +0200
DWM Tab Patch
This DWM tab patch adds a bar with window labels transforming the
monocle layout to a "tabbed" layout.
See: http://lists.suckless.org/dev/1306/16126.html
Diffstat:
3 files changed, 1373 insertions(+), 0 deletions(-)
diff --git a/dwm.suckless.org/patches/dwm-6.0-pertag-tab.diff b/dwm.suckless.org/patches/dwm-6.0-pertag-tab.diff
@@ -0,0 +1,733 @@
+diff -u dwm-6.0-orig/config.def.h dwm-6.0-pertag-tab/config.def.h
+--- dwm-6.0-orig/config.def.h	2011-12-19 16:02:46.000000000 +0100
++++ dwm-6.0-pertag-tab/config.def.h	2013-06-23 00:06:48.000000000 +0200
+@@ -12,6 +12,9 @@
+ 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 */
++static const Bool showtab           = True;     /* False means no tab bar */
++static const Bool toptab            = False;    /* False means bottom tab bar */
++
+ 
+ /* tagging */
+ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+@@ -25,7 +28,7 @@
+ /* 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 */
+@@ -54,6 +57,7 @@
+ 	{ MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
+ 	{ MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },
+ 	{ MODKEY,                       XK_b,      togglebar,      {0} },
++	{ MODKEY,                       XK_w,      toggletab,      {0} },
+ 	{ MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
+ 	{ MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
+ 	{ MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
+@@ -101,5 +105,6 @@
+ 	{ ClkTagBar,            0,              Button3,        toggleview,     {0} },
+ 	{ ClkTagBar,            MODKEY,         Button1,        tag,            {0} },
+ 	{ ClkTagBar,            MODKEY,         Button3,        toggletag,      {0} },
++	{ ClkTabBar,            0,              Button1,        focuswin,       {0} },
+ };
+ 
+diff -u dwm-6.0-orig/dwm.1 dwm-6.0-pertag-tab/dwm.1
+--- dwm-6.0-orig/dwm.1	2011-12-19 16:02:46.000000000 +0100
++++ dwm-6.0-pertag-tab/dwm.1	2013-06-23 00:21:16.000000000 +0200
+@@ -19,14 +19,16 @@
+ 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 bars. A small status 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 small tab bar lists the
++windows in the current view and allows navigation from window to window,
++especially in the monocle mode. 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
+ dwm draws a small border around windows to indicate the focus state.
+ .SH OPTIONS
+@@ -43,7 +45,8 @@
+ .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 +107,9 @@
+ .B Mod1\-h
+ Decrease master area size.
+ .TP
++.B Mod1\-w
++Toggle the tab bar.
++.TP
+ .B Mod1\-Return
+ Zooms/cycles focused window to/from master area (tiled layouts only).
+ .TP
+diff -u dwm-6.0-orig/dwm.c dwm-6.0-pertag-tab/dwm.c
+--- dwm-6.0-orig/dwm.c	2011-12-19 16:02:46.000000000 +0100
++++ dwm-6.0-pertag-tab/dwm.c	2013-06-23 16:57:10.000000000 +0200
+@@ -62,7 +62,7 @@
+        NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+        NetWMWindowTypeDialog, 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 {
+@@ -102,6 +102,7 @@
+ 	unsigned long norm[ColLast];
+ 	unsigned long sel[ColLast];
+ 	Drawable drawable;
++	Drawable tabdrawable;
+ 	GC gc;
+ 	struct {
+ 		int ascent;
+@@ -124,25 +125,36 @@
+ 	void (*arrange)(Monitor *);
+ } Layout;
+ 
++typedef struct Pertag Pertag;
++
++#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];
++	Pertag *pertag;
+ };
+ 
+ typedef struct {
+@@ -178,11 +190,15 @@
+ 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 drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]);
+-static void drawtext(const char *text, unsigned long col[ColLast], Bool invert);
++static void drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert);
++//static void drawtabtext(const char *text, unsigned long col[ColLast], Bool invert);
+ static void enternotify(XEvent *e);
+ static void expose(XEvent *e);
+ static void focus(Client *c);
++static void focuswin(const Arg* arg);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
+@@ -229,6 +245,7 @@
+ static int textnw(const char *text, unsigned int len);
+ static void tile(Monitor *);
+ static void togglebar(const Arg *arg);
++static void toggletab(const Arg *arg);
+ static void togglefloating(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
+@@ -258,6 +275,7 @@
+ 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 *) = {
+@@ -287,6 +305,16 @@
+ /* 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 */
++	Bool showtabs[LENGTH(tags) + 1]; /* display tab bar for the current tag */
++};
++
+ /* compile-time check if all tags fit into an unsigned int bit array. */
+ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+ 
+@@ -454,14 +482,32 @@
+ 		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
+@@ -491,6 +537,7 @@
+ 		XFreeFont(dpy, dc.font.xfont);
+ 	XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ 	XFreePixmap(dpy, dc.drawable);
++	XFreePixmap(dpy, dc.tabdrawable);
+ 	XFreeGC(dpy, dc.gc);
+ 	XFreeCursor(dpy, cursor[CurNormal]);
+ 	XFreeCursor(dpy, cursor[CurResize]);
+@@ -513,6 +560,8 @@
+ 	}
+ 	XUnmapWindow(dpy, mon->barwin);
+ 	XDestroyWindow(dpy, mon->barwin);
++	XUnmapWindow(dpy, mon->tabwin);
++	XDestroyWindow(dpy, mon->tabwin);
+ 	free(mon);
+ }
+ 
+@@ -581,9 +630,14 @@
+ 			if(dc.drawable != 0)
+ 				XFreePixmap(dpy, dc.drawable);
+ 			dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
++			if(dc.tabdrawable != 0)
++				XFreePixmap(dpy, dc.tabdrawable);
++			dc.tabdrawable = XCreatePixmap(dpy, root, sw, th, DefaultDepth(dpy, screen));
+ 			updatebars();
+-			for(m = mons; m; m = m->next)
++			for(m = mons; m; m = m->next){
+ 				XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
++ 				XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
++			}
+ 			focus(NULL);
+ 			arrange(NULL);
+ 		}
+@@ -646,6 +700,7 @@
+ Monitor *
+ createmon(void) {
+ 	Monitor *m;
++	int i;
+ 
+ 	if(!(m = (Monitor *)calloc(1, sizeof(Monitor))))
+ 		die("fatal: could not malloc() %u bytes\n", sizeof(Monitor));
+@@ -653,10 +708,34 @@
+ 	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);
++	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;
++
++		/* init showbar */
++		m->pertag->showbars[i] = m->showbar;
++
++		/* init showtab */
++		m->pertag->showtabs[i] = m->showtab;
++	}
+ 	return m;
+ }
+ 
+@@ -731,13 +810,13 @@
+ 	for(i = 0; i < LENGTH(tags); i++) {
+ 		dc.w = TEXTW(tags[i]);
+ 		col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm;
+-		drawtext(tags[i], col, urg & 1 << i);
++		drawtext(dc.drawable, tags[i], col, urg & 1 << i);
+ 		drawsquare(m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
+ 		           occ & 1 << i, urg & 1 << i, col);
+ 		dc.x += dc.w;
+ 	}
+ 	dc.w = blw = TEXTW(m->ltsymbol);
+-	drawtext(m->ltsymbol, dc.norm, False);
++	drawtext(dc.drawable, m->ltsymbol, dc.norm, False);
+ 	dc.x += dc.w;
+ 	x = dc.x;
+ 	if(m == selmon) { /* status is only drawn on selected monitor */
+@@ -747,19 +826,20 @@
+ 			dc.x = x;
+ 			dc.w = m->ww - x;
+ 		}
+-		drawtext(stext, dc.norm, False);
++		drawtext(dc.drawable, stext, dc.norm, False);
+ 	}
+ 	else
+ 		dc.x = m->ww;
+ 	if((dc.w = dc.x - x) > bh) {
+ 		dc.x = x;
+ 		if(m->sel) {
+-			col = m == selmon ? dc.sel : dc.norm;
+-			drawtext(m->sel->name, col, False);
++		  //			col = m == selmon ? dc.sel : dc.norm;
++		  //	drawtext(dc.drawable, m->sel->name, col, False);
++		  drawtext(dc.drawable, m->sel->name, dc.norm, False);
+ 			drawsquare(m->sel->isfixed, m->sel->isfloating, False, col);
+ 		}
+ 		else
+-			drawtext(NULL, dc.norm, False);
++		  drawtext(dc.drawable, NULL, dc.norm, False);
+ 	}
+ 	XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0, m->ww, bh, 0, 0);
+ 	XSync(dpy, False);
+@@ -774,6 +854,104 @@
+ }
+ 
+ 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) {
++	unsigned long *col;
++	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;
++	dc.x = 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;
++	  dc.w = m->tab_widths[i];
++	  col = (c == m->sel)  ? dc.sel : dc.norm;
++	  drawtext(dc.tabdrawable, c->name, col, 0);
++	  dc.x += dc.w;
++	  ++i;
++	}
++
++	/* cleans interspace between window names and current viewed tag label */
++	dc.w = m->ww - view_info_w - dc.x;
++	drawtext(dc.tabdrawable, NULL, dc.norm, 0);
++
++	/* view info */
++	dc.x += dc.w;
++	dc.w = view_info_w;
++	drawtext(dc.tabdrawable, view_info, dc.norm, 0);
++
++	XCopyArea(dpy, dc.tabdrawable, m->tabwin, dc.gc, 0, 0, m->ww, th, 0, 0);
++	XSync(dpy, False);
++}
++
++
++void
+ drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) {
+ 	int x;
+ 
+@@ -785,13 +963,14 @@
+ 		XDrawRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x, x);
+ }
+ 
++
+ void
+-drawtext(const char *text, unsigned long col[ColLast], Bool invert) {
++drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert) {
+ 	char buf[256];
+ 	int i, x, y, h, len, olen;
+ 
+ 	XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]);
+-	XFillRectangle(dpy, dc.drawable, dc.gc, dc.x, dc.y, dc.w, dc.h);
++	XFillRectangle(dpy, drawable, dc.gc, dc.x, dc.y, dc.w, dc.h);
+ 	if(!text)
+ 		return;
+ 	olen = strlen(text);
+@@ -807,11 +986,12 @@
+ 		for(i = len; i && i > len - 3; buf[--i] = '.');
+ 	XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]);
+ 	if(dc.font.set)
+-		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
++		XmbDrawString(dpy, drawable, dc.font.set, dc.gc, x, y, buf, len);
+ 	else
+-		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
++		XDrawString(dpy, drawable, dc.gc, x, y, buf, len);
+ }
+ 
++
+ void
+ enternotify(XEvent *e) {
+ 	Client *c;
+@@ -836,8 +1016,10 @@
+ 	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
+@@ -862,6 +1044,7 @@
+ 		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ 	selmon->sel = c;
+ 	drawbars();
++	drawtabs();
+ }
+ 
+ void
+@@ -911,6 +1094,19 @@
+ 	}
+ }
+ 
++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;
+@@ -1028,7 +1224,7 @@
+ 
+ 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);
+ }
+ 
+@@ -1311,12 +1507,14 @@
+ 		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);
+@@ -1418,6 +1616,7 @@
+ 	XWindowChanges wc;
+ 
+ 	drawbar(m);
++	drawtab(m);
+ 	if(!m->sel)
+ 		return;
+ 	if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
+@@ -1555,10 +1754,13 @@
+ 
+ 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);
+@@ -1576,7 +1778,7 @@
+ 	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);
+ }
+ 
+@@ -1594,6 +1796,7 @@
+ 	sw = DisplayWidth(dpy, screen);
+ 	sh = DisplayHeight(dpy, screen);
+ 	bh = dc.h = dc.font.height + 2;
++	th = bh;
+ 	updategeom();
+ 	/* init atoms */
+ 	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
+@@ -1619,6 +1822,7 @@
+ 	dc.sel[ColBG] = getcolor(selbgcolor);
+ 	dc.sel[ColFG] = getcolor(selfgcolor);
+ 	dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), bh, DefaultDepth(dpy, screen));
++	dc.tabdrawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), th, DefaultDepth(dpy, screen));
+ 	dc.gc = XCreateGC(dpy, root, 0, NULL);
+ 	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
+ 	if(!dc.font.set)
+@@ -1729,13 +1933,22 @@
+ 
+ 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
++toggletab(const Arg *arg) {
++	selmon->showtab = selmon->pertag->showtabs[selmon->pertag->curtag] = !selmon->showtab;
++	updatebarpos(selmon);
++	XMoveResizeWindow(dpy, selmon->tabwin, selmon->wx, selmon->ty, selmon->ww, th);
++	arrange(selmon);
++}
++
++
++void
+ togglefloating(const Arg *arg) {
+ 	if(!selmon->sel)
+ 		return;
+@@ -1763,9 +1976,31 @@
+ 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);
++		if (selmon->showtab != selmon->pertag->showtabs[selmon->pertag->curtag])
++			toggletab(NULL);
+ 		focus(NULL);
+ 		arrange(selmon);
+ 	}
+@@ -1832,6 +2067,11 @@
+ 		                          CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ 		XDefineCursor(dpy, m->barwin, cursor[CurNormal]);
+ 		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]);
++		XMapRaised(dpy, m->tabwin);
+ 	}
+ }
+ 
+@@ -1842,10 +2082,20 @@
+ 	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;
++		if ( m->topbar )
++			m->wy += bh;
++	} else {
++	        m->by = -bh;
++	}
++
++	if(m->showtab) {
++		m->wh -= th;
++		m->ty = m->toptab ? m->wy : m->wy + m->wh;
++		if ( m->toptab )
++		        m->wy += th;
++	} else {
++	        m->ty = -th;
+ 	}
+-	else
+-		m->by = -bh;
+ }
+ 
+ Bool
+@@ -2043,11 +2293,35 @@
+ 
+ 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);
++	if (selmon->showtab != selmon->pertag->showtabs[selmon->pertag->curtag])
++		toggletab(NULL);
+ 	focus(NULL);
+ 	arrange(selmon);
+ }
+@@ -2073,7 +2347,7 @@
+ 	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-6.0-tab.diff b/dwm.suckless.org/patches/dwm-6.0-tab.diff
@@ -0,0 +1,569 @@
+diff -up dwm-6.0-orig/config.def.h dwm-6.0-tab/config.def.h
+--- dwm-6.0-orig/config.def.h	2011-12-19 16:02:46.000000000 +0100
++++ dwm-6.0-tab/config.def.h	2013-06-23 00:22:50.000000000 +0200
+@@ -12,6 +12,9 @@ static const unsigned int borderpx  = 1;
+ 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 */
++static const Bool showtab           = True;     /* False means no tab bar */
++static const Bool toptab            = False;    /* False means bottom tab bar */
++
+ 
+ /* tagging */
+ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+@@ -25,7 +28,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 */
+@@ -54,6 +57,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,      toggletab,      {0} },
+ 	{ MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
+ 	{ MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
+ 	{ MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
+@@ -101,5 +105,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 -up dwm-6.0-orig/dwm.1 dwm-6.0-tab/dwm.1
+--- dwm-6.0-orig/dwm.1	2011-12-19 16:02:46.000000000 +0100
++++ dwm-6.0-tab/dwm.1	2013-06-23 01:25:12.000000000 +0200
+@@ -19,14 +19,17 @@ 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. 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. Another bar
++contains a tab for each window of the current view and allows navigation from
++window to window, especially in the monocle mode. When a single tag is selected,
++that tag is recalled 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 +46,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 +108,9 @@ Increase master area size.
+ .B Mod1\-h
+ Decrease master area size.
+ .TP
++.B Mod1\-w
++Toggle the tab bar.
++.TP
+ .B Mod1\-Return
+ Zooms/cycles focused window to/from master area (tiled layouts only).
+ .TP
+diff -up dwm-6.0-orig/dwm.c dwm-6.0-tab/dwm.c
+--- dwm-6.0-orig/dwm.c	2011-12-19 16:02:46.000000000 +0100
++++ dwm-6.0-tab/dwm.c	2013-06-23 16:53:31.000000000 +0200
+@@ -62,7 +62,7 @@ enum { NetSupported, NetWMName, NetWMSta
+        NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+        NetWMWindowTypeDialog, 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 {
+@@ -102,6 +102,7 @@ typedef struct {
+ 	unsigned long norm[ColLast];
+ 	unsigned long sel[ColLast];
+ 	Drawable drawable;
++	Drawable tabdrawable;
+ 	GC gc;
+ 	struct {
+ 		int ascent;
+@@ -124,24 +125,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];
+ };
+ 
+@@ -178,11 +187,15 @@ static void die(const char *errstr, ...)
+ 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 drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]);
+-static void drawtext(const char *text, unsigned long col[ColLast], Bool invert);
++static void drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert);
++//static void drawtabtext(const char *text, unsigned long col[ColLast], Bool invert);
+ static void enternotify(XEvent *e);
+ static void expose(XEvent *e);
+ static void focus(Client *c);
++static void focuswin(const Arg* arg);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
+@@ -229,6 +242,7 @@ static void tagmon(const Arg *arg);
+ static int textnw(const char *text, unsigned int len);
+ static void tile(Monitor *);
+ static void togglebar(const Arg *arg);
++static void toggletab(const Arg *arg);
+ static void togglefloating(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
+@@ -258,6 +272,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 *) = {
+@@ -454,14 +469,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
+@@ -491,6 +524,7 @@ cleanup(void) {
+ 		XFreeFont(dpy, dc.font.xfont);
+ 	XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ 	XFreePixmap(dpy, dc.drawable);
++	XFreePixmap(dpy, dc.tabdrawable);
+ 	XFreeGC(dpy, dc.gc);
+ 	XFreeCursor(dpy, cursor[CurNormal]);
+ 	XFreeCursor(dpy, cursor[CurResize]);
+@@ -513,6 +547,8 @@ cleanupmon(Monitor *mon) {
+ 	}
+ 	XUnmapWindow(dpy, mon->barwin);
+ 	XDestroyWindow(dpy, mon->barwin);
++	XUnmapWindow(dpy, mon->tabwin);
++	XDestroyWindow(dpy, mon->tabwin);
+ 	free(mon);
+ }
+ 
+@@ -581,9 +617,14 @@ configurenotify(XEvent *e) {
+ 			if(dc.drawable != 0)
+ 				XFreePixmap(dpy, dc.drawable);
+ 			dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
++			if(dc.tabdrawable != 0)
++				XFreePixmap(dpy, dc.tabdrawable);
++			dc.tabdrawable = XCreatePixmap(dpy, root, sw, th, DefaultDepth(dpy, screen));
+ 			updatebars();
+-			for(m = mons; m; m = m->next)
++			for(m = mons; m; m = m->next){
+ 				XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
++ 				XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
++			}
+ 			focus(NULL);
+ 			arrange(NULL);
+ 		}
+@@ -653,7 +694,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);
+@@ -731,13 +775,13 @@ drawbar(Monitor *m) {
+ 	for(i = 0; i < LENGTH(tags); i++) {
+ 		dc.w = TEXTW(tags[i]);
+ 		col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm;
+-		drawtext(tags[i], col, urg & 1 << i);
++		drawtext(dc.drawable, tags[i], col, urg & 1 << i);
+ 		drawsquare(m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
+ 		           occ & 1 << i, urg & 1 << i, col);
+ 		dc.x += dc.w;
+ 	}
+ 	dc.w = blw = TEXTW(m->ltsymbol);
+-	drawtext(m->ltsymbol, dc.norm, False);
++	drawtext(dc.drawable, m->ltsymbol, dc.norm, False);
+ 	dc.x += dc.w;
+ 	x = dc.x;
+ 	if(m == selmon) { /* status is only drawn on selected monitor */
+@@ -747,19 +791,20 @@ drawbar(Monitor *m) {
+ 			dc.x = x;
+ 			dc.w = m->ww - x;
+ 		}
+-		drawtext(stext, dc.norm, False);
++		drawtext(dc.drawable, stext, dc.norm, False);
+ 	}
+ 	else
+ 		dc.x = m->ww;
+ 	if((dc.w = dc.x - x) > bh) {
+ 		dc.x = x;
+ 		if(m->sel) {
+-			col = m == selmon ? dc.sel : dc.norm;
+-			drawtext(m->sel->name, col, False);
++		  //			col = m == selmon ? dc.sel : dc.norm;
++		  //	drawtext(dc.drawable, m->sel->name, col, False);
++		  drawtext(dc.drawable, m->sel->name, dc.norm, False);
+ 			drawsquare(m->sel->isfixed, m->sel->isfloating, False, col);
+ 		}
+ 		else
+-			drawtext(NULL, dc.norm, False);
++		  drawtext(dc.drawable, NULL, dc.norm, False);
+ 	}
+ 	XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0, m->ww, bh, 0, 0);
+ 	XSync(dpy, False);
+@@ -774,6 +819,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) {
++	unsigned long *col;
++	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;
++	dc.x = 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;
++	  dc.w = m->tab_widths[i];
++	  col = (c == m->sel)  ? dc.sel : dc.norm;
++	  drawtext(dc.tabdrawable, c->name, col, 0);
++	  dc.x += dc.w;
++	  ++i;
++	}
++
++	/* cleans interspace between window names and current viewed tag label */
++	dc.w = m->ww - view_info_w - dc.x;
++	drawtext(dc.tabdrawable, NULL, dc.norm, 0);
++
++	/* view info */
++	dc.x += dc.w;
++	dc.w = view_info_w;
++	drawtext(dc.tabdrawable, view_info, dc.norm, 0);
++
++	XCopyArea(dpy, dc.tabdrawable, m->tabwin, dc.gc, 0, 0, m->ww, th, 0, 0);
++	XSync(dpy, False);
++}
++
++
++void
+ drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) {
+ 	int x;
+ 
+@@ -785,13 +928,14 @@ drawsquare(Bool filled, Bool empty, Bool
+ 		XDrawRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x, x);
+ }
+ 
++
+ void
+-drawtext(const char *text, unsigned long col[ColLast], Bool invert) {
++drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert) {
+ 	char buf[256];
+ 	int i, x, y, h, len, olen;
+ 
+ 	XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]);
+-	XFillRectangle(dpy, dc.drawable, dc.gc, dc.x, dc.y, dc.w, dc.h);
++	XFillRectangle(dpy, drawable, dc.gc, dc.x, dc.y, dc.w, dc.h);
+ 	if(!text)
+ 		return;
+ 	olen = strlen(text);
+@@ -807,11 +951,12 @@ drawtext(const char *text, unsigned long
+ 		for(i = len; i && i > len - 3; buf[--i] = '.');
+ 	XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]);
+ 	if(dc.font.set)
+-		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
++		XmbDrawString(dpy, drawable, dc.font.set, dc.gc, x, y, buf, len);
+ 	else
+-		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
++		XDrawString(dpy, drawable, dc.gc, x, y, buf, len);
+ }
+ 
++
+ void
+ enternotify(XEvent *e) {
+ 	Client *c;
+@@ -836,8 +981,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
+@@ -862,6 +1009,7 @@ focus(Client *c) {
+ 		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ 	selmon->sel = c;
+ 	drawbars();
++	drawtabs();
+ }
+ 
+ void
+@@ -911,6 +1059,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;
+@@ -1311,12 +1472,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);
+@@ -1418,6 +1581,7 @@ restack(Monitor *m) {
+ 	XWindowChanges wc;
+ 
+ 	drawbar(m);
++	drawtab(m);
+ 	if(!m->sel)
+ 		return;
+ 	if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
+@@ -1594,6 +1758,7 @@ setup(void) {
+ 	sw = DisplayWidth(dpy, screen);
+ 	sh = DisplayHeight(dpy, screen);
+ 	bh = dc.h = dc.font.height + 2;
++	th = bh;
+ 	updategeom();
+ 	/* init atoms */
+ 	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
+@@ -1619,6 +1784,7 @@ setup(void) {
+ 	dc.sel[ColBG] = getcolor(selbgcolor);
+ 	dc.sel[ColFG] = getcolor(selfgcolor);
+ 	dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), bh, DefaultDepth(dpy, screen));
++	dc.tabdrawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), th, DefaultDepth(dpy, screen));
+ 	dc.gc = XCreateGC(dpy, root, 0, NULL);
+ 	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
+ 	if(!dc.font.set)
+@@ -1736,6 +1902,15 @@ togglebar(const Arg *arg) {
+ }
+ 
+ void
++toggletab(const Arg *arg) {
++	selmon->showtab = !selmon->showtab;
++	updatebarpos(selmon);
++	XMoveResizeWindow(dpy, selmon->tabwin, selmon->wx, selmon->ty, selmon->ww, th);
++	arrange(selmon);
++}
++
++
++void
+ togglefloating(const Arg *arg) {
+ 	if(!selmon->sel)
+ 		return;
+@@ -1832,6 +2007,11 @@ updatebars(void) {
+ 		                          CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ 		XDefineCursor(dpy, m->barwin, cursor[CurNormal]);
+ 		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]);
++		XMapRaised(dpy, m->tabwin);
+ 	}
+ }
+ 
+@@ -1842,10 +2022,20 @@ updatebarpos(Monitor *m) {
+ 	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;
++		if ( m->topbar )
++			m->wy += bh;
++	} else {
++	        m->by = -bh;
++	}
++
++	if(m->showtab) {
++		m->wh -= th;
++		m->ty = m->toptab ? m->wy : m->wy + m->wh;
++		if ( m->toptab )
++		        m->wy += th;
++	} else {
++	        m->ty = -th;
+ 	}
+-	else
+-		m->by = -bh;
+ }
+ 
+ Bool
+@@ -2073,7 +2263,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/tab.md b/dwm.suckless.org/patches/tab.md
@@ -0,0 +1,71 @@
+Tab
+===
+
+Description
+-----------
+
+Add a bar at top or bottom of the screen with a label for each <i>displayed</i>
+window. This feature was primarily designed for the monocle window layout. In
+this mode the labels appear like tabs, transforming the layout into a
+<i>tabbed</i> layout. Navigating from window to window can be done by clicking
+on the window tabs or using the usual Mod1-j, Mod1-k keys. The bar can be
+enabled and disabled at runtime. This patch can be used as an alternative to the
+[tabbed](http://tools.suckless.org/tabbed/) tool. It differs in two ways: the
+''tab'' feature is limited to the monocle mode; it works with any application
+without requiring to support the XEmbed protocol nor to define in advance the
+set of applications to be supported.
+
+Usage
+-----
+
+With the default configuration, use the key combination Mod1-w to toggle the tab
+bar display. Switch focus to a window with a mouse left-click on its tab or by
+using the usual Mod1-j, Mod1-k commands. Usage is also documented in the dwm man
+page once the patch is applied.
+
+In the right corner of the tab bar the window ''tag'' currenlty selected for
+display is recalled. This feature is interesting when the standard status bar is
+disabled. If multiple tags are selected for viewing then three dots are
+displayed without more details.
+
+Configuration and installation
+------------------------------
+
+Apply to the dwm code the patch you can find in the download section below. If
+you don't already have a dwm config.h file, the make command will produce one
+that contains already the necessary configuration lines. If you already have
+one, then you must add to it the following lines:
+
+    static const Bool showtab           = True;     /* False means no tab bar */
+    static const Bool toptab            = False;    /* False means bottom tab bar */
+
+The first variable tells if the tab bar must be displayed by default; the second
+one if it should be displayed on top of the screen or at bottom.
+
+If the tab bar is displayed at bottom, then a better experience will be obtained
+with the variable resizehints of the config.h file set to False. This setting
+prevents possible gap between the windows and the tab bar.  You can find more
+details about this variable and gaps between windows in the dwm FAQ.
+
+In order to allow enabling and disabling the tab bar by pressing Mod1-w, add to
+the `keys` array, the following element:
+
+    { MODKEY,                       XK_w,      toggletab,      {0} }
+
+Selection of a window by a mouse left-click on its tab is enabled by adding the
+following element to the `buttons` array:
+
+    { ClkTabBar,            0,              Button1,        focuswin,       {0} },
+
+An example on how to insert these line can be found in the default config file template, config.def.h.
+
+Download
+--------
+
+ * [dwm-6.0-tab.diff](dwm-6.0-tab.diff)
+ * [dwm-6.0-pertag-tab.diff](dwm-6.0-pertag-tab.diff) combined patch with the [pertag](pertag) patch from Jan Christoph Ebersbach. Follow the [link](pertag) for the description of this patch and the credits.
+
+Authors
+-------
+ * Philippe Gras - `<philippe dot gras at free dot fr>`
+