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;