dwm-6.1-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.1-pertag-tab-v2b.diff (26836B)
---
1 diff --git a/config.def.h b/config.def.h
2 index 7054c06..f0b33c5 100644
3 --- a/config.def.h
4 +++ b/config.def.h
5 @@ -15,10 +15,21 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
6 static const unsigned int snap = 32; /* snap pixel */
7 static const int showbar = 1; /* 0 means no bar */
8 static const int topbar = 1; /* 0 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 /* tagging */
17 static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
18
19 +/* default layout per tags */
20 +/* The first element is for all-tag view, following i-th element corresponds to */
21 +/* tags[i]. Layout is referred using the layouts array index.*/
22 +static int def_layouts[1 + LENGTH(tags)] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
23 +
24 static const Rule rules[] = {
25 /* xprop(1):
26 * WM_CLASS(STRING) = instance, class
27 @@ -62,6 +73,7 @@ static Key keys[] = {
28 { MODKEY, XK_p, spawn, {.v = dmenucmd } },
29 { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
30 { MODKEY, XK_b, togglebar, {0} },
31 + { MODKEY, XK_w, tabmode, {-1} },
32 { MODKEY, XK_j, focusstack, {.i = +1 } },
33 { MODKEY, XK_k, focusstack, {.i = -1 } },
34 { MODKEY, XK_i, incnmaster, {.i = +1 } },
35 @@ -109,5 +121,6 @@ static Button buttons[] = {
36 { ClkTagBar, 0, Button3, toggleview, {0} },
37 { ClkTagBar, MODKEY, Button1, tag, {0} },
38 { ClkTagBar, MODKEY, Button3, toggletag, {0} },
39 + { ClkTabBar, 0, Button1, focuswin, {0} },
40 };
41
42 diff --git a/dwm.1 b/dwm.1
43 index 6687011..077d92b 100644
44 --- a/dwm.1
45 +++ b/dwm.1
46 @@ -19,14 +19,22 @@ layout applied.
47 Windows are grouped by tags. Each window can be tagged with one or multiple
48 tags. Selecting certain tags displays all windows with these tags.
49 .P
50 -Each screen contains a small status bar which displays all available tags, the
51 -layout, the title of the focused window, and the text read from the root window
52 -name property, if the screen is focused. A floating window is indicated with an
53 -empty square and a maximised floating window is indicated with a filled square
54 -before the windows title. The selected tags are indicated with a different
55 -color. The tags of the focused window are indicated with a filled square in the
56 -top left corner. The tags which are applied to one or more windows are
57 -indicated with an empty square in the top left corner.
58 +Each screen contains two small status bars.
59 +.P
60 +One bar displays all available tags, the layout, the title of the focused
61 +window, and the text read from the root window name property, if the screen is
62 +focused. A floating window is indicated with an empty square and a maximised
63 +floating window is indicated with a filled square before the windows title. The
64 +selected tags are indicated with a different color. The tags of the focused
65 +window are indicated with a filled square in the top left corner. The tags
66 +which are applied to one or more windows are indicated with an empty square in
67 +the top left corner.
68 +.P
69 +Another bar contains a tab for each window of the current view and allows
70 +navigation between windows, especially in the monocle mode. The different
71 +display modes of this bar are described under the Mod1\-w Keybord command
72 +section. When a single tag is selected, that tag is indicated in the left corner
73 +of the tab bar.
74 .P
75 dwm draws a small border around windows to indicate the focus state.
76 .SH OPTIONS
77 @@ -43,7 +51,8 @@ command.
78 .TP
79 .B Button1
80 click on a tag label to display all windows with that tag, click on the layout
81 -label toggles between tiled and floating layout.
82 +label toggles between tiled and floating layout, click on a window name in the
83 +tab bar brings focus to that window.
84 .TP
85 .B Button3
86 click on a tag label adds/removes all windows with that tag to/from the view.
87 @@ -104,6 +113,12 @@ Increase master area size.
88 .B Mod1\-h
89 Decrease master area size.
90 .TP
91 +.B Mod1\-w
92 +Cycle over the tab bar display modes: never displayed, always displayed,
93 +displayed only in monocle mode when the view contains than one window (auto
94 +mode). Some display modes can be disabled in the configuration, config.h. In
95 +the default configuration only "never" and "auto" display modes are enabled.
96 +.TP
97 .B Mod1\-Return
98 Zooms/cycles focused window to/from master area (tiled layouts only).
99 .TP
100 diff --git a/dwm.c b/dwm.c
101 index 0362114..887a839 100644
102 --- a/dwm.c
103 +++ b/dwm.c
104 @@ -64,7 +64,7 @@ enum { NetSupported, NetWMName, NetWMState,
105 NetWMFullscreen, NetActiveWindow, NetWMWindowType,
106 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
107 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
108 -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
109 +enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
110 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
111
112 typedef union {
113 @@ -111,25 +111,35 @@ typedef struct {
114 void (*arrange)(Monitor *);
115 } Layout;
116
117 +#define MAXTABS 50
118 +
119 +typedef struct Pertag Pertag;
120 struct Monitor {
121 char ltsymbol[16];
122 float mfact;
123 int nmaster;
124 int num;
125 int by; /* bar geometry */
126 + int ty; /* tab bar geometry */
127 int mx, my, mw, mh; /* screen size */
128 int wx, wy, ww, wh; /* window area */
129 unsigned int seltags;
130 unsigned int sellt;
131 unsigned int tagset[2];
132 int showbar;
133 + int showtab;
134 int topbar;
135 + int toptab;
136 Client *clients;
137 Client *sel;
138 Client *stack;
139 Monitor *next;
140 Window barwin;
141 + Window tabwin;
142 + int ntabs;
143 + int tab_widths[MAXTABS];
144 const Layout *lt[2];
145 + Pertag *pertag;
146 };
147
148 typedef struct {
149 @@ -164,12 +174,15 @@ static void detachstack(Client *c);
150 static Monitor *dirtomon(int dir);
151 static void drawbar(Monitor *m);
152 static void drawbars(void);
153 +static void drawtab(Monitor *m);
154 +static void drawtabs(void);
155 static void enternotify(XEvent *e);
156 static void expose(XEvent *e);
157 static void focus(Client *c);
158 static void focusin(XEvent *e);
159 static void focusmon(const Arg *arg);
160 static void focusstack(const Arg *arg);
161 +static void focuswin(const Arg* arg);
162 static int getrootptr(int *x, int *y);
163 static long getstate(Window w);
164 static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
165 @@ -206,6 +219,7 @@ static void setup(void);
166 static void showhide(Client *c);
167 static void sigchld(int unused);
168 static void spawn(const Arg *arg);
169 +static void tabmode(const Arg *arg);
170 static void tag(const Arg *arg);
171 static void tagmon(const Arg *arg);
172 static void tile(Monitor *);
173 @@ -240,6 +254,7 @@ static char stext[256];
174 static int screen;
175 static int sw, sh; /* X display screen geometry width, height */
176 static int bh, blw = 0; /* bar geometry */
177 +static int th = 0; /* tab bar geometry */
178 static int (*xerrorxlib)(Display *, XErrorEvent *);
179 static unsigned int numlockmask = 0;
180 static void (*handler[LASTEvent]) (XEvent *) = {
181 @@ -270,6 +285,16 @@ static Window root;
182 /* configuration, allows nested code to access above variables */
183 #include "config.h"
184
185 +struct Pertag {
186 + unsigned int curtag, prevtag; /* current and previous tag */
187 + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
188 + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
189 + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
190 + const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
191 + Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
192 + Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */
193 +};
194 +
195 /* compile-time check if all tags fit into an unsigned int bit array. */
196 struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
197
198 @@ -393,6 +418,8 @@ arrange(Monitor *m)
199 void
200 arrangemon(Monitor *m)
201 {
202 + updatebarpos(m);
203 + XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
204 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
205 if (m->lt[m->sellt]->arrange)
206 m->lt[m->sellt]->arrange(m);
207 @@ -442,14 +469,33 @@ buttonpress(XEvent *e)
208 click = ClkStatusText;
209 else
210 click = ClkWinTitle;
211 - } else if ((c = wintoclient(ev->window))) {
212 + }
213 + if(ev->window == selmon->tabwin) {
214 + i = 0; x = 0;
215 + for(c = selmon->clients; c; c = c->next){
216 + if(!ISVISIBLE(c)) continue;
217 + x += selmon->tab_widths[i];
218 + if (ev->x > x)
219 + ++i;
220 + else
221 + break;
222 + if(i >= m->ntabs) break;
223 + }
224 + if(c) {
225 + click = ClkTabBar;
226 + arg.ui = i;
227 + }
228 + }
229 + else if((c = wintoclient(ev->window))) {
230 focus(c);
231 click = ClkClientWin;
232 }
233 - for (i = 0; i < LENGTH(buttons); i++)
234 - if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
235 - && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
236 - buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
237 + for(i = 0; i < LENGTH(buttons); i++)
238 + if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
239 + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
240 + buttons[i].func(((click == ClkTagBar || click == ClkTabBar)
241 + && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
242 + }
243 }
244
245 void
246 @@ -469,23 +515,24 @@ cleanup(void)
247 Arg a = {.ui = ~0};
248 Layout foo = { "", NULL };
249 Monitor *m;
250 - size_t i;
251
252 view(&a);
253 selmon->lt[selmon->sellt] = &foo;
254 for (m = mons; m; m = m->next)
255 - while (m->stack)
256 - unmanage(m->stack, 0);
257 + while(m->stack)
258 + unmanage(m->stack, False);
259 XUngrabKey(dpy, AnyKey, AnyModifier, root);
260 while (mons)
261 cleanupmon(mons);
262 - for (i = 0; i < CurLast; i++)
263 - drw_cur_free(drw, cursor[i]);
264 - for (i = 0; i < SchemeLast; i++) {
265 - drw_clr_free(scheme[i].border);
266 - drw_clr_free(scheme[i].bg);
267 - drw_clr_free(scheme[i].fg);
268 - }
269 + drw_cur_free(drw, cursor[CurNormal]);
270 + drw_cur_free(drw, cursor[CurResize]);
271 + drw_cur_free(drw, cursor[CurMove]);
272 + drw_clr_free(scheme[SchemeNorm].border);
273 + drw_clr_free(scheme[SchemeNorm].bg);
274 + drw_clr_free(scheme[SchemeNorm].fg);
275 + drw_clr_free(scheme[SchemeSel].border);
276 + drw_clr_free(scheme[SchemeSel].bg);
277 + drw_clr_free(scheme[SchemeSel].fg);
278 drw_free(drw);
279 XSync(dpy, False);
280 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
281 @@ -505,6 +552,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 @@ -526,6 +575,7 @@ clientmessage(XEvent *e)
291 {
292 XClientMessageEvent *cme = &e->xclient;
293 Client *c = wintoclient(cme->window);
294 + int i;
295
296 if (!c)
297 return;
298 @@ -537,6 +587,8 @@ clientmessage(XEvent *e)
299 if (!ISVISIBLE(c)) {
300 c->mon->seltags ^= 1;
301 c->mon->tagset[c->mon->seltags] = c->tags;
302 + for(i=0; !(c->tags & 1 << i); i++);
303 + view(&(Arg){.ui = 1 << i});
304 }
305 pop(c);
306 }
307 @@ -565,6 +617,7 @@ void
308 configurenotify(XEvent *e)
309 {
310 Monitor *m;
311 + Client *c;
312 XConfigureEvent *ev = &e->xconfigure;
313 int dirty;
314
315 @@ -576,8 +629,14 @@ configurenotify(XEvent *e)
316 if (updategeom() || dirty) {
317 drw_resize(drw, sw, bh);
318 updatebars();
319 - for (m = mons; m; m = m->next)
320 + //refreshing display of status bar. The tab bar is handled by the arrange()
321 + //method, which is called below
322 + for(m = mons; m; m = m->next){
323 + for (c = m->clients; c; c = c->next)
324 + if (c->isfullscreen)
325 + resizeclient(c, m->mx, m->my, m->mw, m->mh);
326 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
327 + }
328 focus(NULL);
329 arrange(NULL);
330 }
331 @@ -640,16 +699,41 @@ Monitor *
332 createmon(void)
333 {
334 Monitor *m;
335 + int i;
336
337 m = ecalloc(1, sizeof(Monitor));
338 m->tagset[0] = m->tagset[1] = 1;
339 m->mfact = mfact;
340 m->nmaster = nmaster;
341 m->showbar = showbar;
342 + m->showtab = showtab;
343 m->topbar = topbar;
344 - m->lt[0] = &layouts[0];
345 + m->toptab = toptab;
346 + m->ntabs = 0;
347 + m->lt[0] = &layouts[def_layouts[1] % LENGTH(layouts)];
348 m->lt[1] = &layouts[1 % LENGTH(layouts)];
349 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
350 + if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag))))
351 + die("fatal: could not malloc() %u bytes\n", sizeof(Pertag));
352 + m->pertag->curtag = m->pertag->prevtag = 1;
353 + for(i=0; i <= LENGTH(tags); i++) {
354 + /* init nmaster */
355 + m->pertag->nmasters[i] = m->nmaster;
356 +
357 + /* init mfacts */
358 + m->pertag->mfacts[i] = m->mfact;
359 +
360 + /* init layouts */
361 + m->pertag->ltidxs[i][0] = &layouts[def_layouts[i % LENGTH(def_layouts)] % LENGTH(layouts)];
362 + m->pertag->ltidxs[i][1] = m->lt[1];
363 + m->pertag->sellts[i] = m->sellt;
364 +
365 + /* init showbar */
366 + m->pertag->showbars[i] = m->showbar;
367 +
368 + /* swap focus and zoomswap*/
369 + m->pertag->prevzooms[i] = NULL;
370 + }
371 return m;
372 }
373
374 @@ -763,6 +847,104 @@ drawbars(void)
375 }
376
377 void
378 +drawtabs(void) {
379 + Monitor *m;
380 +
381 + for(m = mons; m; m = m->next)
382 + drawtab(m);
383 +}
384 +
385 +static int
386 +cmpint(const void *p1, const void *p2) {
387 + /* The actual arguments to this function are "pointers to
388 + pointers to char", but strcmp(3) arguments are "pointers
389 + to char", hence the following cast plus dereference */
390 + return *((int*) p1) > * (int*) p2;
391 +}
392 +
393 +
394 +void
395 +drawtab(Monitor *m) {
396 + Client *c;
397 + int i;
398 + int itag = -1;
399 + char view_info[50];
400 + int view_info_w = 0;
401 + int sorted_label_widths[MAXTABS];
402 + int tot_width;
403 + int maxsize = bh;
404 + int x = 0;
405 + int w = 0;
406 +
407 + //view_info: indicate the tag which is displayed in the view
408 + for(i = 0; i < LENGTH(tags); ++i){
409 + if((selmon->tagset[selmon->seltags] >> i) & 1) {
410 + if(itag >=0){ //more than one tag selected
411 + itag = -1;
412 + break;
413 + }
414 + itag = i;
415 + }
416 + }
417 + if(0 <= itag && itag < LENGTH(tags)){
418 + snprintf(view_info, sizeof view_info, "[%s]", tags[itag]);
419 + } else {
420 + strncpy(view_info, "[...]", sizeof view_info);
421 + }
422 + view_info[sizeof(view_info) - 1 ] = 0;
423 + view_info_w = TEXTW(view_info);
424 + tot_width = view_info_w;
425 +
426 + /* Calculates number of labels and their width */
427 + m->ntabs = 0;
428 + for(c = m->clients; c; c = c->next){
429 + if(!ISVISIBLE(c)) continue;
430 + m->tab_widths[m->ntabs] = TEXTW(c->name);
431 + tot_width += m->tab_widths[m->ntabs];
432 + ++m->ntabs;
433 + if(m->ntabs >= MAXTABS) break;
434 + }
435 +
436 + if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated
437 + memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs);
438 + qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint);
439 + tot_width = view_info_w;
440 + for(i = 0; i < m->ntabs; ++i){
441 + if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww)
442 + break;
443 + tot_width += sorted_label_widths[i];
444 + }
445 + maxsize = (m->ww - tot_width) / (m->ntabs - i);
446 + } else{
447 + maxsize = m->ww;
448 + }
449 + i = 0;
450 + for(c = m->clients; c; c = c->next){
451 + if(!ISVISIBLE(c)) continue;
452 + if(i >= m->ntabs) break;
453 + if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize;
454 + w = m->tab_widths[i];
455 + drw_setscheme(drw, (c == m->sel) ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
456 + drw_text(drw, x, 0, w, th, c->name, 0);
457 + x += w;
458 + ++i;
459 + }
460 +
461 + drw_setscheme(drw, &scheme[SchemeNorm]);
462 +
463 + /* cleans interspace between window names and current viewed tag label */
464 + w = m->ww - view_info_w - x;
465 + drw_text(drw, x, 0, w, th, "", 0);
466 +
467 + /* view info */
468 + x += w;
469 + w = view_info_w;
470 + drw_text(drw, x, 0, w, th, view_info, 0);
471 +
472 + drw_map(drw, m->tabwin, 0, 0, m->ww, th);
473 +}
474 +
475 +void
476 enternotify(XEvent *e)
477 {
478 Client *c;
479 @@ -787,8 +969,10 @@ expose(XEvent *e)
480 Monitor *m;
481 XExposeEvent *ev = &e->xexpose;
482
483 - if (ev->count == 0 && (m = wintomon(ev->window)))
484 + if (ev->count == 0 && (m = wintomon(ev->window))){
485 drawbar(m);
486 + drawtab(m);
487 + }
488 }
489
490 void
491 @@ -806,7 +990,7 @@ focus(Client *c)
492 clearurgent(c);
493 detachstack(c);
494 attachstack(c);
495 - grabbuttons(c, 1);
496 + grabbuttons(c, True);
497 XSetWindowBorder(dpy, c->win, scheme[SchemeSel].border->pix);
498 setfocus(c);
499 } else {
500 @@ -815,6 +999,7 @@ focus(Client *c)
501 }
502 selmon->sel = c;
503 drawbars();
504 + drawtabs();
505 }
506
507 /* there are some broken focus acquiring clients */
508 @@ -868,6 +1053,19 @@ focusstack(const Arg *arg)
509 }
510 }
511
512 +void
513 +focuswin(const Arg* arg){
514 + int iwin = arg->i;
515 + Client* c = NULL;
516 + for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
517 + if(ISVISIBLE(c)) --iwin;
518 + };
519 + if(c) {
520 + focus(c);
521 + restack(selmon);
522 + }
523 +}
524 +
525 Atom
526 getatomprop(Client *c, Atom prop)
527 {
528 @@ -981,7 +1179,7 @@ grabkeys(void)
529 void
530 incnmaster(const Arg *arg)
531 {
532 - selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
533 + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
534 arrange(selmon);
535 }
536
537 @@ -1139,7 +1337,7 @@ motionnotify(XEvent *e)
538 if (ev->window != root)
539 return;
540 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
541 - unfocus(selmon->sel, 1);
542 + unfocus(selmon->sel, True);
543 selmon = m;
544 focus(NULL);
545 }
546 @@ -1159,11 +1357,13 @@ movemouse(const Arg *arg)
547 return;
548 if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
549 return;
550 + if(c->isfullscreen) /* no support moving fullscreen windows by mouse */
551 + return;
552 restack(selmon);
553 ocx = c->x;
554 ocy = c->y;
555 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
556 - None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
557 + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
558 return;
559 if (!getrootptr(&x, &y))
560 return;
561 @@ -1250,12 +1450,14 @@ propertynotify(XEvent *e)
562 case XA_WM_HINTS:
563 updatewmhints(c);
564 drawbars();
565 + drawtabs();
566 break;
567 }
568 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
569 updatetitle(c);
570 if (c == c->mon->sel)
571 drawbar(c->mon);
572 + drawtab(c->mon);
573 }
574 if (ev->atom == netatom[NetWMWindowType])
575 updatewindowtype(c);
576 @@ -1317,11 +1519,13 @@ resizemouse(const Arg *arg)
577 return;
578 if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
579 return;
580 + if(c->isfullscreen) /* no support resizing fullscreen windows by mouse */
581 + return;
582 restack(selmon);
583 ocx = c->x;
584 ocy = c->y;
585 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
586 - None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
587 + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
588 return;
589 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
590 do {
591 @@ -1369,6 +1573,7 @@ restack(Monitor *m)
592 XWindowChanges wc;
593
594 drawbar(m);
595 + drawtab(m);
596 if (!m->sel)
597 return;
598 if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
599 @@ -1477,11 +1682,11 @@ sendevent(Client *c, Atom proto)
600 void
601 setfocus(Client *c)
602 {
603 - if (!c->neverfocus) {
604 + if(!c->neverfocus) {
605 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
606 XChangeProperty(dpy, root, netatom[NetActiveWindow],
607 - XA_WINDOW, 32, PropModeReplace,
608 - (unsigned char *) &(c->win), 1);
609 + XA_WINDOW, 32, PropModeReplace,
610 + (unsigned char *) &(c->win), 1);
611 }
612 sendevent(c, wmatom[WMTakeFocus]);
613 }
614 @@ -1517,10 +1722,13 @@ setfullscreen(Client *c, int fullscreen)
615 void
616 setlayout(const Arg *arg)
617 {
618 - if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
619 - selmon->sellt ^= 1;
620 - if (arg && arg->v)
621 - selmon->lt[selmon->sellt] = (Layout *)arg->v;
622 + if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
623 + selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
624 + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
625 + }
626 + if(arg && arg->v)
627 + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
628 + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
629 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
630 if (selmon->sel)
631 arrange(selmon);
632 @@ -1539,7 +1747,7 @@ setmfact(const Arg *arg)
633 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
634 if (f < 0.1 || f > 0.9)
635 return;
636 - selmon->mfact = f;
637 + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
638 arrange(selmon);
639 }
640
641 @@ -1559,8 +1767,9 @@ setup(void)
642 drw = drw_create(dpy, screen, root, sw, sh);
643 drw_load_fonts(drw, fonts, LENGTH(fonts));
644 if (!drw->fontcount)
645 - die("no fonts could be loaded.\n");
646 + die("No fonts could be loaded.\n");
647 bh = drw->fonts[0]->h + 2;
648 + th = bh;
649 updategeom();
650 /* init atoms */
651 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
652 @@ -1632,10 +1841,10 @@ sigchld(int unused)
653 void
654 spawn(const Arg *arg)
655 {
656 - if (arg->v == dmenucmd)
657 + if(arg->v == dmenucmd)
658 dmenumon[0] = '0' + selmon->num;
659 - if (fork() == 0) {
660 - if (dpy)
661 + if(fork() == 0) {
662 + if(dpy)
663 close(ConnectionNumber(dpy));
664 setsid();
665 execvp(((char **)arg->v)[0], (char **)arg->v);
666 @@ -1692,18 +1901,29 @@ tile(Monitor *m)
667 void
668 togglebar(const Arg *arg)
669 {
670 - selmon->showbar = !selmon->showbar;
671 + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
672 updatebarpos(selmon);
673 XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
674 arrange(selmon);
675 }
676
677 void
678 +tabmode(const Arg *arg)
679 +{
680 + if(arg && arg->i >= 0)
681 + selmon->showtab = arg->ui % showtab_nmodes;
682 + else
683 + selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
684 + arrange(selmon);
685 +}
686 +
687 +
688 +void
689 togglefloating(const Arg *arg)
690 {
691 - if (!selmon->sel)
692 + if(!selmon->sel)
693 return;
694 - if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
695 + if(selmon->sel->isfullscreen) /* no support for fullscreen windows */
696 return;
697 selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
698 if (selmon->sel->isfloating)
699 @@ -1731,9 +1951,29 @@ void
700 toggleview(const Arg *arg)
701 {
702 unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
703 + int i;
704
705 if (newtagset) {
706 + if(newtagset == ~0) {
707 + selmon->pertag->prevtag = selmon->pertag->curtag;
708 + selmon->pertag->curtag = 0;
709 + }
710 + /* test if the user did not select the same tag */
711 + if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
712 + selmon->pertag->prevtag = selmon->pertag->curtag;
713 + for (i=0; !(newtagset & 1 << i); i++) ;
714 + selmon->pertag->curtag = i + 1;
715 + }
716 selmon->tagset[selmon->seltags] = newtagset;
717 +
718 + /* apply settings for this view */
719 + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
720 + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
721 + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
722 + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
723 + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
724 + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
725 + togglebar(NULL);
726 focus(NULL);
727 arrange(selmon);
728 }
729 @@ -1744,7 +1984,7 @@ unfocus(Client *c, int setfocus)
730 {
731 if (!c)
732 return;
733 - grabbuttons(c, 0);
734 + grabbuttons(c, False);
735 XSetWindowBorder(dpy, c->win, scheme[SchemeNorm].border->pix);
736 if (setfocus) {
737 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
738 @@ -1809,20 +2049,44 @@ updatebars(void)
739 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
740 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
741 XMapRaised(dpy, m->barwin);
742 + m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
743 + CopyFromParent, DefaultVisual(dpy, screen),
744 + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
745 + XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
746 + XMapRaised(dpy, m->tabwin);
747 }
748 }
749
750 void
751 updatebarpos(Monitor *m)
752 {
753 + Client *c;
754 + int nvis = 0;
755 +
756 m->wy = m->my;
757 m->wh = m->mh;
758 if (m->showbar) {
759 m->wh -= bh;
760 m->by = m->topbar ? m->wy : m->wy + m->wh;
761 - m->wy = m->topbar ? m->wy + bh : m->wy;
762 - } else
763 + if ( m->topbar )
764 + m->wy += bh;
765 + } else {
766 m->by = -bh;
767 + }
768 +
769 + for(c = m->clients; c; c = c->next){
770 + if(ISVISIBLE(c)) ++nvis;
771 + }
772 +
773 + if(m->showtab == showtab_always
774 + || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){
775 + m->wh -= th;
776 + m->ty = m->toptab ? m->wy : m->wy + m->wh;
777 + if ( m->toptab )
778 + m->wy += th;
779 + } else {
780 + m->ty = -th;
781 + }
782 }
783
784 void
785 @@ -2004,9 +2268,9 @@ updatewindowtype(Client *c)
786 Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
787
788 if (state == netatom[NetWMFullscreen])
789 - setfullscreen(c, 1);
790 + setfullscreen(c, True);
791 if (wtype == netatom[NetWMWindowTypeDialog])
792 - c->isfloating = 1;
793 + c->isfloating = True;
794 }
795
796 void
797 @@ -2031,11 +2295,33 @@ updatewmhints(Client *c)
798 void
799 view(const Arg *arg)
800 {
801 - if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
802 + int i;
803 + unsigned int tmptag;
804 +
805 + if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
806 return;
807 selmon->seltags ^= 1; /* toggle sel tagset */
808 - if (arg->ui & TAGMASK)
809 + if(arg->ui & TAGMASK) {
810 + selmon->pertag->prevtag = selmon->pertag->curtag;
811 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
812 + if(arg->ui == ~0)
813 + selmon->pertag->curtag = 0;
814 + else {
815 + for (i=0; !(arg->ui & 1 << i); i++) ;
816 + selmon->pertag->curtag = i + 1;
817 + }
818 + } else {
819 + tmptag = selmon->pertag->prevtag;
820 + selmon->pertag->prevtag = selmon->pertag->curtag;
821 + selmon->pertag->curtag = tmptag;
822 + }
823 + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
824 + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
825 + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
826 + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
827 + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
828 + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
829 + togglebar(NULL);
830 focus(NULL);
831 arrange(selmon);
832 }
833 @@ -2063,7 +2349,7 @@ wintomon(Window w)
834 if (w == root && getrootptr(&x, &y))
835 return recttomon(x, y, 1, 1);
836 for (m = mons; m; m = m->next)
837 - if (w == m->barwin)
838 + if(w == m->barwin || w == m->tabwin)
839 return m;
840 if ((c = wintoclient(w)))
841 return c->mon;