summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLM-LCL <hello@exaltedelite.club>2025-09-13 22:49:15 +0200
committerLM-LCL <hello@exaltedelite.club>2025-09-13 22:49:15 +0200
commiteaeb8684a2c614c709d8a32ae4290128bf830831 (patch)
tree69a8782586ad81f6edf55388b9da32204829884f
parent6760362a1df2efaf68cf6e13685a6cbc68a1684e (diff)
dwm-6.6
-rw-r--r--config.def.h63
-rw-r--r--config.h63
-rw-r--r--config.mk2
-rw-r--r--drw.c116
-rw-r--r--dwm.133
-rw-r--r--dwm.c210
-rw-r--r--patches/dwm-clientresizehints-6.5.diff76
-rw-r--r--patches/dwm-focusonnetactive-6.2.diff57
-rw-r--r--patches/dwm-tab-i3like-20211121-a786211.diff449
-rw-r--r--util.c13
-rw-r--r--util.h1
11 files changed, 854 insertions, 229 deletions
diff --git a/config.def.h b/config.def.h
index c74efdb..8c9a237 100644
--- a/config.def.h
+++ b/config.def.h
@@ -18,13 +18,19 @@ static const char col_gray2[] = "#353b45";
static const char col_white[] = "#abb2bf";
static const char col_orange[] = "#ff6a00";
static const char *colors[][3] = {
- /* fg bg border */
- [SchemeNorm] = { col_white, col_gray1, col_gray2 },
- [SchemeSel] = { col_white, col_gray2, col_orange },
+ /* fg bg border */
+ [SchemeNorm] = { col_white, col_gray1, col_gray2 },
+ [SchemeSel] = { col_white, col_gray2, col_orange },
+ [SchemeSelTab] = { col_orange, col_gray2 },
};
+/* tab */
+enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
+static const int showtab = showtab_auto; /* Default tab bar show mode */
+static const int toptab = False; /* False means bottom tab bar */
+
/* tagging */
-static const char *tags[] = { "", "", "", "", "", "", "", "", "" };
+static const char *tags[] = { "", "", "", "", "", "", "", "", "" };
static const Rule rules[] = {
/* xprop(1):
@@ -32,37 +38,37 @@ static const Rule rules[] = {
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
- { "mpv", NULL, NULL, 1 << 1, 0, -1 },
- { "firefox-esr", NULL, NULL, 1 << 2, 0, -1 },
- { "Brave-browser", NULL, NULL, 1 << 2, 0, -1 },
- { "VSCodium", NULL, NULL, 1 << 3, 0, -1 },
- { "libreoffice-startcenter", NULL, NULL, 1 << 4, 0, -1 },
- { "Soffice", NULL, NULL, 1 << 4, 0, -1 },
- { "Virt-manager", NULL, NULL, 1 << 5, 0, -1 },
{ "Pcmanfm", NULL, NULL, 0, 1, -1 },
- { "scrcpy", NULL, NULL, 1 << 6, 1, -1 },
- { "Gimp", NULL, NULL, 1 << 6, 1, -1 },
- { "Inkscape", NULL, NULL, 1 << 6, 0, -1 },
- { "Audacity", NULL, NULL, 1 << 6, 0, -1 },
- { "kdenlive", NULL, NULL, 1 << 6, 0, -1 },
- { "obs", NULL, NULL, 1 << 6, 0, -1 },
- { "qBittorrent", NULL, NULL, 1 << 6, 0, -1 },
- { "Mumble", NULL, NULL, 1 << 7, 0, -1 },
- { "steam", NULL, NULL, 1 << 7, 0, -1 },
- { "discord", NULL, NULL, 1 << 7, 0, -1 },
+ { "firefox-esr", NULL, NULL, 1 << 1, 0, -1 },
+ { "Brave-browser", NULL, NULL, 1 << 1, 0, -1 },
+ { "VSCodium", NULL, NULL, 1 << 2, 0, -1 },
+ { "mpv", NULL, NULL, 1 << 4, 0, -1 },
+ { "Mumble", NULL, NULL, 1 << 5, 0, -1 },
+ { "dino-im", NULL, NULL, 1 << 5, 0, -1 },
+ { "steam", NULL, NULL, 1 << 5, 0, -1 },
+ { "discord", NULL, NULL, 1 << 5, 0, -1 },
+ { "libreoffice-startcenter", NULL, NULL, 1 << 6, 0, -1 },
+ { "Soffice", NULL, NULL, 1 << 6, 0, -1 },
+ { "scrcpy", NULL, NULL, 1 << 7, 1, -1 },
+ { "Gimp", NULL, NULL, 1 << 7, 1, -1 },
+ { "Inkscape", NULL, NULL, 1 << 7, 0, -1 },
+ { "Audacity", NULL, NULL, 1 << 7, 0, -1 },
+ { "kdenlive", NULL, NULL, 1 << 7, 0, -1 },
+ { "obs", NULL, NULL, 1 << 7, 0, -1 },
+ { "qBittorrent", NULL, NULL, 1 << 7, 0, -1 },
+ { "Virt-manager", NULL, NULL, 1 << 8, 0, -1 },
};
/* layout(s) */
-static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
+static const float mfact = 0.5; /* factor of master area size [0.05..0.95] */
static const int nmaster = 1; /* number of clients in master area */
static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */
-static const int attachbelow = 0; /* 1 means attach after the currently active window */
+static const int attachbelow = 1; /* 1 means attach after the currently active window */
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile }, /* first entry is default */
- { "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
@@ -81,15 +87,16 @@ static const Layout layouts[] = {
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_white, "-sb", col_gray2, "-sf", col_white, NULL };
static const char *termcmd[] = { "st", NULL };
-static const char *dmenutab[] = { "alt-tab" };
static const char *prtsccmd[] = { "prtsc" };
static const char *prtscfcmd[] = { "prtscf" };
+static const char *prtscdcmd[] = { "prtscd" };
#include "movestack.c"
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd} },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd} },
+ { MODKEY, XK_f, spawn, {.v = prtscdcmd} },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1} },
{ MODKEY, XK_k, focusstack, {.i = -1} },
@@ -98,13 +105,11 @@ static const Key keys[] = {
{ MODKEY|ShiftMask, XK_j, movestack, {.i = +1} },
{ MODKEY|ShiftMask, XK_k, movestack, {.i = -1} },
{ MODKEY, XK_Return, zoom, {0} },
- { MODKEY, XK_Tab, spawn, {.v = dmenutab} },
+ { MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
{ MODKEY, XK_n, setlayout, {.v = &layouts[0]} },
- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
- { MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XK_m, setlayout, {.v = &layouts[1]} },
{ MODKEY, XK_space, setlayout, {0} },
- { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
{ MODKEY, XK_0, view, {.ui = ~0} },
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0} },
{ MODKEY, XK_comma, focusmon, {.i = -1} },
diff --git a/config.h b/config.h
index c74efdb..8c9a237 100644
--- a/config.h
+++ b/config.h
@@ -18,13 +18,19 @@ static const char col_gray2[] = "#353b45";
static const char col_white[] = "#abb2bf";
static const char col_orange[] = "#ff6a00";
static const char *colors[][3] = {
- /* fg bg border */
- [SchemeNorm] = { col_white, col_gray1, col_gray2 },
- [SchemeSel] = { col_white, col_gray2, col_orange },
+ /* fg bg border */
+ [SchemeNorm] = { col_white, col_gray1, col_gray2 },
+ [SchemeSel] = { col_white, col_gray2, col_orange },
+ [SchemeSelTab] = { col_orange, col_gray2 },
};
+/* tab */
+enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
+static const int showtab = showtab_auto; /* Default tab bar show mode */
+static const int toptab = False; /* False means bottom tab bar */
+
/* tagging */
-static const char *tags[] = { "", "", "", "", "", "", "", "", "" };
+static const char *tags[] = { "", "", "", "", "", "", "", "", "" };
static const Rule rules[] = {
/* xprop(1):
@@ -32,37 +38,37 @@ static const Rule rules[] = {
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
- { "mpv", NULL, NULL, 1 << 1, 0, -1 },
- { "firefox-esr", NULL, NULL, 1 << 2, 0, -1 },
- { "Brave-browser", NULL, NULL, 1 << 2, 0, -1 },
- { "VSCodium", NULL, NULL, 1 << 3, 0, -1 },
- { "libreoffice-startcenter", NULL, NULL, 1 << 4, 0, -1 },
- { "Soffice", NULL, NULL, 1 << 4, 0, -1 },
- { "Virt-manager", NULL, NULL, 1 << 5, 0, -1 },
{ "Pcmanfm", NULL, NULL, 0, 1, -1 },
- { "scrcpy", NULL, NULL, 1 << 6, 1, -1 },
- { "Gimp", NULL, NULL, 1 << 6, 1, -1 },
- { "Inkscape", NULL, NULL, 1 << 6, 0, -1 },
- { "Audacity", NULL, NULL, 1 << 6, 0, -1 },
- { "kdenlive", NULL, NULL, 1 << 6, 0, -1 },
- { "obs", NULL, NULL, 1 << 6, 0, -1 },
- { "qBittorrent", NULL, NULL, 1 << 6, 0, -1 },
- { "Mumble", NULL, NULL, 1 << 7, 0, -1 },
- { "steam", NULL, NULL, 1 << 7, 0, -1 },
- { "discord", NULL, NULL, 1 << 7, 0, -1 },
+ { "firefox-esr", NULL, NULL, 1 << 1, 0, -1 },
+ { "Brave-browser", NULL, NULL, 1 << 1, 0, -1 },
+ { "VSCodium", NULL, NULL, 1 << 2, 0, -1 },
+ { "mpv", NULL, NULL, 1 << 4, 0, -1 },
+ { "Mumble", NULL, NULL, 1 << 5, 0, -1 },
+ { "dino-im", NULL, NULL, 1 << 5, 0, -1 },
+ { "steam", NULL, NULL, 1 << 5, 0, -1 },
+ { "discord", NULL, NULL, 1 << 5, 0, -1 },
+ { "libreoffice-startcenter", NULL, NULL, 1 << 6, 0, -1 },
+ { "Soffice", NULL, NULL, 1 << 6, 0, -1 },
+ { "scrcpy", NULL, NULL, 1 << 7, 1, -1 },
+ { "Gimp", NULL, NULL, 1 << 7, 1, -1 },
+ { "Inkscape", NULL, NULL, 1 << 7, 0, -1 },
+ { "Audacity", NULL, NULL, 1 << 7, 0, -1 },
+ { "kdenlive", NULL, NULL, 1 << 7, 0, -1 },
+ { "obs", NULL, NULL, 1 << 7, 0, -1 },
+ { "qBittorrent", NULL, NULL, 1 << 7, 0, -1 },
+ { "Virt-manager", NULL, NULL, 1 << 8, 0, -1 },
};
/* layout(s) */
-static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
+static const float mfact = 0.5; /* factor of master area size [0.05..0.95] */
static const int nmaster = 1; /* number of clients in master area */
static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */
-static const int attachbelow = 0; /* 1 means attach after the currently active window */
+static const int attachbelow = 1; /* 1 means attach after the currently active window */
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile }, /* first entry is default */
- { "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
@@ -81,15 +87,16 @@ static const Layout layouts[] = {
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_white, "-sb", col_gray2, "-sf", col_white, NULL };
static const char *termcmd[] = { "st", NULL };
-static const char *dmenutab[] = { "alt-tab" };
static const char *prtsccmd[] = { "prtsc" };
static const char *prtscfcmd[] = { "prtscf" };
+static const char *prtscdcmd[] = { "prtscd" };
#include "movestack.c"
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd} },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd} },
+ { MODKEY, XK_f, spawn, {.v = prtscdcmd} },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1} },
{ MODKEY, XK_k, focusstack, {.i = -1} },
@@ -98,13 +105,11 @@ static const Key keys[] = {
{ MODKEY|ShiftMask, XK_j, movestack, {.i = +1} },
{ MODKEY|ShiftMask, XK_k, movestack, {.i = -1} },
{ MODKEY, XK_Return, zoom, {0} },
- { MODKEY, XK_Tab, spawn, {.v = dmenutab} },
+ { MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
{ MODKEY, XK_n, setlayout, {.v = &layouts[0]} },
- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
- { MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XK_m, setlayout, {.v = &layouts[1]} },
{ MODKEY, XK_space, setlayout, {0} },
- { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
{ MODKEY, XK_0, view, {.ui = ~0} },
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0} },
{ MODKEY, XK_comma, focusmon, {.i = -1} },
diff --git a/config.mk b/config.mk
index 8efca9a..b469a2b 100644
--- a/config.mk
+++ b/config.mk
@@ -1,5 +1,5 @@
# dwm version
-VERSION = 6.5
+VERSION = 6.6
# Customize below to fit your system
diff --git a/drw.c b/drw.c
index a58a2b4..c41e6af 100644
--- a/drw.c
+++ b/drw.c
@@ -9,54 +9,40 @@
#include "util.h"
#define UTF_INVALID 0xFFFD
-#define UTF_SIZ 4
-static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
-static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
-static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
-static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
-
-static long
-utf8decodebyte(const char c, size_t *i)
-{
- for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
- if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
- return (unsigned char)c & ~utfmask[*i];
- return 0;
-}
-
-static size_t
-utf8validate(long *u, size_t i)
-{
- if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
- *u = UTF_INVALID;
- for (i = 1; *u > utfmax[i]; ++i)
- ;
- return i;
-}
-
-static size_t
-utf8decode(const char *c, long *u, size_t clen)
+static int
+utf8decode(const char *s_in, long *u, int *err)
{
- size_t i, j, len, type;
- long udecoded;
-
+ static const unsigned char lens[] = {
+ /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */
+ /* 110XX */ 2, 2, 2, 2,
+ /* 1110X */ 3, 3,
+ /* 11110 */ 4,
+ /* 11111 */ 0, /* invalid */
+ };
+ static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
+ static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
+
+ const unsigned char *s = (const unsigned char *)s_in;
+ int len = lens[*s >> 3];
*u = UTF_INVALID;
- if (!clen)
- return 0;
- udecoded = utf8decodebyte(c[0], &len);
- if (!BETWEEN(len, 1, UTF_SIZ))
+ *err = 1;
+ if (len == 0)
return 1;
- for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
- udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
- if (type)
- return j;
+
+ long cp = s[0] & leading_mask[len - 1];
+ for (int i = 1; i < len; ++i) {
+ if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
+ return i;
+ cp = (cp << 6) | (s[i] & 0x3F);
}
- if (j < len)
- return 0;
- *u = udecoded;
- utf8validate(u, len);
+ /* out of range, surrogate, overlong encoding */
+ if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
+ return len;
+ *err = 0;
+ *u = cp;
return len;
}
@@ -238,11 +224,11 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{
- int i, ty, ellipsis_x = 0;
- unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
+ int ty, ellipsis_x = 0;
+ unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
- int utf8strlen, utf8charlen, render = x || y || w || h;
+ int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
FcCharSet *fccharset;
@@ -251,9 +237,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
XftResult result;
int charexists = 0, overflow = 0;
/* keep track of a couple codepoints for which we have no match. */
- enum { nomatches_len = 64 };
- static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
- static unsigned int ellipsis_width = 0;
+ static unsigned int nomatches[128], ellipsis_width, invalid_width;
+ static const char invalid[] = "�";
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
return 0;
@@ -263,6 +248,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+ if (w < lpad)
+ return x + w;
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
@@ -273,12 +260,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
usedfont = drw->fonts;
if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, "...");
+ if (!invalid_width && render)
+ invalid_width = drw_fontset_getwidth(drw, invalid);
while (1) {
- ew = ellipsis_len = utf8strlen = 0;
+ ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
- utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
+ utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
@@ -300,9 +289,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
else
utf8strlen = ellipsis_len;
} else if (curfont == usedfont) {
- utf8strlen += utf8charlen;
text += utf8charlen;
- ew += tmpw;
+ utf8strlen += utf8err ? 0 : utf8charlen;
+ ew += utf8err ? 0 : tmpw;
} else {
nextfont = curfont;
}
@@ -310,7 +299,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
}
}
- if (overflow || !charexists || nextfont)
+ if (overflow || !charexists || nextfont || utf8err)
break;
else
charexists = 0;
@@ -325,6 +314,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
x += ew;
w -= ew;
}
+ if (utf8err && (!render || invalid_width < w)) {
+ if (render)
+ drw_text(drw, x, y, w, h, 0, invalid, invert);
+ x += invalid_width;
+ w -= invalid_width;
+ }
if (render && overflow)
drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
@@ -338,11 +333,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
* character must be drawn. */
charexists = 1;
- for (i = 0; i < nomatches_len; ++i) {
- /* avoid calling XftFontMatch if we know we won't find a match */
- if (utf8codepoint == nomatches.codepoint[i])
- goto no_match;
- }
+ hash = (unsigned int)utf8codepoint;
+ hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
+ hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
+ h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
+ h1 = (hash >> 17) % LENGTH(nomatches);
+ /* avoid expensive XftFontMatch call when we know we won't find a match */
+ if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
+ goto no_match;
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
@@ -371,7 +369,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
curfont->next = usedfont;
} else {
xfont_free(usedfont);
- nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
+ nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
no_match:
usedfont = drw->fonts;
}
diff --git a/dwm.1 b/dwm.1
index cc75e0a..c3a18a6 100644
--- a/dwm.1
+++ b/dwm.1
@@ -20,14 +20,22 @@ layout applied.
Windows are grouped by tags. Each window can be tagged with one or multiple
tags. Selecting certain tags displays all windows with these tags.
.P
-Each screen contains a small status bar which displays all available tags, the
-layout, the title of the focused window, and the text read from the root window
-name property, if the screen is focused. A floating window is indicated with an
-empty square and a maximised floating window is indicated with a filled square
-before the windows title. The selected tags are indicated with a different
-color. The tags of the focused window are indicated with a filled square in the
-top left corner. The tags which are applied to one or more windows are
-indicated with an empty square in the top left corner.
+Each screen contains two small status bars.
+.P
+One bar displays all available tags, the layout, the title of the focused
+window, and the text read from the root window name property, if the screen is
+focused. A floating window is indicated with an empty square and a maximised
+floating window is indicated with a filled square before the windows title. The
+selected tags are indicated with a different color. The tags of the focused
+window are indicated with a filled square in the top left corner. The tags
+which are applied to one or more windows are indicated with an empty square in
+the top left corner.
+.P
+Another bar contains a tab for each window of the current view and allows
+navigation between windows, especially in the monocle mode. The different
+display modes of this bar are described under the Mod1\-w Keybord command
+section. When a single tag is selected, this tag is indicated in the left corner
+of the tab bar.
.P
The attach below patch makes newly spawned windows attach after the currently
selected window
@@ -47,7 +55,8 @@ command.
.TP
.B Button1
click on a tag label to display all windows with that tag, click on the layout
-label toggles between tiled and floating layout.
+label toggles between tiled and floating layout, click on a window name in the
+tab bar brings focus to that window.
.TP
.B Button3
click on a tag label adds/removes all windows with that tag to/from the view.
@@ -125,6 +134,12 @@ Increase master area size.
.B Mod1\-h
Decrease master area size.
.TP
+.B Mod1\-w
+Cycle over the tab bar display modes: never displayed, always displayed,
+displayed only in monocle mode when the view contains more than one window (auto
+mode). Some display modes can be disabled in the configuration, config.h. In
+the default configuration only "never" and "auto" display modes are enabled.
+.TP
.B Mod1\-Return
Zooms/cycles focused window to/from master area (tiled layouts only).
.TP
diff --git a/dwm.c b/dwm.c
index 50e4551..7cb9c03 100644
--- a/dwm.c
+++ b/dwm.c
@@ -50,7 +50,6 @@
#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
* MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
-#define LENGTH(X) (sizeof X / sizeof X[0])
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
@@ -72,14 +71,14 @@
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
-enum { SchemeNorm, SchemeSel }; /* color schemes */
+enum { SchemeNorm, SchemeSel, SchemeSelTab }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
-enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
typedef union {
@@ -107,7 +106,7 @@ struct Client {
int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
int bw, oldbw;
unsigned int tags;
- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, resizehints;
Client *next;
Client *snext;
Monitor *mon;
@@ -126,24 +125,32 @@ typedef struct {
void (*arrange)(Monitor *);
} Layout;
+#define MAXTABS 50
+
struct Monitor {
char ltsymbol[16];
float mfact;
int nmaster;
int num;
int by; /* bar geometry */
+ int ty; /* tab bar geometry */
int mx, my, mw, mh; /* screen size */
int wx, wy, ww, wh; /* window area */
unsigned int seltags;
unsigned int sellt;
unsigned int tagset[2];
int showbar;
+ int showtab;
int topbar;
+ int toptab;
Client *clients;
Client *sel;
Client *stack;
Monitor *next;
Window barwin;
+ Window tabwin;
+ int ntabs;
+ int tab_widths[MAXTABS];
const Layout *lt[2];
};
@@ -154,6 +161,7 @@ typedef struct {
unsigned int tags;
int isfloating;
int monitor;
+ int resizehints;
} Rule;
typedef struct Systray Systray;
@@ -190,6 +198,7 @@ static void focus(Client *c);
static void focusin(XEvent *e);
static void focusmon(const Arg *arg);
static void focusstack(const Arg *arg);
+static void focuswin(const Arg* arg);
static Atom getatomprop(Client *c, Atom prop);
static int getrootptr(int *x, int *y);
static long getstate(Window w);
@@ -230,6 +239,7 @@ static void setup(void);
static void seturgent(Client *c, int urg);
static void showhide(Client *c);
static void spawn(const Arg *arg);
+static void tabmode(const Arg *arg);
static Monitor *systraytomon(Monitor *m);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
@@ -274,6 +284,7 @@ static char stext[256];
static int screen;
static int sw, sh; /* X display screen geometry width, height */
static int bh; /* bar height */
+static int th = 0; /* tab bar geometry */
static int lrpad; /* sum of left and right padding for text */
static int (*xerrorxlib)(Display *, XErrorEvent *);
static unsigned int numlockmask = 0;
@@ -332,6 +343,7 @@ applyrules(Client *c)
{
c->isfloating = r->isfloating;
c->tags |= r->tags;
+ c->resizehints = r->resizehints;
for (m = mons; m && m->num != r->monitor; m = m->next);
if (m)
c->mon = m;
@@ -376,7 +388,7 @@ applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
*h = bh;
if (*w < bh)
*w = bh;
- if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
+ if (c->resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
if (!c->hintsvalid)
updatesizehints(c);
/* see last two sentences in ICCCM 4.1.2.3 */
@@ -427,8 +439,9 @@ arrange(Monitor *m)
}
void
-arrangemon(Monitor *m)
-{
+arrangemon(Monitor *m) {
+ updatebarpos(m);
+ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
if (m->lt[m->sellt]->arrange)
m->lt[m->sellt]->arrange(m);
@@ -494,6 +507,22 @@ buttonpress(XEvent *e)
click = ClkStatusText;
else
click = ClkWinTitle;
+ }
+ if(ev->window == selmon->tabwin) {
+ i = 0; x = 0;
+ for(c = selmon->clients; c; c = c->next){
+ if(!ISVISIBLE(c)) continue;
+ x += selmon->tab_widths[i];
+ if (ev->x > x)
+ ++i;
+ else
+ break;
+ if(i >= m->ntabs) break;
+ }
+ if(c) {
+ click = ClkTabBar;
+ arg.ui = i;
+ }
} else if ((c = wintoclient(ev->window))) {
if (focusonwheel || (ev->button != Button4 && ev->button != Button5))
focus(c);
@@ -502,8 +531,9 @@ buttonpress(XEvent *e)
}
for (i = 0; i < LENGTH(buttons); i++)
if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
+ buttons[i].func(((click == ClkTagBar || click == ClkTabBar) && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
+ }
}
void
@@ -565,6 +595,8 @@ cleanupmon(Monitor *mon)
}
XUnmapWindow(dpy, mon->barwin);
XDestroyWindow(dpy, mon->barwin);
+ XUnmapWindow(dpy, mon->tabwin);
+ XDestroyWindow(dpy, mon->tabwin);
free(mon);
}
@@ -575,7 +607,6 @@ clientmessage(XEvent *e)
XSetWindowAttributes swa;
XClientMessageEvent *cme = &e->xclient;
Client *c = wintoclient(cme->window);
- unsigned int i;
if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
/* add systray icons */
@@ -632,14 +663,8 @@ clientmessage(XEvent *e)
setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
|| (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
} else if (cme->message_type == netatom[NetActiveWindow]) {
- for (i = 0; i < LENGTH(tags) && !((1 << i) & c->tags); i++);
- if (i < LENGTH(tags)) {
- const Arg a = {.ui = 1 << i};
- selmon = c->mon;
- view(&a);
- focus(c);
- restack(selmon);
- }
+ if (c != selmon->sel && !c->isurgent)
+ seturgent(c, 1);
}
}
@@ -752,11 +777,13 @@ createmon(void)
m->mfact = mfact;
m->nmaster = nmaster;
m->showbar = showbar;
+ m->showtab = showtab;
m->topbar = topbar;
+ m->toptab = toptab;
+ m->ntabs = 0;
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
-
return m;
}
@@ -871,6 +898,56 @@ drawbar(Monitor *m)
}
void
+drawtabs(void) {
+ Monitor *m;
+
+ for(m = mons; m; m = m->next)
+ drawtab(m);
+}
+
+void
+drawtab(Monitor *m) {
+ Client *c;
+ int i;
+ int maxsize;
+ int remainder = 0;
+ int x = 0;
+ int w = 0;
+
+ /* Calculates number of labels and their width */
+ m->ntabs = 0;
+ for(c = m->clients; c; c = c->next){
+ if(!ISVISIBLE(c)) continue;
+ m->tab_widths[m->ntabs] = (int)TEXTW(c->name);
+ ++m->ntabs;
+ if(m->ntabs >= MAXTABS) break;
+ }
+
+ if(m->ntabs > 0) remainder = m->mw % m->ntabs;
+ maxsize = (1.0 / (double)m->ntabs) * m->mw;
+
+ i = 0;
+ int tm; /* middle of the tab*/
+ for(c = m->clients; c; c = c->next){
+ if(!ISVISIBLE(c)) continue;
+ if(i >= m->ntabs) break;
+ m->tab_widths[i] = maxsize;
+ /* add the remainder to the last tab so there is no leftover space left*/
+ if(remainder && i == m->ntabs - 1) m->tab_widths[i] += remainder;
+ w = m->tab_widths[i];
+ drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSelTab : SchemeNorm]);
+ tm = (m->tab_widths[i] - (int)TEXTW(c->name)) / 2;
+ tm = (int)TEXTW(c->name) >= m->tab_widths[i] ? lrpad / 2 : tm;
+ drw_text(drw, x, 0, w, th, tm, c->name, 0);
+ x += w;
+ ++i;
+ }
+
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_map(drw, m->tabwin, 0, 0, m->mw, th);
+}
+
+void
drawbars(void)
{
Monitor *m;
@@ -887,6 +964,7 @@ expose(XEvent *e)
if (ev->count == 0 && (m = wintomon(ev->window))) {
drawbar(m);
+ drawtab(m);
if (m == selmon)
updatesystray();
}
@@ -915,6 +993,7 @@ focus(Client *c)
}
selmon->sel = c;
drawbars();
+ drawtabs();
}
/* there are some broken focus acquiring clients needing extra handling */
@@ -950,16 +1029,16 @@ focusstack(const Arg *arg)
return;
if (arg->i > 0) {
for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
- if (!c)
- for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
+ /* if (!c) */
+ /* for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); */
} else {
for (i = selmon->clients; i != selmon->sel; i = i->next)
if (ISVISIBLE(i))
c = i;
- if (!c)
- for (; i; i = i->next)
- if (ISVISIBLE(i))
- c = i;
+ /* if (!c) */
+ /* for (; i; i = i->next) */
+ /* if (ISVISIBLE(i)) */
+ /* c = i; */
}
if (c) {
focus(c);
@@ -967,6 +1046,19 @@ focusstack(const Arg *arg)
}
}
+void
+focuswin(const Arg* arg){
+ int iwin = arg->i;
+ Client* c = NULL;
+ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
+ if(ISVISIBLE(c)) --iwin;
+ };
+ if(c) {
+ focus(c);
+ restack(selmon);
+ }
+}
+
Atom
getatomprop(Client *c, Atom prop)
{
@@ -1104,7 +1196,7 @@ grabkeys(void)
void
incnmaster(const Arg *arg)
{
- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
arrange(selmon);
}
@@ -1168,6 +1260,7 @@ manage(Window w, XWindowAttributes *wa)
c->w = c->oldw = wa->width;
c->h = c->oldh = wa->height;
c->oldbw = wa->border_width;
+ c->resizehints = resizehints;
updatetitle(c);
if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
@@ -1215,7 +1308,7 @@ manage(Window w, XWindowAttributes *wa)
c->mon->sel = c;
arrange(c->mon);
XMapWindow(dpy, c->win);
- focus(NULL);
+ focus(NULL);
}
void
@@ -1374,12 +1467,14 @@ propertynotify(XEvent *e)
case XA_WM_HINTS:
updatewmhints(c);
drawbars();
+ drawtabs();
break;
}
if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
updatetitle(c);
if (c == c->mon->sel)
drawbar(c->mon);
+ drawtab(c->mon);
}
if (ev->atom == netatom[NetWMWindowType])
updatewindowtype(c);
@@ -1527,6 +1622,7 @@ restack(Monitor *m)
XWindowChanges wc;
drawbar(m);
+ drawtab(m);
if (!m->sel)
return;
if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
@@ -1690,9 +1786,9 @@ void
setlayout(const Arg *arg)
{
if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
- selmon->sellt ^= 1;
+ selmon->sellt ^= 1;
if (arg && arg->v)
- selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ selmon->lt[selmon->sellt] = (Layout *)arg->v;
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
if (selmon->sel)
arrange(selmon);
@@ -1711,7 +1807,7 @@ setmfact(const Arg *arg)
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
if (f < 0.05 || f > 0.95)
return;
- selmon->mfact = f;
+ selmon->mfact = f;
arrange(selmon);
}
@@ -1742,6 +1838,7 @@ setup(void)
die("no fonts could be loaded.");
lrpad = drw->fonts->h;
bh = drw->fonts->h + 2;
+ th = bh;
updategeom();
/* init atoms */
utf8string = XInternAtom(dpy, "UTF8_STRING", False);
@@ -1925,7 +2022,7 @@ tile(Monitor *m)
void
togglebar(const Arg *arg)
{
- selmon->showbar = !selmon->showbar;
+ selmon->showbar = !selmon->showbar;
updatebarpos(selmon);
resizebarwin(selmon);
if (showsystray) {
@@ -1943,6 +2040,17 @@ togglebar(const Arg *arg)
}
void
+tabmode(const Arg *arg)
+{
+ if(arg && arg->i >= 0)
+ selmon->showtab = arg->ui % showtab_nmodes;
+ else
+ selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
+ arrange(selmon);
+}
+
+
+void
togglefloating(const Arg *arg)
{
if (!selmon->sel)
@@ -2066,6 +2174,11 @@ updatebars(void)
if (showsystray && m == systraytomon(m))
XMapRaised(dpy, systray->win);
XMapRaised(dpy, m->barwin);
+ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
+ CopyFromParent, DefaultVisual(dpy, screen),
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
+ XMapRaised(dpy, m->tabwin);
XSetClassHint(dpy, m->barwin, &ch);
}
}
@@ -2073,18 +2186,37 @@ updatebars(void)
void
updatebarpos(Monitor *m)
{
+ Client *c;
+ int nvis = 0;
+
m->wy = m->my;
m->wh = m->mh;
if (m->showbar) {
m->wh -= bh;
m->by = m->topbar ? m->wy : m->wy + m->wh;
- m->wy = m->topbar ? m->wy + bh : m->wy;
- } else
+ if ( m->topbar )
+ m->wy += bh;
+ } else {
m->by = -bh;
+ }
+
+ for(c = m->clients; c; c = c->next) {
+ if(ISVISIBLE(c)) ++nvis;
+ }
+
+ if(m->showtab == showtab_always
+ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))) {
+ m->wh -= th;
+ m->ty = m->toptab ? m->wy : m->wy + m->wh;
+ if ( m->toptab )
+ m->wy += th;
+ } else {
+ m->ty = -th;
+ }
}
void
-updateclientlist()
+updateclientlist(void)
{
Client *c;
Monitor *m;
@@ -2148,10 +2280,10 @@ updategeom(void)
m->clients = c->next;
detachstack(c);
c->mon = mons;
- if( attachbelow )
- attachBelow(c);
- else
- attach(c);
+ if( attachbelow )
+ attachBelow(c);
+ else
+ attach(c);
attachstack(c);
}
if (m == selmon)
@@ -2472,7 +2604,7 @@ wintomon(Window w)
if (w == root && getrootptr(&x, &y))
return recttomon(x, y, 1, 1);
for (m = mons; m; m = m->next)
- if (w == m->barwin)
+ if (w == m->barwin || w == m->tabwin)
return m;
if ((c = wintoclient(w)))
return c->mon;
diff --git a/patches/dwm-clientresizehints-6.5.diff b/patches/dwm-clientresizehints-6.5.diff
new file mode 100644
index 0000000..b05c84d
--- /dev/null
+++ b/patches/dwm-clientresizehints-6.5.diff
@@ -0,0 +1,76 @@
+From b2de13e11e7b4d67b4982ef51befa87ed9202080 Mon Sep 17 00:00:00 2001
+From: Fred Frey <fred@fpf3.net>
+Date: Sun, 22 Sep 2024 16:09:53 -0400
+Subject: [PATCH] implement per-client resizehints
+
+---
+ config.def.h | 6 +++---
+ dwm.c | 7 +++++--
+ 2 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 9efa774..67e7a9c 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -26,9 +26,9 @@ static const Rule rules[] = {
+ * WM_CLASS(STRING) = instance, class
+ * WM_NAME(STRING) = title
+ */
+- /* class instance title tags mask isfloating monitor */
+- { "Gimp", NULL, NULL, 0, 1, -1 },
+- { "Firefox", NULL, NULL, 1 << 8, 0, -1 },
++ /* class instance title tags mask isfloating monitor resizehints */
++ { "Gimp", NULL, NULL, 0, 1, -1, 1},
++ { "Firefox", NULL, NULL, 1 << 8, 0, -1, 1},
+ };
+
+ /* layout(s) */
+diff --git a/dwm.c b/dwm.c
+index 67c6b2b..f179b4c 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -92,7 +92,7 @@ struct Client {
+ int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
+ int bw, oldbw;
+ unsigned int tags;
+- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
++ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, resizehints;
+ Client *next;
+ Client *snext;
+ Monitor *mon;
+@@ -139,6 +139,7 @@ typedef struct {
+ unsigned int tags;
+ int isfloating;
+ int monitor;
++ int resizehints;
+ } Rule;
+
+ /* function declarations */
+@@ -299,6 +300,7 @@ applyrules(Client *c)
+ {
+ c->isfloating = r->isfloating;
+ c->tags |= r->tags;
++ c->resizehints = r->resizehints;
+ for (m = mons; m && m->num != r->monitor; m = m->next);
+ if (m)
+ c->mon = m;
+@@ -343,7 +345,7 @@ applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
+ *h = bh;
+ if (*w < bh)
+ *w = bh;
+- if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
++ if (c->resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
+ if (!c->hintsvalid)
+ updatesizehints(c);
+ /* see last two sentences in ICCCM 4.1.2.3 */
+@@ -1043,6 +1045,7 @@ manage(Window w, XWindowAttributes *wa)
+ c->w = c->oldw = wa->width;
+ c->h = c->oldh = wa->height;
+ c->oldbw = wa->border_width;
++ c->resizehints = resizehints;
+
+ updatetitle(c);
+ if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
+--
+2.46.0
+
diff --git a/patches/dwm-focusonnetactive-6.2.diff b/patches/dwm-focusonnetactive-6.2.diff
deleted file mode 100644
index 5d358d2..0000000
--- a/patches/dwm-focusonnetactive-6.2.diff
+++ /dev/null
@@ -1,57 +0,0 @@
-From 286ca3bb1af08b452bf8140abcc23d4ef61baaa2 Mon Sep 17 00:00:00 2001
-From: bakkeby <bakkeby@gmail.com>
-Date: Tue, 7 Apr 2020 12:33:04 +0200
-Subject: [PATCH] Activate a window in response to _NET_ACTIVE_WINDOW
-
-By default, dwm response to client requests to _NET_ACTIVE_WINDOW client
-messages by setting the urgency bit on the named window.
-
-This patch activates the window instead.
-
-Both behaviours are legitimate according to
-https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472702304
-
-One should decide which of these one should perform based on the message
-senders' untestable claims that it represents the end-user. Setting the
-urgency bit is the conservative decision. This patch implements the more
-trusting alternative.
-
-It also allows dwm to work with `wmctrl -a` and other external window
-management utilities
-
----
- dwm.c | 11 +++++++++--
- 1 file changed, 9 insertions(+), 2 deletions(-)
-
-diff --git a/dwm.c b/dwm.c
-index 4465af1..3919d47 100644
---- a/dwm.c
-+++ b/dwm.c
-@@ -514,6 +514,7 @@ clientmessage(XEvent *e)
- {
- XClientMessageEvent *cme = &e->xclient;
- Client *c = wintoclient(cme->window);
-+ unsigned int i;
-
- if (!c)
- return;
-@@ -523,8 +524,14 @@ clientmessage(XEvent *e)
- setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
- || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
- } else if (cme->message_type == netatom[NetActiveWindow]) {
-- if (c != selmon->sel && !c->isurgent)
-- seturgent(c, 1);
-+ for (i = 0; i < LENGTH(tags) && !((1 << i) & c->tags); i++);
-+ if (i < LENGTH(tags)) {
-+ const Arg a = {.ui = 1 << i};
-+ selmon = c->mon;
-+ view(&a);
-+ focus(c);
-+ restack(selmon);
-+ }
- }
- }
-
---
-2.17.1
-
diff --git a/patches/dwm-tab-i3like-20211121-a786211.diff b/patches/dwm-tab-i3like-20211121-a786211.diff
new file mode 100644
index 0000000..f170402
--- /dev/null
+++ b/patches/dwm-tab-i3like-20211121-a786211.diff
@@ -0,0 +1,449 @@
+From 5e489a57cdce6517996df26808b58bdd32bbd99f Mon Sep 17 00:00:00 2001
+From: howoz <howoz@airmail.cc>
+Date: Sun, 21 Nov 2021 16:23:04 +0300
+Subject: [PATCH] [dwm][patches][tab] i3 like tabs that cover the whole screen
+ width
+
+---
+ config.def.h | 9 +++
+ dwm.1 | 33 ++++++++---
+ dwm.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++----
+ 3 files changed, 183 insertions(+), 19 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index a2ac963..931d7ae 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -5,6 +5,13 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
+ static const unsigned int snap = 32; /* snap pixel */
+ static const int showbar = 1; /* 0 means no bar */
+ static const int topbar = 1; /* 0 means bottom bar */
++/* Display modes of the tab bar: never shown, always shown, shown only in */
++/* monocle mode in the presence of several windows. */
++/* Modes after showtab_nmodes are disabled. */
++enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
++static const int showtab = showtab_auto; /* Default tab bar show mode */
++static const int toptab = False; /* False means bottom tab bar */
++
+ static const char *fonts[] = { "monospace:size=10" };
+ static const char dmenufont[] = "monospace:size=10";
+ static const char col_gray1[] = "#222222";
+@@ -65,6 +72,7 @@ static Key keys[] = {
+ { MODKEY, XK_p, spawn, {.v = dmenucmd } },
+ { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
+ { MODKEY, XK_b, togglebar, {0} },
++ { MODKEY, XK_w, tabmode, {-1} },
+ { MODKEY, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY, XK_i, incnmaster, {.i = +1 } },
+@@ -112,5 +120,6 @@ static Button buttons[] = {
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
++ { ClkTabBar, 0, Button1, focuswin, {0} },
+ };
+
+diff --git a/dwm.1 b/dwm.1
+index ddc8321..7752444 100644
+--- a/dwm.1
++++ b/dwm.1
+@@ -20,14 +20,22 @@ layout applied.
+ Windows are grouped by tags. Each window can be tagged with one or multiple
+ tags. Selecting certain tags displays all windows with these tags.
+ .P
+-Each screen contains a small status bar which displays all available tags, the
+-layout, the title of the focused window, and the text read from the root window
+-name property, if the screen is focused. A floating window is indicated with an
+-empty square and a maximised floating window is indicated with a filled square
+-before the windows title. The selected tags are indicated with a different
+-color. The tags of the focused window are indicated with a filled square in the
+-top left corner. The tags which are applied to one or more windows are
+-indicated with an empty square in the top left corner.
++Each screen contains two small status bars.
++.P
++One bar displays all available tags, the layout, the title of the focused
++window, and the text read from the root window name property, if the screen is
++focused. A floating window is indicated with an empty square and a maximised
++floating window is indicated with a filled square before the windows title. The
++selected tags are indicated with a different color. The tags of the focused
++window are indicated with a filled square in the top left corner. The tags
++which are applied to one or more windows are indicated with an empty square in
++the top left corner.
++.P
++Another bar contains a tab for each window of the current view and allows
++navigation between windows, especially in the monocle mode. The different
++display modes of this bar are described under the Mod1\-w Keybord command
++section. When a single tag is selected, this tag is indicated in the left corner
++of the tab bar.
+ .P
+ dwm draws a small border around windows to indicate the focus state.
+ .SH OPTIONS
+@@ -44,7 +52,8 @@ command.
+ .TP
+ .B Button1
+ click on a tag label to display all windows with that tag, click on the layout
+-label toggles between tiled and floating layout.
++label toggles between tiled and floating layout, click on a window name in the
++tab bar brings focus to that window.
+ .TP
+ .B Button3
+ click on a tag label adds/removes all windows with that tag to/from the view.
+@@ -110,6 +119,12 @@ Increase master area size.
+ .B Mod1\-h
+ Decrease master area size.
+ .TP
++.B Mod1\-w
++Cycle over the tab bar display modes: never displayed, always displayed,
++displayed only in monocle mode when the view contains more than one window (auto
++mode). Some display modes can be disabled in the configuration, config.h. In
++the default configuration only "never" and "auto" display modes are enabled.
++.TP
+ .B Mod1\-Return
+ Zooms/cycles focused window to/from master area (tiled layouts only).
+ .TP
+diff --git a/dwm.c b/dwm.c
+index 5e4d494..8404747 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -64,7 +64,7 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+-enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
++enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+
+ typedef union {
+@@ -111,24 +111,32 @@ typedef struct {
+ void (*arrange)(Monitor *);
+ } Layout;
+
++#define MAXTABS 50
++
+ struct Monitor {
+ char ltsymbol[16];
+ float mfact;
+ int nmaster;
+ int num;
+ int by; /* bar geometry */
++ int ty; /* tab bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
+ unsigned int seltags;
+ unsigned int sellt;
+ unsigned int tagset[2];
+ int showbar;
++ int showtab;
+ int topbar;
++ int toptab;
+ Client *clients;
+ Client *sel;
+ Client *stack;
+ Monitor *next;
+ Window barwin;
++ Window tabwin;
++ int ntabs;
++ int tab_widths[MAXTABS];
+ const Layout *lt[2];
+ };
+
+@@ -169,6 +177,7 @@ static void focus(Client *c);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
++static void focuswin(const Arg* arg);
+ static Atom getatomprop(Client *c, Atom prop);
+ static int getrootptr(int *x, int *y);
+ static long getstate(Window w);
+@@ -207,6 +216,7 @@ static void seturgent(Client *c, int urg);
+ static void showhide(Client *c);
+ static void sigchld(int unused);
+ static void spawn(const Arg *arg);
++static void tabmode(const Arg *arg);
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
+ static void tile(Monitor *);
+@@ -241,6 +251,7 @@ static char stext[256];
+ static int screen;
+ static int sw, sh; /* X display screen geometry width, height */
+ static int bh, blw = 0; /* bar geometry */
++static int th = 0; /* tab bar geometry */
+ static int lrpad; /* sum of left and right padding for text */
+ static int (*xerrorxlib)(Display *, XErrorEvent *);
+ static unsigned int numlockmask = 0;
+@@ -393,8 +404,9 @@ arrange(Monitor *m)
+ }
+
+ void
+-arrangemon(Monitor *m)
+-{
++arrangemon(Monitor *m) {
++ updatebarpos(m);
++ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+@@ -444,7 +456,24 @@ buttonpress(XEvent *e)
+ click = ClkStatusText;
+ else
+ click = ClkWinTitle;
+- } else if ((c = wintoclient(ev->window))) {
++ }
++ if(ev->window == selmon->tabwin) {
++ i = 0; x = 0;
++ for(c = selmon->clients; c; c = c->next){
++ if(!ISVISIBLE(c)) continue;
++ x += selmon->tab_widths[i];
++ if (ev->x > x)
++ ++i;
++ else
++ break;
++ if(i >= m->ntabs) break;
++ }
++ if(c) {
++ click = ClkTabBar;
++ arg.ui = i;
++ }
++ }
++ else if((c = wintoclient(ev->window))) {
+ focus(c);
+ restack(selmon);
+ XAllowEvents(dpy, ReplayPointer, CurrentTime);
+@@ -452,8 +481,9 @@ buttonpress(XEvent *e)
+ }
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
+- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
+- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
++ buttons[i].func(((click == ClkTagBar || click == ClkTabBar) && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
++ }
+ }
+
+ void
+@@ -507,6 +537,8 @@ cleanupmon(Monitor *mon)
+ }
+ XUnmapWindow(dpy, mon->barwin);
+ XDestroyWindow(dpy, mon->barwin);
++ XUnmapWindow(dpy, mon->tabwin);
++ XDestroyWindow(dpy, mon->tabwin);
+ free(mon);
+ }
+
+@@ -638,7 +670,10 @@ createmon(void)
+ m->mfact = mfact;
+ m->nmaster = nmaster;
+ m->showbar = showbar;
++ m->showtab = showtab;
+ m->topbar = topbar;
++ m->toptab = toptab;
++ m->ntabs = 0;
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+@@ -752,6 +787,56 @@ drawbars(void)
+ drawbar(m);
+ }
+
++void
++drawtabs(void) {
++ Monitor *m;
++
++ for(m = mons; m; m = m->next)
++ drawtab(m);
++}
++
++void
++drawtab(Monitor *m) {
++ Client *c;
++ int i;
++ int maxsize;
++ int remainder = 0;
++ int x = 0;
++ int w = 0;
++
++ /* Calculates number of labels and their width */
++ m->ntabs = 0;
++ for(c = m->clients; c; c = c->next){
++ if(!ISVISIBLE(c)) continue;
++ m->tab_widths[m->ntabs] = (int)TEXTW(c->name);
++ ++m->ntabs;
++ if(m->ntabs >= MAXTABS) break;
++ }
++
++ if(m->ntabs > 0) remainder = m->mw % m->ntabs;
++ maxsize = (1.0 / (double)m->ntabs) * m->mw;
++
++ i = 0;
++ int tm; /* middle of the tab*/
++ for(c = m->clients; c; c = c->next){
++ if(!ISVISIBLE(c)) continue;
++ if(i >= m->ntabs) break;
++ m->tab_widths[i] = maxsize;
++ /* add the remainder to the last tab so there is no leftover space left*/
++ if(remainder && i == m->ntabs - 1) m->tab_widths[i] += remainder;
++ w = m->tab_widths[i];
++ drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSel : SchemeNorm]);
++ tm = (m->tab_widths[i] - (int)TEXTW(c->name)) / 2;
++ tm = (int)TEXTW(c->name) >= m->tab_widths[i] ? lrpad / 2 : tm;
++ drw_text(drw, x, 0, w, th, tm, c->name, 0);
++ x += w;
++ ++i;
++ }
++
++ drw_setscheme(drw, scheme[SchemeNorm]);
++ drw_map(drw, m->tabwin, 0, 0, m->mw, th);
++}
++
+ void
+ enternotify(XEvent *e)
+ {
+@@ -777,8 +862,10 @@ expose(XEvent *e)
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+- if (ev->count == 0 && (m = wintomon(ev->window)))
++ if(ev->count == 0 && (m = wintomon(ev->window))){
+ drawbar(m);
++ drawtab(m);
++ }
+ }
+
+ void
+@@ -804,6 +891,7 @@ focus(Client *c)
+ }
+ selmon->sel = c;
+ drawbars();
++ drawtabs();
+ }
+
+ /* there are some broken focus acquiring clients needing extra handling */
+@@ -856,6 +944,19 @@ focusstack(const Arg *arg)
+ }
+ }
+
++void
++focuswin(const Arg* arg){
++ int iwin = arg->i;
++ Client* c = NULL;
++ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
++ if(ISVISIBLE(c)) --iwin;
++ };
++ if(c) {
++ focus(c);
++ restack(selmon);
++ }
++}
++
+ Atom
+ getatomprop(Client *c, Atom prop)
+ {
+@@ -1234,12 +1335,14 @@ propertynotify(XEvent *e)
+ case XA_WM_HINTS:
+ updatewmhints(c);
+ drawbars();
++ drawtabs();
+ break;
+ }
+ if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
+ updatetitle(c);
+ if (c == c->mon->sel)
+ drawbar(c->mon);
++ drawtab(c->mon);
+ }
+ if (ev->atom == netatom[NetWMWindowType])
+ updatewindowtype(c);
+@@ -1353,6 +1456,7 @@ restack(Monitor *m)
+ XWindowChanges wc;
+
+ drawbar(m);
++ drawtab(m);
+ if (!m->sel)
+ return;
+ if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
+@@ -1547,6 +1651,7 @@ setup(void)
+ die("no fonts could be loaded.");
+ lrpad = drw->fonts->h;
+ bh = drw->fonts->h + 2;
++ th = bh;
+ updategeom();
+ /* init atoms */
+ utf8string = XInternAtom(dpy, "UTF8_STRING", False);
+@@ -1708,6 +1813,17 @@ togglebar(const Arg *arg)
+ arrange(selmon);
+ }
+
++void
++tabmode(const Arg *arg)
++{
++ if(arg && arg->i >= 0)
++ selmon->showtab = arg->ui % showtab_nmodes;
++ else
++ selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
++ arrange(selmon);
++}
++
++
+ void
+ togglefloating(const Arg *arg)
+ {
+@@ -1819,6 +1935,11 @@ updatebars(void)
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
+ XMapRaised(dpy, m->barwin);
++ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
++ CopyFromParent, DefaultVisual(dpy, screen),
++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
++ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
++ XMapRaised(dpy, m->tabwin);
+ XSetClassHint(dpy, m->barwin, &ch);
+ }
+ }
+@@ -1826,14 +1947,33 @@ updatebars(void)
+ void
+ updatebarpos(Monitor *m)
+ {
++ Client *c;
++ int nvis = 0;
++
+ m->wy = m->my;
+ m->wh = m->mh;
+ if (m->showbar) {
+ m->wh -= bh;
+ m->by = m->topbar ? m->wy : m->wy + m->wh;
+- m->wy = m->topbar ? m->wy + bh : m->wy;
+- } else
++ if ( m->topbar )
++ m->wy += bh;
++ } else {
+ m->by = -bh;
++ }
++
++ for(c = m->clients; c; c = c->next) {
++ if(ISVISIBLE(c)) ++nvis;
++ }
++
++ if(m->showtab == showtab_always
++ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))) {
++ m->wh -= th;
++ m->ty = m->toptab ? m->wy : m->wy + m->wh;
++ if ( m->toptab )
++ m->wy += th;
++ } else {
++ m->ty = -th;
++ }
+ }
+
+ void
+@@ -2070,7 +2210,7 @@ wintomon(Window w)
+ if (w == root && getrootptr(&x, &y))
+ return recttomon(x, y, 1, 1);
+ for (m = mons; m; m = m->next)
+- if (w == m->barwin)
++ if (w == m->barwin || w == m->tabwin)
+ return m;
+ if ((c = wintoclient(w)))
+ return c->mon;
+--
+2.34.0
+
diff --git a/util.c b/util.c
index 96b82c9..8e26a51 100644
--- a/util.c
+++ b/util.c
@@ -1,4 +1,5 @@
/* See LICENSE file for copyright and license details. */
+#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -10,17 +11,17 @@ void
die(const char *fmt, ...)
{
va_list ap;
+ int saved_errno;
+
+ saved_errno = errno;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
- if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
- fputc(' ', stderr);
- perror(NULL);
- } else {
- fputc('\n', stderr);
- }
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':')
+ fprintf(stderr, " %s", strerror(saved_errno));
+ fputc('\n', stderr);
exit(1);
}
diff --git a/util.h b/util.h
index f633b51..c0a50d4 100644
--- a/util.h
+++ b/util.h
@@ -3,6 +3,7 @@
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
+#define LENGTH(X) (sizeof (X) / sizeof (X)[0])
void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size);