diff --git a/config.def.h b/config.def.h index fd77a07..8cc91c0 100644 --- a/config.def.h +++ b/config.def.h @@ -5,6 +5,14 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ static const unsigned int snap = 32; /* snap pixel */ static const int showbar = 1; /* 0 means no bar */ static const int topbar = 1; /* 0 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 */ + static const char *fonts[] = { "monospace:size=10" }; static const char dmenufont[] = "monospace:size=10"; static const char col_gray1[] = "#222222"; @@ -18,9 +26,15 @@ static const char *colors[SchemeLast][3] = { [SchemeSel] = { col_gray4, col_cyan, col_cyan }, }; + /* tagging */ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +/* default layout per tags */ +/* The first element is for all-tag view, following i-th element corresponds to */ +/* tags[i]. Layout is referred using the layouts array index.*/ +static int def_layouts[1 + LENGTH(tags)] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + static const Rule rules[] = { /* xprop(1): * WM_CLASS(STRING) = instance, class @@ -64,6 +78,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 } }, @@ -111,5 +126,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 6687011..077d92b 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 between windows, 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 indicated 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 "never" 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 b2bc9bd..0c34020 100644 --- a/dwm.c +++ b/dwm.c @@ -65,7 +65,7 @@ enum { NetSupported, NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDialog, NetClientList, 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 { @@ -112,25 +112,35 @@ typedef struct { void (*arrange)(Monitor *); } Layout; +#define MAXTABS 50 + +typedef struct Pertag Pertag; 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]; int showbar; + int showtab; int topbar; + int 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 { @@ -165,12 +175,15 @@ static void detachstack(Client *c); 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 enternotify(XEvent *e); static void expose(XEvent *e); static void focus(Client *c); static void focusin(XEvent *e); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static void focuswin(const Arg* arg); static int getrootptr(int *x, int *y); static long getstate(Window w); static int gettextprop(Window w, Atom atom, char *text, unsigned int size); @@ -207,6 +220,7 @@ static void setup(void); static void showhide(Client *c); static void sigchld(int unused); static void spawn(const Arg *arg); +static void tabmode(const Arg *arg); static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *); @@ -241,6 +255,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 lrpad; /* sum of left and right padding for text */ static int (*xerrorxlib)(Display *, XErrorEvent *); static unsigned int numlockmask = 0; @@ -272,6 +287,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 */ + Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */ +}; + /* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; @@ -395,6 +420,8 @@ 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); @@ -444,14 +471,33 @@ buttonpress(XEvent *e) click = ClkStatusText; else click = ClkWinTitle; - } else if ((c = wintoclient(ev->window))) { + } + 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); + 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 || click == ClkTabBar) + && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg); + } } void @@ -476,8 +522,8 @@ cleanup(void) view(&a); selmon->lt[selmon->sellt] = &foo; for (m = mons; m; m = m->next) - while (m->stack) - unmanage(m->stack, 0); + while(m->stack) + unmanage(m->stack, False); XUngrabKey(dpy, AnyKey, AnyModifier, root); while (mons) cleanupmon(mons); @@ -504,6 +550,8 @@ cleanupmon(Monitor *mon) } XUnmapWindow(dpy, mon->barwin); XDestroyWindow(dpy, mon->barwin); + XUnmapWindow(dpy, mon->tabwin); + XDestroyWindow(dpy, mon->tabwin); free(mon); } @@ -525,6 +573,7 @@ clientmessage(XEvent *e) { XClientMessageEvent *cme = &e->xclient; Client *c = wintoclient(cme->window); + int i; if (!c) return; @@ -536,6 +585,8 @@ clientmessage(XEvent *e) if (!ISVISIBLE(c)) { c->mon->seltags ^= 1; c->mon->tagset[c->mon->seltags] = c->tags; + for(i=0; !(c->tags & 1 << i); i++); + view(&(Arg){.ui = 1 << i}); } pop(c); } @@ -564,11 +615,10 @@ void configurenotify(XEvent *e) { Monitor *m; - Client *c; XConfigureEvent *ev = &e->xconfigure; int dirty; - /* TODO: updategeom handling sucks, needs to be simplified */ + // TODO: updategeom handling sucks, needs to be simplified if (ev->window == root) { dirty = (sw != ev->width || sh != ev->height); sw = ev->width; @@ -576,10 +626,9 @@ configurenotify(XEvent *e) if (updategeom() || dirty) { drw_resize(drw, sw, bh); updatebars(); - for (m = mons; m; m = m->next) { - for (c = m->clients; c; c = c->next) - if (c->isfullscreen) - resizeclient(c, m->mx, m->my, m->mw, m->mh); + //refreshing display of status bar. The tab bar is handled by the arrange() + //method, which is called below + for (m = mons; m; m = m->next){ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); } focus(NULL); @@ -644,16 +693,41 @@ Monitor * createmon(void) { Monitor *m; + int i; m = ecalloc(1, sizeof(Monitor)); m->tagset[0] = m->tagset[1] = 1; m->mfact = mfact; m->nmaster = nmaster; m->showbar = showbar; + m->showtab = showtab; m->topbar = topbar; - m->lt[0] = &layouts[0]; + m->toptab = toptab; + m->ntabs = 0; + m->lt[0] = &layouts[def_layouts[1] % LENGTH(layouts)]; 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] = &layouts[def_layouts[i % LENGTH(def_layouts)] % LENGTH(layouts)]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + /* init showbar */ + m->pertag->showbars[i] = m->showbar; + + /* swap focus and zoomswap*/ + m->pertag->prevzooms[i] = NULL; + } return m; } @@ -765,6 +839,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) { + 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; + int x = 0; + int w = 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; + w = m->tab_widths[i]; + drw_setscheme(drw, (c == m->sel) ? scheme[SchemeSel] : scheme[SchemeNorm]); + drw_text(drw, x, 0, w, th, lrpad / 2, c->name, 0); + x += w; + ++i; + } + + drw_setscheme(drw, scheme[SchemeNorm]); + + /* cleans interspace between window names and current viewed tag label */ + w = m->ww - view_info_w - x; + drw_text(drw, x, 0, w, th, lrpad / 2, "", 0); + + /* view info */ + x += w; + w = view_info_w; + drw_text(drw, x, 0, w, th, lrpad / 2, view_info, 0); + + drw_map(drw, m->tabwin, 0, 0, m->ww, th); +} + +void enternotify(XEvent *e) { Client *c; @@ -789,8 +961,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 @@ -817,6 +991,7 @@ focus(Client *c) } selmon->sel = c; drawbars(); + drawtabs(); } /* there are some broken focus acquiring clients */ @@ -870,6 +1045,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) { @@ -983,7 +1171,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); } @@ -1142,7 +1330,7 @@ motionnotify(XEvent *e) if (ev->window != root) return; if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { - unfocus(selmon->sel, 1); + unfocus(selmon->sel, True); selmon = m; focus(NULL); } @@ -1162,11 +1350,13 @@ movemouse(const Arg *arg) return; if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ return; + if(c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; restack(selmon); ocx = c->x; ocy = c->y; if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) return; if (!getrootptr(&x, &y)) return; @@ -1253,12 +1443,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); @@ -1320,11 +1512,13 @@ resizemouse(const Arg *arg) return; if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ return; + if(c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; restack(selmon); ocx = c->x; ocy = c->y; if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) return; XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); do { @@ -1372,6 +1566,7 @@ restack(Monitor *m) XWindowChanges wc; drawbar(m); + drawtab(m); if (!m->sel) return; if (m->sel->isfloating || !m->lt[m->sellt]->arrange) @@ -1480,11 +1675,11 @@ sendevent(Client *c, Atom proto) void setfocus(Client *c) { - if (!c->neverfocus) { + if(!c->neverfocus) { XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); XChangeProperty(dpy, root, netatom[NetActiveWindow], - XA_WINDOW, 32, PropModeReplace, - (unsigned char *) &(c->win), 1); + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); } sendevent(c, wmatom[WMTakeFocus]); } @@ -1520,10 +1715,13 @@ setfullscreen(Client *c, int fullscreen) void setlayout(const Arg *arg) { - if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) - selmon->sellt ^= 1; - if (arg && arg->v) - selmon->lt[selmon->sellt] = (Layout *)arg->v; + 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->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); @@ -1542,7 +1740,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); } @@ -1564,6 +1762,7 @@ setup(void) die("no fonts could be loaded.\n"); lrpad = drw->fonts->h; bh = drw->fonts->h + 2; + th = bh; updategeom(); /* init atoms */ wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); @@ -1631,10 +1830,10 @@ sigchld(int unused) void spawn(const Arg *arg) { - if (arg->v == dmenucmd) + if(arg->v == dmenucmd) dmenumon[0] = '0' + selmon->num; - if (fork() == 0) { - if (dpy) + if(fork() == 0) { + if(dpy) close(ConnectionNumber(dpy)); setsid(); execvp(((char **)arg->v)[0], (char **)arg->v); @@ -1691,18 +1890,29 @@ 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; + arrange(selmon); +} + + +void togglefloating(const Arg *arg) { - if (!selmon->sel) + if(!selmon->sel) return; - if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + if(selmon->sel->isfullscreen) /* no support for fullscreen windows */ return; selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; if (selmon->sel->isfloating) @@ -1730,9 +1940,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); } @@ -1808,20 +2038,44 @@ updatebars(void) CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 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]->cursor); + 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; + } } void @@ -2003,9 +2257,9 @@ updatewindowtype(Client *c) Atom wtype = getatomprop(c, netatom[NetWMWindowType]); if (state == netatom[NetWMFullscreen]) - setfullscreen(c, 1); + setfullscreen(c, True); if (wtype == netatom[NetWMWindowTypeDialog]) - c->isfloating = 1; + c->isfloating = True; } void @@ -2030,11 +2284,33 @@ updatewmhints(Client *c) void view(const Arg *arg) { - if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + 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); } @@ -2062,7 +2338,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;