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