sites

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

dwm-6.1-pertag-tab-v2b.diff (26836B)


      1 diff --git a/config.def.h b/config.def.h
      2 index 7054c06..f0b33c5 100644
      3 --- a/config.def.h
      4 +++ b/config.def.h
      5 @@ -15,10 +15,21 @@ static const unsigned int borderpx  = 1;        /* border pixel of windows */
      6  static const unsigned int snap      = 32;       /* snap pixel */
      7  static const int showbar            = 1;        /* 0 means no bar */
      8  static const int topbar             = 1;        /* 0 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  /* tagging */
     17  static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
     18  
     19 +/* default layout per tags */
     20 +/* The first element is for all-tag view, following i-th element corresponds to */
     21 +/* tags[i]. Layout is referred using the layouts array index.*/
     22 +static int def_layouts[1 + LENGTH(tags)]  = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     23 +
     24  static const Rule rules[] = {
     25  	/* xprop(1):
     26  	 *	WM_CLASS(STRING) = instance, class
     27 @@ -62,6 +73,7 @@ static Key keys[] = {
     28  	{ MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
     29  	{ MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },
     30  	{ MODKEY,                       XK_b,      togglebar,      {0} },
     31 +	{ MODKEY,                       XK_w,      tabmode,        {-1} },
     32  	{ MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
     33  	{ MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
     34  	{ MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
     35 @@ -109,5 +121,6 @@ static Button buttons[] = {
     36  	{ ClkTagBar,            0,              Button3,        toggleview,     {0} },
     37  	{ ClkTagBar,            MODKEY,         Button1,        tag,            {0} },
     38  	{ ClkTagBar,            MODKEY,         Button3,        toggletag,      {0} },
     39 +	{ ClkTabBar,            0,              Button1,        focuswin,       {0} },
     40  };
     41  
     42 diff --git a/dwm.1 b/dwm.1
     43 index 6687011..077d92b 100644
     44 --- a/dwm.1
     45 +++ b/dwm.1
     46 @@ -19,14 +19,22 @@ layout applied.
     47  Windows are grouped by tags. Each window can be tagged with one or multiple
     48  tags. Selecting certain tags displays all windows with these tags.
     49  .P
     50 -Each screen contains a small status bar which displays all available tags, the
     51 -layout, the title of the focused window, and the text read from the root window
     52 -name property, if the screen is focused. A floating window is indicated with an
     53 -empty square and a maximised floating window is indicated with a filled square
     54 -before the windows title.  The selected tags are indicated with a different
     55 -color. The tags of the focused window are indicated with a filled square in the
     56 -top left corner.  The tags which are applied to one or more windows are
     57 -indicated with an empty square in the top left corner.
     58 +Each screen contains two small status bars.
     59 +.P
     60 +One bar displays all available tags, the layout, the title of the focused
     61 +window, and the text read from the root window name property, if the screen is
     62 +focused. A floating window is indicated with an empty square and a maximised
     63 +floating window is indicated with a filled square before the windows title.  The
     64 +selected tags are indicated with a different color. The tags of the focused
     65 +window are indicated with a filled square in the top left corner.  The tags
     66 +which are applied to one or more windows are indicated with an empty square in
     67 +the top left corner.
     68 +.P
     69 +Another bar contains a tab for each window of the current view and allows
     70 +navigation between windows, especially in the monocle mode. The different
     71 +display modes of this bar are described under the Mod1\-w Keybord command
     72 +section.  When a single tag is selected, that tag is indicated in the left corner
     73 +of the tab bar.
     74  .P
     75  dwm draws a small border around windows to indicate the focus state.
     76  .SH OPTIONS
     77 @@ -43,7 +51,8 @@ command.
     78  .TP
     79  .B Button1
     80  click on a tag label to display all windows with that tag, click on the layout
     81 -label toggles between tiled and floating layout.
     82 +label toggles between tiled and floating layout, click on a window name in the
     83 +tab bar brings focus to that window.
     84  .TP
     85  .B Button3
     86  click on a tag label adds/removes all windows with that tag to/from the view.
     87 @@ -104,6 +113,12 @@ Increase master area size.
     88  .B Mod1\-h
     89  Decrease master area size.
     90  .TP
     91 +.B Mod1\-w
     92 +Cycle over the tab bar display modes: never displayed, always displayed,
     93 +displayed only in monocle mode when the view contains than one window (auto
     94 +mode). Some display modes can be disabled in the configuration, config.h. In
     95 +the default configuration only "never" and "auto" display modes are enabled.
     96 +.TP
     97  .B Mod1\-Return
     98  Zooms/cycles focused window to/from master area (tiled layouts only).
     99  .TP
    100 diff --git a/dwm.c b/dwm.c
    101 index 0362114..887a839 100644
    102 --- a/dwm.c
    103 +++ b/dwm.c
    104 @@ -64,7 +64,7 @@ enum { NetSupported, NetWMName, NetWMState,
    105         NetWMFullscreen, NetActiveWindow, NetWMWindowType,
    106         NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
    107  enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
    108 -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
    109 +enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
    110         ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
    111  
    112  typedef union {
    113 @@ -111,25 +111,35 @@ typedef struct {
    114  	void (*arrange)(Monitor *);
    115  } Layout;
    116  
    117 +#define MAXTABS 50
    118 +
    119 +typedef struct Pertag Pertag;
    120  struct Monitor {
    121  	char ltsymbol[16];
    122  	float mfact;
    123  	int nmaster;
    124  	int num;
    125  	int by;               /* bar geometry */
    126 +	int ty;               /* tab bar geometry */
    127  	int mx, my, mw, mh;   /* screen size */
    128  	int wx, wy, ww, wh;   /* window area  */
    129  	unsigned int seltags;
    130  	unsigned int sellt;
    131  	unsigned int tagset[2];
    132  	int showbar;
    133 +	int showtab;
    134  	int topbar;
    135 +	int toptab;
    136  	Client *clients;
    137  	Client *sel;
    138  	Client *stack;
    139  	Monitor *next;
    140  	Window barwin;
    141 +	Window tabwin;
    142 +	int ntabs;
    143 +	int tab_widths[MAXTABS];
    144  	const Layout *lt[2];
    145 +	Pertag *pertag;
    146  };
    147  
    148  typedef struct {
    149 @@ -164,12 +174,15 @@ static void detachstack(Client *c);
    150  static Monitor *dirtomon(int dir);
    151  static void drawbar(Monitor *m);
    152  static void drawbars(void);
    153 +static void drawtab(Monitor *m);
    154 +static void drawtabs(void);
    155  static void enternotify(XEvent *e);
    156  static void expose(XEvent *e);
    157  static void focus(Client *c);
    158  static void focusin(XEvent *e);
    159  static void focusmon(const Arg *arg);
    160  static void focusstack(const Arg *arg);
    161 +static void focuswin(const Arg* arg);
    162  static int getrootptr(int *x, int *y);
    163  static long getstate(Window w);
    164  static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
    165 @@ -206,6 +219,7 @@ static void setup(void);
    166  static void showhide(Client *c);
    167  static void sigchld(int unused);
    168  static void spawn(const Arg *arg);
    169 +static void tabmode(const Arg *arg);
    170  static void tag(const Arg *arg);
    171  static void tagmon(const Arg *arg);
    172  static void tile(Monitor *);
    173 @@ -240,6 +254,7 @@ static char stext[256];
    174  static int screen;
    175  static int sw, sh;           /* X display screen geometry width, height */
    176  static int bh, blw = 0;      /* bar geometry */
    177 +static int th = 0;           /* tab bar geometry */
    178  static int (*xerrorxlib)(Display *, XErrorEvent *);
    179  static unsigned int numlockmask = 0;
    180  static void (*handler[LASTEvent]) (XEvent *) = {
    181 @@ -270,6 +285,16 @@ static Window root;
    182  /* configuration, allows nested code to access above variables */
    183  #include "config.h"
    184  
    185 +struct Pertag {
    186 +	unsigned int curtag, prevtag; /* current and previous tag */
    187 +	int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
    188 +	float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
    189 +	unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
    190 +	const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes  */
    191 +	Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
    192 +	Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */
    193 +};
    194 +
    195  /* compile-time check if all tags fit into an unsigned int bit array. */
    196  struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
    197  
    198 @@ -393,6 +418,8 @@ arrange(Monitor *m)
    199  void
    200  arrangemon(Monitor *m)
    201  {
    202 +	updatebarpos(m);
    203 +	XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
    204  	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
    205  	if (m->lt[m->sellt]->arrange)
    206  		m->lt[m->sellt]->arrange(m);
    207 @@ -442,14 +469,33 @@ buttonpress(XEvent *e)
    208  			click = ClkStatusText;
    209  		else
    210  			click = ClkWinTitle;
    211 -	} else if ((c = wintoclient(ev->window))) {
    212 +	}
    213 +	if(ev->window == selmon->tabwin) {
    214 +		i = 0; x = 0;
    215 +		for(c = selmon->clients; c; c = c->next){
    216 +		  if(!ISVISIBLE(c)) continue;
    217 +		  x += selmon->tab_widths[i];
    218 +		  if (ev->x > x)
    219 +		    ++i;
    220 +		  else
    221 +		    break;
    222 +		  if(i >= m->ntabs) break;
    223 +		}
    224 +		if(c) {
    225 +		  click = ClkTabBar;
    226 +		  arg.ui = i;
    227 +		}
    228 +	}
    229 +	else if((c = wintoclient(ev->window))) {
    230  		focus(c);
    231  		click = ClkClientWin;
    232  	}
    233 -	for (i = 0; i < LENGTH(buttons); i++)
    234 -		if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
    235 -		&& CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
    236 -			buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
    237 +	for(i = 0; i < LENGTH(buttons); i++)
    238 +		if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
    239 +		   && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
    240 +		  buttons[i].func(((click == ClkTagBar || click == ClkTabBar)
    241 +				   && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
    242 +		}
    243  }
    244  
    245  void
    246 @@ -469,23 +515,24 @@ cleanup(void)
    247  	Arg a = {.ui = ~0};
    248  	Layout foo = { "", NULL };
    249  	Monitor *m;
    250 -	size_t i;
    251  
    252  	view(&a);
    253  	selmon->lt[selmon->sellt] = &foo;
    254  	for (m = mons; m; m = m->next)
    255 -		while (m->stack)
    256 -			unmanage(m->stack, 0);
    257 +		while(m->stack)
    258 +			unmanage(m->stack, False);
    259  	XUngrabKey(dpy, AnyKey, AnyModifier, root);
    260  	while (mons)
    261  		cleanupmon(mons);
    262 -	for (i = 0; i < CurLast; i++)
    263 -		drw_cur_free(drw, cursor[i]);
    264 -	for (i = 0; i < SchemeLast; i++) {
    265 -		drw_clr_free(scheme[i].border);
    266 -		drw_clr_free(scheme[i].bg);
    267 -		drw_clr_free(scheme[i].fg);
    268 -	}
    269 +	drw_cur_free(drw, cursor[CurNormal]);
    270 +	drw_cur_free(drw, cursor[CurResize]);
    271 +	drw_cur_free(drw, cursor[CurMove]);
    272 +	drw_clr_free(scheme[SchemeNorm].border);
    273 +	drw_clr_free(scheme[SchemeNorm].bg);
    274 +	drw_clr_free(scheme[SchemeNorm].fg);
    275 +	drw_clr_free(scheme[SchemeSel].border);
    276 +	drw_clr_free(scheme[SchemeSel].bg);
    277 +	drw_clr_free(scheme[SchemeSel].fg);
    278  	drw_free(drw);
    279  	XSync(dpy, False);
    280  	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
    281 @@ -505,6 +552,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 @@ -526,6 +575,7 @@ clientmessage(XEvent *e)
    291  {
    292  	XClientMessageEvent *cme = &e->xclient;
    293  	Client *c = wintoclient(cme->window);
    294 +	int i;
    295  
    296  	if (!c)
    297  		return;
    298 @@ -537,6 +587,8 @@ clientmessage(XEvent *e)
    299  		if (!ISVISIBLE(c)) {
    300  			c->mon->seltags ^= 1;
    301  			c->mon->tagset[c->mon->seltags] = c->tags;
    302 +			for(i=0; !(c->tags & 1 << i); i++);
    303 +			view(&(Arg){.ui = 1 << i});
    304  		}
    305  		pop(c);
    306  	}
    307 @@ -565,6 +617,7 @@ void
    308  configurenotify(XEvent *e)
    309  {
    310  	Monitor *m;
    311 +	Client *c;
    312  	XConfigureEvent *ev = &e->xconfigure;
    313  	int dirty;
    314  
    315 @@ -576,8 +629,14 @@ configurenotify(XEvent *e)
    316  		if (updategeom() || dirty) {
    317  			drw_resize(drw, sw, bh);
    318  			updatebars();
    319 -			for (m = mons; m; m = m->next)
    320 +			//refreshing display of status bar. The tab bar is handled by the arrange()
    321 +			//method, which is called below
    322 +			for(m = mons; m; m = m->next){
    323 +				for (c = m->clients; c; c = c->next)
    324 +					if (c->isfullscreen)
    325 +						resizeclient(c, m->mx, m->my, m->mw, m->mh);
    326  				XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
    327 +			}
    328  			focus(NULL);
    329  			arrange(NULL);
    330  		}
    331 @@ -640,16 +699,41 @@ Monitor *
    332  createmon(void)
    333  {
    334  	Monitor *m;
    335 +	int i;
    336  
    337  	m = ecalloc(1, sizeof(Monitor));
    338  	m->tagset[0] = m->tagset[1] = 1;
    339  	m->mfact = mfact;
    340  	m->nmaster = nmaster;
    341  	m->showbar = showbar;
    342 +	m->showtab = showtab;
    343  	m->topbar = topbar;
    344 -	m->lt[0] = &layouts[0];
    345 +	m->toptab = toptab;
    346 +	m->ntabs = 0;
    347 +	m->lt[0] = &layouts[def_layouts[1] % LENGTH(layouts)];
    348  	m->lt[1] = &layouts[1 % LENGTH(layouts)];
    349  	strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
    350 +	if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag))))
    351 +		die("fatal: could not malloc() %u bytes\n", sizeof(Pertag));
    352 +	m->pertag->curtag = m->pertag->prevtag = 1;
    353 +	for(i=0; i <= LENGTH(tags); i++) {
    354 +		/* init nmaster */
    355 +		m->pertag->nmasters[i] = m->nmaster;
    356 +
    357 +		/* init mfacts */
    358 +		m->pertag->mfacts[i] = m->mfact;
    359 +
    360 +		/* init layouts */
    361 +		m->pertag->ltidxs[i][0] = &layouts[def_layouts[i % LENGTH(def_layouts)] % LENGTH(layouts)];
    362 +		m->pertag->ltidxs[i][1] = m->lt[1];
    363 +		m->pertag->sellts[i] = m->sellt;
    364 +
    365 +		/* init showbar */
    366 +		m->pertag->showbars[i] = m->showbar;
    367 +
    368 +		/* swap focus and zoomswap*/
    369 +		m->pertag->prevzooms[i] = NULL;
    370 +	}
    371  	return m;
    372  }
    373  
    374 @@ -763,6 +847,104 @@ drawbars(void)
    375  }
    376  
    377  void
    378 +drawtabs(void) {
    379 +	Monitor *m;
    380 +
    381 +	for(m = mons; m; m = m->next)
    382 +		drawtab(m);
    383 +}
    384 +
    385 +static int
    386 +cmpint(const void *p1, const void *p2) {
    387 +  /* The actual arguments to this function are "pointers to
    388 +     pointers to char", but strcmp(3) arguments are "pointers
    389 +     to char", hence the following cast plus dereference */
    390 +  return *((int*) p1) > * (int*) p2;
    391 +}
    392 +
    393 +
    394 +void
    395 +drawtab(Monitor *m) {
    396 +	Client *c;
    397 +	int i;
    398 +	int itag = -1;
    399 +	char view_info[50];
    400 +	int view_info_w = 0;
    401 +	int sorted_label_widths[MAXTABS];
    402 +	int tot_width;
    403 +	int maxsize = bh;
    404 +	int x = 0;
    405 +	int w = 0;
    406 +
    407 +	//view_info: indicate the tag which is displayed in the view
    408 +	for(i = 0; i < LENGTH(tags); ++i){
    409 +	  if((selmon->tagset[selmon->seltags] >> i) & 1) {
    410 +	    if(itag >=0){ //more than one tag selected
    411 +	      itag = -1;
    412 +	      break;
    413 +	    }
    414 +	    itag = i;
    415 +	  }
    416 +	}
    417 +	if(0 <= itag  && itag < LENGTH(tags)){
    418 +	  snprintf(view_info, sizeof view_info, "[%s]", tags[itag]);
    419 +	} else {
    420 +	  strncpy(view_info, "[...]", sizeof view_info);
    421 +	}
    422 +	view_info[sizeof(view_info) - 1 ] = 0;
    423 +	view_info_w = TEXTW(view_info);
    424 +	tot_width = view_info_w;
    425 +
    426 +	/* Calculates number of labels and their width */
    427 +	m->ntabs = 0;
    428 +	for(c = m->clients; c; c = c->next){
    429 +	  if(!ISVISIBLE(c)) continue;
    430 +	  m->tab_widths[m->ntabs] = TEXTW(c->name);
    431 +	  tot_width += m->tab_widths[m->ntabs];
    432 +	  ++m->ntabs;
    433 +	  if(m->ntabs >= MAXTABS) break;
    434 +	}
    435 +
    436 +	if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated
    437 +	  memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs);
    438 +	  qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint);
    439 +	  tot_width = view_info_w;
    440 +	  for(i = 0; i < m->ntabs; ++i){
    441 +	    if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww)
    442 +	      break;
    443 +	    tot_width += sorted_label_widths[i];
    444 +	  }
    445 +	  maxsize = (m->ww - tot_width) / (m->ntabs - i);
    446 +	} else{
    447 +	  maxsize = m->ww;
    448 +	}
    449 +	i = 0;
    450 +	for(c = m->clients; c; c = c->next){
    451 +	  if(!ISVISIBLE(c)) continue;
    452 +	  if(i >= m->ntabs) break;
    453 +	  if(m->tab_widths[i] >  maxsize) m->tab_widths[i] = maxsize;
    454 +	  w = m->tab_widths[i];
    455 +	  drw_setscheme(drw, (c == m->sel) ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
    456 +	  drw_text(drw, x, 0, w, th, c->name, 0);
    457 +	  x += w;
    458 +	  ++i;
    459 +	}
    460 +
    461 +	drw_setscheme(drw, &scheme[SchemeNorm]);
    462 +
    463 +	/* cleans interspace between window names and current viewed tag label */
    464 +	w = m->ww - view_info_w - x;
    465 +	drw_text(drw, x, 0, w, th, "", 0);
    466 +
    467 +	/* view info */
    468 +	x += w;
    469 +	w = view_info_w;
    470 +	drw_text(drw, x, 0, w, th, view_info, 0);
    471 +
    472 +	drw_map(drw, m->tabwin, 0, 0, m->ww, th);
    473 +}
    474 +
    475 +void
    476  enternotify(XEvent *e)
    477  {
    478  	Client *c;
    479 @@ -787,8 +969,10 @@ expose(XEvent *e)
    480  	Monitor *m;
    481  	XExposeEvent *ev = &e->xexpose;
    482  
    483 -	if (ev->count == 0 && (m = wintomon(ev->window)))
    484 +	if (ev->count == 0 && (m = wintomon(ev->window))){
    485  		drawbar(m);
    486 +		drawtab(m);
    487 +	}
    488  }
    489  
    490  void
    491 @@ -806,7 +990,7 @@ focus(Client *c)
    492  			clearurgent(c);
    493  		detachstack(c);
    494  		attachstack(c);
    495 -		grabbuttons(c, 1);
    496 +		grabbuttons(c, True);
    497  		XSetWindowBorder(dpy, c->win, scheme[SchemeSel].border->pix);
    498  		setfocus(c);
    499  	} else {
    500 @@ -815,6 +999,7 @@ focus(Client *c)
    501  	}
    502  	selmon->sel = c;
    503  	drawbars();
    504 +	drawtabs();
    505  }
    506  
    507  /* there are some broken focus acquiring clients */
    508 @@ -868,6 +1053,19 @@ focusstack(const Arg *arg)
    509  	}
    510  }
    511  
    512 +void
    513 +focuswin(const Arg* arg){
    514 +  int iwin = arg->i;
    515 +  Client* c = NULL;
    516 +  for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
    517 +    if(ISVISIBLE(c)) --iwin;
    518 +  };
    519 +  if(c) {
    520 +    focus(c);
    521 +    restack(selmon);
    522 +  }
    523 +}
    524 +
    525  Atom
    526  getatomprop(Client *c, Atom prop)
    527  {
    528 @@ -981,7 +1179,7 @@ grabkeys(void)
    529  void
    530  incnmaster(const Arg *arg)
    531  {
    532 -	selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
    533 +	selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
    534  	arrange(selmon);
    535  }
    536  
    537 @@ -1139,7 +1337,7 @@ motionnotify(XEvent *e)
    538  	if (ev->window != root)
    539  		return;
    540  	if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
    541 -		unfocus(selmon->sel, 1);
    542 +		unfocus(selmon->sel, True);
    543  		selmon = m;
    544  		focus(NULL);
    545  	}
    546 @@ -1159,11 +1357,13 @@ movemouse(const Arg *arg)
    547  		return;
    548  	if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
    549  		return;
    550 +	if(c->isfullscreen) /* no support moving fullscreen windows by mouse */
    551 +		return;
    552  	restack(selmon);
    553  	ocx = c->x;
    554  	ocy = c->y;
    555  	if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
    556 -	None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
    557 +			 None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
    558  		return;
    559  	if (!getrootptr(&x, &y))
    560  		return;
    561 @@ -1250,12 +1450,14 @@ propertynotify(XEvent *e)
    562  		case XA_WM_HINTS:
    563  			updatewmhints(c);
    564  			drawbars();
    565 +			drawtabs();
    566  			break;
    567  		}
    568  		if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
    569  			updatetitle(c);
    570  			if (c == c->mon->sel)
    571  				drawbar(c->mon);
    572 +			drawtab(c->mon);
    573  		}
    574  		if (ev->atom == netatom[NetWMWindowType])
    575  			updatewindowtype(c);
    576 @@ -1317,11 +1519,13 @@ resizemouse(const Arg *arg)
    577  		return;
    578  	if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
    579  		return;
    580 +	if(c->isfullscreen) /* no support resizing fullscreen windows by mouse */
    581 +		return;
    582  	restack(selmon);
    583  	ocx = c->x;
    584  	ocy = c->y;
    585  	if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
    586 -	                None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
    587 +			 None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
    588  		return;
    589  	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
    590  	do {
    591 @@ -1369,6 +1573,7 @@ restack(Monitor *m)
    592  	XWindowChanges wc;
    593  
    594  	drawbar(m);
    595 +	drawtab(m);
    596  	if (!m->sel)
    597  		return;
    598  	if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
    599 @@ -1477,11 +1682,11 @@ sendevent(Client *c, Atom proto)
    600  void
    601  setfocus(Client *c)
    602  {
    603 -	if (!c->neverfocus) {
    604 +	if(!c->neverfocus) {
    605  		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
    606  		XChangeProperty(dpy, root, netatom[NetActiveWindow],
    607 -		                XA_WINDOW, 32, PropModeReplace,
    608 -		                (unsigned char *) &(c->win), 1);
    609 + 		                XA_WINDOW, 32, PropModeReplace,
    610 + 		                (unsigned char *) &(c->win), 1);
    611  	}
    612  	sendevent(c, wmatom[WMTakeFocus]);
    613  }
    614 @@ -1517,10 +1722,13 @@ setfullscreen(Client *c, int fullscreen)
    615  void
    616  setlayout(const Arg *arg)
    617  {
    618 -	if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
    619 -		selmon->sellt ^= 1;
    620 -	if (arg && arg->v)
    621 -		selmon->lt[selmon->sellt] = (Layout *)arg->v;
    622 +	if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
    623 +		selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
    624 +		selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
    625 +	}
    626 +	if(arg && arg->v)
    627 +		selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
    628 +	selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
    629  	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
    630  	if (selmon->sel)
    631  		arrange(selmon);
    632 @@ -1539,7 +1747,7 @@ setmfact(const Arg *arg)
    633  	f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
    634  	if (f < 0.1 || f > 0.9)
    635  		return;
    636 -	selmon->mfact = f;
    637 +	selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
    638  	arrange(selmon);
    639  }
    640  
    641 @@ -1559,8 +1767,9 @@ setup(void)
    642  	drw = drw_create(dpy, screen, root, sw, sh);
    643  	drw_load_fonts(drw, fonts, LENGTH(fonts));
    644  	if (!drw->fontcount)
    645 -		die("no fonts could be loaded.\n");
    646 +		die("No fonts could be loaded.\n");
    647  	bh = drw->fonts[0]->h + 2;
    648 +	th = bh;
    649  	updategeom();
    650  	/* init atoms */
    651  	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
    652 @@ -1632,10 +1841,10 @@ sigchld(int unused)
    653  void
    654  spawn(const Arg *arg)
    655  {
    656 -	if (arg->v == dmenucmd)
    657 +	if(arg->v == dmenucmd)
    658  		dmenumon[0] = '0' + selmon->num;
    659 -	if (fork() == 0) {
    660 -		if (dpy)
    661 +	if(fork() == 0) {
    662 +		if(dpy)
    663  			close(ConnectionNumber(dpy));
    664  		setsid();
    665  		execvp(((char **)arg->v)[0], (char **)arg->v);
    666 @@ -1692,18 +1901,29 @@ tile(Monitor *m)
    667  void
    668  togglebar(const Arg *arg)
    669  {
    670 -	selmon->showbar = !selmon->showbar;
    671 +	selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
    672  	updatebarpos(selmon);
    673  	XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
    674  	arrange(selmon);
    675  }
    676  
    677  void
    678 +tabmode(const Arg *arg)
    679 +{
    680 +	if(arg && arg->i >= 0)
    681 +		selmon->showtab = arg->ui % showtab_nmodes;
    682 +	else
    683 +		selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
    684 +	arrange(selmon);
    685 +}
    686 +
    687 +
    688 +void
    689  togglefloating(const Arg *arg)
    690  {
    691 -	if (!selmon->sel)
    692 +	if(!selmon->sel)
    693  		return;
    694 -	if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
    695 +	if(selmon->sel->isfullscreen) /* no support for fullscreen windows */
    696  		return;
    697  	selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
    698  	if (selmon->sel->isfloating)
    699 @@ -1731,9 +1951,29 @@ void
    700  toggleview(const Arg *arg)
    701  {
    702  	unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
    703 +	int i;
    704  
    705  	if (newtagset) {
    706 +		if(newtagset == ~0) {
    707 +			selmon->pertag->prevtag = selmon->pertag->curtag;
    708 +			selmon->pertag->curtag = 0;
    709 +		}
    710 +		/* test if the user did not select the same tag */
    711 +		if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
    712 +			selmon->pertag->prevtag = selmon->pertag->curtag;
    713 +			for (i=0; !(newtagset & 1 << i); i++) ;
    714 +			selmon->pertag->curtag = i + 1;
    715 +		}
    716  		selmon->tagset[selmon->seltags] = newtagset;
    717 +
    718 +		/* apply settings for this view */
    719 +		selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
    720 +		selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
    721 +		selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
    722 +		selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
    723 +		selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
    724 +		if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
    725 +			togglebar(NULL);
    726  		focus(NULL);
    727  		arrange(selmon);
    728  	}
    729 @@ -1744,7 +1984,7 @@ unfocus(Client *c, int setfocus)
    730  {
    731  	if (!c)
    732  		return;
    733 -	grabbuttons(c, 0);
    734 +	grabbuttons(c, False);
    735  	XSetWindowBorder(dpy, c->win, scheme[SchemeNorm].border->pix);
    736  	if (setfocus) {
    737  		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
    738 @@ -1809,20 +2049,44 @@ updatebars(void)
    739  		                          CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
    740  		XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
    741  		XMapRaised(dpy, m->barwin);
    742 +		m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
    743 +					  CopyFromParent, DefaultVisual(dpy, screen),
    744 +					  CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
    745 +		XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
    746 +		XMapRaised(dpy, m->tabwin);
    747  	}
    748  }
    749  
    750  void
    751  updatebarpos(Monitor *m)
    752  {
    753 +	Client *c;
    754 +	int nvis = 0;
    755 +	
    756  	m->wy = m->my;
    757  	m->wh = m->mh;
    758  	if (m->showbar) {
    759  		m->wh -= bh;
    760  		m->by = m->topbar ? m->wy : m->wy + m->wh;
    761 -		m->wy = m->topbar ? m->wy + bh : m->wy;
    762 -	} else
    763 +		if ( m->topbar )
    764 +			m->wy += bh;
    765 +	} else {
    766  		m->by = -bh;
    767 +	}
    768 +
    769 +	for(c = m->clients; c; c = c->next){
    770 +	  if(ISVISIBLE(c)) ++nvis;
    771 +	}
    772 +
    773 +	if(m->showtab == showtab_always
    774 +	   || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){
    775 +		m->wh -= th;
    776 +		m->ty = m->toptab ? m->wy : m->wy + m->wh;
    777 +		if ( m->toptab )
    778 +			m->wy += th;
    779 +	} else {
    780 +		m->ty = -th;
    781 +	}
    782  }
    783  
    784  void
    785 @@ -2004,9 +2268,9 @@ updatewindowtype(Client *c)
    786  	Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
    787  
    788  	if (state == netatom[NetWMFullscreen])
    789 -		setfullscreen(c, 1);
    790 +		setfullscreen(c, True);
    791  	if (wtype == netatom[NetWMWindowTypeDialog])
    792 -		c->isfloating = 1;
    793 +		c->isfloating = True;
    794  }
    795  
    796  void
    797 @@ -2031,11 +2295,33 @@ updatewmhints(Client *c)
    798  void
    799  view(const Arg *arg)
    800  {
    801 -	if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
    802 +	int i;
    803 +	unsigned int tmptag;
    804 +
    805 +	if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
    806  		return;
    807  	selmon->seltags ^= 1; /* toggle sel tagset */
    808 -	if (arg->ui & TAGMASK)
    809 +	if(arg->ui & TAGMASK) {
    810 +		selmon->pertag->prevtag = selmon->pertag->curtag;
    811  		selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
    812 +		if(arg->ui == ~0)
    813 +			selmon->pertag->curtag = 0;
    814 +		else {
    815 +			for (i=0; !(arg->ui & 1 << i); i++) ;
    816 +			selmon->pertag->curtag = i + 1;
    817 +		}
    818 +	} else {
    819 +		tmptag = selmon->pertag->prevtag;
    820 +		selmon->pertag->prevtag = selmon->pertag->curtag;
    821 +		selmon->pertag->curtag = tmptag;
    822 +	}
    823 +	selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
    824 +	selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
    825 +	selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
    826 +	selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
    827 +	selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
    828 +	if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
    829 +		togglebar(NULL);
    830  	focus(NULL);
    831  	arrange(selmon);
    832  }
    833 @@ -2063,7 +2349,7 @@ wintomon(Window w)
    834  	if (w == root && getrootptr(&x, &y))
    835  		return recttomon(x, y, 1, 1);
    836  	for (m = mons; m; m = m->next)
    837 -		if (w == m->barwin)
    838 +		if(w == m->barwin || w == m->tabwin)
    839  			return m;
    840  	if ((c = wintoclient(w)))
    841  		return c->mon;