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