sites

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

dwm-tab-v2b-pertab-56a31dc.diff (26011B)


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