dwm-tab-v2b-pertab-56a31dc.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) 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;