sites

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

dwm-6.0-pertag-tab-v2b.diff (31678B)


      1 diff --git a/config.def.h b/config.def.h
      2 index 77ff358..9ca435c 100644
      3 --- a/config.def.h
      4 +++ b/config.def.h
      5 @@ -12,10 +12,22 @@ static const unsigned int borderpx  = 1;        /* border pixel of windows */
      6  static const unsigned int snap      = 32;       /* snap pixel */
      7  static const Bool showbar           = True;     /* False means no bar */
      8  static const Bool topbar            = True;     /* False means bottom bar */
      9 +/*   Display modes of the tab bar: never shown, always shown, shown only in */
     10 +/*   monocle mode in presence of several windows.                           */
     11 +/*   Modes after showtab_nmodes are disabled                                */
     12 +enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
     13 +static const int showtab            = showtab_auto; /* Default tab bar show mode */
     14 +static const Bool toptab            = False;    /* False means bottom tab bar */
     15 +
     16  
     17  /* tagging */
     18  static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
     19  
     20 +/* default layout per tags */
     21 +/* The first element is for all-tag view, following i-th element corresponds to */
     22 +/* tags[i]. Layout is referred using the layouts array index.*/
     23 +static int def_layouts[1 + LENGTH(tags)]  = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     24 +
     25  static const Rule rules[] = {
     26  	/* class      instance    title       tags mask     isfloating   monitor */
     27  	{ "Gimp",     NULL,       NULL,       0,            True,        -1 },
     28 @@ -25,7 +37,7 @@ static const Rule rules[] = {
     29  /* layout(s) */
     30  static const float mfact      = 0.55; /* factor of master area size [0.05..0.95] */
     31  static const int nmaster      = 1;    /* number of clients in master area */
     32 -static const Bool resizehints = True; /* True means respect size hints in tiled resizals */
     33 +static const Bool resizehints = False; /* True means respect size hints in tiled resizals */
     34  
     35  static const Layout layouts[] = {
     36  	/* symbol     arrange function */
     37 @@ -54,6 +66,7 @@ static Key keys[] = {
     38  	{ MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
     39  	{ MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },
     40  	{ MODKEY,                       XK_b,      togglebar,      {0} },
     41 +	{ MODKEY,                       XK_w,      tabmode,        {-1} },
     42  	{ MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
     43  	{ MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
     44  	{ MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
     45 @@ -101,5 +114,6 @@ static Button buttons[] = {
     46  	{ ClkTagBar,            0,              Button3,        toggleview,     {0} },
     47  	{ ClkTagBar,            MODKEY,         Button1,        tag,            {0} },
     48  	{ ClkTagBar,            MODKEY,         Button3,        toggletag,      {0} },
     49 +	{ ClkTabBar,            0,              Button1,        focuswin,       {0} },
     50  };
     51  
     52 diff --git a/dwm.1 b/dwm.1
     53 index 5268a06..19b4f1d 100644
     54 --- a/dwm.1
     55 +++ b/dwm.1
     56 @@ -19,14 +19,22 @@ layout applied.
     57  Windows are grouped by tags. Each window can be tagged with one or multiple
     58  tags. Selecting certain tags displays all windows with these tags.
     59  .P
     60 -Each screen contains a small status bar which displays all available tags, the
     61 -layout, the title of the focused window, and the text read from the root window
     62 -name property, if the screen is focused. A floating window is indicated with an
     63 -empty square and a maximised floating window is indicated with a filled square
     64 -before the windows title.  The selected tags are indicated with a different
     65 -color. The tags of the focused window are indicated with a filled square in the
     66 -top left corner.  The tags which are applied to one or more windows are
     67 -indicated with an empty square in the top left corner.
     68 +Each screen contains two small status bars. 
     69 +.P
     70 +One bar displays all available tags, the layout, the title of the focused
     71 +window, and the text read from the root window name property, if the screen is
     72 +focused. A floating window is indicated with an empty square and a maximised
     73 +floating window is indicated with a filled square before the windows title.  The
     74 +selected tags are indicated with a different color. The tags of the focused
     75 +window are indicated with a filled square in the top left corner.  The tags
     76 +which are applied to one or more windows are indicated with an empty square in
     77 +the top left corner. 
     78 +.P
     79 +Another bar contains a tab for each window of the current view and allows
     80 +navigation between windows, especially in the monocle mode. The different
     81 +display modes of this bar are described under the Mod1\-w Keybord command
     82 +section.  When a single tag is selected, that tag is indicated in the left corner
     83 +of the tab bar.
     84  .P
     85  dwm draws a small border around windows to indicate the focus state.
     86  .SH OPTIONS
     87 @@ -43,7 +51,8 @@ command.
     88  .TP
     89  .B Button1
     90  click on a tag label to display all windows with that tag, click on the layout
     91 -label toggles between tiled and floating layout.
     92 +label toggles between tiled and floating layout, click on a window name in the
     93 +tab bar brings focus to that window.
     94  .TP
     95  .B Button3
     96  click on a tag label adds/removes all windows with that tag to/from the view.
     97 @@ -104,6 +113,12 @@ Increase master area size.
     98  .B Mod1\-h
     99  Decrease master area size.
    100  .TP
    101 +.B Mod1\-w
    102 +Cycle over the tab bar display modes: never displayed, always displayed,
    103 +displayed only in monocle mode when the view contains than one window (auto
    104 +mode). Some display modes can be disabled in the configuration, config.h. In
    105 +the default configuration only "never" and "auto" display modes are enabled.
    106 +.TP
    107  .B Mod1\-Return
    108  Zooms/cycles focused window to/from master area (tiled layouts only).
    109  .TP
    110 diff --git a/dwm.c b/dwm.c
    111 index 1d78655..fbc4137 100644
    112 --- a/dwm.c
    113 +++ b/dwm.c
    114 @@ -44,7 +44,7 @@
    115  #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
    116  #define CLEANMASK(mask)         (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
    117  #define INTERSECT(x,y,w,h,m)    (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
    118 -                               * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
    119 +			       * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
    120  #define ISVISIBLE(C)            ((C->tags & C->mon->tagset[C->mon->seltags]))
    121  #define LENGTH(X)               (sizeof X / sizeof X[0])
    122  #define MAX(A, B)               ((A) > (B) ? (A) : (B))
    123 @@ -62,7 +62,7 @@ enum { NetSupported, NetWMName, NetWMState,
    124         NetWMFullscreen, NetActiveWindow, NetWMWindowType,
    125         NetWMWindowTypeDialog, NetLast };     /* EWMH atoms */
    126  enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
    127 -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
    128 +enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
    129         ClkClientWin, ClkRootWin, ClkLast };             /* clicks */
    130  
    131  typedef union {
    132 @@ -102,6 +102,7 @@ typedef struct {
    133  	unsigned long norm[ColLast];
    134  	unsigned long sel[ColLast];
    135  	Drawable drawable;
    136 +	Drawable tabdrawable;
    137  	GC gc;
    138  	struct {
    139  		int ascent;
    140 @@ -124,25 +125,36 @@ typedef struct {
    141  	void (*arrange)(Monitor *);
    142  } Layout;
    143  
    144 +typedef struct Pertag Pertag;
    145 +
    146 +#define MAXTABS 50
    147 +
    148  struct Monitor {
    149  	char ltsymbol[16];
    150  	float mfact;
    151  	int nmaster;
    152  	int num;
    153  	int by;               /* bar geometry */
    154 +	int ty;               /* tab bar geometry */
    155  	int mx, my, mw, mh;   /* screen size */
    156  	int wx, wy, ww, wh;   /* window area  */
    157  	unsigned int seltags;
    158  	unsigned int sellt;
    159  	unsigned int tagset[2];
    160  	Bool showbar;
    161 +	Bool showtab;
    162  	Bool topbar;
    163 +	Bool toptab;
    164  	Client *clients;
    165  	Client *sel;
    166  	Client *stack;
    167  	Monitor *next;
    168  	Window barwin;
    169 +	Window tabwin;
    170 +	int ntabs;
    171 +	int tab_widths[MAXTABS];
    172  	const Layout *lt[2];
    173 +	Pertag *pertag;
    174  };
    175  
    176  typedef struct {
    177 @@ -178,11 +190,15 @@ static void die(const char *errstr, ...);
    178  static Monitor *dirtomon(int dir);
    179  static void drawbar(Monitor *m);
    180  static void drawbars(void);
    181 +static void drawtab(Monitor *m);
    182 +static void drawtabs(void);
    183  static void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]);
    184 -static void drawtext(const char *text, unsigned long col[ColLast], Bool invert);
    185 +static void drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert);
    186 +//static void drawtabtext(const char *text, unsigned long col[ColLast], Bool invert);
    187  static void enternotify(XEvent *e);
    188  static void expose(XEvent *e);
    189  static void focus(Client *c);
    190 +static void focuswin(const Arg* arg);
    191  static void focusin(XEvent *e);
    192  static void focusmon(const Arg *arg);
    193  static void focusstack(const Arg *arg);
    194 @@ -229,6 +245,7 @@ static void tagmon(const Arg *arg);
    195  static int textnw(const char *text, unsigned int len);
    196  static void tile(Monitor *);
    197  static void togglebar(const Arg *arg);
    198 +static void tabmode(const Arg *arg);
    199  static void togglefloating(const Arg *arg);
    200  static void toggletag(const Arg *arg);
    201  static void toggleview(const Arg *arg);
    202 @@ -258,6 +275,7 @@ static char stext[256];
    203  static int screen;
    204  static int sw, sh;           /* X display screen geometry width, height */
    205  static int bh, blw = 0;      /* bar geometry */
    206 +static int th = 0;           /* tab bar geometry */
    207  static int (*xerrorxlib)(Display *, XErrorEvent *);
    208  static unsigned int numlockmask = 0;
    209  static void (*handler[LASTEvent]) (XEvent *) = {
    210 @@ -287,6 +305,16 @@ static Window root;
    211  /* configuration, allows nested code to access above variables */
    212  #include "config.h"
    213  
    214 +struct Pertag {
    215 +	unsigned int curtag, prevtag; /* current and previous tag */
    216 +	int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
    217 +	float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
    218 +	unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
    219 +	const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes  */
    220 +	Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
    221 +	Bool showtabs[LENGTH(tags) + 1]; /* display tab bar for the current tag */
    222 +};
    223 +
    224  /* compile-time check if all tags fit into an unsigned int bit array. */
    225  struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
    226  
    227 @@ -405,6 +433,10 @@ arrange(Monitor *m) {
    228  
    229  void
    230  arrangemon(Monitor *m) {
    231 +	/* Following two lines needed for the auto mode of the tab bar */
    232 +	updatebarpos(m);
    233 +	XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
    234 +
    235  	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
    236  	if(m->lt[m->sellt]->arrange)
    237  		m->lt[m->sellt]->arrange(m);
    238 @@ -454,14 +486,32 @@ buttonpress(XEvent *e) {
    239  		else
    240  			click = ClkWinTitle;
    241  	}
    242 +	if(ev->window == selmon->tabwin) {
    243 +		i = 0; x = 0;
    244 +		for(c = selmon->clients; c; c = c->next){
    245 +		  if(!ISVISIBLE(c)) continue;
    246 +		  x += selmon->tab_widths[i];
    247 +		  if (ev->x > x)
    248 +		    ++i;
    249 +		  else
    250 +		    break;
    251 +		  if(i >= m->ntabs) break;
    252 +		}
    253 +		if(c) {
    254 +		  click = ClkTabBar;
    255 +		  arg.ui = i;
    256 +		}
    257 +	}
    258  	else if((c = wintoclient(ev->window))) {
    259  		focus(c);
    260  		click = ClkClientWin;
    261  	}
    262  	for(i = 0; i < LENGTH(buttons); i++)
    263  		if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
    264 -		&& CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
    265 -			buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
    266 +		   && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
    267 +		  buttons[i].func(((click == ClkTagBar || click == ClkTabBar)
    268 +				   && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
    269 +		}
    270  }
    271  
    272  void
    273 @@ -491,6 +541,7 @@ cleanup(void) {
    274  		XFreeFont(dpy, dc.font.xfont);
    275  	XUngrabKey(dpy, AnyKey, AnyModifier, root);
    276  	XFreePixmap(dpy, dc.drawable);
    277 +	XFreePixmap(dpy, dc.tabdrawable);
    278  	XFreeGC(dpy, dc.gc);
    279  	XFreeCursor(dpy, cursor[CurNormal]);
    280  	XFreeCursor(dpy, cursor[CurResize]);
    281 @@ -513,6 +564,8 @@ cleanupmon(Monitor *mon) {
    282  	}
    283  	XUnmapWindow(dpy, mon->barwin);
    284  	XDestroyWindow(dpy, mon->barwin);
    285 +	XUnmapWindow(dpy, mon->tabwin);
    286 +	XDestroyWindow(dpy, mon->tabwin);
    287  	free(mon);
    288  }
    289  
    290 @@ -538,7 +591,7 @@ clientmessage(XEvent *e) {
    291  	if(cme->message_type == netatom[NetWMState]) {
    292  		if(cme->data.l[1] == netatom[NetWMFullscreen] || cme->data.l[2] == netatom[NetWMFullscreen])
    293  			setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD    */
    294 -			              || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
    295 +				      || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
    296  	}
    297  	else if(cme->message_type == netatom[NetActiveWindow]) {
    298  		if(!ISVISIBLE(c)) {
    299 @@ -581,9 +634,13 @@ configurenotify(XEvent *e) {
    300  			if(dc.drawable != 0)
    301  				XFreePixmap(dpy, dc.drawable);
    302  			dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
    303 +			if(dc.tabdrawable != 0)
    304 +				XFreePixmap(dpy, dc.tabdrawable);
    305 +			dc.tabdrawable = XCreatePixmap(dpy, root, sw, th, DefaultDepth(dpy, screen));
    306  			updatebars();
    307 -			for(m = mons; m; m = m->next)
    308 +			for(m = mons; m; m = m->next){
    309  				XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
    310 +			}
    311  			focus(NULL);
    312  			arrange(NULL);
    313  		}
    314 @@ -646,6 +703,7 @@ configurerequest(XEvent *e) {
    315  Monitor *
    316  createmon(void) {
    317  	Monitor *m;
    318 +	int i;
    319  
    320  	if(!(m = (Monitor *)calloc(1, sizeof(Monitor))))
    321  		die("fatal: could not malloc() %u bytes\n", sizeof(Monitor));
    322 @@ -653,10 +711,34 @@ createmon(void) {
    323  	m->mfact = mfact;
    324  	m->nmaster = nmaster;
    325  	m->showbar = showbar;
    326 +	m->showtab = showtab;
    327  	m->topbar = topbar;
    328 -	m->lt[0] = &layouts[0];
    329 +	m->ntabs = 0;
    330 +	m->sellt = 0;
    331 +	m->lt[0] = &layouts[def_layouts[1] % LENGTH(layouts)];
    332  	m->lt[1] = &layouts[1 % LENGTH(layouts)];
    333  	strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
    334 +	if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag))))
    335 +		die("fatal: could not malloc() %u bytes\n", sizeof(Pertag));
    336 +	m->pertag->curtag = m->pertag->prevtag = 1;
    337 +	for(i=0; i <= LENGTH(tags); i++) {
    338 +		/* init nmaster */
    339 +		m->pertag->nmasters[i] = m->nmaster;
    340 +
    341 +		/* init mfacts */
    342 +		m->pertag->mfacts[i] = m->mfact;
    343 +
    344 +		/* init layouts */
    345 +		m->pertag->ltidxs[i][0] = &layouts[def_layouts[i % LENGTH(def_layouts)] % LENGTH(layouts)];
    346 +		m->pertag->ltidxs[i][1] = m->lt[1];
    347 +		m->pertag->sellts[i] = m->sellt;
    348 +
    349 +		/* init showbar */
    350 +		m->pertag->showbars[i] = m->showbar;
    351 +
    352 +		/* init showtab */
    353 +		m->pertag->showtabs[i] = m->showtab;
    354 +	}
    355  	return m;
    356  }
    357  
    358 @@ -731,13 +813,13 @@ drawbar(Monitor *m) {
    359  	for(i = 0; i < LENGTH(tags); i++) {
    360  		dc.w = TEXTW(tags[i]);
    361  		col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm;
    362 -		drawtext(tags[i], col, urg & 1 << i);
    363 +		drawtext(dc.drawable, tags[i], col, urg & 1 << i);
    364  		drawsquare(m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
    365 -		           occ & 1 << i, urg & 1 << i, col);
    366 +			   occ & 1 << i, urg & 1 << i, col);
    367  		dc.x += dc.w;
    368  	}
    369  	dc.w = blw = TEXTW(m->ltsymbol);
    370 -	drawtext(m->ltsymbol, dc.norm, False);
    371 +	drawtext(dc.drawable, m->ltsymbol, dc.norm, False);
    372  	dc.x += dc.w;
    373  	x = dc.x;
    374  	if(m == selmon) { /* status is only drawn on selected monitor */
    375 @@ -747,19 +829,20 @@ drawbar(Monitor *m) {
    376  			dc.x = x;
    377  			dc.w = m->ww - x;
    378  		}
    379 -		drawtext(stext, dc.norm, False);
    380 +		drawtext(dc.drawable, stext, dc.norm, False);
    381  	}
    382  	else
    383  		dc.x = m->ww;
    384  	if((dc.w = dc.x - x) > bh) {
    385  		dc.x = x;
    386  		if(m->sel) {
    387 -			col = m == selmon ? dc.sel : dc.norm;
    388 -			drawtext(m->sel->name, col, False);
    389 +		  //			col = m == selmon ? dc.sel : dc.norm;
    390 +		  //	drawtext(dc.drawable, m->sel->name, col, False);
    391 +		  drawtext(dc.drawable, m->sel->name, dc.norm, False);
    392  			drawsquare(m->sel->isfixed, m->sel->isfloating, False, col);
    393  		}
    394  		else
    395 -			drawtext(NULL, dc.norm, False);
    396 +		  drawtext(dc.drawable, NULL, dc.norm, False);
    397  	}
    398  	XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0, m->ww, bh, 0, 0);
    399  	XSync(dpy, False);
    400 @@ -774,6 +857,104 @@ drawbars(void) {
    401  }
    402  
    403  void
    404 +drawtabs(void) {
    405 +	Monitor *m;
    406 +
    407 +	for(m = mons; m; m = m->next)
    408 +		drawtab(m);
    409 +}
    410 +
    411 +static int
    412 +cmpint(const void *p1, const void *p2) {
    413 +  /* The actual arguments to this function are "pointers to
    414 +     pointers to char", but strcmp(3) arguments are "pointers
    415 +     to char", hence the following cast plus dereference */
    416 +  return *((int*) p1) > * (int*) p2;
    417 +}
    418 +
    419 +
    420 +void
    421 +drawtab(Monitor *m) {
    422 +	unsigned long *col;
    423 +	Client *c;
    424 +	int i;
    425 +	int itag = -1;
    426 +	char view_info[50];
    427 +	int view_info_w = 0;
    428 +	int sorted_label_widths[MAXTABS];
    429 +	int tot_width;
    430 +	int maxsize = bh;
    431 +	dc.x = 0;
    432 +
    433 +	//view_info: indicate the tag which is displayed in the view
    434 +	for(i = 0; i < LENGTH(tags); ++i){
    435 +	  if((selmon->tagset[selmon->seltags] >> i) & 1) {
    436 +	    if(itag >=0){ //more than one tag selected
    437 +	      itag = -1;
    438 +	      break;
    439 +	    }
    440 +	    itag = i;
    441 +	  }
    442 +	}
    443 +	if(0 <= itag  && itag < LENGTH(tags)){
    444 +	  snprintf(view_info, sizeof view_info, "[%s]", tags[itag]);
    445 +	} else {
    446 +	  strncpy(view_info, "[...]", sizeof view_info);
    447 +	}
    448 +	view_info[sizeof(view_info) - 1 ] = 0;
    449 +	view_info_w = TEXTW(view_info);
    450 +	tot_width = view_info_w;
    451 +
    452 +	/* Calculates number of labels and their width */
    453 +	m->ntabs = 0;
    454 +	for(c = m->clients; c; c = c->next){
    455 +	  if(!ISVISIBLE(c)) continue;
    456 +	  m->tab_widths[m->ntabs] = TEXTW(c->name);
    457 +	  tot_width += m->tab_widths[m->ntabs];
    458 +	  ++m->ntabs;
    459 +	  if(m->ntabs >= MAXTABS) break;
    460 +	}
    461 +
    462 +	if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated
    463 +	  memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs);
    464 +	  qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint);
    465 +	  tot_width = view_info_w;
    466 +	  for(i = 0; i < m->ntabs; ++i){
    467 +	    if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww)
    468 +	      break;
    469 +	    tot_width += sorted_label_widths[i];
    470 +	  }
    471 +	  maxsize = (m->ww - tot_width) / (m->ntabs - i);
    472 +	} else{
    473 +	  maxsize = m->ww;
    474 +	}
    475 +	i = 0;
    476 +	for(c = m->clients; c; c = c->next){
    477 +	  if(!ISVISIBLE(c)) continue;
    478 +	  if(i >= m->ntabs) break;
    479 +	  if(m->tab_widths[i] >  maxsize) m->tab_widths[i] = maxsize;
    480 +	  dc.w = m->tab_widths[i];
    481 +	  col = (c == m->sel)  ? dc.sel : dc.norm;
    482 +	  drawtext(dc.tabdrawable, c->name, col, 0);
    483 +	  dc.x += dc.w;
    484 +	  ++i;
    485 +	}
    486 +
    487 +	/* cleans interspace between window names and current viewed tag label */
    488 +	dc.w = m->ww - view_info_w - dc.x;
    489 +	drawtext(dc.tabdrawable, NULL, dc.norm, 0);
    490 +
    491 +	/* view info */
    492 +	dc.x += dc.w;
    493 +	dc.w = view_info_w;
    494 +	drawtext(dc.tabdrawable, view_info, dc.norm, 0);
    495 +
    496 +	XCopyArea(dpy, dc.tabdrawable, m->tabwin, dc.gc, 0, 0, m->ww, th, 0, 0);
    497 +	XSync(dpy, False);
    498 +}
    499 +
    500 +
    501 +void
    502  drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) {
    503  	int x;
    504  
    505 @@ -785,13 +966,14 @@ drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) {
    506  		XDrawRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x, x);
    507  }
    508  
    509 +
    510  void
    511 -drawtext(const char *text, unsigned long col[ColLast], Bool invert) {
    512 +drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert) {
    513  	char buf[256];
    514  	int i, x, y, h, len, olen;
    515  
    516  	XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]);
    517 -	XFillRectangle(dpy, dc.drawable, dc.gc, dc.x, dc.y, dc.w, dc.h);
    518 +	XFillRectangle(dpy, drawable, dc.gc, dc.x, dc.y, dc.w, dc.h);
    519  	if(!text)
    520  		return;
    521  	olen = strlen(text);
    522 @@ -807,11 +989,12 @@ drawtext(const char *text, unsigned long col[ColLast], Bool invert) {
    523  		for(i = len; i && i > len - 3; buf[--i] = '.');
    524  	XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]);
    525  	if(dc.font.set)
    526 -		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
    527 +		XmbDrawString(dpy, drawable, dc.font.set, dc.gc, x, y, buf, len);
    528  	else
    529 -		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
    530 +		XDrawString(dpy, drawable, dc.gc, x, y, buf, len);
    531  }
    532  
    533 +
    534  void
    535  enternotify(XEvent *e) {
    536  	Client *c;
    537 @@ -836,8 +1019,10 @@ expose(XEvent *e) {
    538  	Monitor *m;
    539  	XExposeEvent *ev = &e->xexpose;
    540  
    541 -	if(ev->count == 0 && (m = wintomon(ev->window)))
    542 +	if(ev->count == 0 && (m = wintomon(ev->window))){
    543  		drawbar(m);
    544 +		drawtab(m);
    545 +	}
    546  }
    547  
    548  void
    549 @@ -862,6 +1047,7 @@ focus(Client *c) {
    550  		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
    551  	selmon->sel = c;
    552  	drawbars();
    553 +	drawtabs();
    554  }
    555  
    556  void
    557 @@ -911,6 +1097,19 @@ focusstack(const Arg *arg) {
    558  	}
    559  }
    560  
    561 +void
    562 +focuswin(const Arg* arg){
    563 +  int iwin = arg->i;
    564 +  Client* c = NULL;
    565 +  for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
    566 +    if(ISVISIBLE(c)) --iwin;
    567 +  };
    568 +  if(c) {
    569 +    focus(c);
    570 +    restack(selmon);
    571 +  }
    572 +}
    573 +
    574  Atom
    575  getatomprop(Client *c, Atom prop) {
    576  	int di;
    577 @@ -919,7 +1118,7 @@ getatomprop(Client *c, Atom prop) {
    578  	Atom da, atom = None;
    579  
    580  	if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
    581 -	                      &da, &di, &dl, &dl, &p) == Success && p) {
    582 +			      &da, &di, &dl, &dl, &p) == Success && p) {
    583  		atom = *(Atom *)p;
    584  		XFree(p);
    585  	}
    586 @@ -954,7 +1153,7 @@ getstate(Window w) {
    587  	Atom real;
    588  
    589  	if(XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
    590 -	                      &real, &format, &n, &extra, (unsigned char **)&p) != Success)
    591 +			      &real, &format, &n, &extra, (unsigned char **)&p) != Success)
    592  		return -1;
    593  	if(n != 0)
    594  		result = *p;
    595 @@ -999,13 +1198,13 @@ grabbuttons(Client *c, Bool focused) {
    596  				if(buttons[i].click == ClkClientWin)
    597  					for(j = 0; j < LENGTH(modifiers); j++)
    598  						XGrabButton(dpy, buttons[i].button,
    599 -						            buttons[i].mask | modifiers[j],
    600 -						            c->win, False, BUTTONMASK,
    601 -						            GrabModeAsync, GrabModeSync, None, None);
    602 +							    buttons[i].mask | modifiers[j],
    603 +							    c->win, False, BUTTONMASK,
    604 +							    GrabModeAsync, GrabModeSync, None, None);
    605  		}
    606  		else
    607  			XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
    608 -			            BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
    609 +				    BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
    610  	}
    611  }
    612  
    613 @@ -1028,7 +1227,7 @@ grabkeys(void) {
    614  
    615  void
    616  incnmaster(const Arg *arg) {
    617 -	selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
    618 +	selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
    619  	arrange(selmon);
    620  }
    621  
    622 @@ -1139,7 +1338,7 @@ manage(Window w, XWindowAttributes *wa) {
    623  	c->x = MAX(c->x, c->mon->mx);
    624  	/* only fix client y-offset, if the client center might cover the bar */
    625  	c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
    626 -	           && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
    627 +		   && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
    628  	c->bw = borderpx;
    629  
    630  	wc.border_width = c->bw;
    631 @@ -1311,12 +1510,14 @@ propertynotify(XEvent *e) {
    632  		case XA_WM_HINTS:
    633  			updatewmhints(c);
    634  			drawbars();
    635 +			drawtabs();
    636  			break;
    637  		}
    638  		if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
    639  			updatetitle(c);
    640  			if(c == c->mon->sel)
    641  				drawbar(c->mon);
    642 +			drawtab(c->mon);
    643  		}
    644  		if(ev->atom == netatom[NetWMWindowType])
    645  			updatewindowtype(c);
    646 @@ -1375,7 +1576,7 @@ resizemouse(const Arg *arg) {
    647  	ocx = c->x;
    648  	ocy = c->y;
    649  	if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
    650 -	                None, cursor[CurResize], CurrentTime) != GrabSuccess)
    651 +			None, cursor[CurResize], CurrentTime) != GrabSuccess)
    652  		return;
    653  	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
    654  	do {
    655 @@ -1418,6 +1619,7 @@ restack(Monitor *m) {
    656  	XWindowChanges wc;
    657  
    658  	drawbar(m);
    659 +	drawtab(m);
    660  	if(!m->sel)
    661  		return;
    662  	if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
    663 @@ -1529,7 +1731,7 @@ void
    664  setfullscreen(Client *c, Bool fullscreen) {
    665  	if(fullscreen) {
    666  		XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
    667 -		                PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
    668 +				PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
    669  		c->isfullscreen = True;
    670  		c->oldstate = c->isfloating;
    671  		c->oldbw = c->bw;
    672 @@ -1540,7 +1742,7 @@ setfullscreen(Client *c, Bool fullscreen) {
    673  	}
    674  	else {
    675  		XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
    676 -		                PropModeReplace, (unsigned char*)0, 0);
    677 +				PropModeReplace, (unsigned char*)0, 0);
    678  		c->isfullscreen = False;
    679  		c->isfloating = c->oldstate;
    680  		c->bw = c->oldbw;
    681 @@ -1555,10 +1757,13 @@ setfullscreen(Client *c, Bool fullscreen) {
    682  
    683  void
    684  setlayout(const Arg *arg) {
    685 -	if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
    686 -		selmon->sellt ^= 1;
    687 +	if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
    688 +		selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
    689 +		selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
    690 +	}
    691  	if(arg && arg->v)
    692 -		selmon->lt[selmon->sellt] = (Layout *)arg->v;
    693 +		selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
    694 +	selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
    695  	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
    696  	if(selmon->sel)
    697  		arrange(selmon);
    698 @@ -1576,7 +1781,7 @@ setmfact(const Arg *arg) {
    699  	f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
    700  	if(f < 0.1 || f > 0.9)
    701  		return;
    702 -	selmon->mfact = f;
    703 +	selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
    704  	arrange(selmon);
    705  }
    706  
    707 @@ -1594,6 +1799,7 @@ setup(void) {
    708  	sw = DisplayWidth(dpy, screen);
    709  	sh = DisplayHeight(dpy, screen);
    710  	bh = dc.h = dc.font.height + 2;
    711 +	th = bh;
    712  	updategeom();
    713  	/* init atoms */
    714  	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
    715 @@ -1619,6 +1825,7 @@ setup(void) {
    716  	dc.sel[ColBG] = getcolor(selbgcolor);
    717  	dc.sel[ColFG] = getcolor(selfgcolor);
    718  	dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), bh, DefaultDepth(dpy, screen));
    719 +	dc.tabdrawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), th, DefaultDepth(dpy, screen));
    720  	dc.gc = XCreateGC(dpy, root, 0, NULL);
    721  	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
    722  	if(!dc.font.set)
    723 @@ -1632,7 +1839,7 @@ setup(void) {
    724  	/* select for events */
    725  	wa.cursor = cursor[CurNormal];
    726  	wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|PointerMotionMask
    727 -	                |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
    728 +			|EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
    729  	XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
    730  	XSelectInput(dpy, root, wa.event_mask);
    731  	grabkeys();
    732 @@ -1729,13 +1936,24 @@ tile(Monitor *m) {
    733  
    734  void
    735  togglebar(const Arg *arg) {
    736 -	selmon->showbar = !selmon->showbar;
    737 +	selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
    738  	updatebarpos(selmon);
    739  	XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
    740  	arrange(selmon);
    741  }
    742  
    743  void
    744 +tabmode(const Arg *arg) {
    745 +	if(arg && arg->i >= 0)
    746 +		selmon->showtab = arg->ui % showtab_nmodes;
    747 +	else
    748 +		selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
    749 +	selmon->pertag->showtabs[selmon->pertag->curtag] = selmon->showtab;
    750 +	arrange(selmon);
    751 +}
    752 +
    753 +
    754 +void
    755  togglefloating(const Arg *arg) {
    756  	if(!selmon->sel)
    757  		return;
    758 @@ -1763,9 +1981,31 @@ toggletag(const Arg *arg) {
    759  void
    760  toggleview(const Arg *arg) {
    761  	unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
    762 +	int i;
    763  
    764  	if(newtagset) {
    765 +		if(newtagset == ~0) {
    766 +			selmon->pertag->prevtag = selmon->pertag->curtag;
    767 +			selmon->pertag->curtag = 0;
    768 +		}
    769 +		/* test if the user did not select the same tag */
    770 +		if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
    771 +			selmon->pertag->prevtag = selmon->pertag->curtag;
    772 +			for (i=0; !(newtagset & 1 << i); i++) ;
    773 +			selmon->pertag->curtag = i + 1;
    774 +		}
    775  		selmon->tagset[selmon->seltags] = newtagset;
    776 +
    777 +		/* apply settings for this view */
    778 +		selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
    779 +		selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
    780 +		selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
    781 +		selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
    782 +		selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
    783 +		if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
    784 +			togglebar(NULL);
    785 +		if (selmon->showtab != selmon->pertag->showtabs[selmon->pertag->curtag])
    786 +			tabmode(NULL);
    787  		focus(NULL);
    788  		arrange(selmon);
    789  	}
    790 @@ -1828,24 +2068,47 @@ updatebars(void) {
    791  	};
    792  	for(m = mons; m; m = m->next) {
    793  		m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
    794 -		                          CopyFromParent, DefaultVisual(dpy, screen),
    795 -		                          CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
    796 +					  CopyFromParent, DefaultVisual(dpy, screen),
    797 +					  CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
    798  		XDefineCursor(dpy, m->barwin, cursor[CurNormal]);
    799  		XMapRaised(dpy, m->barwin);
    800 +		m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
    801 +					  CopyFromParent, DefaultVisual(dpy, screen),
    802 +					  CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
    803 +		XDefineCursor(dpy, m->tabwin, cursor[CurNormal]);
    804 +		XMapRaised(dpy, m->tabwin);
    805  	}
    806  }
    807  
    808  void
    809  updatebarpos(Monitor *m) {
    810 +	Client *c;
    811 +	int nvis = 0;
    812 +
    813  	m->wy = m->my;
    814  	m->wh = m->mh;
    815  	if(m->showbar) {
    816  		m->wh -= bh;
    817  		m->by = m->topbar ? m->wy : m->wy + m->wh;
    818 -		m->wy = m->topbar ? m->wy + bh : m->wy;
    819 -	}
    820 -	else
    821 +		if ( m->topbar )
    822 +			m->wy += bh;
    823 +	} else {
    824  		m->by = -bh;
    825 +	}
    826 +
    827 +	for(c = m->clients; c; c = c->next){
    828 +	  if(ISVISIBLE(c)) ++nvis;
    829 +	}
    830 +
    831 +	if(m->showtab == showtab_always
    832 +	   || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){
    833 +		m->wh -= th;
    834 +		m->ty = m->toptab ? m->wy : m->wy + m->wh;
    835 +		if ( m->toptab )
    836 +			m->wy += th;
    837 +	} else {
    838 +		m->ty = -th;
    839 +	}
    840  }
    841  
    842  Bool
    843 @@ -1992,7 +2255,7 @@ updatesizehints(Client *c) {
    844  	else
    845  		c->maxa = c->mina = 0.0;
    846  	c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
    847 -	             && c->maxw == c->minw && c->maxh == c->minh);
    848 +		     && c->maxw == c->minw && c->maxh == c->minh);
    849  }
    850  
    851  void
    852 @@ -2043,11 +2306,35 @@ updatewmhints(Client *c) {
    853  
    854  void
    855  view(const Arg *arg) {
    856 +	int i;
    857 +	unsigned int tmptag;
    858 +
    859  	if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
    860  		return;
    861  	selmon->seltags ^= 1; /* toggle sel tagset */
    862 -	if(arg->ui & TAGMASK)
    863 +	if(arg->ui & TAGMASK) {
    864 +		selmon->pertag->prevtag = selmon->pertag->curtag;
    865  		selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
    866 +		if(arg->ui == ~0)
    867 +			selmon->pertag->curtag = 0;
    868 +		else {
    869 +			for (i=0; !(arg->ui & 1 << i); i++) ;
    870 +			selmon->pertag->curtag = i + 1;
    871 +		}
    872 +	} else {
    873 +		tmptag = selmon->pertag->prevtag;
    874 +		selmon->pertag->prevtag = selmon->pertag->curtag;
    875 +		selmon->pertag->curtag = tmptag;
    876 +	}
    877 +	selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
    878 +	selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
    879 +	selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
    880 +	selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
    881 +	selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
    882 +	if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
    883 +		togglebar(NULL);
    884 +	if (selmon->showtab != selmon->pertag->showtabs[selmon->pertag->curtag])
    885 +		tabmode(NULL);
    886  	focus(NULL);
    887  	arrange(selmon);
    888  }
    889 @@ -2073,7 +2360,7 @@ wintomon(Window w) {
    890  	if(w == root && getrootptr(&x, &y))
    891  		return recttomon(x, y, 1, 1);
    892  	for(m = mons; m; m = m->next)
    893 -		if(w == m->barwin)
    894 +		if(w == m->barwin || w == m->tabwin)
    895  			return m;
    896  	if((c = wintoclient(w)))
    897  		return c->mon;