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