sites

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 39a6cca26a26c6d725bfd436157afc47c42b8eda
parent 7130deb8ac0a357609b72bc02fbede362f592aae
Author: Marcin Szamotulski <mszamot@gmail.com>
Date:   Mon,  8 Jul 2013 17:02:03 +0100

Merge branch 'master' of git://git.suckless.org/sites

Diffstat:
Adwm.suckless.org/patches/dwm-6.0-pertag-tab-v2.diff | 887+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddwm.suckless.org/patches/dwm-6.0-pertag.diff | 181-------------------------------------------------------------------------------
Adwm.suckless.org/patches/dwm-6.0-tab-v2.diff | 720+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddwm.suckless.org/patches/dwm-6.0-tab.diff | 569-------------------------------------------------------------------------------
Mdwm.suckless.org/patches/tab.md | 101++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
5 files changed, 1679 insertions(+), 779 deletions(-)

diff --git a/dwm.suckless.org/patches/dwm-6.0-pertag-tab-v2.diff b/dwm.suckless.org/patches/dwm-6.0-pertag-tab-v2.diff @@ -0,0 +1,887 @@ +diff --git a/config.def.h b/config.def.h +index 77ff358..666b9c0 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -12,6 +12,13 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ + static const Bool showbar = True; /* False means no bar */ + static const Bool topbar = True; /* False means bottom bar */ ++/* Display modes of the tab bar: never shown, always shown, shown only in */ ++/* monocle mode in presence of several windows. */ ++/* Modes after showtab_nmodes are disabled */ ++enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always}; ++static const int showtab = showtab_auto; /* Default tab bar show mode */ ++static const Bool toptab = False; /* False means bottom tab bar */ ++ + + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +@@ -25,7 +32,7 @@ static const Rule rules[] = { + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +-static const Bool resizehints = True; /* True means respect size hints in tiled resizals */ ++static const Bool resizehints = False; /* True means respect size hints in tiled resizals */ + + static const Layout layouts[] = { + /* symbol arrange function */ +@@ -54,6 +61,7 @@ static Key keys[] = { + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, ++ { MODKEY, XK_w, tabmode, {-1} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, +@@ -101,5 +109,6 @@ static Button buttons[] = { + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, ++ { ClkTabBar, 0, Button1, focuswin, {0} }, + }; + +diff --git a/dwm.1 b/dwm.1 +index 5268a06..d213208 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -19,14 +19,22 @@ layout applied. + Windows are grouped by tags. Each window can be tagged with one or multiple + tags. Selecting certain tags displays all windows with these tags. + .P +-Each screen contains a small status bar which displays all available tags, the +-layout, the title of the focused window, and the text read from the root window +-name property, if the screen is focused. A floating window is indicated with an +-empty square and a maximised floating window is indicated with a filled square +-before the windows title. The selected tags are indicated with a different +-color. The tags of the focused window are indicated with a filled square in the +-top left corner. The tags which are applied to one or more windows are +-indicated with an empty square in the top left corner. ++Each screen contains two small status bars. ++.P ++One bar displays all available tags, the layout, the title of the focused ++window, and the text read from the root window name property, if the screen is ++focused. A floating window is indicated with an empty square and a maximised ++floating window is indicated with a filled square before the windows title. The ++selected tags are indicated with a different color. The tags of the focused ++window are indicated with a filled square in the top left corner. The tags ++which are applied to one or more windows are indicated with an empty square in ++the top left corner. ++.P ++Another bar contains a tab for each window of the current view and allows ++navigation from window to window, especially in the monocle mode. The different ++display modes of this bar are described under the Mod1\-w Keybord command ++section. When a single tag is selected, that tag is recalled in the left corner ++of the tab bar. + .P + dwm draws a small border around windows to indicate the focus state. + .SH OPTIONS +@@ -43,7 +51,8 @@ command. + .TP + .B Button1 + click on a tag label to display all windows with that tag, click on the layout +-label toggles between tiled and floating layout. ++label toggles between tiled and floating layout, click on a window name in the ++tab bar brings focus to that window. + .TP + .B Button3 + click on a tag label adds/removes all windows with that tag to/from the view. +@@ -104,6 +113,12 @@ Increase master area size. + .B Mod1\-h + Decrease master area size. + .TP ++.B Mod1\-w ++Cycle over the tab bar display modes: never displayed, always displayed, ++displayed only in monocle mode when the view contains than one window (auto ++mode). Some display modes can be disabled in the configuration, config.h. In ++the default configuration only "always" and "auto" display modes are enabled. ++.TP + .B Mod1\-Return + Zooms/cycles focused window to/from master area (tiled layouts only). + .TP +diff --git a/dwm.c b/dwm.c +index 1d78655..33eb637 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -44,7 +44,7 @@ + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) + #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ +- * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) ++ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) + #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define MAX(A, B) ((A) > (B) ? (A) : (B)) +@@ -62,7 +62,7 @@ enum { NetSupported, NetWMName, NetWMState, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +-enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ++enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + + typedef union { +@@ -102,6 +102,7 @@ typedef struct { + unsigned long norm[ColLast]; + unsigned long sel[ColLast]; + Drawable drawable; ++ Drawable tabdrawable; + GC gc; + struct { + int ascent; +@@ -124,25 +125,36 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++typedef struct Pertag Pertag; ++ ++#define MAXTABS 50 ++ + struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* bar geometry */ ++ int ty; /* tab bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + Bool showbar; ++ Bool showtab; + Bool topbar; ++ Bool toptab; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; ++ Window tabwin; ++ int ntabs; ++ int tab_widths[MAXTABS]; + const Layout *lt[2]; ++ Pertag *pertag; + }; + + typedef struct { +@@ -178,11 +190,15 @@ static void die(const char *errstr, ...); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawtab(Monitor *m); ++static void drawtabs(void); + static void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]); +-static void drawtext(const char *text, unsigned long col[ColLast], Bool invert); ++static void drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert); ++//static void drawtabtext(const char *text, unsigned long col[ColLast], Bool invert); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); ++static void focuswin(const Arg* arg); + static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); +@@ -229,6 +245,7 @@ static void tagmon(const Arg *arg); + static int textnw(const char *text, unsigned int len); + static void tile(Monitor *); + static void togglebar(const Arg *arg); ++static void tabmode(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); +@@ -258,6 +275,7 @@ static char stext[256]; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ ++static int th = 0; /* tab bar geometry */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; + static void (*handler[LASTEvent]) (XEvent *) = { +@@ -287,6 +305,16 @@ static Window root; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++struct Pertag { ++ unsigned int curtag, prevtag; /* current and previous tag */ ++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ ++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ ++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ ++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ ++ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ ++ Bool showtabs[LENGTH(tags) + 1]; /* display tab bar for the current tag */ ++}; ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -405,6 +433,10 @@ arrange(Monitor *m) { + + void + arrangemon(Monitor *m) { ++ /* Following two lines needed for the auto mode of the tab bar */ ++ updatebarpos(m); ++ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th); ++ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if(m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +@@ -454,14 +486,32 @@ buttonpress(XEvent *e) { + else + click = ClkWinTitle; + } ++ if(ev->window == selmon->tabwin) { ++ i = 0; x = 0; ++ for(c = selmon->clients; c; c = c->next){ ++ if(!ISVISIBLE(c)) continue; ++ x += selmon->tab_widths[i]; ++ if (ev->x > x) ++ ++i; ++ else ++ break; ++ if(i >= m->ntabs) break; ++ } ++ if(c) { ++ click = ClkTabBar; ++ arg.ui = i; ++ } ++ } + else if((c = wintoclient(ev->window))) { + focus(c); + click = ClkClientWin; + } + for(i = 0; i < LENGTH(buttons); i++) + if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button +- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) +- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){ ++ buttons[i].func(((click == ClkTagBar || click == ClkTabBar) ++ && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg); ++ } + } + + void +@@ -491,6 +541,7 @@ cleanup(void) { + XFreeFont(dpy, dc.font.xfont); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XFreePixmap(dpy, dc.drawable); ++ XFreePixmap(dpy, dc.tabdrawable); + XFreeGC(dpy, dc.gc); + XFreeCursor(dpy, cursor[CurNormal]); + XFreeCursor(dpy, cursor[CurResize]); +@@ -513,6 +564,8 @@ cleanupmon(Monitor *mon) { + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ XUnmapWindow(dpy, mon->tabwin); ++ XDestroyWindow(dpy, mon->tabwin); + free(mon); + } + +@@ -538,7 +591,7 @@ clientmessage(XEvent *e) { + if(cme->message_type == netatom[NetWMState]) { + if(cme->data.l[1] == netatom[NetWMFullscreen] || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ +- || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); ++ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } + else if(cme->message_type == netatom[NetActiveWindow]) { + if(!ISVISIBLE(c)) { +@@ -581,9 +634,13 @@ configurenotify(XEvent *e) { + if(dc.drawable != 0) + XFreePixmap(dpy, dc.drawable); + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); ++ if(dc.tabdrawable != 0) ++ XFreePixmap(dpy, dc.tabdrawable); ++ dc.tabdrawable = XCreatePixmap(dpy, root, sw, th, DefaultDepth(dpy, screen)); + updatebars(); +- for(m = mons; m; m = m->next) ++ for(m = mons; m; m = m->next){ + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ } + focus(NULL); + arrange(NULL); + } +@@ -646,6 +703,7 @@ configurerequest(XEvent *e) { + Monitor * + createmon(void) { + Monitor *m; ++ int i; + + if(!(m = (Monitor *)calloc(1, sizeof(Monitor)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Monitor)); +@@ -653,10 +711,34 @@ createmon(void) { + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; ++ m->showtab = showtab; + m->topbar = topbar; ++ m->toptab = toptab; ++ m->ntabs = 0; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); ++ m->pertag->curtag = m->pertag->prevtag = 1; ++ for(i=0; i <= LENGTH(tags); i++) { ++ /* init nmaster */ ++ m->pertag->nmasters[i] = m->nmaster; ++ ++ /* init mfacts */ ++ m->pertag->mfacts[i] = m->mfact; ++ ++ /* init layouts */ ++ m->pertag->ltidxs[i][0] = m->lt[0]; ++ m->pertag->ltidxs[i][1] = m->lt[1]; ++ m->pertag->sellts[i] = m->sellt; ++ ++ /* init showbar */ ++ m->pertag->showbars[i] = m->showbar; ++ ++ /* init showtab */ ++ m->pertag->showtabs[i] = m->showtab; ++ } + return m; + } + +@@ -731,13 +813,13 @@ drawbar(Monitor *m) { + for(i = 0; i < LENGTH(tags); i++) { + dc.w = TEXTW(tags[i]); + col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm; +- drawtext(tags[i], col, urg & 1 << i); ++ drawtext(dc.drawable, tags[i], col, urg & 1 << i); + drawsquare(m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- occ & 1 << i, urg & 1 << i, col); ++ occ & 1 << i, urg & 1 << i, col); + dc.x += dc.w; + } + dc.w = blw = TEXTW(m->ltsymbol); +- drawtext(m->ltsymbol, dc.norm, False); ++ drawtext(dc.drawable, m->ltsymbol, dc.norm, False); + dc.x += dc.w; + x = dc.x; + if(m == selmon) { /* status is only drawn on selected monitor */ +@@ -747,19 +829,20 @@ drawbar(Monitor *m) { + dc.x = x; + dc.w = m->ww - x; + } +- drawtext(stext, dc.norm, False); ++ drawtext(dc.drawable, stext, dc.norm, False); + } + else + dc.x = m->ww; + if((dc.w = dc.x - x) > bh) { + dc.x = x; + if(m->sel) { +- col = m == selmon ? dc.sel : dc.norm; +- drawtext(m->sel->name, col, False); ++ // col = m == selmon ? dc.sel : dc.norm; ++ // drawtext(dc.drawable, m->sel->name, col, False); ++ drawtext(dc.drawable, m->sel->name, dc.norm, False); + drawsquare(m->sel->isfixed, m->sel->isfloating, False, col); + } + else +- drawtext(NULL, dc.norm, False); ++ drawtext(dc.drawable, NULL, dc.norm, False); + } + XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0, m->ww, bh, 0, 0); + XSync(dpy, False); +@@ -774,6 +857,104 @@ drawbars(void) { + } + + void ++drawtabs(void) { ++ Monitor *m; ++ ++ for(m = mons; m; m = m->next) ++ drawtab(m); ++} ++ ++static int ++cmpint(const void *p1, const void *p2) { ++ /* The actual arguments to this function are "pointers to ++ pointers to char", but strcmp(3) arguments are "pointers ++ to char", hence the following cast plus dereference */ ++ return *((int*) p1) > * (int*) p2; ++} ++ ++ ++void ++drawtab(Monitor *m) { ++ unsigned long *col; ++ Client *c; ++ int i; ++ int itag = -1; ++ char view_info[50]; ++ int view_info_w = 0; ++ int sorted_label_widths[MAXTABS]; ++ int tot_width; ++ int maxsize = bh; ++ dc.x = 0; ++ ++ //view_info: indicate the tag which is displayed in the view ++ for(i = 0; i < LENGTH(tags); ++i){ ++ if((selmon->tagset[selmon->seltags] >> i) & 1) { ++ if(itag >=0){ //more than one tag selected ++ itag = -1; ++ break; ++ } ++ itag = i; ++ } ++ } ++ if(0 <= itag && itag < LENGTH(tags)){ ++ snprintf(view_info, sizeof view_info, "[%s]", tags[itag]); ++ } else { ++ strncpy(view_info, "[...]", sizeof view_info); ++ } ++ view_info[sizeof(view_info) - 1 ] = 0; ++ view_info_w = TEXTW(view_info); ++ tot_width = view_info_w; ++ ++ /* Calculates number of labels and their width */ ++ m->ntabs = 0; ++ for(c = m->clients; c; c = c->next){ ++ if(!ISVISIBLE(c)) continue; ++ m->tab_widths[m->ntabs] = TEXTW(c->name); ++ tot_width += m->tab_widths[m->ntabs]; ++ ++m->ntabs; ++ if(m->ntabs >= MAXTABS) break; ++ } ++ ++ if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated ++ memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); ++ qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); ++ tot_width = view_info_w; ++ for(i = 0; i < m->ntabs; ++i){ ++ if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) ++ break; ++ tot_width += sorted_label_widths[i]; ++ } ++ maxsize = (m->ww - tot_width) / (m->ntabs - i); ++ } else{ ++ maxsize = m->ww; ++ } ++ i = 0; ++ for(c = m->clients; c; c = c->next){ ++ if(!ISVISIBLE(c)) continue; ++ if(i >= m->ntabs) break; ++ if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize; ++ dc.w = m->tab_widths[i]; ++ col = (c == m->sel) ? dc.sel : dc.norm; ++ drawtext(dc.tabdrawable, c->name, col, 0); ++ dc.x += dc.w; ++ ++i; ++ } ++ ++ /* cleans interspace between window names and current viewed tag label */ ++ dc.w = m->ww - view_info_w - dc.x; ++ drawtext(dc.tabdrawable, NULL, dc.norm, 0); ++ ++ /* view info */ ++ dc.x += dc.w; ++ dc.w = view_info_w; ++ drawtext(dc.tabdrawable, view_info, dc.norm, 0); ++ ++ XCopyArea(dpy, dc.tabdrawable, m->tabwin, dc.gc, 0, 0, m->ww, th, 0, 0); ++ XSync(dpy, False); ++} ++ ++ ++void + drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) { + int x; + +@@ -785,13 +966,14 @@ drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) { + XDrawRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x, x); + } + ++ + void +-drawtext(const char *text, unsigned long col[ColLast], Bool invert) { ++drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert) { + char buf[256]; + int i, x, y, h, len, olen; + + XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]); +- XFillRectangle(dpy, dc.drawable, dc.gc, dc.x, dc.y, dc.w, dc.h); ++ XFillRectangle(dpy, drawable, dc.gc, dc.x, dc.y, dc.w, dc.h); + if(!text) + return; + olen = strlen(text); +@@ -807,11 +989,12 @@ drawtext(const char *text, unsigned long col[ColLast], Bool invert) { + for(i = len; i && i > len - 3; buf[--i] = '.'); + XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]); + if(dc.font.set) +- XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); ++ XmbDrawString(dpy, drawable, dc.font.set, dc.gc, x, y, buf, len); + else +- XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); ++ XDrawString(dpy, drawable, dc.gc, x, y, buf, len); + } + ++ + void + enternotify(XEvent *e) { + Client *c; +@@ -836,8 +1019,10 @@ expose(XEvent *e) { + Monitor *m; + XExposeEvent *ev = &e->xexpose; + +- if(ev->count == 0 && (m = wintomon(ev->window))) ++ if(ev->count == 0 && (m = wintomon(ev->window))){ + drawbar(m); ++ drawtab(m); ++ } + } + + void +@@ -862,6 +1047,7 @@ focus(Client *c) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + selmon->sel = c; + drawbars(); ++ drawtabs(); + } + + void +@@ -911,6 +1097,19 @@ focusstack(const Arg *arg) { + } + } + ++void ++focuswin(const Arg* arg){ ++ int iwin = arg->i; ++ Client* c = NULL; ++ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){ ++ if(ISVISIBLE(c)) --iwin; ++ }; ++ if(c) { ++ focus(c); ++ restack(selmon); ++ } ++} ++ + Atom + getatomprop(Client *c, Atom prop) { + int di; +@@ -919,7 +1118,7 @@ getatomprop(Client *c, Atom prop) { + Atom da, atom = None; + + if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, +- &da, &di, &dl, &dl, &p) == Success && p) { ++ &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } +@@ -954,7 +1153,7 @@ getstate(Window w) { + Atom real; + + if(XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], +- &real, &format, &n, &extra, (unsigned char **)&p) != Success) ++ &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if(n != 0) + result = *p; +@@ -999,13 +1198,13 @@ grabbuttons(Client *c, Bool focused) { + if(buttons[i].click == ClkClientWin) + for(j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, +- buttons[i].mask | modifiers[j], +- c->win, False, BUTTONMASK, +- GrabModeAsync, GrabModeSync, None, None); ++ buttons[i].mask | modifiers[j], ++ c->win, False, BUTTONMASK, ++ GrabModeAsync, GrabModeSync, None, None); + } + else + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, +- BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); ++ BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); + } + } + +@@ -1028,7 +1227,7 @@ grabkeys(void) { + + void + incnmaster(const Arg *arg) { +- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); + } + +@@ -1139,7 +1338,7 @@ manage(Window w, XWindowAttributes *wa) { + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ + c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) +- && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); ++ && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + + wc.border_width = c->bw; +@@ -1311,12 +1510,14 @@ propertynotify(XEvent *e) { + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); ++ drawtabs(); + break; + } + if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if(c == c->mon->sel) + drawbar(c->mon); ++ drawtab(c->mon); + } + if(ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); +@@ -1375,7 +1576,7 @@ resizemouse(const Arg *arg) { + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, +- None, cursor[CurResize], CurrentTime) != GrabSuccess) ++ None, cursor[CurResize], CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { +@@ -1418,6 +1619,7 @@ restack(Monitor *m) { + XWindowChanges wc; + + drawbar(m); ++ drawtab(m); + if(!m->sel) + return; + if(m->sel->isfloating || !m->lt[m->sellt]->arrange) +@@ -1529,7 +1731,7 @@ void + setfullscreen(Client *c, Bool fullscreen) { + if(fullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = True; + c->oldstate = c->isfloating; + c->oldbw = c->bw; +@@ -1540,7 +1742,7 @@ setfullscreen(Client *c, Bool fullscreen) { + } + else { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)0, 0); ++ PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = False; + c->isfloating = c->oldstate; + c->bw = c->oldbw; +@@ -1555,10 +1757,13 @@ setfullscreen(Client *c, Bool fullscreen) { + + void + setlayout(const Arg *arg) { +- if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) +- selmon->sellt ^= 1; ++ if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { ++ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ } + if(arg && arg->v) +- selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if(selmon->sel) + arrange(selmon); +@@ -1576,7 +1781,7 @@ setmfact(const Arg *arg) { + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if(f < 0.1 || f > 0.9) + return; +- selmon->mfact = f; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); + } + +@@ -1594,6 +1799,7 @@ setup(void) { + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + bh = dc.h = dc.font.height + 2; ++ th = bh; + updategeom(); + /* init atoms */ + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); +@@ -1619,6 +1825,7 @@ setup(void) { + dc.sel[ColBG] = getcolor(selbgcolor); + dc.sel[ColFG] = getcolor(selfgcolor); + dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), bh, DefaultDepth(dpy, screen)); ++ dc.tabdrawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), th, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + if(!dc.font.set) +@@ -1632,7 +1839,7 @@ setup(void) { + /* select for events */ + wa.cursor = cursor[CurNormal]; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|PointerMotionMask +- |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; ++ |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); +@@ -1729,13 +1936,24 @@ tile(Monitor *m) { + + void + togglebar(const Arg *arg) { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); + } + + void ++tabmode(const Arg *arg) { ++ if(arg && arg->i >= 0) ++ selmon->showtab = arg->ui % showtab_nmodes; ++ else ++ selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes; ++ selmon->pertag->showtabs[selmon->pertag->curtag] = selmon->showtab; ++ arrange(selmon); ++} ++ ++ ++void + togglefloating(const Arg *arg) { + if(!selmon->sel) + return; +@@ -1763,9 +1981,31 @@ toggletag(const Arg *arg) { + void + toggleview(const Arg *arg) { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); ++ int i; + + if(newtagset) { ++ if(newtagset == ~0) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = 0; ++ } ++ /* test if the user did not select the same tag */ ++ if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ for (i=0; !(newtagset & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } + selmon->tagset[selmon->seltags] = newtagset; ++ ++ /* apply settings for this view */ ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); ++ if (selmon->showtab != selmon->pertag->showtabs[selmon->pertag->curtag]) ++ tabmode(NULL); + focus(NULL); + arrange(selmon); + } +@@ -1828,24 +2068,47 @@ updatebars(void) { + }; + for(m = mons; m; m = m->next) { + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]); + XMapRaised(dpy, m->barwin); ++ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]); ++ XMapRaised(dpy, m->tabwin); + } + } + + void + updatebarpos(Monitor *m) { ++ Client *c; ++ int nvis = 0; ++ + m->wy = m->my; + m->wh = m->mh; + if(m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; +- m->wy = m->topbar ? m->wy + bh : m->wy; +- } +- else ++ if ( m->topbar ) ++ m->wy += bh; ++ } else { + m->by = -bh; ++ } ++ ++ for(c = m->clients; c; c = c->next){ ++ if(ISVISIBLE(c)) ++nvis; ++ } ++ ++ if(m->showtab == showtab_always ++ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){ ++ m->wh -= th; ++ m->ty = m->toptab ? m->wy : m->wy + m->wh; ++ if ( m->toptab ) ++ m->wy += th; ++ } else { ++ m->ty = -th; ++ } + } + + Bool +@@ -1992,7 +2255,7 @@ updatesizehints(Client *c) { + else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->minw && c->maxh && c->minh +- && c->maxw == c->minw && c->maxh == c->minh); ++ && c->maxw == c->minw && c->maxh == c->minh); + } + + void +@@ -2043,11 +2306,35 @@ updatewmhints(Client *c) { + + void + view(const Arg *arg) { ++ int i; ++ unsigned int tmptag; ++ + if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ +- if(arg->ui & TAGMASK) ++ if(arg->ui & TAGMASK) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ if(arg->ui == ~0) ++ selmon->pertag->curtag = 0; ++ else { ++ for (i=0; !(arg->ui & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ } else { ++ tmptag = selmon->pertag->prevtag; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = tmptag; ++ } ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); ++ if (selmon->showtab != selmon->pertag->showtabs[selmon->pertag->curtag]) ++ tabmode(NULL); + focus(NULL); + arrange(selmon); + } +@@ -2073,7 +2360,7 @@ wintomon(Window w) { + if(w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for(m = mons; m; m = m->next) +- if(w == m->barwin) ++ if(w == m->barwin || w == m->tabwin) + return m; + if((c = wintoclient(w))) + return c->mon; diff --git a/dwm.suckless.org/patches/dwm-6.0-pertag.diff b/dwm.suckless.org/patches/dwm-6.0-pertag.diff @@ -1,181 +0,0 @@ -Author: Jan Christoph Ebersbach <jceb@e-jc.de> -URL: http://dwm.suckless.org/patches/pertag -This patch keeps layout, mwfact, barpos and nmaster per tag. - -diff -r ec4baab78314 dwm.c ---- a/dwm.c Mon Dec 19 15:38:30 2011 +0100 -+++ b/dwm.c Fri Apr 06 08:23:29 2012 +0200 -@@ -124,6 +124,7 @@ - void (*arrange)(Monitor *); - } Layout; - -+typedef struct Pertag Pertag; - struct Monitor { - char ltsymbol[16]; - float mfact; -@@ -143,6 +144,7 @@ - Monitor *next; - Window barwin; - const Layout *lt[2]; -+ Pertag *pertag; - }; - - typedef struct { -@@ -287,6 +289,15 @@ - /* configuration, allows nested code to access above variables */ - #include "config.h" - -+struct Pertag { -+ unsigned int curtag, prevtag; /* current and previous tag */ -+ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ -+ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ -+ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ -+ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ -+ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ -+}; -+ - /* compile-time check if all tags fit into an unsigned int bit array. */ - struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; - -@@ -646,6 +657,7 @@ - Monitor * - createmon(void) { - Monitor *m; -+ int i; - - if(!(m = (Monitor *)calloc(1, sizeof(Monitor)))) - die("fatal: could not malloc() %u bytes\n", sizeof(Monitor)); -@@ -657,6 +669,24 @@ - m->lt[0] = &layouts[0]; - m->lt[1] = &layouts[1 % LENGTH(layouts)]; - strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); -+ if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) -+ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); -+ m->pertag->curtag = m->pertag->prevtag = 1; -+ for(i=0; i <= LENGTH(tags); i++) { -+ /* init nmaster */ -+ m->pertag->nmasters[i] = m->nmaster; -+ -+ /* init mfacts */ -+ m->pertag->mfacts[i] = m->mfact; -+ -+ /* init layouts */ -+ m->pertag->ltidxs[i][0] = m->lt[0]; -+ m->pertag->ltidxs[i][1] = m->lt[1]; -+ m->pertag->sellts[i] = m->sellt; -+ -+ /* init showbar */ -+ m->pertag->showbars[i] = m->showbar; -+ } - return m; - } - -@@ -1028,7 +1058,7 @@ - - void - incnmaster(const Arg *arg) { -- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); -+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); - arrange(selmon); - } - -@@ -1555,10 +1585,13 @@ - - void - setlayout(const Arg *arg) { -- if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) -- selmon->sellt ^= 1; -+ if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { -+ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; -+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; -+ } - if(arg && arg->v) -- selmon->lt[selmon->sellt] = (Layout *)arg->v; -+ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; -+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; - strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); - if(selmon->sel) - arrange(selmon); -@@ -1576,7 +1609,7 @@ - f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; - if(f < 0.1 || f > 0.9) - return; -- selmon->mfact = f; -+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; - arrange(selmon); - } - -@@ -1729,7 +1762,7 @@ - - void - togglebar(const Arg *arg) { -- selmon->showbar = !selmon->showbar; -+ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; - updatebarpos(selmon); - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); - arrange(selmon); -@@ -1763,9 +1796,29 @@ - void - toggleview(const Arg *arg) { - unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); -+ int i; - - if(newtagset) { -+ if(newtagset == ~0) { -+ selmon->pertag->prevtag = selmon->pertag->curtag; -+ selmon->pertag->curtag = 0; -+ } -+ /* test if the user did not select the same tag */ -+ if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) { -+ selmon->pertag->prevtag = selmon->pertag->curtag; -+ for (i=0; !(newtagset & 1 << i); i++) ; -+ selmon->pertag->curtag = i + 1; -+ } - selmon->tagset[selmon->seltags] = newtagset; -+ -+ /* apply settings for this view */ -+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; -+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; -+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; -+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; -+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; -+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) -+ togglebar(NULL); - focus(NULL); - arrange(selmon); - } -@@ -2043,11 +2096,33 @@ - - void - view(const Arg *arg) { -+ int i; -+ unsigned int tmptag; -+ - if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) - return; - selmon->seltags ^= 1; /* toggle sel tagset */ -- if(arg->ui & TAGMASK) -+ if(arg->ui & TAGMASK) { -+ selmon->pertag->prevtag = selmon->pertag->curtag; - selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; -+ if(arg->ui == ~0) -+ selmon->pertag->curtag = 0; -+ else { -+ for (i=0; !(arg->ui & 1 << i); i++) ; -+ selmon->pertag->curtag = i + 1; -+ } -+ } else { -+ tmptag = selmon->pertag->prevtag; -+ selmon->pertag->prevtag = selmon->pertag->curtag; -+ selmon->pertag->curtag = tmptag; -+ } -+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; -+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; -+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; -+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; -+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; -+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) -+ togglebar(NULL); - focus(NULL); - arrange(selmon); - } diff --git a/dwm.suckless.org/patches/dwm-6.0-tab-v2.diff b/dwm.suckless.org/patches/dwm-6.0-tab-v2.diff @@ -0,0 +1,720 @@ +diff --git a/config.def.h b/config.def.h +index 77ff358..666b9c0 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -12,6 +12,13 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ + static const Bool showbar = True; /* False means no bar */ + static const Bool topbar = True; /* False means bottom bar */ ++/* Display modes of the tab bar: never shown, always shown, shown only in */ ++/* monocle mode in presence of several windows. */ ++/* Modes after showtab_nmodes are disabled */ ++enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always}; ++static const int showtab = showtab_auto; /* Default tab bar show mode */ ++static const Bool toptab = False; /* False means bottom tab bar */ ++ + + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +@@ -25,7 +32,7 @@ static const Rule rules[] = { + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +-static const Bool resizehints = True; /* True means respect size hints in tiled resizals */ ++static const Bool resizehints = False; /* True means respect size hints in tiled resizals */ + + static const Layout layouts[] = { + /* symbol arrange function */ +@@ -54,6 +61,7 @@ static Key keys[] = { + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, ++ { MODKEY, XK_w, tabmode, {-1} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, +@@ -101,5 +109,6 @@ static Button buttons[] = { + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, ++ { ClkTabBar, 0, Button1, focuswin, {0} }, + }; + +diff --git a/dwm.1 b/dwm.1 +index 5268a06..d213208 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -19,14 +19,22 @@ layout applied. + Windows are grouped by tags. Each window can be tagged with one or multiple + tags. Selecting certain tags displays all windows with these tags. + .P +-Each screen contains a small status bar which displays all available tags, the +-layout, the title of the focused window, and the text read from the root window +-name property, if the screen is focused. A floating window is indicated with an +-empty square and a maximised floating window is indicated with a filled square +-before the windows title. The selected tags are indicated with a different +-color. The tags of the focused window are indicated with a filled square in the +-top left corner. The tags which are applied to one or more windows are +-indicated with an empty square in the top left corner. ++Each screen contains two small status bars. ++.P ++One bar displays all available tags, the layout, the title of the focused ++window, and the text read from the root window name property, if the screen is ++focused. A floating window is indicated with an empty square and a maximised ++floating window is indicated with a filled square before the windows title. The ++selected tags are indicated with a different color. The tags of the focused ++window are indicated with a filled square in the top left corner. The tags ++which are applied to one or more windows are indicated with an empty square in ++the top left corner. ++.P ++Another bar contains a tab for each window of the current view and allows ++navigation from window to window, especially in the monocle mode. The different ++display modes of this bar are described under the Mod1\-w Keybord command ++section. When a single tag is selected, that tag is recalled in the left corner ++of the tab bar. + .P + dwm draws a small border around windows to indicate the focus state. + .SH OPTIONS +@@ -43,7 +51,8 @@ command. + .TP + .B Button1 + click on a tag label to display all windows with that tag, click on the layout +-label toggles between tiled and floating layout. ++label toggles between tiled and floating layout, click on a window name in the ++tab bar brings focus to that window. + .TP + .B Button3 + click on a tag label adds/removes all windows with that tag to/from the view. +@@ -104,6 +113,12 @@ Increase master area size. + .B Mod1\-h + Decrease master area size. + .TP ++.B Mod1\-w ++Cycle over the tab bar display modes: never displayed, always displayed, ++displayed only in monocle mode when the view contains than one window (auto ++mode). Some display modes can be disabled in the configuration, config.h. In ++the default configuration only "always" and "auto" display modes are enabled. ++.TP + .B Mod1\-Return + Zooms/cycles focused window to/from master area (tiled layouts only). + .TP +diff --git a/dwm.c b/dwm.c +index 1d78655..a892b7a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -44,7 +44,7 @@ + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) + #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ +- * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) ++ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) + #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define MAX(A, B) ((A) > (B) ? (A) : (B)) +@@ -62,7 +62,7 @@ enum { NetSupported, NetWMName, NetWMState, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +-enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ++enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + + typedef union { +@@ -102,6 +102,7 @@ typedef struct { + unsigned long norm[ColLast]; + unsigned long sel[ColLast]; + Drawable drawable; ++ Drawable tabdrawable; + GC gc; + struct { + int ascent; +@@ -124,24 +125,32 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++#define MAXTABS 50 ++ + struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* bar geometry */ ++ int ty; /* tab bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + Bool showbar; ++ Bool showtab; + Bool topbar; ++ Bool toptab; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; ++ Window tabwin; ++ int ntabs; ++ int tab_widths[MAXTABS]; + const Layout *lt[2]; + }; + +@@ -178,11 +187,15 @@ static void die(const char *errstr, ...); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawtab(Monitor *m); ++static void drawtabs(void); + static void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]); +-static void drawtext(const char *text, unsigned long col[ColLast], Bool invert); ++static void drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert); ++//static void drawtabtext(const char *text, unsigned long col[ColLast], Bool invert); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); ++static void focuswin(const Arg* arg); + static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); +@@ -229,6 +242,7 @@ static void tagmon(const Arg *arg); + static int textnw(const char *text, unsigned int len); + static void tile(Monitor *); + static void togglebar(const Arg *arg); ++static void tabmode(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); +@@ -258,6 +272,7 @@ static char stext[256]; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ ++static int th = 0; /* tab bar geometry */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; + static void (*handler[LASTEvent]) (XEvent *) = { +@@ -405,6 +420,9 @@ arrange(Monitor *m) { + + void + arrangemon(Monitor *m) { ++ updatebarpos(m); ++ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th); ++ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if(m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +@@ -454,14 +472,32 @@ buttonpress(XEvent *e) { + else + click = ClkWinTitle; + } ++ if(ev->window == selmon->tabwin) { ++ i = 0; x = 0; ++ for(c = selmon->clients; c; c = c->next){ ++ if(!ISVISIBLE(c)) continue; ++ x += selmon->tab_widths[i]; ++ if (ev->x > x) ++ ++i; ++ else ++ break; ++ if(i >= m->ntabs) break; ++ } ++ if(c) { ++ click = ClkTabBar; ++ arg.ui = i; ++ } ++ } + else if((c = wintoclient(ev->window))) { + focus(c); + click = ClkClientWin; + } + for(i = 0; i < LENGTH(buttons); i++) + if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button +- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) +- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){ ++ buttons[i].func(((click == ClkTagBar || click == ClkTabBar) ++ && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg); ++ } + } + + void +@@ -491,6 +527,7 @@ cleanup(void) { + XFreeFont(dpy, dc.font.xfont); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XFreePixmap(dpy, dc.drawable); ++ XFreePixmap(dpy, dc.tabdrawable); + XFreeGC(dpy, dc.gc); + XFreeCursor(dpy, cursor[CurNormal]); + XFreeCursor(dpy, cursor[CurResize]); +@@ -513,6 +550,8 @@ cleanupmon(Monitor *mon) { + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ XUnmapWindow(dpy, mon->tabwin); ++ XDestroyWindow(dpy, mon->tabwin); + free(mon); + } + +@@ -538,7 +577,7 @@ clientmessage(XEvent *e) { + if(cme->message_type == netatom[NetWMState]) { + if(cme->data.l[1] == netatom[NetWMFullscreen] || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ +- || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); ++ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } + else if(cme->message_type == netatom[NetActiveWindow]) { + if(!ISVISIBLE(c)) { +@@ -581,9 +620,13 @@ configurenotify(XEvent *e) { + if(dc.drawable != 0) + XFreePixmap(dpy, dc.drawable); + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); ++ if(dc.tabdrawable != 0) ++ XFreePixmap(dpy, dc.tabdrawable); ++ dc.tabdrawable = XCreatePixmap(dpy, root, sw, th, DefaultDepth(dpy, screen)); + updatebars(); +- for(m = mons; m; m = m->next) ++ for(m = mons; m; m = m->next){ + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ } + focus(NULL); + arrange(NULL); + } +@@ -653,7 +696,10 @@ createmon(void) { + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; ++ m->showtab = showtab; + m->topbar = topbar; ++ m->toptab = toptab; ++ m->ntabs = 0; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); +@@ -731,13 +777,13 @@ drawbar(Monitor *m) { + for(i = 0; i < LENGTH(tags); i++) { + dc.w = TEXTW(tags[i]); + col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm; +- drawtext(tags[i], col, urg & 1 << i); ++ drawtext(dc.drawable, tags[i], col, urg & 1 << i); + drawsquare(m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- occ & 1 << i, urg & 1 << i, col); ++ occ & 1 << i, urg & 1 << i, col); + dc.x += dc.w; + } + dc.w = blw = TEXTW(m->ltsymbol); +- drawtext(m->ltsymbol, dc.norm, False); ++ drawtext(dc.drawable, m->ltsymbol, dc.norm, False); + dc.x += dc.w; + x = dc.x; + if(m == selmon) { /* status is only drawn on selected monitor */ +@@ -747,19 +793,20 @@ drawbar(Monitor *m) { + dc.x = x; + dc.w = m->ww - x; + } +- drawtext(stext, dc.norm, False); ++ drawtext(dc.drawable, stext, dc.norm, False); + } + else + dc.x = m->ww; + if((dc.w = dc.x - x) > bh) { + dc.x = x; + if(m->sel) { +- col = m == selmon ? dc.sel : dc.norm; +- drawtext(m->sel->name, col, False); ++ // col = m == selmon ? dc.sel : dc.norm; ++ // drawtext(dc.drawable, m->sel->name, col, False); ++ drawtext(dc.drawable, m->sel->name, dc.norm, False); + drawsquare(m->sel->isfixed, m->sel->isfloating, False, col); + } + else +- drawtext(NULL, dc.norm, False); ++ drawtext(dc.drawable, NULL, dc.norm, False); + } + XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0, m->ww, bh, 0, 0); + XSync(dpy, False); +@@ -774,6 +821,104 @@ drawbars(void) { + } + + void ++drawtabs(void) { ++ Monitor *m; ++ ++ for(m = mons; m; m = m->next) ++ drawtab(m); ++} ++ ++static int ++cmpint(const void *p1, const void *p2) { ++ /* The actual arguments to this function are "pointers to ++ pointers to char", but strcmp(3) arguments are "pointers ++ to char", hence the following cast plus dereference */ ++ return *((int*) p1) > * (int*) p2; ++} ++ ++ ++void ++drawtab(Monitor *m) { ++ unsigned long *col; ++ Client *c; ++ int i; ++ int itag = -1; ++ char view_info[50]; ++ int view_info_w = 0; ++ int sorted_label_widths[MAXTABS]; ++ int tot_width; ++ int maxsize = bh; ++ dc.x = 0; ++ ++ //view_info: indicate the tag which is displayed in the view ++ for(i = 0; i < LENGTH(tags); ++i){ ++ if((selmon->tagset[selmon->seltags] >> i) & 1) { ++ if(itag >=0){ //more than one tag selected ++ itag = -1; ++ break; ++ } ++ itag = i; ++ } ++ } ++ if(0 <= itag && itag < LENGTH(tags)){ ++ snprintf(view_info, sizeof view_info, "[%s]", tags[itag]); ++ } else { ++ strncpy(view_info, "[...]", sizeof view_info); ++ } ++ view_info[sizeof(view_info) - 1 ] = 0; ++ view_info_w = TEXTW(view_info); ++ tot_width = view_info_w; ++ ++ /* Calculates number of labels and their width */ ++ m->ntabs = 0; ++ for(c = m->clients; c; c = c->next){ ++ if(!ISVISIBLE(c)) continue; ++ m->tab_widths[m->ntabs] = TEXTW(c->name); ++ tot_width += m->tab_widths[m->ntabs]; ++ ++m->ntabs; ++ if(m->ntabs >= MAXTABS) break; ++ } ++ ++ if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated ++ memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); ++ qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); ++ tot_width = view_info_w; ++ for(i = 0; i < m->ntabs; ++i){ ++ if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) ++ break; ++ tot_width += sorted_label_widths[i]; ++ } ++ maxsize = (m->ww - tot_width) / (m->ntabs - i); ++ } else{ ++ maxsize = m->ww; ++ } ++ i = 0; ++ for(c = m->clients; c; c = c->next){ ++ if(!ISVISIBLE(c)) continue; ++ if(i >= m->ntabs) break; ++ if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize; ++ dc.w = m->tab_widths[i]; ++ col = (c == m->sel) ? dc.sel : dc.norm; ++ drawtext(dc.tabdrawable, c->name, col, 0); ++ dc.x += dc.w; ++ ++i; ++ } ++ ++ /* cleans interspace between window names and current viewed tag label */ ++ dc.w = m->ww - view_info_w - dc.x; ++ drawtext(dc.tabdrawable, NULL, dc.norm, 0); ++ ++ /* view info */ ++ dc.x += dc.w; ++ dc.w = view_info_w; ++ drawtext(dc.tabdrawable, view_info, dc.norm, 0); ++ ++ XCopyArea(dpy, dc.tabdrawable, m->tabwin, dc.gc, 0, 0, m->ww, th, 0, 0); ++ XSync(dpy, False); ++} ++ ++ ++void + drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) { + int x; + +@@ -785,13 +930,14 @@ drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) { + XDrawRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x, x); + } + ++ + void +-drawtext(const char *text, unsigned long col[ColLast], Bool invert) { ++drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert) { + char buf[256]; + int i, x, y, h, len, olen; + + XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]); +- XFillRectangle(dpy, dc.drawable, dc.gc, dc.x, dc.y, dc.w, dc.h); ++ XFillRectangle(dpy, drawable, dc.gc, dc.x, dc.y, dc.w, dc.h); + if(!text) + return; + olen = strlen(text); +@@ -807,11 +953,12 @@ drawtext(const char *text, unsigned long col[ColLast], Bool invert) { + for(i = len; i && i > len - 3; buf[--i] = '.'); + XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]); + if(dc.font.set) +- XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); ++ XmbDrawString(dpy, drawable, dc.font.set, dc.gc, x, y, buf, len); + else +- XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); ++ XDrawString(dpy, drawable, dc.gc, x, y, buf, len); + } + ++ + void + enternotify(XEvent *e) { + Client *c; +@@ -836,8 +983,10 @@ expose(XEvent *e) { + Monitor *m; + XExposeEvent *ev = &e->xexpose; + +- if(ev->count == 0 && (m = wintomon(ev->window))) ++ if(ev->count == 0 && (m = wintomon(ev->window))){ + drawbar(m); ++ drawtab(m); ++ } + } + + void +@@ -862,6 +1011,7 @@ focus(Client *c) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + selmon->sel = c; + drawbars(); ++ drawtabs(); + } + + void +@@ -911,6 +1061,19 @@ focusstack(const Arg *arg) { + } + } + ++void ++focuswin(const Arg* arg){ ++ int iwin = arg->i; ++ Client* c = NULL; ++ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){ ++ if(ISVISIBLE(c)) --iwin; ++ }; ++ if(c) { ++ focus(c); ++ restack(selmon); ++ } ++} ++ + Atom + getatomprop(Client *c, Atom prop) { + int di; +@@ -919,7 +1082,7 @@ getatomprop(Client *c, Atom prop) { + Atom da, atom = None; + + if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, +- &da, &di, &dl, &dl, &p) == Success && p) { ++ &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } +@@ -954,7 +1117,7 @@ getstate(Window w) { + Atom real; + + if(XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], +- &real, &format, &n, &extra, (unsigned char **)&p) != Success) ++ &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if(n != 0) + result = *p; +@@ -999,13 +1162,13 @@ grabbuttons(Client *c, Bool focused) { + if(buttons[i].click == ClkClientWin) + for(j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, +- buttons[i].mask | modifiers[j], +- c->win, False, BUTTONMASK, +- GrabModeAsync, GrabModeSync, None, None); ++ buttons[i].mask | modifiers[j], ++ c->win, False, BUTTONMASK, ++ GrabModeAsync, GrabModeSync, None, None); + } + else + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, +- BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); ++ BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); + } + } + +@@ -1139,7 +1302,7 @@ manage(Window w, XWindowAttributes *wa) { + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ + c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) +- && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); ++ && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + + wc.border_width = c->bw; +@@ -1311,12 +1474,14 @@ propertynotify(XEvent *e) { + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); ++ drawtabs(); + break; + } + if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if(c == c->mon->sel) + drawbar(c->mon); ++ drawtab(c->mon); + } + if(ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); +@@ -1375,7 +1540,7 @@ resizemouse(const Arg *arg) { + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, +- None, cursor[CurResize], CurrentTime) != GrabSuccess) ++ None, cursor[CurResize], CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { +@@ -1418,6 +1583,7 @@ restack(Monitor *m) { + XWindowChanges wc; + + drawbar(m); ++ drawtab(m); + if(!m->sel) + return; + if(m->sel->isfloating || !m->lt[m->sellt]->arrange) +@@ -1529,7 +1695,7 @@ void + setfullscreen(Client *c, Bool fullscreen) { + if(fullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = True; + c->oldstate = c->isfloating; + c->oldbw = c->bw; +@@ -1540,7 +1706,7 @@ setfullscreen(Client *c, Bool fullscreen) { + } + else { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)0, 0); ++ PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = False; + c->isfloating = c->oldstate; + c->bw = c->oldbw; +@@ -1594,6 +1760,7 @@ setup(void) { + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + bh = dc.h = dc.font.height + 2; ++ th = bh; + updategeom(); + /* init atoms */ + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); +@@ -1619,6 +1786,7 @@ setup(void) { + dc.sel[ColBG] = getcolor(selbgcolor); + dc.sel[ColFG] = getcolor(selfgcolor); + dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), bh, DefaultDepth(dpy, screen)); ++ dc.tabdrawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), th, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + if(!dc.font.set) +@@ -1632,7 +1800,7 @@ setup(void) { + /* select for events */ + wa.cursor = cursor[CurNormal]; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|PointerMotionMask +- |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; ++ |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); +@@ -1736,6 +1904,16 @@ togglebar(const Arg *arg) { + } + + void ++tabmode(const Arg *arg) { ++ if(arg && arg->i >= 0) ++ selmon->showtab = arg->ui % showtab_nmodes; ++ else ++ selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes; ++ arrange(selmon); ++} ++ ++ ++void + togglefloating(const Arg *arg) { + if(!selmon->sel) + return; +@@ -1828,24 +2006,47 @@ updatebars(void) { + }; + for(m = mons; m; m = m->next) { + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]); + XMapRaised(dpy, m->barwin); ++ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]); ++ XMapRaised(dpy, m->tabwin); + } + } + + void + updatebarpos(Monitor *m) { ++ Client *c; ++ int nvis = 0; ++ + m->wy = m->my; + m->wh = m->mh; + if(m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; +- m->wy = m->topbar ? m->wy + bh : m->wy; +- } +- else ++ if ( m->topbar ) ++ m->wy += bh; ++ } else { + m->by = -bh; ++ } ++ ++ for(c = m->clients; c; c = c->next){ ++ if(ISVISIBLE(c)) ++nvis; ++ } ++ ++ if(m->showtab == showtab_always ++ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){ ++ m->wh -= th; ++ m->ty = m->toptab ? m->wy : m->wy + m->wh; ++ if ( m->toptab ) ++ m->wy += th; ++ } else { ++ m->ty = -th; ++ } + } + + Bool +@@ -1992,7 +2193,7 @@ updatesizehints(Client *c) { + else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->minw && c->maxh && c->minh +- && c->maxw == c->minw && c->maxh == c->minh); ++ && c->maxw == c->minw && c->maxh == c->minh); + } + + void +@@ -2073,7 +2274,7 @@ wintomon(Window w) { + if(w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for(m = mons; m; m = m->next) +- if(w == m->barwin) ++ if(w == m->barwin || w == m->tabwin) + return m; + if((c = wintoclient(w))) + return c->mon; diff --git a/dwm.suckless.org/patches/dwm-6.0-tab.diff b/dwm.suckless.org/patches/dwm-6.0-tab.diff @@ -1,569 +0,0 @@ -diff -up dwm-6.0-orig/config.def.h dwm-6.0-tab/config.def.h ---- dwm-6.0-orig/config.def.h 2011-12-19 16:02:46.000000000 +0100 -+++ dwm-6.0-tab/config.def.h 2013-06-23 00:22:50.000000000 +0200 -@@ -12,6 +12,9 @@ static const unsigned int borderpx = 1; - static const unsigned int snap = 32; /* snap pixel */ - static const Bool showbar = True; /* False means no bar */ - static const Bool topbar = True; /* False means bottom bar */ -+static const Bool showtab = True; /* False means no tab bar */ -+static const Bool toptab = False; /* False means bottom tab bar */ -+ - - /* tagging */ - static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; -@@ -25,7 +28,7 @@ static const Rule rules[] = { - /* layout(s) */ - static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ - static const int nmaster = 1; /* number of clients in master area */ --static const Bool resizehints = True; /* True means respect size hints in tiled resizals */ -+static const Bool resizehints = False; /* True means respect size hints in tiled resizals */ - - static const Layout layouts[] = { - /* symbol arrange function */ -@@ -54,6 +57,7 @@ static Key keys[] = { - { MODKEY, XK_p, spawn, {.v = dmenucmd } }, - { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, - { MODKEY, XK_b, togglebar, {0} }, -+ { MODKEY, XK_w, toggletab, {0} }, - { MODKEY, XK_j, focusstack, {.i = +1 } }, - { MODKEY, XK_k, focusstack, {.i = -1 } }, - { MODKEY, XK_i, incnmaster, {.i = +1 } }, -@@ -101,5 +105,6 @@ static Button buttons[] = { - { ClkTagBar, 0, Button3, toggleview, {0} }, - { ClkTagBar, MODKEY, Button1, tag, {0} }, - { ClkTagBar, MODKEY, Button3, toggletag, {0} }, -+ { ClkTabBar, 0, Button1, focuswin, {0} }, - }; - -diff -up dwm-6.0-orig/dwm.1 dwm-6.0-tab/dwm.1 ---- dwm-6.0-orig/dwm.1 2011-12-19 16:02:46.000000000 +0100 -+++ dwm-6.0-tab/dwm.1 2013-06-23 01:25:12.000000000 +0200 -@@ -19,14 +19,17 @@ layout applied. - Windows are grouped by tags. Each window can be tagged with one or multiple - tags. Selecting certain tags displays all windows with these tags. - .P --Each screen contains a small status bar which displays all available tags, the --layout, the title of the focused window, and the text read from the root window --name property, if the screen is focused. A floating window is indicated with an --empty square and a maximised floating window is indicated with a filled square --before the windows title. The selected tags are indicated with a different --color. The tags of the focused window are indicated with a filled square in the --top left corner. The tags which are applied to one or more windows are --indicated with an empty square in the top left corner. -+Each screen contains two small status bars. One bar displays all available tags, -+the layout, the title of the focused window, and the text read from the root -+window name property, if the screen is focused. A floating window is indicated -+with an empty square and a maximised floating window is indicated with a filled -+square before the windows title. The selected tags are indicated with a -+different color. The tags of the focused window are indicated with a filled -+square in the top left corner. The tags which are applied to one or more -+windows are indicated with an empty square in the top left corner. Another bar -+contains a tab for each window of the current view and allows navigation from -+window to window, especially in the monocle mode. When a single tag is selected, -+that tag is recalled in the left corner of the tab bar. - .P - dwm draws a small border around windows to indicate the focus state. - .SH OPTIONS -@@ -43,7 +46,8 @@ command. - .TP - .B Button1 - click on a tag label to display all windows with that tag, click on the layout --label toggles between tiled and floating layout. -+label toggles between tiled and floating layout, click on a window name in the -+tab bar brings focus to that window. - .TP - .B Button3 - click on a tag label adds/removes all windows with that tag to/from the view. -@@ -104,6 +108,9 @@ Increase master area size. - .B Mod1\-h - Decrease master area size. - .TP -+.B Mod1\-w -+Toggle the tab bar. -+.TP - .B Mod1\-Return - Zooms/cycles focused window to/from master area (tiled layouts only). - .TP -diff -up dwm-6.0-orig/dwm.c dwm-6.0-tab/dwm.c ---- dwm-6.0-orig/dwm.c 2011-12-19 16:02:46.000000000 +0100 -+++ dwm-6.0-tab/dwm.c 2013-06-23 16:53:31.000000000 +0200 -@@ -62,7 +62,7 @@ enum { NetSupported, NetWMName, NetWMSta - NetWMFullscreen, NetActiveWindow, NetWMWindowType, - NetWMWindowTypeDialog, NetLast }; /* EWMH atoms */ - enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ --enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, -+enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, - ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ - - typedef union { -@@ -102,6 +102,7 @@ typedef struct { - unsigned long norm[ColLast]; - unsigned long sel[ColLast]; - Drawable drawable; -+ Drawable tabdrawable; - GC gc; - struct { - int ascent; -@@ -124,24 +125,32 @@ typedef struct { - void (*arrange)(Monitor *); - } Layout; - -+#define MAXTABS 50 -+ - struct Monitor { - char ltsymbol[16]; - float mfact; - int nmaster; - int num; - int by; /* bar geometry */ -+ int ty; /* tab bar geometry */ - int mx, my, mw, mh; /* screen size */ - int wx, wy, ww, wh; /* window area */ - unsigned int seltags; - unsigned int sellt; - unsigned int tagset[2]; - Bool showbar; -+ Bool showtab; - Bool topbar; -+ Bool toptab; - Client *clients; - Client *sel; - Client *stack; - Monitor *next; - Window barwin; -+ Window tabwin; -+ int ntabs; -+ int tab_widths[MAXTABS]; - const Layout *lt[2]; - }; - -@@ -178,11 +187,15 @@ static void die(const char *errstr, ...) - static Monitor *dirtomon(int dir); - static void drawbar(Monitor *m); - static void drawbars(void); -+static void drawtab(Monitor *m); -+static void drawtabs(void); - static void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]); --static void drawtext(const char *text, unsigned long col[ColLast], Bool invert); -+static void drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert); -+//static void drawtabtext(const char *text, unsigned long col[ColLast], Bool invert); - static void enternotify(XEvent *e); - static void expose(XEvent *e); - static void focus(Client *c); -+static void focuswin(const Arg* arg); - static void focusin(XEvent *e); - static void focusmon(const Arg *arg); - static void focusstack(const Arg *arg); -@@ -229,6 +242,7 @@ static void tagmon(const Arg *arg); - static int textnw(const char *text, unsigned int len); - static void tile(Monitor *); - static void togglebar(const Arg *arg); -+static void toggletab(const Arg *arg); - static void togglefloating(const Arg *arg); - static void toggletag(const Arg *arg); - static void toggleview(const Arg *arg); -@@ -258,6 +272,7 @@ static char stext[256]; - static int screen; - static int sw, sh; /* X display screen geometry width, height */ - static int bh, blw = 0; /* bar geometry */ -+static int th = 0; /* tab bar geometry */ - static int (*xerrorxlib)(Display *, XErrorEvent *); - static unsigned int numlockmask = 0; - static void (*handler[LASTEvent]) (XEvent *) = { -@@ -454,14 +469,32 @@ buttonpress(XEvent *e) { - else - click = ClkWinTitle; - } -+ if(ev->window == selmon->tabwin) { -+ i = 0; x = 0; -+ for(c = selmon->clients; c; c = c->next){ -+ if(!ISVISIBLE(c)) continue; -+ x += selmon->tab_widths[i]; -+ if (ev->x > x) -+ ++i; -+ else -+ break; -+ if(i >= m->ntabs) break; -+ } -+ if(c) { -+ click = ClkTabBar; -+ arg.ui = i; -+ } -+ } - else if((c = wintoclient(ev->window))) { - focus(c); - click = ClkClientWin; - } - for(i = 0; i < LENGTH(buttons); i++) - if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button -- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) -- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); -+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){ -+ buttons[i].func(((click == ClkTagBar || click == ClkTabBar) -+ && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg); -+ } - } - - void -@@ -491,6 +524,7 @@ cleanup(void) { - XFreeFont(dpy, dc.font.xfont); - XUngrabKey(dpy, AnyKey, AnyModifier, root); - XFreePixmap(dpy, dc.drawable); -+ XFreePixmap(dpy, dc.tabdrawable); - XFreeGC(dpy, dc.gc); - XFreeCursor(dpy, cursor[CurNormal]); - XFreeCursor(dpy, cursor[CurResize]); -@@ -513,6 +547,8 @@ cleanupmon(Monitor *mon) { - } - XUnmapWindow(dpy, mon->barwin); - XDestroyWindow(dpy, mon->barwin); -+ XUnmapWindow(dpy, mon->tabwin); -+ XDestroyWindow(dpy, mon->tabwin); - free(mon); - } - -@@ -581,9 +617,14 @@ configurenotify(XEvent *e) { - if(dc.drawable != 0) - XFreePixmap(dpy, dc.drawable); - dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); -+ if(dc.tabdrawable != 0) -+ XFreePixmap(dpy, dc.tabdrawable); -+ dc.tabdrawable = XCreatePixmap(dpy, root, sw, th, DefaultDepth(dpy, screen)); - updatebars(); -- for(m = mons; m; m = m->next) -+ for(m = mons; m; m = m->next){ - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); -+ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th); -+ } - focus(NULL); - arrange(NULL); - } -@@ -653,7 +694,10 @@ createmon(void) { - m->mfact = mfact; - m->nmaster = nmaster; - m->showbar = showbar; -+ m->showtab = showtab; - m->topbar = topbar; -+ m->toptab = toptab; -+ m->ntabs = 0; - m->lt[0] = &layouts[0]; - m->lt[1] = &layouts[1 % LENGTH(layouts)]; - strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); -@@ -731,13 +775,13 @@ drawbar(Monitor *m) { - for(i = 0; i < LENGTH(tags); i++) { - dc.w = TEXTW(tags[i]); - col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm; -- drawtext(tags[i], col, urg & 1 << i); -+ drawtext(dc.drawable, tags[i], col, urg & 1 << i); - drawsquare(m == selmon && selmon->sel && selmon->sel->tags & 1 << i, - occ & 1 << i, urg & 1 << i, col); - dc.x += dc.w; - } - dc.w = blw = TEXTW(m->ltsymbol); -- drawtext(m->ltsymbol, dc.norm, False); -+ drawtext(dc.drawable, m->ltsymbol, dc.norm, False); - dc.x += dc.w; - x = dc.x; - if(m == selmon) { /* status is only drawn on selected monitor */ -@@ -747,19 +791,20 @@ drawbar(Monitor *m) { - dc.x = x; - dc.w = m->ww - x; - } -- drawtext(stext, dc.norm, False); -+ drawtext(dc.drawable, stext, dc.norm, False); - } - else - dc.x = m->ww; - if((dc.w = dc.x - x) > bh) { - dc.x = x; - if(m->sel) { -- col = m == selmon ? dc.sel : dc.norm; -- drawtext(m->sel->name, col, False); -+ // col = m == selmon ? dc.sel : dc.norm; -+ // drawtext(dc.drawable, m->sel->name, col, False); -+ drawtext(dc.drawable, m->sel->name, dc.norm, False); - drawsquare(m->sel->isfixed, m->sel->isfloating, False, col); - } - else -- drawtext(NULL, dc.norm, False); -+ drawtext(dc.drawable, NULL, dc.norm, False); - } - XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0, m->ww, bh, 0, 0); - XSync(dpy, False); -@@ -774,6 +819,104 @@ drawbars(void) { - } - - void -+drawtabs(void) { -+ Monitor *m; -+ -+ for(m = mons; m; m = m->next) -+ drawtab(m); -+} -+ -+static int -+cmpint(const void *p1, const void *p2) { -+ /* The actual arguments to this function are "pointers to -+ pointers to char", but strcmp(3) arguments are "pointers -+ to char", hence the following cast plus dereference */ -+ return *((int*) p1) > * (int*) p2; -+} -+ -+ -+void -+drawtab(Monitor *m) { -+ unsigned long *col; -+ Client *c; -+ int i; -+ int itag = -1; -+ char view_info[50]; -+ int view_info_w = 0; -+ int sorted_label_widths[MAXTABS]; -+ int tot_width; -+ int maxsize = bh; -+ dc.x = 0; -+ -+ //view_info: indicate the tag which is displayed in the view -+ for(i = 0; i < LENGTH(tags); ++i){ -+ if((selmon->tagset[selmon->seltags] >> i) & 1) { -+ if(itag >=0){ //more than one tag selected -+ itag = -1; -+ break; -+ } -+ itag = i; -+ } -+ } -+ if(0 <= itag && itag < LENGTH(tags)){ -+ snprintf(view_info, sizeof view_info, "[%s]", tags[itag]); -+ } else { -+ strncpy(view_info, "[...]", sizeof view_info); -+ } -+ view_info[sizeof(view_info) - 1 ] = 0; -+ view_info_w = TEXTW(view_info); -+ tot_width = view_info_w; -+ -+ /* Calculates number of labels and their width */ -+ m->ntabs = 0; -+ for(c = m->clients; c; c = c->next){ -+ if(!ISVISIBLE(c)) continue; -+ m->tab_widths[m->ntabs] = TEXTW(c->name); -+ tot_width += m->tab_widths[m->ntabs]; -+ ++m->ntabs; -+ if(m->ntabs >= MAXTABS) break; -+ } -+ -+ if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated -+ memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); -+ qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); -+ tot_width = view_info_w; -+ for(i = 0; i < m->ntabs; ++i){ -+ if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) -+ break; -+ tot_width += sorted_label_widths[i]; -+ } -+ maxsize = (m->ww - tot_width) / (m->ntabs - i); -+ } else{ -+ maxsize = m->ww; -+ } -+ i = 0; -+ for(c = m->clients; c; c = c->next){ -+ if(!ISVISIBLE(c)) continue; -+ if(i >= m->ntabs) break; -+ if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize; -+ dc.w = m->tab_widths[i]; -+ col = (c == m->sel) ? dc.sel : dc.norm; -+ drawtext(dc.tabdrawable, c->name, col, 0); -+ dc.x += dc.w; -+ ++i; -+ } -+ -+ /* cleans interspace between window names and current viewed tag label */ -+ dc.w = m->ww - view_info_w - dc.x; -+ drawtext(dc.tabdrawable, NULL, dc.norm, 0); -+ -+ /* view info */ -+ dc.x += dc.w; -+ dc.w = view_info_w; -+ drawtext(dc.tabdrawable, view_info, dc.norm, 0); -+ -+ XCopyArea(dpy, dc.tabdrawable, m->tabwin, dc.gc, 0, 0, m->ww, th, 0, 0); -+ XSync(dpy, False); -+} -+ -+ -+void - drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) { - int x; - -@@ -785,13 +928,14 @@ drawsquare(Bool filled, Bool empty, Bool - XDrawRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x, x); - } - -+ - void --drawtext(const char *text, unsigned long col[ColLast], Bool invert) { -+drawtext(Drawable drawable, const char *text, unsigned long col[ColLast], Bool invert) { - char buf[256]; - int i, x, y, h, len, olen; - - XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]); -- XFillRectangle(dpy, dc.drawable, dc.gc, dc.x, dc.y, dc.w, dc.h); -+ XFillRectangle(dpy, drawable, dc.gc, dc.x, dc.y, dc.w, dc.h); - if(!text) - return; - olen = strlen(text); -@@ -807,11 +951,12 @@ drawtext(const char *text, unsigned long - for(i = len; i && i > len - 3; buf[--i] = '.'); - XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]); - if(dc.font.set) -- XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); -+ XmbDrawString(dpy, drawable, dc.font.set, dc.gc, x, y, buf, len); - else -- XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); -+ XDrawString(dpy, drawable, dc.gc, x, y, buf, len); - } - -+ - void - enternotify(XEvent *e) { - Client *c; -@@ -836,8 +981,10 @@ expose(XEvent *e) { - Monitor *m; - XExposeEvent *ev = &e->xexpose; - -- if(ev->count == 0 && (m = wintomon(ev->window))) -+ if(ev->count == 0 && (m = wintomon(ev->window))){ - drawbar(m); -+ drawtab(m); -+ } - } - - void -@@ -862,6 +1009,7 @@ focus(Client *c) { - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); - selmon->sel = c; - drawbars(); -+ drawtabs(); - } - - void -@@ -911,6 +1059,19 @@ focusstack(const Arg *arg) { - } - } - -+void -+focuswin(const Arg* arg){ -+ int iwin = arg->i; -+ Client* c = NULL; -+ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){ -+ if(ISVISIBLE(c)) --iwin; -+ }; -+ if(c) { -+ focus(c); -+ restack(selmon); -+ } -+} -+ - Atom - getatomprop(Client *c, Atom prop) { - int di; -@@ -1311,12 +1472,14 @@ propertynotify(XEvent *e) { - case XA_WM_HINTS: - updatewmhints(c); - drawbars(); -+ drawtabs(); - break; - } - if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { - updatetitle(c); - if(c == c->mon->sel) - drawbar(c->mon); -+ drawtab(c->mon); - } - if(ev->atom == netatom[NetWMWindowType]) - updatewindowtype(c); -@@ -1418,6 +1581,7 @@ restack(Monitor *m) { - XWindowChanges wc; - - drawbar(m); -+ drawtab(m); - if(!m->sel) - return; - if(m->sel->isfloating || !m->lt[m->sellt]->arrange) -@@ -1594,6 +1758,7 @@ setup(void) { - sw = DisplayWidth(dpy, screen); - sh = DisplayHeight(dpy, screen); - bh = dc.h = dc.font.height + 2; -+ th = bh; - updategeom(); - /* init atoms */ - wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); -@@ -1619,6 +1784,7 @@ setup(void) { - dc.sel[ColBG] = getcolor(selbgcolor); - dc.sel[ColFG] = getcolor(selfgcolor); - dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), bh, DefaultDepth(dpy, screen)); -+ dc.tabdrawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), th, DefaultDepth(dpy, screen)); - dc.gc = XCreateGC(dpy, root, 0, NULL); - XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); - if(!dc.font.set) -@@ -1736,6 +1902,15 @@ togglebar(const Arg *arg) { - } - - void -+toggletab(const Arg *arg) { -+ selmon->showtab = !selmon->showtab; -+ updatebarpos(selmon); -+ XMoveResizeWindow(dpy, selmon->tabwin, selmon->wx, selmon->ty, selmon->ww, th); -+ arrange(selmon); -+} -+ -+ -+void - togglefloating(const Arg *arg) { - if(!selmon->sel) - return; -@@ -1832,6 +2007,11 @@ updatebars(void) { - CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); - XDefineCursor(dpy, m->barwin, cursor[CurNormal]); - XMapRaised(dpy, m->barwin); -+ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), -+ CopyFromParent, DefaultVisual(dpy, screen), -+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); -+ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]); -+ XMapRaised(dpy, m->tabwin); - } - } - -@@ -1842,10 +2022,20 @@ updatebarpos(Monitor *m) { - if(m->showbar) { - m->wh -= bh; - m->by = m->topbar ? m->wy : m->wy + m->wh; -- m->wy = m->topbar ? m->wy + bh : m->wy; -+ if ( m->topbar ) -+ m->wy += bh; -+ } else { -+ m->by = -bh; -+ } -+ -+ if(m->showtab) { -+ m->wh -= th; -+ m->ty = m->toptab ? m->wy : m->wy + m->wh; -+ if ( m->toptab ) -+ m->wy += th; -+ } else { -+ m->ty = -th; - } -- else -- m->by = -bh; - } - - Bool -@@ -2073,7 +2263,7 @@ wintomon(Window w) { - if(w == root && getrootptr(&x, &y)) - return recttomon(x, y, 1, 1); - for(m = mons; m; m = m->next) -- if(w == m->barwin) -+ if(w == m->barwin || w == m->tabwin) - return m; - if((c = wintoclient(w))) - return c->mon; diff --git a/dwm.suckless.org/patches/tab.md b/dwm.suckless.org/patches/tab.md @@ -4,12 +4,15 @@ Tab Description ----------- -Add a bar at top or bottom of the screen with a label for each <i>displayed</i> -window. This feature was primarily designed for the monocle window layout. In -this mode the labels appear like tabs, transforming the layout into a -<i>tabbed</i> layout. Navigating from window to window can be done by clicking -on the window tabs or using the usual Mod1-j, Mod1-k keys. The bar can be -enabled and disabled at runtime. This patch can be used as an alternative to the +Attached tabs to the windows transforming the monocle layout into a ''tabbed'' +layout. Navigating from window to window is done by clicking on the window tabs +or using the usual Mod1-j, Mod1-k keys. The tabs are arranged in a bar on top or +at bottom of the screen, which can be displayed in the other layouts than +monocle. Three display modes can be selected at run time, auto display, +permanent display and no-display. In auto mode the tabs are displayed only with +the monocle layout and in presence of several windows. + +This patch can be used as an alternative to the [tabbed](http://tools.suckless.org/tabbed/) tool. It differs in two ways: the ''tab'' feature is limited to the monocle mode; it works with any application without requiring to support the XEmbed protocol nor to define in advance the @@ -23,49 +26,89 @@ bar display. Switch focus to a window with a mouse left-click on its tab or by using the usual Mod1-j, Mod1-k commands. Usage is also documented in the dwm man page once the patch is applied. -In the right corner of the tab bar the window ''tag'' currenlty selected for +In the right corner of the tab bar the window ''tag'' currently selected for display is recalled. This feature is interesting when the standard status bar is disabled. If multiple tags are selected for viewing then three dots are displayed without more details. -Configuration and installation +Configuration and Installation ------------------------------ -Apply to the dwm code the patch you can find in the download section below. If -you don't already have a dwm config.h file, the make command will produce one -that contains already the necessary configuration lines. If you already have -one, then you must add to it the following lines: +### Quick installation - static const Bool showtab = True; /* False means no tab bar */ - static const Bool toptab = False; /* False means bottom tab bar */ +#### Using the default configuration file -The first variable tells if the tab bar must be displayed by default; the second -one if it should be displayed on top of the screen or at bottom. +* Make sure the directory where you build dwm does not contain a config.h file; +* Apply the patch; +* Run make and make install. -If the tab bar is displayed at bottom, then a better experience will be obtained -with the variable resizehints of the config.h file set to False. This setting -prevents possible gap between the windows and the tab bar. You can find more -details about this variable and gaps between windows in the dwm FAQ. +The bar is displayed only with monocle layout when the view contains more than +one window. The section "More Options" explains how to add more display options. -In order to allow enabling and disabling the tab bar by pressing Mod1-w, add to -the `keys` array, the following element: +#### Using an existing customised configuration file - { MODKEY, XK_w, toggletab, {0} } +<ul> +<li>Apply the patch; +<li>Add the following lines to your config.h dwm configuration file: +</ul> + /* Display modes of the tab bar: never shown, always shown, shown only in */ + /* monocle mode in presence of several windows. */ + /* A mode can be disabled by moving it after the showtab_nmodes end marker */ + enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always}; + static const int showtab = showtab_auto; /* Default tab bar show mode */ + static const Bool toptab = True; /* False means bottom tab bar */ +<ul> +<li>Run make and make install. +</ul> + +The tab bar is displayed only with the monocle layout when the view contains +more than one window. The Mod1-w key and the mouse support are not included in +this configuration. Activation of these options is explained in the next +section. + +### More Options + +Pressing the key Mod1-w will cycle over the display modes of the tab bar described below with +the following element added to the `keys` array: + + { MODKEY, XK_w, tabmode, {-1} } Selection of a window by a mouse left-click on its tab is enabled by adding the following element to the `buttons` array: - { ClkTabBar, 0, Button1, focuswin, {0} }, + { ClkTabBar, 0, Button1, focuswin, {0} }, + +An example on how to insert these lines can be found in the default config file +template, config.def.h. + +The tab bar includes three display options: always, never, auto. In auto mode, +the tab bar is displayed only with the monocle layout and when the view contains +more than one window. The modes available at run time can be selected by +changing the order of the elements in the `showtab_mode` enum of the config.h +configuration file: the modes before `showtab_nmodes` are enabled, the ones +after are disabled. The default mode is specified in the `showtab` variable, it +must be one of the enabled modes. + +Note: keyboard shortcuts to switch to a given display mode can be defined by +using the `tabmode` function like in the definition of the `Mod1-w` +key provided above and passing as argument the display mode +(`showtab_never`, `showtab_always`, +`showtab_auto`) instead of -1. -An example on how to insert these line can be found in the default config file template, config.def.h. +The tab bar can be displayed on top or at bottom of the screen, which is +controlled by the 'toptab' variable. If the tab bar is displayed at bottom, then +it is recommended to set the variable `resizehints` of the config.h file to +False. This setting prevents possible gap between the windows and the tab bar. +You can find more details about this variable and gap between windows in the +dwm FAQ. Download -------- - * [dwm-6.0-tab.diff](dwm-6.0-tab.diff) - * [dwm-6.0-pertag-tab.diff](dwm-6.0-pertag-tab.diff) combined patch with the [pertag](pertag) patch from Jan Christoph Ebersbach. Follow the [link](pertag) for the description of this patch and the credits. + * [dwm-6.0-tab-v2.diff](dwm-6.0-tab-v2.diff) + * [dwm-6.0-pertag-tab-v2.diff](dwm-6.0-pertag-tab-v2.diff) combined patch with the [pertag](pertag) patch from Jan Christoph Ebersbach. Follow the [link](pertag) for the description of this patch and the credits. -Authors -------- +Author +------ * Philippe Gras - `<philippe dot gras at free dot fr>`