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