diff options
| author | LM-LCL <hello@exaltedelite.club> | 2025-09-13 22:49:15 +0200 |
|---|---|---|
| committer | LM-LCL <hello@exaltedelite.club> | 2025-09-13 22:49:15 +0200 |
| commit | eaeb8684a2c614c709d8a32ae4290128bf830831 (patch) | |
| tree | 69a8782586ad81f6edf55388b9da32204829884f | |
| parent | 6760362a1df2efaf68cf6e13685a6cbc68a1684e (diff) | |
dwm-6.6
| -rw-r--r-- | config.def.h | 63 | ||||
| -rw-r--r-- | config.h | 63 | ||||
| -rw-r--r-- | config.mk | 2 | ||||
| -rw-r--r-- | drw.c | 116 | ||||
| -rw-r--r-- | dwm.1 | 33 | ||||
| -rw-r--r-- | dwm.c | 210 | ||||
| -rw-r--r-- | patches/dwm-clientresizehints-6.5.diff | 76 | ||||
| -rw-r--r-- | patches/dwm-focusonnetactive-6.2.diff | 57 | ||||
| -rw-r--r-- | patches/dwm-tab-i3like-20211121-a786211.diff | 449 | ||||
| -rw-r--r-- | util.c | 13 | ||||
| -rw-r--r-- | util.h | 1 |
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} }, @@ -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} }, @@ -1,5 +1,5 @@ # dwm version -VERSION = 6.5 +VERSION = 6.6 # Customize below to fit your system @@ -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; } @@ -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 @@ -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 + @@ -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); } @@ -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); |
