dwm-tab-v2b-56a31dc.diff (15708B)
1 diff --git a/config.def.h b/config.def.h 2 index fd77a07..dd2442b 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 int 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 @@ -64,6 +72,7 @@ static Key keys[] = { 21 { MODKEY, XK_p, spawn, {.v = dmenucmd } }, 22 { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, 23 { MODKEY, XK_b, togglebar, {0} }, 24 + { MODKEY, XK_w, tabmode, {-1} }, 25 { MODKEY, XK_j, focusstack, {.i = +1 } }, 26 { MODKEY, XK_k, focusstack, {.i = -1 } }, 27 { MODKEY, XK_i, incnmaster, {.i = +1 } }, 28 @@ -111,5 +120,6 @@ static Button buttons[] = { 29 { ClkTagBar, 0, Button3, toggleview, {0} }, 30 { ClkTagBar, MODKEY, Button1, tag, {0} }, 31 { ClkTagBar, MODKEY, Button3, toggletag, {0} }, 32 + { ClkTabBar, 0, Button1, focuswin, {0} }, 33 }; 34 35 diff --git a/dwm.1 b/dwm.1 36 index 6687011..9ff827c 100644 37 --- a/dwm.1 38 +++ b/dwm.1 39 @@ -19,14 +19,22 @@ layout applied. 40 Windows are grouped by tags. Each window can be tagged with one or multiple 41 tags. Selecting certain tags displays all windows with these tags. 42 .P 43 -Each screen contains a small status bar which displays all available tags, the 44 -layout, the title of the focused window, and the text read from the root window 45 -name property, if the screen is focused. A floating window is indicated with an 46 -empty square and a maximised floating window is indicated with a filled square 47 -before the windows title. The selected tags are indicated with a different 48 -color. The tags of the focused window are indicated with a filled square in the 49 -top left corner. The tags which are applied to one or more windows are 50 -indicated with an empty square in the top left corner. 51 +Each screen contains two small status bars. 52 +.P 53 +One bar displays all available tags, the layout, the title of the focused 54 +window, and the text read from the root window name property, if the screen is 55 +focused. A floating window is indicated with an empty square and a maximised 56 +floating window is indicated with a filled square before the windows title. The 57 +selected tags are indicated with a different color. The tags of the focused 58 +window are indicated with a filled square in the top left corner. The tags 59 +which are applied to one or more windows are indicated with an empty square in 60 +the top left corner. 61 +.P 62 +Another bar contains a tab for each window of the current view and allows 63 +navigation between windows, especially in the monocle mode. The different 64 +display modes of this bar are described under the Mod1\-w Keybord command 65 +section. When a single tag is selected, this tag is indicated in the left corner 66 +of the tab bar. 67 .P 68 dwm draws a small border around windows to indicate the focus state. 69 .SH OPTIONS 70 @@ -43,7 +51,8 @@ command. 71 .TP 72 .B Button1 73 click on a tag label to display all windows with that tag, click on the layout 74 -label toggles between tiled and floating layout. 75 +label toggles between tiled and floating layout, click on a window name in the 76 +tab bar brings focus to that window. 77 .TP 78 .B Button3 79 click on a tag label adds/removes all windows with that tag to/from the view. 80 @@ -104,6 +113,12 @@ Increase master area size. 81 .B Mod1\-h 82 Decrease master area size. 83 .TP 84 +.B Mod1\-w 85 +Cycle over the tab bar display modes: never displayed, always displayed, 86 +displayed only in monocle mode when the view contains more than one window (auto 87 +mode). Some display modes can be disabled in the configuration, config.h. In 88 +the default configuration only "never" and "auto" display modes are enabled. 89 +.TP 90 .B Mod1\-Return 91 Zooms/cycles focused window to/from master area (tiled layouts only). 92 .TP 93 diff --git a/dwm.c b/dwm.c 94 index b2bc9bd..134a5b0 100644 95 --- a/dwm.c 96 +++ b/dwm.c 97 @@ -65,7 +65,7 @@ enum { NetSupported, NetWMName, NetWMState, 98 NetWMFullscreen, NetActiveWindow, NetWMWindowType, 99 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 100 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 101 -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 102 +enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 103 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 104 105 typedef union { 106 @@ -112,24 +112,32 @@ typedef struct { 107 void (*arrange)(Monitor *); 108 } Layout; 109 110 +#define MAXTABS 50 111 + 112 struct Monitor { 113 char ltsymbol[16]; 114 float mfact; 115 int nmaster; 116 int num; 117 int by; /* bar geometry */ 118 + int ty; /* tab bar geometry */ 119 int mx, my, mw, mh; /* screen size */ 120 int wx, wy, ww, wh; /* window area */ 121 unsigned int seltags; 122 unsigned int sellt; 123 unsigned int tagset[2]; 124 int showbar; 125 + int showtab; 126 int topbar; 127 + int toptab; 128 Client *clients; 129 Client *sel; 130 Client *stack; 131 Monitor *next; 132 Window barwin; 133 + Window tabwin; 134 + int ntabs; 135 + int tab_widths[MAXTABS]; 136 const Layout *lt[2]; 137 }; 138 139 @@ -165,12 +173,15 @@ static void detachstack(Client *c); 140 static Monitor *dirtomon(int dir); 141 static void drawbar(Monitor *m); 142 static void drawbars(void); 143 +static void drawtab(Monitor *m); 144 +static void drawtabs(void); 145 static void enternotify(XEvent *e); 146 static void expose(XEvent *e); 147 static void focus(Client *c); 148 static void focusin(XEvent *e); 149 static void focusmon(const Arg *arg); 150 static void focusstack(const Arg *arg); 151 +static void focuswin(const Arg* arg); 152 static int getrootptr(int *x, int *y); 153 static long getstate(Window w); 154 static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 155 @@ -207,6 +218,7 @@ static void setup(void); 156 static void showhide(Client *c); 157 static void sigchld(int unused); 158 static void spawn(const Arg *arg); 159 +static void tabmode(const Arg *arg); 160 static void tag(const Arg *arg); 161 static void tagmon(const Arg *arg); 162 static void tile(Monitor *); 163 @@ -241,6 +253,7 @@ static char stext[256]; 164 static int screen; 165 static int sw, sh; /* X display screen geometry width, height */ 166 static int bh, blw = 0; /* bar geometry */ 167 +static int th = 0; /* tab bar geometry */ 168 static int lrpad; /* sum of left and right padding for text */ 169 static int (*xerrorxlib)(Display *, XErrorEvent *); 170 static unsigned int numlockmask = 0; 171 @@ -393,8 +406,9 @@ arrange(Monitor *m) 172 } 173 174 void 175 -arrangemon(Monitor *m) 176 -{ 177 +arrangemon(Monitor *m) { 178 + updatebarpos(m); 179 + XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th); 180 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); 181 if (m->lt[m->sellt]->arrange) 182 m->lt[m->sellt]->arrange(m); 183 @@ -444,14 +458,33 @@ buttonpress(XEvent *e) 184 click = ClkStatusText; 185 else 186 click = ClkWinTitle; 187 - } else if ((c = wintoclient(ev->window))) { 188 + } 189 + if(ev->window == selmon->tabwin) { 190 + i = 0; x = 0; 191 + for(c = selmon->clients; c; c = c->next){ 192 + if(!ISVISIBLE(c)) continue; 193 + x += selmon->tab_widths[i]; 194 + if (ev->x > x) 195 + ++i; 196 + else 197 + break; 198 + if(i >= m->ntabs) break; 199 + } 200 + if(c) { 201 + click = ClkTabBar; 202 + arg.ui = i; 203 + } 204 + } 205 + else if((c = wintoclient(ev->window))) { 206 focus(c); 207 click = ClkClientWin; 208 } 209 for (i = 0; i < LENGTH(buttons); i++) 210 if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button 211 - && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) 212 - buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); 213 + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){ 214 + buttons[i].func(((click == ClkTagBar || click == ClkTabBar) 215 + && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg); 216 + } 217 } 218 219 void 220 @@ -504,6 +537,8 @@ cleanupmon(Monitor *mon) 221 } 222 XUnmapWindow(dpy, mon->barwin); 223 XDestroyWindow(dpy, mon->barwin); 224 + XUnmapWindow(dpy, mon->tabwin); 225 + XDestroyWindow(dpy, mon->tabwin); 226 free(mon); 227 } 228 229 @@ -576,7 +611,9 @@ configurenotify(XEvent *e) 230 if (updategeom() || dirty) { 231 drw_resize(drw, sw, bh); 232 updatebars(); 233 - for (m = mons; m; m = m->next) { 234 + //refreshing display of status bar. The tab bar is handled by the arrange() 235 + //method, which is called below 236 + for(m = mons; m; m = m->next){ 237 for (c = m->clients; c; c = c->next) 238 if (c->isfullscreen) 239 resizeclient(c, m->mx, m->my, m->mw, m->mh); 240 @@ -650,7 +687,10 @@ createmon(void) 241 m->mfact = mfact; 242 m->nmaster = nmaster; 243 m->showbar = showbar; 244 + m->showtab = showtab; 245 m->topbar = topbar; 246 + m->toptab = toptab; 247 + m->ntabs = 0; 248 m->lt[0] = &layouts[0]; 249 m->lt[1] = &layouts[1 % LENGTH(layouts)]; 250 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 251 @@ -765,6 +805,105 @@ drawbars(void) 252 } 253 254 void 255 +drawtabs(void) { 256 + Monitor *m; 257 + 258 + for(m = mons; m; m = m->next) 259 + drawtab(m); 260 +} 261 + 262 +static int 263 +cmpint(const void *p1, const void *p2) { 264 + /* The actual arguments to this function are "pointers to 265 + pointers to char", but strcmp(3) arguments are "pointers 266 + to char", hence the following cast plus dereference */ 267 + return *((int*) p1) > * (int*) p2; 268 +} 269 + 270 + 271 +void 272 +drawtab(Monitor *m) { 273 + Client *c; 274 + int i; 275 + int itag = -1; 276 + char view_info[50]; 277 + int view_info_w = 0; 278 + int sorted_label_widths[MAXTABS]; 279 + int tot_width; 280 + int maxsize = bh; 281 + int x = 0; 282 + int w = 0; 283 + 284 + //view_info: indicate the tag which is displayed in the view 285 + for(i = 0; i < LENGTH(tags); ++i){ 286 + if((selmon->tagset[selmon->seltags] >> i) & 1) { 287 + if(itag >=0){ //more than one tag selected 288 + itag = -1; 289 + break; 290 + } 291 + itag = i; 292 + } 293 + } 294 + 295 + if(0 <= itag && itag < LENGTH(tags)){ 296 + snprintf(view_info, sizeof view_info, "[%s]", tags[itag]); 297 + } else { 298 + strncpy(view_info, "[...]", sizeof view_info); 299 + } 300 + view_info[sizeof(view_info) - 1 ] = 0; 301 + view_info_w = TEXTW(view_info); 302 + tot_width = view_info_w; 303 + 304 + /* Calculates number of labels and their width */ 305 + m->ntabs = 0; 306 + for(c = m->clients; c; c = c->next){ 307 + if(!ISVISIBLE(c)) continue; 308 + m->tab_widths[m->ntabs] = TEXTW(c->name); 309 + tot_width += m->tab_widths[m->ntabs]; 310 + ++m->ntabs; 311 + if(m->ntabs >= MAXTABS) break; 312 + } 313 + 314 + if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated 315 + memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); 316 + qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); 317 + tot_width = view_info_w; 318 + for(i = 0; i < m->ntabs; ++i){ 319 + if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) 320 + break; 321 + tot_width += sorted_label_widths[i]; 322 + } 323 + maxsize = (m->ww - tot_width) / (m->ntabs - i); 324 + } else{ 325 + maxsize = m->ww; 326 + } 327 + i = 0; 328 + for(c = m->clients; c; c = c->next){ 329 + if(!ISVISIBLE(c)) continue; 330 + if(i >= m->ntabs) break; 331 + if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize; 332 + w = m->tab_widths[i]; 333 + drw_setscheme(drw, (c == m->sel) ? scheme[SchemeSel] : scheme[SchemeNorm]); 334 + drw_text(drw, x, 0, w, th, lrpad / 2, c->name, 0); 335 + x += w; 336 + ++i; 337 + } 338 + 339 + drw_setscheme(drw, scheme[SchemeNorm]); 340 + 341 + /* cleans interspace between window names and current viewed tag label */ 342 + w = m->ww - view_info_w - x; 343 + drw_text(drw, x, 0, w, th, lrpad / 2, "", 0); 344 + 345 + /* view info */ 346 + x += w; 347 + w = view_info_w; 348 + drw_text(drw, x, 0, w, th, lrpad / 2, view_info, 0); 349 + 350 + drw_map(drw, m->tabwin, 0, 0, m->ww, th); 351 +} 352 + 353 +void 354 enternotify(XEvent *e) 355 { 356 Client *c; 357 @@ -789,8 +928,10 @@ expose(XEvent *e) 358 Monitor *m; 359 XExposeEvent *ev = &e->xexpose; 360 361 - if (ev->count == 0 && (m = wintomon(ev->window))) 362 + if(ev->count == 0 && (m = wintomon(ev->window))){ 363 drawbar(m); 364 + drawtab(m); 365 + } 366 } 367 368 void 369 @@ -817,6 +958,7 @@ focus(Client *c) 370 } 371 selmon->sel = c; 372 drawbars(); 373 + drawtabs(); 374 } 375 376 /* there are some broken focus acquiring clients */ 377 @@ -870,6 +1012,19 @@ focusstack(const Arg *arg) 378 } 379 } 380 381 +void 382 +focuswin(const Arg* arg){ 383 + int iwin = arg->i; 384 + Client* c = NULL; 385 + for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){ 386 + if(ISVISIBLE(c)) --iwin; 387 + }; 388 + if(c) { 389 + focus(c); 390 + restack(selmon); 391 + } 392 +} 393 + 394 Atom 395 getatomprop(Client *c, Atom prop) 396 { 397 @@ -1253,12 +1408,14 @@ propertynotify(XEvent *e) 398 case XA_WM_HINTS: 399 updatewmhints(c); 400 drawbars(); 401 + drawtabs(); 402 break; 403 } 404 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { 405 updatetitle(c); 406 if (c == c->mon->sel) 407 drawbar(c->mon); 408 + drawtab(c->mon); 409 } 410 if (ev->atom == netatom[NetWMWindowType]) 411 updatewindowtype(c); 412 @@ -1372,6 +1529,7 @@ restack(Monitor *m) 413 XWindowChanges wc; 414 415 drawbar(m); 416 + drawtab(m); 417 if (!m->sel) 418 return; 419 if (m->sel->isfloating || !m->lt[m->sellt]->arrange) 420 @@ -1564,6 +1722,7 @@ setup(void) 421 die("no fonts could be loaded.\n"); 422 lrpad = drw->fonts->h; 423 bh = drw->fonts->h + 2; 424 + th = bh; 425 updategeom(); 426 /* init atoms */ 427 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 428 @@ -1698,6 +1857,17 @@ togglebar(const Arg *arg) 429 } 430 431 void 432 +tabmode(const Arg *arg) 433 +{ 434 + if(arg && arg->i >= 0) 435 + selmon->showtab = arg->ui % showtab_nmodes; 436 + else 437 + selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes; 438 + arrange(selmon); 439 +} 440 + 441 + 442 +void 443 togglefloating(const Arg *arg) 444 { 445 if (!selmon->sel) 446 @@ -1808,20 +1978,44 @@ updatebars(void) 447 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 448 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 449 XMapRaised(dpy, m->barwin); 450 + m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), 451 + CopyFromParent, DefaultVisual(dpy, screen), 452 + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 453 + XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor); 454 + XMapRaised(dpy, m->tabwin); 455 } 456 } 457 458 void 459 updatebarpos(Monitor *m) 460 { 461 + Client *c; 462 + int nvis = 0; 463 + 464 m->wy = m->my; 465 m->wh = m->mh; 466 if (m->showbar) { 467 m->wh -= bh; 468 m->by = m->topbar ? m->wy : m->wy + m->wh; 469 - m->wy = m->topbar ? m->wy + bh : m->wy; 470 - } else 471 + if ( m->topbar ) 472 + m->wy += bh; 473 + } else { 474 m->by = -bh; 475 + } 476 + 477 + for(c = m->clients; c; c = c->next){ 478 + if(ISVISIBLE(c)) ++nvis; 479 + } 480 + 481 + if(m->showtab == showtab_always 482 + || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){ 483 + m->wh -= th; 484 + m->ty = m->toptab ? m->wy : m->wy + m->wh; 485 + if ( m->toptab ) 486 + m->wy += th; 487 + } else { 488 + m->ty = -th; 489 + } 490 } 491 492 void 493 @@ -2062,7 +2256,7 @@ wintomon(Window w) 494 if (w == root && getrootptr(&x, &y)) 495 return recttomon(x, y, 1, 1); 496 for (m = mons; m; m = m->next) 497 - if (w == m->barwin) 498 + if (w == m->barwin || w == m->tabwin) 499 return m; 500 if ((c = wintoclient(w))) 501 return c->mon;