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