sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

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