sites

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

dwm-6.0-pertag-tab-v2.diff (31132B)


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