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