dwm-tab_pertag-20260512-44dbc68.diff (16672B)
1 From d7c08095ed378b72710dfb61808fd78ca9384bfb Mon Sep 17 00:00:00 2001 2 From: kanishk <saini07kanishk@gmail.com> 3 Date: Tue, 12 May 2026 18:16:49 +0530 4 Subject: [PATCH] tab with pertag 5 6 --- 7 config.def.h | 7 ++ 8 dwm.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++----- 9 2 files changed, 226 insertions(+), 22 deletions(-) 10 11 diff --git a/config.def.h b/config.def.h 12 index 81c3fc0..925979b 100644 13 --- a/config.def.h 14 +++ b/config.def.h 15 @@ -5,8 +5,13 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ 16 static const unsigned int snap = 32; /* snap pixel */ 17 static const int showbar = 1; /* 0 means no bar */ 18 static const int topbar = 1; /* 0 means bottom bar */ 19 +static const int showtab = 1; /* 0 means no tabbar */ 20 +static const int alltab = 0; /* 0 means no tabbar for all layouts */ 21 +static const int toptab = 0; /* 1 means top tab bar */ 22 static const char *fonts[] = { "monospace:size=10" }; 23 static const char dmenufont[] = "monospace:size=10"; 24 + 25 + 26 static const char col_gray1[] = "#222222"; 27 static const char col_gray2[] = "#444444"; 28 static const char col_gray3[] = "#bbbbbb"; 29 @@ -66,6 +71,7 @@ static const Key keys[] = { 30 { MODKEY, XK_p, spawn, {.v = dmenucmd } }, 31 { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, 32 { MODKEY, XK_b, togglebar, {0} }, 33 + { MODKEY, XK_w, toggletab, {0} }, // tabmodes 34 { MODKEY, XK_j, focusstack, {.i = +1 } }, 35 { MODKEY, XK_k, focusstack, {.i = -1 } }, 36 { MODKEY, XK_i, incnmaster, {.i = +1 } }, 37 @@ -113,5 +119,6 @@ static const Button buttons[] = { 38 { ClkTagBar, 0, Button3, toggleview, {0} }, 39 { ClkTagBar, MODKEY, Button1, tag, {0} }, 40 { ClkTagBar, MODKEY, Button3, toggletag, {0} }, 41 + { ClkTabBar, 0, Button1, focuswin, {0} }, // tab mode 42 }; 43 44 diff --git a/dwm.c b/dwm.c 45 index fc3365c..cd9c6a6 100644 46 --- a/dwm.c 47 +++ b/dwm.c 48 @@ -56,6 +56,8 @@ 49 #define TAGMASK ((1 << LENGTH(tags)) - 1) 50 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 51 52 + 53 + 54 /* enums */ 55 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 56 enum { SchemeNorm, SchemeSel }; /* color schemes */ 57 @@ -63,7 +65,7 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 58 NetWMFullscreen, NetActiveWindow, NetWMWindowType, 59 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 60 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 61 -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 62 +enum { ClkTagBar,ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 63 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 64 65 typedef union { 66 @@ -110,6 +112,8 @@ typedef struct { 67 void (*arrange)(Monitor *); 68 } Layout; 69 70 +#define MAXTABS 50 // tab 71 + 72 typedef struct Pertag Pertag; 73 struct Monitor { 74 char ltsymbol[16]; 75 @@ -117,18 +121,24 @@ struct Monitor { 76 int nmaster; 77 int num; 78 int by; /* bar geometry */ 79 + int ty; /* tab bar geometry */ 80 int mx, my, mw, mh; /* screen size */ 81 int wx, wy, ww, wh; /* window area */ 82 unsigned int seltags; 83 unsigned int sellt; 84 unsigned int tagset[2]; 85 int showbar; 86 + int showtab;// tab 87 int topbar; 88 + int toptab;// tab 89 Client *clients; 90 Client *sel; 91 Client *stack; 92 Monitor *next; 93 Window barwin; 94 + Window tabwin;// tab 95 + int ntabs;// tab 96 + int tab_widths[MAXTABS];// tab 97 const Layout *lt[2]; 98 Pertag *pertag; 99 }; 100 @@ -164,12 +174,15 @@ static void detachstack(Client *c); 101 static Monitor *dirtomon(int dir); 102 static void drawbar(Monitor *m); 103 static void drawbars(void); 104 +static void drawtab(Monitor *m);// tab 105 +static void drawtabs(void);// tab 106 static void enternotify(XEvent *e); 107 static void expose(XEvent *e); 108 static void focus(Client *c); 109 static void focusin(XEvent *e); 110 static void focusmon(const Arg *arg); 111 static void focusstack(const Arg *arg); 112 +static void focuswin(const Arg* arg);// tab 113 static Atom getatomprop(Client *c, Atom prop); 114 static int getrootptr(int *x, int *y); 115 static long getstate(Window w); 116 @@ -207,6 +220,7 @@ static void setup(void); 117 static void seturgent(Client *c, int urg); 118 static void showhide(Client *c); 119 static void spawn(const Arg *arg); 120 +static void toggletab(const Arg *arg);// tab 121 static void tag(const Arg *arg); 122 static void tagmon(const Arg *arg); 123 static void tile(Monitor *m); 124 @@ -241,6 +255,7 @@ static char stext[256]; 125 static int screen; 126 static int sw, sh; /* X display screen geometry width, height */ 127 static int bh; /* bar height */ 128 +static int th = 0; /* tab bar geometry */// tab 129 static int lrpad; /* sum of left and right padding for text */ 130 static int (*xerrorxlib)(Display *, XErrorEvent *); 131 static unsigned int numlockmask = 0; 132 @@ -279,6 +294,7 @@ struct Pertag { 133 unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ 134 const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ 135 int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ 136 + int showtabs[LENGTH(tags) + 1]; /* display tab for the current tag */ 137 }; 138 139 /* compile-time check if all tags fit into an unsigned int bit array. */ 140 @@ -406,6 +422,8 @@ arrange(Monitor *m) 141 void 142 arrangemon(Monitor *m) 143 { 144 + updatebarpos(selmon);//tab 145 + XMoveResizeWindow(dpy, selmon->tabwin, selmon->wx, selmon->ty, selmon->ww, th);//tab 146 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); 147 if (m->lt[m->sellt]->arrange) 148 m->lt[m->sellt]->arrange(m); 149 @@ -451,11 +469,28 @@ buttonpress(XEvent *e) 150 arg.ui = 1 << i; 151 } else if (ev->x < x + TEXTW(selmon->ltsymbol)) 152 click = ClkLtSymbol; 153 - else if (ev->x > selmon->ww - (int)TEXTW(stext) + lrpad - 2) 154 + else if (ev->x > selmon->ww - (int)TEXTW(stext)) 155 click = ClkStatusText; 156 else 157 click = ClkWinTitle; 158 - } else if ((c = wintoclient(ev->window))) { 159 + } 160 + if(ev->window == selmon->tabwin) {//tab 161 + i = 0; x = 0;//tab 162 + for(c = selmon->clients; c; c = c->next){//tab 163 + if(!ISVISIBLE(c)) continue;//tab 164 + x += selmon->tab_widths[i];//tab 165 + if (ev->x > x)//tab 166 + ++i;//tab 167 + else//tab 168 + break;//tab 169 + if(i >= m->ntabs) break;//tab 170 + }//tab 171 + if(c) {//tab 172 + click = ClkTabBar;//tab 173 + arg.ui = i;//tab 174 + } 175 + } 176 + else if((c = wintoclient(ev->window))) { 177 focus(c); 178 restack(selmon); 179 XAllowEvents(dpy, ReplayPointer, CurrentTime); 180 @@ -463,8 +498,9 @@ buttonpress(XEvent *e) 181 } 182 for (i = 0; i < LENGTH(buttons); i++) 183 if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button 184 - && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) 185 - buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); 186 + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){//tab 187 + buttons[i].func(((click == ClkTagBar || click == ClkTabBar) && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);//tab 188 + } 189 } 190 191 void 192 @@ -519,6 +555,8 @@ cleanupmon(Monitor *mon) 193 } 194 XUnmapWindow(dpy, mon->barwin); 195 XDestroyWindow(dpy, mon->barwin); 196 + XUnmapWindow(dpy, mon->tabwin);//tab 197 + XDestroyWindow(dpy, mon->tabwin);//tab 198 free(mon); 199 } 200 201 @@ -651,7 +689,10 @@ createmon(void) 202 m->mfact = mfact; 203 m->nmaster = nmaster; 204 m->showbar = showbar; 205 + m->showtab = showtab;//tab 206 m->topbar = topbar; 207 + m->toptab = toptab;//tab 208 + m->ntabs = 0;//tab 209 m->lt[0] = &layouts[0]; 210 m->lt[1] = &layouts[1 % LENGTH(layouts)]; 211 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 212 @@ -667,6 +708,7 @@ createmon(void) 213 m->pertag->sellts[i] = m->sellt; 214 215 m->pertag->showbars[i] = m->showbar; 216 + m->pertag->showtabs[i] = m->showtab; 217 } 218 219 return m; 220 @@ -782,6 +824,106 @@ drawbars(void) 221 drawbar(m); 222 } 223 224 +void 225 +drawtabs(void) { 226 + 227 + Monitor *m; 228 + 229 + for(m = mons; m; m = m->next) 230 + drawtab(m); 231 +} 232 + 233 +static int 234 +cmpint(const void *p1, const void *p2) { 235 + /* The actual arguments to this function are "pointers to 236 + pointers to char", but strcmp(3) arguments are "pointers 237 + to char", hence the following cast plus dereference */ 238 + return *((int*) p1) > * (int*) p2; 239 +} 240 + 241 + 242 +void 243 +drawtab(Monitor *m) { 244 + Client *c; 245 + int i; 246 + int itag = -1; 247 + char view_info[50]; 248 + int view_info_w = 0; 249 + int sorted_label_widths[MAXTABS]; 250 + int tot_width; 251 + int maxsize = bh; 252 + int x = 0; 253 + int w = 0; 254 + 255 + //view_info: indicate the tag which is displayed in the view 256 + for(i = 0; i < LENGTH(tags); ++i){ 257 + if((selmon->tagset[selmon->seltags] >> i) & 1) { 258 + if(itag >=0){ //more than one tag selected 259 + itag = -1; 260 + break; 261 + } 262 + itag = i; 263 + } 264 + } 265 + 266 + if(0 <= itag && itag < LENGTH(tags)){ 267 + snprintf(view_info, sizeof view_info, "[%s]", tags[itag]); 268 + } else { 269 + strncpy(view_info, "[...]", sizeof view_info); 270 + } 271 + view_info[sizeof(view_info) - 1 ] = 0; 272 + view_info_w = TEXTW(view_info); 273 + tot_width = view_info_w; 274 + 275 + /* Calculates number of labels and their width */ 276 + m->ntabs = 0; 277 + for(c = m->clients; c; c = c->next){ 278 + if(!ISVISIBLE(c)) continue; 279 + m->tab_widths[m->ntabs] = TEXTW(c->name); 280 + tot_width += m->tab_widths[m->ntabs]; 281 + ++m->ntabs; 282 + if(m->ntabs >= MAXTABS) break; 283 + } 284 + 285 + if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated 286 + memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); 287 + qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); 288 + tot_width = view_info_w; 289 + for(i = 0; i < m->ntabs; ++i){ 290 + if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) 291 + break; 292 + tot_width += sorted_label_widths[i]; 293 + } 294 + maxsize = (m->ww - tot_width) / (m->ntabs - i); 295 + } else{ 296 + maxsize = m->ww; 297 + } 298 + i = 0; 299 + for(c = m->clients; c; c = c->next){ 300 + if(!ISVISIBLE(c)) continue; 301 + if(i >= m->ntabs) break; 302 + if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize; 303 + w = m->tab_widths[i]; 304 + drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSel : SchemeNorm]); 305 + drw_text(drw, x, 0, w, th, 0, c->name, 0); 306 + x += w; 307 + ++i; 308 + } 309 + 310 + drw_setscheme(drw, scheme[SchemeNorm]); 311 + 312 + /* cleans interspace between window names and current viewed tag label */ 313 + w = m->ww - view_info_w - x; 314 + drw_text(drw, x, 0, w, th, 0, "", 0); 315 + 316 + /* view info */ 317 + x += w; 318 + w = view_info_w; 319 + drw_text(drw, x, 0, w, th, 0, view_info, 0); 320 + 321 + drw_map(drw, m->tabwin, 0, 0, m->ww, th); 322 +} 323 + 324 void 325 enternotify(XEvent *e) 326 { 327 @@ -807,8 +949,10 @@ expose(XEvent *e) 328 Monitor *m; 329 XExposeEvent *ev = &e->xexpose; 330 331 - if (ev->count == 0 && (m = wintomon(ev->window))) 332 + if (ev->count == 0 && (m = wintomon(ev->window))){ 333 drawbar(m); 334 + drawtab(m);//tab 335 + } 336 } 337 338 void 339 @@ -834,6 +978,7 @@ focus(Client *c) 340 } 341 selmon->sel = c; 342 drawbars(); 343 + drawtabs();//tab 344 } 345 346 /* there are some broken focus acquiring clients needing extra handling */ 347 @@ -886,18 +1031,31 @@ focusstack(const Arg *arg) 348 } 349 } 350 351 +void 352 +focuswin(const Arg* arg){ 353 + int iwin = arg->i; 354 + Client* c = NULL; 355 + for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){ 356 + if(ISVISIBLE(c)) --iwin; 357 + }; 358 + if(c) { 359 + focus(c); 360 + restack(selmon); 361 + } 362 +} 363 + 364 Atom 365 getatomprop(Client *c, Atom prop) 366 { 367 - int format; 368 + int di; 369 unsigned long nitems, dl; 370 unsigned char *p = NULL; 371 Atom da, atom = None; 372 373 if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 374 - &da, &format, &nitems, &dl, &p) == Success && p) { 375 - if (nitems > 0 && format == 32) 376 - atom = *(long *)p; 377 + &da, &di, &nitems, &dl, &p) == Success && p) { 378 + if (nitems > 0) 379 + atom = *(Atom *)p; 380 XFree(p); 381 } 382 return atom; 383 @@ -923,10 +1081,10 @@ getstate(Window w) 384 Atom real; 385 386 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], 387 - &real, &format, &n, &extra, &p) != Success) 388 + &real, &format, &n, &extra, (unsigned char **)&p) != Success) 389 return -1; 390 - if (n != 0 && format == 32) 391 - result = *(long *)p; 392 + if (n != 0) 393 + result = *p; 394 XFree(p); 395 return result; 396 } 397 @@ -1269,12 +1427,14 @@ propertynotify(XEvent *e) 398 case XA_WM_HINTS: 399 updatewmhints(c); 400 drawbars(); 401 + drawtabs();//tab 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);//tab 409 } 410 if (ev->atom == netatom[NetWMWindowType]) 411 updatewindowtype(c); 412 @@ -1388,6 +1548,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 @@ -1402,7 +1563,8 @@ restack(Monitor *m) 421 } 422 } 423 XSync(dpy, False); 424 - while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 425 + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)) 426 + ; 427 } 428 429 void 430 @@ -1455,8 +1617,6 @@ sendmon(Client *c, Monitor *m) 431 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 432 attach(c); 433 attachstack(c); 434 - if (c->isfullscreen) 435 - resizeclient(c, m->mx, m->my, m->mw, m->mh); 436 focus(NULL); 437 arrange(NULL); 438 } 439 @@ -1498,10 +1658,12 @@ sendevent(Client *c, Atom proto) 440 void 441 setfocus(Client *c) 442 { 443 - if (!c->neverfocus) 444 + if (!c->neverfocus) { 445 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 446 - XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, 447 - PropModeReplace, (unsigned char *)&c->win, 1); 448 + XChangeProperty(dpy, root, netatom[NetActiveWindow], 449 + XA_WINDOW, 32, PropModeReplace, 450 + (unsigned char *) &(c->win), 1); 451 + } 452 sendevent(c, wmatom[WMTakeFocus]); 453 } 454 455 @@ -1589,6 +1751,7 @@ setup(void) 456 die("no fonts could be loaded."); 457 lrpad = drw->fonts->h; 458 bh = drw->fonts->h + 2; 459 + th = bh;//tab 460 updategeom(); 461 /* init atoms */ 462 utf8string = XInternAtom(dpy, "UTF8_STRING", False); 463 @@ -1747,6 +1910,13 @@ togglebar(const Arg *arg) 464 arrange(selmon); 465 } 466 467 + void 468 +toggletab(const Arg *arg) 469 +{ 470 + selmon->showtab= selmon->pertag->showtabs[selmon->pertag->curtag]=!selmon->showtab; 471 + arrange(selmon); 472 +} 473 + 474 void 475 togglefloating(const Arg *arg) 476 { 477 @@ -1807,6 +1977,9 @@ toggleview(const Arg *arg) 478 if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) 479 togglebar(NULL); 480 481 + if (selmon->showtab != selmon->pertag->showtabs[selmon->pertag->curtag]) 482 + toggletab(NULL); 483 + 484 focus(NULL); 485 arrange(selmon); 486 } 487 @@ -1883,6 +2056,11 @@ updatebars(void) 488 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 489 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 490 XMapRaised(dpy, m->barwin); 491 + m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), 492 + CopyFromParent, DefaultVisual(dpy, screen), 493 + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 494 + XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor); 495 + XMapRaised(dpy, m->tabwin); 496 XSetClassHint(dpy, m->barwin, &ch); 497 } 498 } 499 @@ -1890,14 +2068,31 @@ updatebars(void) 500 void 501 updatebarpos(Monitor *m) 502 { 503 + Client *c;//tab 504 + int nvis = 0;//tab 505 m->wy = m->my; 506 m->wh = m->mh; 507 if (m->showbar) { 508 m->wh -= bh; 509 m->by = m->topbar ? m->wy : m->wy + m->wh; 510 - m->wy = m->topbar ? m->wy + bh : m->wy; 511 - } else 512 + if ( m->topbar ) m->wy += bh;//tab 513 + } else {//tab 514 m->by = -bh; 515 + } 516 + 517 + for(c = m->clients; c; c = c->next) {//tab 518 + if(ISVISIBLE(c)) ++nvis;//tab 519 + }//tab 520 +//this check condition to show the tabbar coditions showtab_always and others. 521 +// if(m->showtab == showtab_always || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))) 522 + if((m->showtab==1 && alltab==1) || (m->showtab== 1 && ( nvis >1) && (m->lt[m->sellt]->arrange==monocle )) ){//tab 523 + m->wh -= th;//tab 524 + m->ty = m->toptab ? m->wy : m->wy + m->wh;//tab 525 + if ( m->toptab )//tab 526 + m->wy += th;//tab 527 + } else {//tab 528 + m->ty = -th;//tab 529 + }//tab 530 } 531 532 void 533 @@ -2134,6 +2329,8 @@ view(const Arg *arg) 534 if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) 535 togglebar(NULL); 536 537 + if (selmon->showtab != selmon->pertag->showbars[selmon->pertag->curtag]) 538 + toggletab(NULL); 539 focus(NULL); 540 arrange(selmon); 541 } 542 @@ -2161,7 +2358,7 @@ wintomon(Window w) 543 if (w == root && getrootptr(&x, &y)) 544 return recttomon(x, y, 1, 1); 545 for (m = mons; m; m = m->next) 546 - if (w == m->barwin) 547 + if (w == m->barwin || w == m->tabwin)// condition for tab 548 return m; 549 if ((c = wintoclient(w))) 550 return c->mon; 551 -- 552 2.54.0 553