dwm-tab-20260512-44dbc68.diff (15408B)
1 From d72a4be8178a9704753e9c5c00f083e499e001ac Mon Sep 17 00:00:00 2001 2 From: kanishk <saini07kanishk@gmail.com> 3 Date: Tue, 12 May 2026 16:55:03 +0530 4 Subject: [PATCH] tab feature 5 6 --- 7 config.def.h | 7 ++ 8 dwm.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++----- 9 2 files changed, 219 insertions(+), 22 deletions(-) 10 11 diff --git a/config.def.h b/config.def.h 12 index 81c3fc0..2aba0a4 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 in 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 ab3a84c..18628ef 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,24 +112,32 @@ typedef struct { 67 void (*arrange)(Monitor *); 68 } Layout; 69 70 +#define MAXTABS 50 // tab 71 + 72 struct Monitor { 73 char ltsymbol[16]; 74 float mfact; 75 int nmaster; 76 int num; 77 int by; /* bar geometry */ 78 + int ty; /* tab bar geometry */ 79 int mx, my, mw, mh; /* screen size */ 80 int wx, wy, ww, wh; /* window area */ 81 unsigned int seltags; 82 unsigned int sellt; 83 unsigned int tagset[2]; 84 int showbar; 85 + int showtab;// tab 86 int topbar; 87 + int toptab;// tab 88 Client *clients; 89 Client *sel; 90 Client *stack; 91 Monitor *next; 92 Window barwin; 93 + Window tabwin;// tab 94 + int ntabs;// tab 95 + int tab_widths[MAXTABS];// tab 96 const Layout *lt[2]; 97 }; 98 99 @@ -162,12 +172,15 @@ static void detachstack(Client *c); 100 static Monitor *dirtomon(int dir); 101 static void drawbar(Monitor *m); 102 static void drawbars(void); 103 +static void drawtab(Monitor *m);// tab 104 +static void drawtabs(void);// tab 105 static void enternotify(XEvent *e); 106 static void expose(XEvent *e); 107 static void focus(Client *c); 108 static void focusin(XEvent *e); 109 static void focusmon(const Arg *arg); 110 static void focusstack(const Arg *arg); 111 +static void focuswin(const Arg* arg);// tab 112 static Atom getatomprop(Client *c, Atom prop); 113 static int getrootptr(int *x, int *y); 114 static long getstate(Window w); 115 @@ -205,6 +218,7 @@ static void setup(void); 116 static void seturgent(Client *c, int urg); 117 static void showhide(Client *c); 118 static void spawn(const Arg *arg); 119 +static void toggletab(const Arg *arg);// tab 120 static void tag(const Arg *arg); 121 static void tagmon(const Arg *arg); 122 static void tile(Monitor *m); 123 @@ -239,6 +253,7 @@ static char stext[256]; 124 static int screen; 125 static int sw, sh; /* X display screen geometry width, height */ 126 static int bh; /* bar height */ 127 +static int th = 0; /* tab bar geometry */// tab 128 static int lrpad; /* sum of left and right padding for text */ 129 static int (*xerrorxlib)(Display *, XErrorEvent *); 130 static unsigned int numlockmask = 0; 131 @@ -395,6 +410,8 @@ arrange(Monitor *m) 132 void 133 arrangemon(Monitor *m) 134 { 135 + updatebarpos(selmon);//tab//tab 136 + XMoveResizeWindow(dpy, selmon->tabwin, selmon->wx, selmon->ty, selmon->ww, th);//tab 137 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); 138 if (m->lt[m->sellt]->arrange) 139 m->lt[m->sellt]->arrange(m); 140 @@ -440,11 +457,28 @@ buttonpress(XEvent *e) 141 arg.ui = 1 << i; 142 } else if (ev->x < x + TEXTW(selmon->ltsymbol)) 143 click = ClkLtSymbol; 144 - else if (ev->x > selmon->ww - (int)TEXTW(stext) + lrpad - 2) 145 + else if (ev->x > selmon->ww - (int)TEXTW(stext)) 146 click = ClkStatusText; 147 else 148 click = ClkWinTitle; 149 - } else if ((c = wintoclient(ev->window))) { 150 + } 151 + if(ev->window == selmon->tabwin) {//tab 152 + i = 0; x = 0;//tab 153 + for(c = selmon->clients; c; c = c->next){//tab 154 + if(!ISVISIBLE(c)) continue;//tab 155 + x += selmon->tab_widths[i];//tab 156 + if (ev->x > x)//tab 157 + ++i;//tab 158 + else//tab 159 + break;//tab 160 + if(i >= m->ntabs) break;//tab 161 + }//tab 162 + if(c) {//tab 163 + click = ClkTabBar;//tab 164 + arg.ui = i;//tab 165 + } 166 + } 167 + else if((c = wintoclient(ev->window))) { 168 focus(c); 169 restack(selmon); 170 XAllowEvents(dpy, ReplayPointer, CurrentTime); 171 @@ -452,8 +486,9 @@ buttonpress(XEvent *e) 172 } 173 for (i = 0; i < LENGTH(buttons); i++) 174 if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button 175 - && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) 176 - buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); 177 + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){//tab 178 + buttons[i].func(((click == ClkTagBar || click == ClkTabBar) && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);//tab 179 + } 180 } 181 182 void 183 @@ -508,6 +543,8 @@ cleanupmon(Monitor *mon) 184 } 185 XUnmapWindow(dpy, mon->barwin); 186 XDestroyWindow(dpy, mon->barwin); 187 + XUnmapWindow(dpy, mon->tabwin);//tab 188 + XDestroyWindow(dpy, mon->tabwin);//tab 189 free(mon); 190 } 191 192 @@ -639,7 +676,10 @@ createmon(void) 193 m->mfact = mfact; 194 m->nmaster = nmaster; 195 m->showbar = showbar; 196 + m->showtab = showtab;//tab 197 m->topbar = topbar; 198 + m->toptab = toptab;//tab 199 + m->ntabs = 0;//tab 200 m->lt[0] = &layouts[0]; 201 m->lt[1] = &layouts[1 % LENGTH(layouts)]; 202 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 203 @@ -756,6 +796,106 @@ drawbars(void) 204 drawbar(m); 205 } 206 207 +void 208 +drawtabs(void) { 209 + 210 + Monitor *m; 211 + 212 + for(m = mons; m; m = m->next) 213 + drawtab(m); 214 +} 215 + 216 +static int 217 +cmpint(const void *p1, const void *p2) { 218 + /* The actual arguments to this function are "pointers to 219 + pointers to char", but strcmp(3) arguments are "pointers 220 + to char", hence the following cast plus dereference */ 221 + return *((int*) p1) > * (int*) p2; 222 +} 223 + 224 + 225 +void 226 +drawtab(Monitor *m) { 227 + Client *c; 228 + int i; 229 + int itag = -1; 230 + char view_info[50]; 231 + int view_info_w = 0; 232 + int sorted_label_widths[MAXTABS]; 233 + int tot_width; 234 + int maxsize = bh; 235 + int x = 0; 236 + int w = 0; 237 + 238 + //view_info: indicate the tag which is displayed in the view 239 + for(i = 0; i < LENGTH(tags); ++i){ 240 + if((selmon->tagset[selmon->seltags] >> i) & 1) { 241 + if(itag >=0){ //more than one tag selected 242 + itag = -1; 243 + break; 244 + } 245 + itag = i; 246 + } 247 + } 248 + 249 + if(0 <= itag && itag < LENGTH(tags)){ 250 + snprintf(view_info, sizeof view_info, "[%s]", tags[itag]); 251 + } else { 252 + strncpy(view_info, "[...]", sizeof view_info); 253 + } 254 + view_info[sizeof(view_info) - 1 ] = 0; 255 + view_info_w = TEXTW(view_info); 256 + tot_width = view_info_w; 257 + 258 + /* Calculates number of labels and their width */ 259 + m->ntabs = 0; 260 + for(c = m->clients; c; c = c->next){ 261 + if(!ISVISIBLE(c)) continue; 262 + m->tab_widths[m->ntabs] = TEXTW(c->name); 263 + tot_width += m->tab_widths[m->ntabs]; 264 + ++m->ntabs; 265 + if(m->ntabs >= MAXTABS) break; 266 + } 267 + 268 + if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated 269 + memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); 270 + qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); 271 + tot_width = view_info_w; 272 + for(i = 0; i < m->ntabs; ++i){ 273 + if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) 274 + break; 275 + tot_width += sorted_label_widths[i]; 276 + } 277 + maxsize = (m->ww - tot_width) / (m->ntabs - i); 278 + } else{ 279 + maxsize = m->ww; 280 + } 281 + i = 0; 282 + for(c = m->clients; c; c = c->next){ 283 + if(!ISVISIBLE(c)) continue; 284 + if(i >= m->ntabs) break; 285 + if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize; 286 + w = m->tab_widths[i]; 287 + drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSel : SchemeNorm]); 288 + drw_text(drw, x, 0, w, th, 0, c->name, 0); 289 + x += w; 290 + ++i; 291 + } 292 + 293 + drw_setscheme(drw, scheme[SchemeNorm]); 294 + 295 + /* cleans interspace between window names and current viewed tag label */ 296 + w = m->ww - view_info_w - x; 297 + drw_text(drw, x, 0, w, th, 0, "", 0); 298 + 299 + /* view info */ 300 + x += w; 301 + w = view_info_w; 302 + drw_text(drw, x, 0, w, th, 0, view_info, 0); 303 + 304 + drw_map(drw, m->tabwin, 0, 0, m->ww, th); 305 +} 306 + 307 void 308 enternotify(XEvent *e) 309 { 310 @@ -781,8 +921,10 @@ expose(XEvent *e) 311 Monitor *m; 312 XExposeEvent *ev = &e->xexpose; 313 314 - if (ev->count == 0 && (m = wintomon(ev->window))) 315 + if (ev->count == 0 && (m = wintomon(ev->window))){ 316 drawbar(m); 317 + drawtab(m);//tab 318 + } 319 } 320 321 void 322 @@ -808,6 +950,7 @@ focus(Client *c) 323 } 324 selmon->sel = c; 325 drawbars(); 326 + drawtabs();//tab 327 } 328 329 /* there are some broken focus acquiring clients needing extra handling */ 330 @@ -860,18 +1003,31 @@ focusstack(const Arg *arg) 331 } 332 } 333 334 +void 335 +focuswin(const Arg* arg){ 336 + int iwin = arg->i; 337 + Client* c = NULL; 338 + for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){ 339 + if(ISVISIBLE(c)) --iwin; 340 + }; 341 + if(c) { 342 + focus(c); 343 + restack(selmon); 344 + } 345 +} 346 + 347 Atom 348 getatomprop(Client *c, Atom prop) 349 { 350 - int format; 351 + int di; 352 unsigned long nitems, dl; 353 unsigned char *p = NULL; 354 Atom da, atom = None; 355 356 if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 357 - &da, &format, &nitems, &dl, &p) == Success && p) { 358 - if (nitems > 0 && format == 32) 359 - atom = *(long *)p; 360 + &da, &di, &nitems, &dl, &p) == Success && p) { 361 + if (nitems > 0) 362 + atom = *(Atom *)p; 363 XFree(p); 364 } 365 return atom; 366 @@ -897,10 +1053,10 @@ getstate(Window w) 367 Atom real; 368 369 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], 370 - &real, &format, &n, &extra, &p) != Success) 371 + &real, &format, &n, &extra, (unsigned char **)&p) != Success) 372 return -1; 373 - if (n != 0 && format == 32) 374 - result = *(long *)p; 375 + if (n != 0) 376 + result = *p; 377 XFree(p); 378 return result; 379 } 380 @@ -1243,12 +1399,14 @@ propertynotify(XEvent *e) 381 case XA_WM_HINTS: 382 updatewmhints(c); 383 drawbars(); 384 + drawtabs();//tab 385 break; 386 } 387 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { 388 updatetitle(c); 389 if (c == c->mon->sel) 390 drawbar(c->mon); 391 + drawtab(c->mon);//tab 392 } 393 if (ev->atom == netatom[NetWMWindowType]) 394 updatewindowtype(c); 395 @@ -1362,6 +1520,7 @@ restack(Monitor *m) 396 XWindowChanges wc; 397 398 drawbar(m); 399 + drawtab(m); 400 if (!m->sel) 401 return; 402 if (m->sel->isfloating || !m->lt[m->sellt]->arrange) 403 @@ -1376,7 +1535,8 @@ restack(Monitor *m) 404 } 405 } 406 XSync(dpy, False); 407 - while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 408 + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)) 409 + ; 410 } 411 412 void 413 @@ -1429,8 +1589,6 @@ sendmon(Client *c, Monitor *m) 414 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 415 attach(c); 416 attachstack(c); 417 - if (c->isfullscreen) 418 - resizeclient(c, m->mx, m->my, m->mw, m->mh); 419 focus(NULL); 420 arrange(NULL); 421 } 422 @@ -1472,10 +1630,12 @@ sendevent(Client *c, Atom proto) 423 void 424 setfocus(Client *c) 425 { 426 - if (!c->neverfocus) 427 + if (!c->neverfocus) { 428 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 429 - XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, 430 - PropModeReplace, (unsigned char *)&c->win, 1); 431 + XChangeProperty(dpy, root, netatom[NetActiveWindow], 432 + XA_WINDOW, 32, PropModeReplace, 433 + (unsigned char *) &(c->win), 1); 434 + } 435 sendevent(c, wmatom[WMTakeFocus]); 436 } 437 438 @@ -1563,6 +1723,7 @@ setup(void) 439 die("no fonts could be loaded."); 440 lrpad = drw->fonts->h; 441 bh = drw->fonts->h + 2; 442 + th = bh;//tab 443 updategeom(); 444 /* init atoms */ 445 utf8string = XInternAtom(dpy, "UTF8_STRING", False); 446 @@ -1721,6 +1882,13 @@ togglebar(const Arg *arg) 447 arrange(selmon); 448 } 449 450 + void 451 +toggletab(const Arg *arg) 452 +{ 453 + selmon->showtab=!selmon->showtab; 454 + arrange(selmon); 455 +} 456 + 457 void 458 togglefloating(const Arg *arg) 459 { 460 @@ -1833,6 +2001,11 @@ updatebars(void) 461 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 462 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 463 XMapRaised(dpy, m->barwin); 464 + m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), 465 + CopyFromParent, DefaultVisual(dpy, screen), 466 + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 467 + XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor); 468 + XMapRaised(dpy, m->tabwin); 469 XSetClassHint(dpy, m->barwin, &ch); 470 } 471 } 472 @@ -1840,14 +2013,31 @@ updatebars(void) 473 void 474 updatebarpos(Monitor *m) 475 { 476 + Client *c;//tab 477 + int nvis = 0;//tab 478 m->wy = m->my; 479 m->wh = m->mh; 480 if (m->showbar) { 481 m->wh -= bh; 482 m->by = m->topbar ? m->wy : m->wy + m->wh; 483 - m->wy = m->topbar ? m->wy + bh : m->wy; 484 - } else 485 + if ( m->topbar ) m->wy += bh;//tab 486 + } else {//tab 487 m->by = -bh; 488 + } 489 + 490 + for(c = m->clients; c; c = c->next) {//tab 491 + if(ISVISIBLE(c)) ++nvis;//tab 492 + }//tab 493 +//this check condition to show the tabbar coditions showtab_always and others. 494 +// if(m->showtab == showtab_always || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))) 495 + if((m->showtab==1 && alltab==1) || (m->showtab== 1 && ( nvis >1) && (m->lt[m->sellt]->arrange==monocle )) ){//tab 496 + m->wh -= th;//tab 497 + m->ty = m->toptab ? m->wy : m->wy + m->wh;//tab 498 + if ( m->toptab )//tab 499 + m->wy += th;//tab 500 + } else {//tab 501 + m->ty = -th;//tab 502 + }//tab 503 } 504 505 void 506 @@ -2085,7 +2275,7 @@ wintomon(Window w) 507 if (w == root && getrootptr(&x, &y)) 508 return recttomon(x, y, 1, 1); 509 for (m = mons; m; m = m->next) 510 - if (w == m->barwin) 511 + if (w == m->barwin || w == m->tabwin)// condition for tab 512 return m; 513 if ((c = wintoclient(w))) 514 return c->mon; 515 -- 516 2.54.0 517