sites

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

commit d02758b4c026d0e2627d841dec9bfc311cbe341f
parent d9b14470bb15cf0156ecc2b05d68cb3ec5bf406c
Author: 8dcc <8dcc.git@gmail.com>
Date:   Sat, 11 Apr 2026 17:55:59 +0200

[dwm][patches] Add 'workflows' patch

Diffstat:
Adwm.suckless.org/patches/workspaces/dwm-workspaces-20260411-23a25b2.diff | 500+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adwm.suckless.org/patches/workspaces/index.md | 35+++++++++++++++++++++++++++++++++++
2 files changed, 535 insertions(+), 0 deletions(-)

diff --git a/dwm.suckless.org/patches/workspaces/dwm-workspaces-20260411-23a25b2.diff b/dwm.suckless.org/patches/workspaces/dwm-workspaces-20260411-23a25b2.diff @@ -0,0 +1,500 @@ +From 23a25b2918e083e7c9ecad934db7841004aa4629 Mon Sep 17 00:00:00 2001 +From: 8dcc <8dcc.git@gmail.com> +Date: Sat, 11 Apr 2026 17:38:52 +0200 +Subject: [PATCH] Add support for multiple workspaces + +Each monitor now has N independently-named workspaces (defined via the +new 'workspaces' array in 'config.h', like 'tags'). Every client belongs +to exactly one workspace; switching workspaces hides all clients from +the previous workspace and shows only those from the new one. + +Each workspace maintains its own tag view, layout, and layout history +independently of all others. Switching workspaces carries the current +tag selection over so the viewed tag does not change. + +Default keybinds (where 'F<n>' represents function keys): + + * Mod+F<n>: Switch to workspace N. + * Mod+Shift+F<n>: Move focused window to workspace N. + +The bar shows workspace buttons leftmost, styled identically to tag +buttons ('SchemeSel' for active, 'SchemeNorm' for others). Clicking a +workspace button switches to it. +--- + config.def.h | 8 +++ + dwm.c | 181 ++++++++++++++++++++++++++++++++++++--------------- + 2 files changed, 135 insertions(+), 54 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..db6ce40 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -20,6 +20,7 @@ static const char *colors[][3] = { + + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; ++static const char *workspaces[] = { "A", "B", "C" }; + + static const Rule rules[] = { + /* xprop(1): +@@ -50,6 +51,9 @@ static const Layout layouts[] = { + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, ++#define WSKEYS(KEY,WS) \ ++ { MODKEY, KEY, viewws, {.i = (WS)} }, \ ++ { MODKEY|ShiftMask, KEY, tagws, {.i = (WS)} }, + + /* helper for spawning shell commands in the pre dwm-5.0 fashion */ + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } +@@ -93,6 +97,9 @@ static Key keys[] = { + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) ++ WSKEYS( XK_F1, 0) ++ WSKEYS( XK_F2, 1) ++ WSKEYS( XK_F3, 2) + { MODKEY|ShiftMask, XK_q, quit, {0} }, + }; + +@@ -100,6 +107,7 @@ static Key keys[] = { + /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ + static Button buttons[] = { + /* click event mask button function argument */ ++ { ClkWsBar, 0, Button1, viewws, {0} }, + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, +diff --git a/dwm.c b/dwm.c +index 4465af1..7534363 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -49,7 +49,10 @@ + #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) + #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 MWS(m) ((m)->ws[(m)->selws]) ++#define MTAGSET(m) (MWS(m).tagset[MWS(m).seltags]) ++#define ISVISIBLE(C) ((C)->tags & MTAGSET((C)->mon) \ ++ && (C)->ws == (C)->mon->selws) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) +@@ -64,7 +67,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 { ClkWsBar, ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + + typedef union { +@@ -92,6 +95,7 @@ struct Client { + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; ++ int ws; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + Client *next; + Client *snext; +@@ -111,17 +115,23 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + +-struct Monitor { ++typedef struct { ++ unsigned int tagset[2]; ++ unsigned int seltags; ++ unsigned int sellt; ++ const Layout *lt[2]; + char ltsymbol[16]; ++} WsState; ++ ++struct Monitor { + float mfact; + int nmaster; + int num; + int by; /* 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 selws; ++ WsState *ws; + int showbar; + int topbar; + Client *clients; +@@ -129,7 +139,6 @@ struct Monitor { + Client *stack; + Monitor *next; + Window barwin; +- const Layout *lt[2]; + }; + + typedef struct { +@@ -208,7 +217,9 @@ static void sigchld(int unused); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); ++static void tagws(const Arg *arg); + static void tile(Monitor *); ++static void viewws(const Arg *arg); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -308,7 +319,7 @@ applyrules(Client *c) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); +- c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; ++ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : MTAGSET(c->mon); + } + + int +@@ -343,7 +354,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 (resizehints || c->isfloating || !MWS(c->mon).lt[MWS(c->mon).sellt]->arrange) { + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ +@@ -394,9 +405,10 @@ arrange(Monitor *m) + void + arrangemon(Monitor *m) + { +- strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); +- if (m->lt[m->sellt]->arrange) +- m->lt[m->sellt]->arrange(m); ++ strncpy(MWS(m).ltsymbol, MWS(m).lt[MWS(m).sellt]->symbol, ++ sizeof MWS(m).ltsymbol); ++ if (MWS(m).lt[MWS(m).sellt]->arrange) ++ MWS(m).lt[MWS(m).sellt]->arrange(m); + } + + void +@@ -432,17 +444,26 @@ buttonpress(XEvent *e) + if (ev->window == selmon->barwin) { + i = x = 0; + do +- x += TEXTW(tags[i]); +- while (ev->x >= x && ++i < LENGTH(tags)); +- if (i < LENGTH(tags)) { +- click = ClkTagBar; +- arg.ui = 1 << i; +- } else if (ev->x < x + blw) +- click = ClkLtSymbol; +- else if (ev->x > selmon->ww - TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; ++ x += TEXTW(workspaces[i]); ++ while (ev->x >= x && ++i < LENGTH(workspaces)); ++ if (i < LENGTH(workspaces)) { ++ click = ClkWsBar; ++ arg.i = i; ++ } else { ++ i = 0; ++ do ++ x += TEXTW(tags[i]); ++ while (ev->x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ click = ClkTagBar; ++ arg.ui = 1 << i; ++ } else if (ev->x < x + blw) ++ click = ClkLtSymbol; ++ else if (ev->x > selmon->ww - TEXTW(stext)) ++ click = ClkStatusText; ++ else ++ click = ClkWinTitle; ++ } + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); +@@ -452,7 +473,8 @@ 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); ++ buttons[i].func((click == ClkTagBar || click == ClkWsBar) ++ && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); + } + + void +@@ -475,7 +497,7 @@ cleanup(void) + size_t i; + + view(&a); +- selmon->lt[selmon->sellt] = &foo; ++ MWS(selmon).lt[MWS(selmon).sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); +@@ -506,6 +528,7 @@ cleanupmon(Monitor *mon) + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ free(mon->ws); + free(mon); + } + +@@ -586,7 +609,7 @@ configurerequest(XEvent *e) + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; +- else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { ++ else if (c->isfloating || !MWS(selmon).lt[MWS(selmon).sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; +@@ -631,16 +654,22 @@ Monitor * + createmon(void) + { + Monitor *m; ++ unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); +- m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; +- m->lt[0] = &layouts[0]; +- m->lt[1] = &layouts[1 % LENGTH(layouts)]; +- strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ m->selws = 0; ++ m->ws = ecalloc(LENGTH(workspaces), sizeof(WsState)); ++ for (i = 0; i < LENGTH(workspaces); i++) { ++ m->ws[i].tagset[0] = m->ws[i].tagset[1] = 1; ++ m->ws[i].lt[0] = &layouts[0]; ++ m->ws[i].lt[1] = &layouts[1 % LENGTH(layouts)]; ++ strncpy(m->ws[i].ltsymbol, layouts[0].symbol, ++ sizeof m->ws[i].ltsymbol); ++ } + return m; + } + +@@ -709,14 +738,22 @@ drawbar(Monitor *m) + } + + for (c = m->clients; c; c = c->next) { +- occ |= c->tags; ++ if (c->ws == m->selws) /* only count occupied tags for current workspace */ ++ occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; ++ for (i = 0; i < LENGTH(workspaces); i++) { ++ w = TEXTW(workspaces[i]); ++ drw_setscheme(drw, scheme[i == (unsigned int)m->selws ++ ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, workspaces[i], 0); ++ x += w; ++ } + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_setscheme(drw, scheme[MWS(m).tagset[MWS(m).seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, +@@ -724,9 +761,9 @@ drawbar(Monitor *m) + urg & 1 << i); + x += w; + } +- w = blw = TEXTW(m->ltsymbol); ++ w = blw = TEXTW(MWS(m).ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); ++ x = drw_text(drw, x, 0, w, bh, lrpad / 2, MWS(m).ltsymbol, 0); + + if ((w = m->ww - sw - x) > bh) { + if (m->sel) { +@@ -1034,9 +1071,11 @@ manage(Window w, XWindowAttributes *wa) + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; ++ c->ws = t->ws; + } else { + c->mon = selmon; + applyrules(c); ++ c->ws = c->mon->selws; /* set after applyrules, which may change c->mon */ + } + + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) +@@ -1110,7 +1149,7 @@ monocle(Monitor *m) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ +- snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); ++ snprintf(MWS(m).ltsymbol, sizeof MWS(m).ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); + } +@@ -1176,10 +1215,10 @@ movemouse(const Arg *arg) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); +- if (!c->isfloating && selmon->lt[selmon->sellt]->arrange ++ if (!c->isfloating && MWS(selmon).lt[MWS(selmon).sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); +- if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) ++ if (!MWS(selmon).lt[MWS(selmon).sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } +@@ -1325,11 +1364,11 @@ resizemouse(const Arg *arg) + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { +- if (!c->isfloating && selmon->lt[selmon->sellt]->arrange ++ if (!c->isfloating && MWS(selmon).lt[MWS(selmon).sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } +- if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) ++ if (!MWS(selmon).lt[MWS(selmon).sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } +@@ -1354,9 +1393,9 @@ restack(Monitor *m) + drawbar(m); + if (!m->sel) + return; +- if (m->sel->isfloating || !m->lt[m->sellt]->arrange) ++ if (m->sel->isfloating || !MWS(m).lt[MWS(m).sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); +- if (m->lt[m->sellt]->arrange) { ++ if (MWS(m).lt[MWS(m).sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) +@@ -1416,7 +1455,8 @@ sendmon(Client *c, Monitor *m) + detach(c); + detachstack(c); + c->mon = m; +- c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ ++ c->tags = MTAGSET(m); /* assign tags of target monitor */ ++ c->ws = m->selws; + attach(c); + attachstack(c); + focus(NULL); +@@ -1500,11 +1540,12 @@ setfullscreen(Client *c, int fullscreen) + void + setlayout(const Arg *arg) + { +- if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) +- selmon->sellt ^= 1; ++ if (!arg || !arg->v || arg->v != MWS(selmon).lt[MWS(selmon).sellt]) ++ MWS(selmon).sellt ^= 1; + if (arg && arg->v) +- selmon->lt[selmon->sellt] = (Layout *)arg->v; +- strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); ++ MWS(selmon).lt[MWS(selmon).sellt] = (Layout *)arg->v; ++ strncpy(MWS(selmon).ltsymbol, MWS(selmon).lt[MWS(selmon).sellt]->symbol, ++ sizeof MWS(selmon).ltsymbol); + if (selmon->sel) + arrange(selmon); + else +@@ -1517,7 +1558,7 @@ setmfact(const Arg *arg) + { + float f; + +- if (!arg || !selmon->lt[selmon->sellt]->arrange) ++ if (!arg || !MWS(selmon).lt[MWS(selmon).sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.1 || f > 0.9) +@@ -1618,7 +1659,7 @@ showhide(Client *c) + if (ISVISIBLE(c)) { + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); +- if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) ++ if ((!MWS(c->mon).lt[MWS(c->mon).sellt]->arrange || c->isfloating) && !c->isfullscreen) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { +@@ -1670,6 +1711,38 @@ tagmon(const Arg *arg) + sendmon(selmon->sel, dirtomon(arg->i)); + } + ++/* Move the focused client to the workspace given by arg->i */ ++void ++tagws(const Arg *arg) ++{ ++ if (!selmon->sel) ++ return; ++ if (arg->i < 0 || arg->i >= (int)LENGTH(workspaces)) ++ return; ++ selmon->sel->ws = arg->i; ++ focus(NULL); ++ arrange(selmon); ++} ++ ++/* Switch the active workspace on selmon to arg->i */ ++void ++viewws(const Arg *arg) ++{ ++ if (arg->i < 0 || arg->i >= (int)LENGTH(workspaces)) ++ return; ++ if (arg->i == selmon->selws) ++ return; ++ ++ /* carry tag selection so the viewed tag does not change */ ++ selmon->ws[arg->i].tagset[0] = MWS(selmon).tagset[0]; ++ selmon->ws[arg->i].tagset[1] = MWS(selmon).tagset[1]; ++ selmon->ws[arg->i].seltags = MWS(selmon).seltags; ++ ++ selmon->selws = arg->i; ++ focus(NULL); ++ arrange(selmon); ++} ++ + void + tile(Monitor *m) + { +@@ -1737,10 +1810,10 @@ toggletag(const Arg *arg) + void + toggleview(const Arg *arg) + { +- unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); ++ unsigned int newtagset = MTAGSET(selmon) ^ (arg->ui & TAGMASK); + + if (newtagset) { +- selmon->tagset[selmon->seltags] = newtagset; ++ MWS(selmon).tagset[MWS(selmon).seltags] = newtagset; + focus(NULL); + arrange(selmon); + } +@@ -2035,11 +2108,11 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { +- if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) ++ if ((arg->ui & TAGMASK) == MTAGSET(selmon)) + return; +- selmon->seltags ^= 1; /* toggle sel tagset */ ++ MWS(selmon).seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) +- selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ MWS(selmon).tagset[MWS(selmon).seltags] = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +@@ -2115,7 +2188,7 @@ zoom(const Arg *arg) + { + Client *c = selmon->sel; + +- if (!selmon->lt[selmon->sellt]->arrange ++ if (!MWS(selmon).lt[MWS(selmon).sellt]->arrange + || (selmon->sel && selmon->sel->isfloating)) + return; + if (c == nexttiled(selmon->clients)) +-- +2.53.0 + diff --git a/dwm.suckless.org/patches/workspaces/index.md b/dwm.suckless.org/patches/workspaces/index.md @@ -0,0 +1,35 @@ +workspaces +========== + +Description +----------- + +This patch adds support for workspaces. Each monitor now has *N* +independently-named workspaces (defined via the new `workspaces` array in +`config.h`, like `tags`). Every client belongs to exactly one workspace; +switching workspaces hides all clients from the previous workspace and shows +only those from the new one. + +Each workspace maintains its own tag view, layout, and layout history +independently of all others. Switching workspaces carries the current tag +selection over so the viewed tag does not change. + +Default keybinds (where `F<n>` represents function keys): + +* `Mod+F<n>`: Switch to workspace *n*. +* `Mod+Shift+F<n>`: Move focused window to workspace *n*. + +The bar shows workspace buttons leftmost, styled identically to tag buttons +(`SchemeSel` for active, `SchemeNorm` for others). Clicking a workspace button +switches to it. + + +Download +-------- + +* [dwm-workspaces-20260411-23a25b2.diff](dwm-workspaces-20260411-23a25b2.diff) + +Authors +------- + +* [8dcc](https://github.com/8dcc) - <8dcc.git@gmail.com>