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