From 0f9104285bab49c1e701ea429380ace9230309f0 Mon Sep 17 00:00:00 2001 From: bakkeby Date: Tue, 9 Mar 2021 13:23:39 +0100 Subject: [PATCH] Adding tab patch --- README.md | 9 +++ config.def.h | 14 ++++ dwm.c | 123 ++++++++++++++++++++++++++++++++-- patch/bar_powerline_tags.c | 2 +- patch/include.c | 3 + patch/include.h | 3 + patch/tab.c | 132 +++++++++++++++++++++++++++++++++++++ patch/tab.h | 4 ++ patches.def.h | 8 +++ 9 files changed, 290 insertions(+), 8 deletions(-) create mode 100644 patch/tab.c create mode 100644 patch/tab.h diff --git a/README.md b/README.md index fef9468..7cfdc2d 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Refer to [https://dwm.suckless.org/](https://dwm.suckless.org/) for details on t ### Changelog: +2021-03-09 - Added the tab patch + 2021-02-11 - Added the riodraw and focusdir patches 2021-01-22 - Added the placemouse patch @@ -575,6 +577,13 @@ Refer to [https://dwm.suckless.org/](https://dwm.suckless.org/) for details on t - [systray](https://dwm.suckless.org/patches/systray/) - adds system tray in the status bar + - [tab](https://dwm.suckless.org/patches/tab/) + - transforms the monocle layout into a "tabbed" layout if more than one window is present on the monocle view + - this is essentially just a specific bar + - the patch has been added for demonstration purposes only and has limited compatibility with other patches + - it will conflict space-wise with a second bar + - note that fancybar, awesomebar, bartabgroups and similar patches make the tab patch redundant + - [tagall](https://dwm.suckless.org/patches/tagall/) - adds keyboard shortcuts to move all (or only floating) windows from one tag to another diff --git a/config.def.h b/config.def.h index ab03439..ce368ee 100644 --- a/config.def.h +++ b/config.def.h @@ -38,6 +38,14 @@ static const int showbar = 0; /* 0 means no bar */ static const int showbar = 1; /* 0 means no bar */ #endif // BAR_HOLDBAR_PATCH static const int topbar = 1; /* 0 means bottom bar */ +#if TAB_PATCH +/* 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 */ +#endif // TAB_PATCH #if BAR_HEIGHT_PATCH static const int bar_height = 0; /* 0 means derive from font, >= 1 explicit height */ #endif // BAR_HEIGHT_PATCH @@ -769,6 +777,9 @@ static Key keys[] = { { MODKEY, XK_s, rioresize, {0} }, #endif // RIODRAW_PATCH { MODKEY, XK_b, togglebar, {0} }, + #if TAB_PATCH + { MODKEY|ControlMask, XK_b, tabmode, {-1} }, + #endif // TAB_PATCH #if FOCUSMASTER_PATCH { MODKEY|ControlMask, XK_space, focusmaster, {0} }, #endif // FOCUSMASTER_PATCH @@ -1206,6 +1217,9 @@ static Button buttons[] = { { ClkTagBar, 0, Button3, toggleview, {0} }, { ClkTagBar, MODKEY, Button1, tag, {0} }, { ClkTagBar, MODKEY, Button3, toggletag, {0} }, + #if TAB_PATCH + { ClkTabBar, 0, Button1, focuswin, {0} }, + #endif // TAB_PATCH }; #if DWMC_PATCH diff --git a/dwm.c b/dwm.c index 8199199..9cabc5b 100644 --- a/dwm.c +++ b/dwm.c @@ -69,6 +69,9 @@ #define Button9 9 #define NUMTAGS 9 #define BARRULES 20 +#if TAB_PATCH +#define MAXTABS 50 +#endif // TAB_PATCH #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) #if BAR_ANYBAR_PATCH @@ -205,6 +208,9 @@ enum { #if BAR_STATUSBUTTON_PATCH ClkButton, #endif // BAR_STATUSBUTTON_PATCH + #if TAB_PATCH + ClkTabBar, + #endif // TAB_PATCH ClkTagBar, ClkLtSymbol, ClkStatusText, @@ -414,6 +420,9 @@ struct Monitor { int num; int mx, my, mw, mh; /* screen size */ int wx, wy, ww, wh; /* window area */ + #if TAB_PATCH + int ty; /* tab bar geometry */ + #endif // TAB_PATCH #if VANITYGAPS_PATCH int gappih; /* horizontal gap between windows */ int gappiv; /* vertical gap between windows */ @@ -427,6 +436,13 @@ struct Monitor { unsigned int sellt; unsigned int tagset[2]; int showbar; + #if TAB_PATCH + int showtab; + int toptab; + Window tabwin; + int ntabs; + int tab_widths[MAXTABS]; + #endif // TAB_PATCH Client *clients; Client *sel; Client *stack; @@ -973,6 +989,10 @@ arrange(Monitor *m) void arrangemon(Monitor *m) { + #if TAB_PATCH + updatebarpos(m); + XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th); + #endif // TAB_PATCH strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); @@ -1000,7 +1020,7 @@ attachstack(Client *c) void buttonpress(XEvent *e) { - int click, i, r; + int click, i, x, r; Arg arg = {0}; Client *c; Monitor *m; @@ -1043,6 +1063,26 @@ buttonpress(XEvent *e) } } + #if TAB_PATCH + if (ev->window == selmon->tabwin) { + for (i = 0, x = 0, c = selmon->clients; c; c = c->next) { + if (!ISVISIBLE(c) || HIDDEN(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; + } + } + #endif // TAB_PATCH + if (click == ClkRootWin && (c = wintoclient(ev->window))) { #if FOCUSONCLICK_PATCH if (focusonwheel || (ev->button != Button4 && ev->button != Button5)) @@ -1058,11 +1098,17 @@ 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)) { - #if BAR_WINTITLEACTIONS_PATCH - buttons[i].func((click == ClkTagBar || click == ClkWinTitle) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); - #else - buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); - #endif // BAR_WINTITLEACTIONS_PATCH + buttons[i].func( + ( + click == ClkTagBar + #if TAB_PATCH + || click == ClkTabBar + #endif // TAB_PATCH + #if BAR_WINTITLEACTIONS_PATCH + || click == ClkWinTitle + #endif // BAR_WINTITLEACTIONS_PATCH + ) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg + ); } } } @@ -1149,6 +1195,10 @@ cleanupmon(Monitor *mon) #endif // BAR_SYSTRAY_PATCH free(bar); } + #if TAB_PATCH + XUnmapWindow(dpy, mon->tabwin); + XDestroyWindow(dpy, mon->tabwin); + #endif // TAB_PATCH free(mon); } @@ -1413,6 +1463,11 @@ createmon(void) m->nstack = nstack; #endif // FLEXTILE_DELUXE_LAYOUT m->showbar = showbar; + #if TAB_PATCH + m->showtab = showtab; + m->toptab = toptab; + m->ntabs = 0; + #endif // TAB_PATCH #if SETBORDERPX_PATCH m->borderpx = borderpx; #endif // SETBORDERPX_PATCH @@ -1807,8 +1862,12 @@ 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); + #if TAB_PATCH + drawtabs(); + #endif // TAB_PATCH + } } void @@ -2567,12 +2626,18 @@ propertynotify(XEvent *e) updatewmhints(c); if (c->isurgent) drawbars(); + #if TAB_PATCH + drawtabs(); + #endif // TAB_PATCH break; } if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { updatetitle(c); if (c == c->mon->sel) drawbar(c->mon); + #if TAB_PATCH + drawtab(c->mon); + #endif // TAB_PATCH } #if DECORATION_HINTS_PATCH if (ev->atom == motifatom) @@ -2864,6 +2929,9 @@ restack(Monitor *m) #endif // WARP_PATCH drawbar(m); + #if TAB_PATCH + drawtab(m); + #endif // TAB_PATCH if (!m->sel) return; if (m->sel->isfloating || !m->lt[m->sellt]->arrange) @@ -3345,6 +3413,9 @@ setup(void) bh = drw->fonts->h + 2; #endif // BAR_HEIGHT_PATCH #endif // BAR_STATUSPADDING_PATCH + #if TAB_PATCH + th = bh; + #endif // TAB_PATCH updategeom(); /* init atoms */ utf8string = XInternAtom(dpy, "UTF8_STRING", False); @@ -4069,12 +4140,32 @@ updatebars(void) XSetClassHint(dpy, bar->win, &ch); } } + #if TAB_PATCH + if (!m->tabwin) { + #if BAR_ALPHA_PATCH + m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, depth, + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + #else + m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + #endif // BAR_ALPHA_PATCH + XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->tabwin); + } + #endif // TAB_PATCH } } void updatebarpos(Monitor *m) { + #if TAB_PATCH + Client *c; + int nvis = 0; + #endif // TAB_PATCH + m->wx = m->mx; m->wy = m->my; m->ww = m->mw; @@ -4105,6 +4196,24 @@ updatebarpos(Monitor *m) for (bar = m->bar; bar; bar = bar->next) if (!m->showbar || !bar->showbar) bar->by = -bar->bh - y_pad; + + #if TAB_PATCH + for (c = m->clients; c; c = c->next) { + if (ISVISIBLE(c) && !HIDDEN(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; + } + #endif // TAB_PATCH + if (!m->showbar) return; for (bar = m->bar; bar; bar = bar->next) { diff --git a/patch/bar_powerline_tags.c b/patch/bar_powerline_tags.c index f4dd923..7806725 100644 --- a/patch/bar_powerline_tags.c +++ b/patch/bar_powerline_tags.c @@ -52,7 +52,7 @@ draw_pwrl_tags(Bar *bar, BarArg *a) icon = tagicon(bar->mon, i); invert = 0; w = TEXTW(icon); - if (urg & 1 << i ) { + if (urg & 1 << i) { drw_settrans(drw, prevscheme, (nxtscheme = scheme[bar->mon->tagset[bar->mon->seltags] & 1 << i ? SchemeSel : SchemeUrg])); } else { drw_settrans(drw, prevscheme, (nxtscheme = scheme[bar->mon->tagset[bar->mon->seltags] & 1 << i ? SchemeSel : SchemeNorm])); diff --git a/patch/include.c b/patch/include.c index 7f79624..73c3536 100644 --- a/patch/include.c +++ b/patch/include.c @@ -253,6 +253,9 @@ #if SWITCHCOL_PATCH #include "switchcol.c" #endif +#if TAB_PATCH +#include "tab.c" +#endif #if TAGALL_PATCH #include "tagall.c" #endif diff --git a/patch/include.h b/patch/include.h index 9b3de50..27cf77b 100644 --- a/patch/include.h +++ b/patch/include.h @@ -249,6 +249,9 @@ #if SWITCHCOL_PATCH #include "switchcol.h" #endif +#if TAB_PATCH +#include "tab.h" +#endif #if TAGALL_PATCH #include "tagall.h" #endif diff --git a/patch/tab.c b/patch/tab.c new file mode 100644 index 0000000..be6a59a --- /dev/null +++ b/patch/tab.c @@ -0,0 +1,132 @@ +static int th = 0; /* tab bar geometry */ + +void +drawtabs(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawtab(m); +} + +static int +cmpint(const void *p1, const void *p2) +{ + /* The actual arguments to this function are "pointers to + pointers to char", but strcmp(3) arguments are "pointers + to char", hence the following cast plus dereference */ + return *((int*) p1) > * (int*) p2; +} + +void +drawtab(Monitor *m) +{ + Client *c; + int i; + int itag = -1; + char view_info[50]; + int view_info_w = 0; + int sorted_label_widths[MAXTABS]; + int tot_width; + int maxsize = bh; + int x = 0; + int w = 0; + + // view_info: indicate the tag which is displayed in the view + for (i = 0; i < NUMTAGS; ++i) { + if ((selmon->tagset[selmon->seltags] >> i) & 1) { + if (itag >=0) { // more than one tag selected + itag = -1; + break; + } + itag = i; + } + } + + if (0 <= itag && itag < NUMTAGS) { + snprintf(view_info, sizeof view_info, "[%s]", tagicon(m, itag)); + } else { + strncpy(view_info, "[...]", sizeof view_info); + } + view_info[sizeof(view_info) - 1 ] = 0; + view_info_w = TEXTW(view_info); + tot_width = view_info_w; + + /* Calculates number of labels and their width */ + m->ntabs = 0; + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c) || HIDDEN(c)) + continue; + m->tab_widths[m->ntabs] = TEXTW(c->name); + tot_width += m->tab_widths[m->ntabs]; + ++m->ntabs; + if (m->ntabs >= MAXTABS) + break; + } + + if (tot_width > m->ww) { // not enough space to display the labels, they need to be truncated + memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); + qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); + tot_width = view_info_w; + for (i = 0; i < m->ntabs; ++i) { + if (tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) + break; + tot_width += sorted_label_widths[i]; + } + maxsize = (m->ww - tot_width) / (m->ntabs - i); + } else { + maxsize = m->ww; + } + i = 0; + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c) || HIDDEN(c)) + continue; + if (i >= m->ntabs) + break; + if (m->tab_widths[i] > maxsize) + m->tab_widths[i] = maxsize; + w = m->tab_widths[i]; + drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, th, 0, c->name, 0, False); + x += w; + ++i; + } + + drw_setscheme(drw, scheme[SchemeNorm]); + + /* cleans interspace between window names and current viewed tag label */ + w = m->ww - view_info_w - x; + drw_text(drw, x, 0, w, th, 0, "", 0, False); + + /* view info */ + x += w; + w = view_info_w; + drw_text(drw, x, 0, w, th, 0, view_info, 0, False); + + drw_map(drw, m->tabwin, 0, 0, m->ww, th); +} + +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) && !HIDDEN(c)) + --iwin; + }; + if (c) { + focus(c); + restack(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); +} \ No newline at end of file diff --git a/patch/tab.h b/patch/tab.h new file mode 100644 index 0000000..7b4d20f --- /dev/null +++ b/patch/tab.h @@ -0,0 +1,4 @@ +static void drawtab(Monitor *m); +static void drawtabs(void); +static void focuswin(const Arg* arg); +static void tabmode(const Arg *arg); diff --git a/patches.def.h b/patches.def.h index 0a27b1c..ef1a0d6 100644 --- a/patches.def.h +++ b/patches.def.h @@ -963,6 +963,14 @@ */ #define SWITCHTAG_PATCH 0 +/* This patch transforms the monocle layout into a "tabbed" layout if more than one window is + * present on the monocle view. This patch has been added for demonstration purposes only and has + * limited compatibility with other patches. It will conflict space-wise with a second bar. + * Note that fancybar, awesomebar, bartabgroups and similar patches make the tab patch redundant. + * https://dwm.suckless.org/patches/tab/ + */ +#define TAB_PATCH 0 + /* Adds keyboard shortcuts to move all (or only floating) windows from one tag to another. * https://dwm.suckless.org/patches/tagall/ */