sites

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

dwm-tab-i3like-20211121-a786211.diff (14158B)


      1 From 5e489a57cdce6517996df26808b58bdd32bbd99f Mon Sep 17 00:00:00 2001
      2 From: howoz <howoz@airmail.cc>
      3 Date: Sun, 21 Nov 2021 16:23:04 +0300
      4 Subject: [PATCH] [dwm][patches][tab] i3 like tabs that cover the whole screen
      5  width
      6 
      7 ---
      8  config.def.h |   9 +++
      9  dwm.1        |  33 ++++++++---
     10  dwm.c        | 160 +++++++++++++++++++++++++++++++++++++++++++++++----
     11  3 files changed, 183 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..8404747 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,56 @@ 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 +void
    257 +drawtab(Monitor *m) {
    258 +	Client *c;
    259 +	int i;
    260 +	int maxsize;
    261 +	int remainder = 0;
    262 +	int x = 0;
    263 +	int w = 0;
    264 +
    265 +	/* Calculates number of labels and their width */
    266 +	m->ntabs = 0;
    267 +	for(c = m->clients; c; c = c->next){
    268 +	  if(!ISVISIBLE(c)) continue;
    269 +	  m->tab_widths[m->ntabs] = (int)TEXTW(c->name);
    270 +	  ++m->ntabs;
    271 +	  if(m->ntabs >= MAXTABS) break;
    272 +	}
    273 +
    274 +	if(m->ntabs > 0) remainder = m->mw % m->ntabs;
    275 +	maxsize = (1.0 / (double)m->ntabs) * m->mw;
    276 +
    277 +	i = 0;
    278 +	int tm; /* middle of the tab*/
    279 +	for(c = m->clients; c; c = c->next){
    280 +	  if(!ISVISIBLE(c)) continue;
    281 +	  if(i >= m->ntabs) break;
    282 +	  m->tab_widths[i] = maxsize;
    283 +	  /* add the remainder to the last tab so there is no leftover space left*/
    284 +	  if(remainder && i == m->ntabs - 1) m->tab_widths[i] += remainder;
    285 +	  w = m->tab_widths[i];
    286 +	  drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSel : SchemeNorm]);
    287 +	  tm = (m->tab_widths[i] - (int)TEXTW(c->name)) / 2;
    288 +	  tm = (int)TEXTW(c->name) >= m->tab_widths[i] ? lrpad / 2 : tm;
    289 +	  drw_text(drw, x, 0, w, th, tm, c->name, 0);
    290 +	  x += w;
    291 +	  ++i;
    292 +	}
    293 +
    294 +	drw_setscheme(drw, scheme[SchemeNorm]);
    295 +	drw_map(drw, m->tabwin, 0, 0, m->mw, th);
    296 +}
    297 +
    298  void
    299  enternotify(XEvent *e)
    300  {
    301 @@ -777,8 +862,10 @@ expose(XEvent *e)
    302  	Monitor *m;
    303  	XExposeEvent *ev = &e->xexpose;
    304  
    305 -	if (ev->count == 0 && (m = wintomon(ev->window)))
    306 +	if(ev->count == 0 && (m = wintomon(ev->window))){
    307  		drawbar(m);
    308 +		drawtab(m);
    309 +	}
    310  }
    311  
    312  void
    313 @@ -804,6 +891,7 @@ focus(Client *c)
    314  	}
    315  	selmon->sel = c;
    316  	drawbars();
    317 +	drawtabs();
    318  }
    319  
    320  /* there are some broken focus acquiring clients needing extra handling */
    321 @@ -856,6 +944,19 @@ focusstack(const Arg *arg)
    322  	}
    323  }
    324  
    325 +void
    326 +focuswin(const Arg* arg){
    327 +  int iwin = arg->i;
    328 +  Client* c = NULL;
    329 +  for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
    330 +    if(ISVISIBLE(c)) --iwin;
    331 +  };
    332 +  if(c) {
    333 +    focus(c);
    334 +    restack(selmon);
    335 +  }
    336 +}
    337 +
    338  Atom
    339  getatomprop(Client *c, Atom prop)
    340  {
    341 @@ -1234,12 +1335,14 @@ propertynotify(XEvent *e)
    342  		case XA_WM_HINTS:
    343  			updatewmhints(c);
    344  			drawbars();
    345 +			drawtabs();
    346  			break;
    347  		}
    348  		if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
    349  			updatetitle(c);
    350  			if (c == c->mon->sel)
    351  				drawbar(c->mon);
    352 +			drawtab(c->mon);
    353  		}
    354  		if (ev->atom == netatom[NetWMWindowType])
    355  			updatewindowtype(c);
    356 @@ -1353,6 +1456,7 @@ restack(Monitor *m)
    357  	XWindowChanges wc;
    358  
    359  	drawbar(m);
    360 +	drawtab(m);
    361  	if (!m->sel)
    362  		return;
    363  	if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
    364 @@ -1547,6 +1651,7 @@ setup(void)
    365  		die("no fonts could be loaded.");
    366  	lrpad = drw->fonts->h;
    367  	bh = drw->fonts->h + 2;
    368 +	th = bh;
    369  	updategeom();
    370  	/* init atoms */
    371  	utf8string = XInternAtom(dpy, "UTF8_STRING", False);
    372 @@ -1708,6 +1813,17 @@ togglebar(const Arg *arg)
    373  	arrange(selmon);
    374  }
    375  
    376 +void
    377 +tabmode(const Arg *arg)
    378 +{
    379 +	if(arg && arg->i >= 0)
    380 +		selmon->showtab = arg->ui % showtab_nmodes;
    381 +	else
    382 +		selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
    383 +	arrange(selmon);
    384 +}
    385 +
    386 +
    387  void
    388  togglefloating(const Arg *arg)
    389  {
    390 @@ -1819,6 +1935,11 @@ updatebars(void)
    391  				CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
    392  		XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
    393  		XMapRaised(dpy, m->barwin);
    394 +		m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
    395 +						CopyFromParent, DefaultVisual(dpy, screen),
    396 +						CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
    397 +		XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
    398 +		XMapRaised(dpy, m->tabwin);
    399  		XSetClassHint(dpy, m->barwin, &ch);
    400  	}
    401  }
    402 @@ -1826,14 +1947,33 @@ updatebars(void)
    403  void
    404  updatebarpos(Monitor *m)
    405  {
    406 +	Client *c;
    407 +	int nvis = 0;
    408 +
    409  	m->wy = m->my;
    410  	m->wh = m->mh;
    411  	if (m->showbar) {
    412  		m->wh -= bh;
    413  		m->by = m->topbar ? m->wy : m->wy + m->wh;
    414 -		m->wy = m->topbar ? m->wy + bh : m->wy;
    415 -	} else
    416 +		if ( m->topbar )
    417 +			m->wy += bh;
    418 +	} else {
    419  		m->by = -bh;
    420 +	}
    421 +
    422 +	for(c = m->clients; c; c = c->next) {
    423 +		if(ISVISIBLE(c)) ++nvis;
    424 +	}
    425 +
    426 +	if(m->showtab == showtab_always
    427 +	   || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))) {
    428 +		m->wh -= th;
    429 +		m->ty = m->toptab ? m->wy : m->wy + m->wh;
    430 +		if ( m->toptab )
    431 +			m->wy += th;
    432 +	} else {
    433 +		m->ty = -th;
    434 +	}
    435  }
    436  
    437  void
    438 @@ -2070,7 +2210,7 @@ wintomon(Window w)
    439  	if (w == root && getrootptr(&x, &y))
    440  		return recttomon(x, y, 1, 1);
    441  	for (m = mons; m; m = m->next)
    442 -		if (w == m->barwin)
    443 +		if (w == m->barwin || w == m->tabwin)
    444  			return m;
    445  	if ((c = wintoclient(w)))
    446  		return c->mon;
    447 -- 
    448 2.34.0
    449