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