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