aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.def.h16
-rwxr-xr-xdwmbin39024 -> 41776 bytes
-rw-r--r--dwm.133
-rw-r--r--dwm.c378
-rw-r--r--dwm.obin57672 -> 63144 bytes
5 files changed, 372 insertions, 55 deletions
diff --git a/config.def.h b/config.def.h
index 77ff358..9ca435c 100644
--- a/config.def.h
+++ b/config.def.h
@@ -12,10 +12,22 @@ 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" };
+/* 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[] = {
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, True, -1 },
@@ -25,7 +37,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 +66,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 +114,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 b/dwm
index 0d3553f..e24a30c 100755
--- a/dwm
+++ b/dwm
Binary files differ
diff --git a/dwm.1 b/dwm.1
index 5268a06..23feaf7 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 c9be96b..7adffb2 100644
--- a/dwm.c
+++ b/dwm.c
@@ -45,7 +45,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))
@@ -63,7 +63,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 {
@@ -103,6 +103,7 @@ typedef struct {
unsigned long norm[ColLast];
unsigned long sel[ColLast];
Drawable drawable;
+ Drawable tabdrawable;
GC gc;
struct {
int ascent;
@@ -125,25 +126,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 {
@@ -179,11 +191,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);
@@ -230,6 +246,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);
@@ -259,6 +276,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 *) = {
@@ -288,6 +306,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]; };
@@ -406,6 +434,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);
@@ -455,14 +487,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
@@ -492,6 +542,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]);
@@ -514,6 +565,8 @@ cleanupmon(Monitor *mon) {
}
XUnmapWindow(dpy, mon->barwin);
XDestroyWindow(dpy, mon->barwin);
+ XUnmapWindow(dpy, mon->tabwin);
+ XDestroyWindow(dpy, mon->tabwin);
free(mon);
}
@@ -539,7 +592,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)) {
@@ -582,9 +635,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);
}
@@ -647,6 +704,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));
@@ -654,10 +712,35 @@ createmon(void) {
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->sellt = 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] = 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;
}
@@ -732,13 +815,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 */
@@ -748,19 +831,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);
@@ -775,6 +859,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;
@@ -786,13 +968,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);
@@ -808,11 +991,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;
@@ -837,8 +1021,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
@@ -863,6 +1049,7 @@ focus(Client *c) {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
selmon->sel = c;
drawbars();
+ drawtabs();
}
void
@@ -912,6 +1099,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;
@@ -920,7 +1120,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);
}
@@ -955,7 +1155,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;
@@ -1000,13 +1200,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);
}
}
@@ -1029,7 +1229,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);
}
@@ -1140,7 +1340,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;
@@ -1312,12 +1512,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);
@@ -1376,7 +1578,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 {
@@ -1419,6 +1621,7 @@ restack(Monitor *m) {
XWindowChanges wc;
drawbar(m);
+ drawtab(m);
if(!m->sel)
return;
if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
@@ -1530,7 +1733,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;
@@ -1541,7 +1744,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;
@@ -1556,10 +1759,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);
@@ -1577,7 +1783,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);
}
@@ -1595,6 +1801,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);
@@ -1620,6 +1827,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)
@@ -1633,7 +1841,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();
@@ -1730,13 +1938,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;
@@ -1764,9 +1983,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);
}
@@ -1829,24 +2070,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
@@ -1993,7 +2257,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
@@ -2044,11 +2308,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);
}
@@ -2074,7 +2362,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.o b/dwm.o
index 313bb81..2dcbbf2 100644
--- a/dwm.o
+++ b/dwm.o
Binary files differ