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