diff --git a/dmenu/dmenu-alpha-5.0_20210725_523aa08.diff b/dmenu/dmenu-alpha-5.0_20210725_523aa08.diff index a868d8d..83d95dd 100644 --- a/dmenu/dmenu-alpha-5.0_20210725_523aa08.diff +++ b/dmenu/dmenu-alpha-5.0_20210725_523aa08.diff @@ -277,5 +277,5 @@ index 4c67419..4f66f0d 100644 /* Cursor abstraction */ Cur *drw_cur_create(Drw *drw, int shape); -- -2.33.0 +2.34.1 diff --git a/dwm/dwm-alpha-6.3.diff b/dwm/dwm-alpha-6.3.diff new file mode 100644 index 0000000..5afb8fa --- /dev/null +++ b/dwm/dwm-alpha-6.3.diff @@ -0,0 +1,297 @@ +From 29ce86e6162c47ee5cd830df5781a572e3fed43e Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 10:55:28 +0100 +Subject: [PATCH] Alpha, adds transparency for the status bar. + +Allow dwm to have translucent bars, while keeping all the text on it opaque, just like the alpha-patch for st. + +Refer to https://dwm.suckless.org/patches/alpha/ + +Authors: + +Eon S. Jeon - esjeon@hyunmu.am +Laslo Hunhold - dev@frign.de (6.1 port) +Thomas Oltmann - thomas.oltmann.hhg@gmail.com (20180613-b69c870 port) +--- + config.def.h | 7 ++++++ + config.mk | 2 +- + drw.c | 26 ++++++++++++----------- + drw.h | 9 +++++--- + dwm.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++------ + 5 files changed, 82 insertions(+), 22 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..ca7a9ba 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -12,11 +12,18 @@ static const char col_gray2[] = "#444444"; + static const char col_gray3[] = "#bbbbbb"; + static const char col_gray4[] = "#eeeeee"; + static const char col_cyan[] = "#005577"; ++static const unsigned int baralpha = 0xd0; ++static const unsigned int borderalpha = OPAQUE; + static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; ++static const unsigned int alphas[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { OPAQUE, baralpha, borderalpha }, ++ [SchemeSel] = { OPAQUE, baralpha, borderalpha }, ++}; + + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +diff --git a/config.mk b/config.mk +index b6eb7e0..848aef9 100644 +--- a/config.mk ++++ b/config.mk +@@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2 + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/drw.c b/drw.c +index 4cdbcbe..fe3aadd 100644 +--- a/drw.c ++++ b/drw.c +@@ -61,7 +61,7 @@ utf8decode(const char *c, long *u, size_t clen) + } + + Drw * +-drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) ++drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) + { + Drw *drw = ecalloc(1, sizeof(Drw)); + +@@ -70,8 +70,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h + drw->root = root; + drw->w = w; + drw->h = h; +- drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); +- drw->gc = XCreateGC(dpy, root, 0, NULL); ++ drw->visual = visual; ++ drw->depth = depth; ++ drw->cmap = cmap; ++ drw->drawable = XCreatePixmap(dpy, root, w, h, depth); ++ drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +@@ -87,7 +90,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h) + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); +- drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); ++ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); + } + + void +@@ -194,21 +197,22 @@ drw_fontset_free(Fnt *font) + } + + void +-drw_clr_create(Drw *drw, Clr *dest, const char *clrname) ++drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha) + { + if (!drw || !dest || !clrname) + return; + +- if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), +- DefaultColormap(drw->dpy, drw->screen), ++ if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); ++ ++ dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24); + } + + /* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ + Clr * +-drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) ++drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount) + { + size_t i; + Clr *ret; +@@ -218,7 +222,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) + return NULL; + + for (i = 0; i < clrcount; i++) +- drw_clr_create(drw, &ret[i], clrnames[i]); ++ drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]); + return ret; + } + +@@ -274,9 +278,7 @@ 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); +- d = XftDrawCreate(drw->dpy, drw->drawable, +- DefaultVisual(drw->dpy, drw->screen), +- DefaultColormap(drw->dpy, drw->screen)); ++ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); + x += lpad; + w -= lpad; + } +diff --git a/drw.h b/drw.h +index 4bcd5ad..a56f523 100644 +--- a/drw.h ++++ b/drw.h +@@ -20,6 +20,9 @@ typedef struct { + Display *dpy; + int screen; + Window root; ++ Visual *visual; ++ unsigned int depth; ++ Colormap cmap; + Drawable drawable; + GC gc; + Clr *scheme; +@@ -27,7 +30,7 @@ typedef struct { + } Drw; + + /* Drawable abstraction */ +-Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); ++Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap); + void drw_resize(Drw *drw, unsigned int w, unsigned int h); + void drw_free(Drw *drw); + +@@ -38,8 +41,8 @@ unsigned int drw_fontset_getwidth(Drw *drw, const char *text); + void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + + /* Colorscheme abstraction */ +-void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +-Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); ++void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha); ++Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount); + + /* Cursor abstraction */ + Cur *drw_cur_create(Drw *drw, int shape); +diff --git a/dwm.c b/dwm.c +index a96f33c..34b9a32 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -57,6 +57,8 @@ + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++#define OPAQUE 0xffU ++ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +@@ -233,6 +235,7 @@ static Monitor *wintomon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); ++static void xinitvisual(); + static void zoom(const Arg *arg); + + /* variables */ +@@ -269,6 +272,11 @@ static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; + ++static int useargb = 0; ++static Visual *visual; ++static int depth; ++static Colormap cmap; ++ + /* configuration, allows nested code to access above variables */ + #include "config.h" + +@@ -1545,7 +1553,8 @@ setup(void) + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); +- drw = drw_create(dpy, screen, root, sw, sh); ++ xinitvisual(); ++ drw = drw_create(dpy, screen, root, sw, sh, visual, depth, cmap); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; +@@ -1573,7 +1582,7 @@ setup(void) + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) +- scheme[i] = drw_scm_create(drw, colors[i], 3); ++ scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1810,16 +1819,18 @@ updatebars(void) + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, +- .background_pixmap = ParentRelative, ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); +@@ -2116,6 +2127,43 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++void ++xinitvisual() ++{ ++ XVisualInfo *infos; ++ XRenderPictFormat *fmt; ++ int nitems; ++ int i; ++ ++ XVisualInfo tpl = { ++ .screen = screen, ++ .depth = 32, ++ .class = TrueColor ++ }; ++ long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; ++ ++ infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); ++ visual = NULL; ++ for(i = 0; i < nitems; i ++) { ++ fmt = XRenderFindVisualFormat(dpy, infos[i].visual); ++ if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { ++ visual = infos[i].visual; ++ depth = infos[i].depth; ++ cmap = XCreateColormap(dpy, root, visual, AllocNone); ++ useargb = 1; ++ break; ++ } ++ } ++ ++ XFree(infos); ++ ++ if (! visual) { ++ visual = DefaultVisual(dpy, screen); ++ depth = DefaultDepth(dpy, screen); ++ cmap = DefaultColormap(dpy, screen); ++ } ++} ++ + void + zoom(const Arg *arg) + { +-- +2.19.1 + diff --git a/dwm/dwm-alpha-systray-6.3.diff b/dwm/dwm-alpha-systray-6.3.diff new file mode 100644 index 0000000..06f9391 --- /dev/null +++ b/dwm/dwm-alpha-systray-6.3.diff @@ -0,0 +1,721 @@ +From 14803952902c1cbde814bc1233a6c0d766a0febf Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:45:19 +0100 +Subject: [PATCH 2/2] Adding systray patch + +Refer to https://dwm.suckless.org/patches/systray/ +--- + config.def.h | 4 + + dwm.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 390 insertions(+), 24 deletions(-) + +diff --git a/config.def.h b/config.def.h +index ca7a9ba..0b82307 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,10 @@ 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 */ ++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ ++static const int showsystray = 1; /* 0 means no systray */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/dwm.c b/dwm.c +index 34b9a32..2ae3e2b 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -59,12 +59,30 @@ + + #define OPAQUE 0xffU + ++#define SYSTEM_TRAY_REQUEST_DOCK 0 ++#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 ++ ++/* XEMBED messages */ ++#define XEMBED_EMBEDDED_NOTIFY 0 ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_FOCUS_IN 4 ++#define XEMBED_MODALITY_ON 10 ++ ++#define XEMBED_MAPPED (1 << 0) ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_WINDOW_DEACTIVATE 2 ++ ++#define VERSION_MAJOR 0 ++#define VERSION_MINOR 0 ++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR ++ + /* enums */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +-enum { NetSupported, NetWMName, NetWMState, NetWMCheck, +- NetWMFullscreen, NetActiveWindow, NetWMWindowType, +- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++enum { NetSupported, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayVisual, ++ NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDock, ++ NetSystemTrayOrientationHorz, NetWMWindowTypeDialog, NetClientList, NetWMCheck, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +@@ -143,6 +161,12 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++}; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -175,6 +199,7 @@ static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); ++static unsigned int getsystraywidth(); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); + static void incnmaster(const Arg *arg); +@@ -194,10 +219,12 @@ static Monitor *recttomon(int x, int y, int w, int h); + static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); ++static void removesystrayicon(Client *i); ++static void resizerequest(XEvent *e); + static void restack(Monitor *m); + static void run(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); +@@ -209,6 +236,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 Monitor *systraytomon(Monitor *m); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +@@ -226,12 +254,16 @@ static int updategeom(void); + static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); ++static void updatesystray(int updatebar); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); + static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); ++static Client *wintosystrayicon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); +@@ -261,9 +293,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -272,6 +305,9 @@ static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; + ++static Systray *systray = NULL; ++static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; ++ + static int useargb = 0; + static Visual *visual; + static int depth; +@@ -448,7 +484,7 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth()) + click = ClkStatusText; + else + click = ClkWinTitle; +@@ -491,6 +527,13 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ if (showsystray) { ++ while (systray->icons) ++ removesystrayicon(systray->icons); ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ free(systray); ++ } + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -521,9 +564,49 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ if (!(c->win = cme->data.l[2])) { ++ free(c); ++ return; ++ } ++ ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ XGetWindowAttributes(dpy, c->win, &wa); ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ setclientstate(c, NormalState); ++ updatesystray(1); ++ } ++ return; ++ } ++ + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -661,6 +744,10 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if (showsystray && (c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ updatesystray(1); ++ } + } + + void +@@ -704,7 +791,7 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int x, w, tw = 0, stw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; +@@ -713,11 +800,17 @@ drawbar(Monitor *m) + if (!m->showbar) + return; + ++ if (showsystray && m == systraytomon(m)) { ++ stw = getsystraywidth(); ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, m->ww - stw, 0, stw, bh, 1, 1); ++ } ++ + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ drw_text(drw, m->ww - tw - stw, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { +@@ -740,7 +833,7 @@ drawbar(Monitor *m) + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + +- if ((w = m->ww - tw - x) > bh) { ++ if ((w = m->ww - tw - stw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +@@ -761,6 +854,9 @@ drawbars(void) + + for (m = mons; m; m = m->next) + drawbar(m); ++ ++ if (showsystray && !systraypinning) ++ updatesystray(0); + } + + void +@@ -788,8 +884,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 (showsystray && m == systraytomon(m)) ++ updatesystray(0); ++ } + } + + void +@@ -875,9 +975,17 @@ getatomprop(Client *c, Atom prop) + unsigned char *p = NULL; + Atom da, atom = None; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ /* FIXME getatomprop should return the number of items and a pointer to ++ * the stored data instead of this workaround */ ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; ++ ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; +@@ -911,6 +1019,16 @@ getstate(Window w) + return result; + } + ++unsigned int ++getsystraywidth() ++{ ++ unsigned int w = 0; ++ Client *i; ++ if (showsystray) ++ for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next); ++ return w ? w + systrayspacing : 0; ++} ++ + int + gettextprop(Window w, Atom atom, char *text, unsigned int size) + { +@@ -1015,7 +1133,7 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1104,6 +1222,12 @@ maprequest(XEvent *e) + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + ++ Client *i; ++ if (showsystray && (i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ updatesystray(1); ++ } ++ + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if (wa.override_redirect) +@@ -1227,6 +1351,16 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + ++ if (showsystray && (c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } ++ else ++ updatesystrayiconstate(c, ev); ++ updatesystray(1); ++ } ++ + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) +@@ -1277,6 +1411,19 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removesystrayicon(Client *i) ++{ ++ Client **ii; ++ ++ if (!showsystray || !i) ++ return; ++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); ++ if (ii) ++ *ii = i->next; ++ free(i); ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1356,6 +1503,18 @@ resizemouse(const Arg *arg) + } + } + ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ updatesystray(1); ++ } ++} ++ + void + restack(Monitor *m) + { +@@ -1445,26 +1604,35 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; +- Atom *protocols; ++ Atom *protocols, mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ } else { ++ exists = True; ++ mt = proto; + } + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1478,7 +1646,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1568,13 +1736,22 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); ++ netatom[NetSystemTrayVisual] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_VISUAL", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); ++ netatom[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); +@@ -1583,6 +1760,9 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); ++ /* init system tray */ ++ if (showsystray) ++ updatesystray(0); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1665,6 +1845,23 @@ spawn(const Arg *arg) + } + } + ++Monitor * ++systraytomon(Monitor *m) ++{ ++ Monitor *t; ++ int i, n; ++ if (!systraypinning) { ++ if (!m) ++ return selmon; ++ return m == selmon ? m : NULL; ++ } ++ for (n = 1, t = mons; t && t->next; n++, t = t->next); ++ for (i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next); ++ if (systraypinningfailfirst && n < systraypinning) ++ return mons; ++ return t; ++} ++ + void + tag(const Arg *arg) + { +@@ -1717,6 +1914,23 @@ togglebar(const Arg *arg) + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ if (showsystray) { ++ XWindowChanges wc; ++ if (!selmon->showbar) ++ wc.y = -bh; ++ else if (selmon->showbar) { ++ #if BARPADDING_PATCH ++ wc.y = vp; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh + vp; ++ #else ++ wc.y = 0; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh; ++ #endif // BARPADDING_PATCH ++ } ++ XConfigureWindow(dpy, systray->win, CWY, &wc); ++ } + arrange(selmon); + } + +@@ -1810,6 +2024,11 @@ unmapnotify(XEvent *e) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); ++ } else if (showsystray && (c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ updatesystray(1); + } + } + +@@ -1832,6 +2051,8 @@ updatebars(void) + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); ++ if (showsystray && m == systraytomon(m)) ++ XMapRaised(dpy, systray->win); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +@@ -2009,6 +2230,137 @@ updatestatus(void) + drawbar(selmon); + } + ++void ++updatesystray(int updatebar) ++{ ++ XSetWindowAttributes wa; ++ XWindowChanges wc; ++ Client *i; ++ Monitor *m = systraytomon(NULL); ++ unsigned int x = m->mx + m->mw; ++ unsigned int w = 1, xpad = 0, ypad = 0; ++ #if BARPADDING_PATCH ++ xpad = sp; ++ ypad = vp; ++ #endif // BARPADDING_PATCH ++ ++ if (!showsystray) ++ return; ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ ++ wa.override_redirect = True; ++ wa.event_mask = ButtonPressMask|ExposureMask; ++ wa.background_pixel = 0; ++ wa.border_pixel = 0; ++ wa.colormap = cmap; ++ systray->win = XCreateWindow(dpy, root, x - xpad, m->by + ypad, w, bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&systrayorientation, 1); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayVisual], XA_VISUALID, 32, ++ PropModeReplace, (unsigned char *)&visual->visualid, 1); ++ XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1); ++ XMapRaised(dpy, systray->win); ++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); ++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { ++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); ++ XSync(dpy, False); ++ } ++ else { ++ fprintf(stderr, "dwm: unable to obtain system tray.\n"); ++ free(systray); ++ systray = NULL; ++ return; ++ } ++ } ++ ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ wa.background_pixel = 0; ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ w += systrayspacing; ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->mon != m) ++ i->mon = m; ++ } ++ w = w ? w + systrayspacing : 1; ++ x -= w; ++ XMoveResizeWindow(dpy, systray->win, x - xpad, m->by + ypad, w, bh); ++ wc.x = x - xpad; ++ wc.y = m->by + ypad; ++ wc.width = w; ++ wc.height = bh; ++ wc.stack_mode = Above; wc.sibling = m->barwin; ++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); ++ XMapWindow(dpy, systray->win); ++ XMapSubwindows(dpy, systray->win); ++ XSync(dpy, False); ++ ++ if (updatebar) ++ drawbar(m); ++} ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((float)w / (float)h)); ++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); ++ /* force icons into the systray dimensions if they don't want to */ ++ if (i->h > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ if (i->w > 2*bh) ++ i->w = bh; ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !i || ev->atom != xatom[XembedInfo] || ++ !(flags = getatomprop(i, xatom[XembedInfo]))) ++ return; ++ ++ if (flags & XEMBED_MAPPED && !i->tags) { ++ i->tags = 1; ++ code = XEMBED_WINDOW_ACTIVATE; ++ XMapRaised(dpy, i->win); ++ setclientstate(i, NormalState); ++ } ++ else if (!(flags & XEMBED_MAPPED) && i->tags) { ++ i->tags = 0; ++ code = XEMBED_WINDOW_DEACTIVATE; ++ XUnmapWindow(dpy, i->win); ++ setclientstate(i, WithdrawnState); ++ } ++ else ++ return; ++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, ++ systray->win, XEMBED_EMBEDDED_VERSION); ++} ++ + void + updatetitle(Client *c) + { +@@ -2091,6 +2443,16 @@ wintomon(Window w) + return selmon; + } + ++Client * ++wintosystrayicon(Window w) { ++ Client *i = NULL; ++ ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next); ++ return i; ++} ++ + /* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +-- +2.19.1 + diff --git a/dwm/dwm-alpha-systray-6.3_full.diff b/dwm/dwm-alpha-systray-6.3_full.diff new file mode 100644 index 0000000..fba3a64 --- /dev/null +++ b/dwm/dwm-alpha-systray-6.3_full.diff @@ -0,0 +1,1019 @@ +From 29ce86e6162c47ee5cd830df5781a572e3fed43e Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 10:55:28 +0100 +Subject: [PATCH 1/2] Alpha, adds transparency for the status bar. + +Allow dwm to have translucent bars, while keeping all the text on it opaque, just like the alpha-patch for st. + +Refer to https://dwm.suckless.org/patches/alpha/ + +Authors: + +Eon S. Jeon - esjeon@hyunmu.am +Laslo Hunhold - dev@frign.de (6.1 port) +Thomas Oltmann - thomas.oltmann.hhg@gmail.com (20180613-b69c870 port) +--- + config.def.h | 7 ++++++ + config.mk | 2 +- + drw.c | 26 ++++++++++++----------- + drw.h | 9 +++++--- + dwm.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++------ + 5 files changed, 82 insertions(+), 22 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..ca7a9ba 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -12,11 +12,18 @@ static const char col_gray2[] = "#444444"; + static const char col_gray3[] = "#bbbbbb"; + static const char col_gray4[] = "#eeeeee"; + static const char col_cyan[] = "#005577"; ++static const unsigned int baralpha = 0xd0; ++static const unsigned int borderalpha = OPAQUE; + static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; ++static const unsigned int alphas[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { OPAQUE, baralpha, borderalpha }, ++ [SchemeSel] = { OPAQUE, baralpha, borderalpha }, ++}; + + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +diff --git a/config.mk b/config.mk +index b6eb7e0..848aef9 100644 +--- a/config.mk ++++ b/config.mk +@@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2 + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/drw.c b/drw.c +index 4cdbcbe..fe3aadd 100644 +--- a/drw.c ++++ b/drw.c +@@ -61,7 +61,7 @@ utf8decode(const char *c, long *u, size_t clen) + } + + Drw * +-drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) ++drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) + { + Drw *drw = ecalloc(1, sizeof(Drw)); + +@@ -70,8 +70,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h + drw->root = root; + drw->w = w; + drw->h = h; +- drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); +- drw->gc = XCreateGC(dpy, root, 0, NULL); ++ drw->visual = visual; ++ drw->depth = depth; ++ drw->cmap = cmap; ++ drw->drawable = XCreatePixmap(dpy, root, w, h, depth); ++ drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +@@ -87,7 +90,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h) + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); +- drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); ++ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); + } + + void +@@ -194,21 +197,22 @@ drw_fontset_free(Fnt *font) + } + + void +-drw_clr_create(Drw *drw, Clr *dest, const char *clrname) ++drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha) + { + if (!drw || !dest || !clrname) + return; + +- if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), +- DefaultColormap(drw->dpy, drw->screen), ++ if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); ++ ++ dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24); + } + + /* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ + Clr * +-drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) ++drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount) + { + size_t i; + Clr *ret; +@@ -218,7 +222,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) + return NULL; + + for (i = 0; i < clrcount; i++) +- drw_clr_create(drw, &ret[i], clrnames[i]); ++ drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]); + return ret; + } + +@@ -274,9 +278,7 @@ 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); +- d = XftDrawCreate(drw->dpy, drw->drawable, +- DefaultVisual(drw->dpy, drw->screen), +- DefaultColormap(drw->dpy, drw->screen)); ++ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); + x += lpad; + w -= lpad; + } +diff --git a/drw.h b/drw.h +index 4bcd5ad..a56f523 100644 +--- a/drw.h ++++ b/drw.h +@@ -20,6 +20,9 @@ typedef struct { + Display *dpy; + int screen; + Window root; ++ Visual *visual; ++ unsigned int depth; ++ Colormap cmap; + Drawable drawable; + GC gc; + Clr *scheme; +@@ -27,7 +30,7 @@ typedef struct { + } Drw; + + /* Drawable abstraction */ +-Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); ++Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap); + void drw_resize(Drw *drw, unsigned int w, unsigned int h); + void drw_free(Drw *drw); + +@@ -38,8 +41,8 @@ unsigned int drw_fontset_getwidth(Drw *drw, const char *text); + void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + + /* Colorscheme abstraction */ +-void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +-Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); ++void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha); ++Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount); + + /* Cursor abstraction */ + Cur *drw_cur_create(Drw *drw, int shape); +diff --git a/dwm.c b/dwm.c +index a96f33c..34b9a32 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -57,6 +57,8 @@ + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++#define OPAQUE 0xffU ++ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +@@ -233,6 +235,7 @@ static Monitor *wintomon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); ++static void xinitvisual(); + static void zoom(const Arg *arg); + + /* variables */ +@@ -269,6 +272,11 @@ static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; + ++static int useargb = 0; ++static Visual *visual; ++static int depth; ++static Colormap cmap; ++ + /* configuration, allows nested code to access above variables */ + #include "config.h" + +@@ -1545,7 +1553,8 @@ setup(void) + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); +- drw = drw_create(dpy, screen, root, sw, sh); ++ xinitvisual(); ++ drw = drw_create(dpy, screen, root, sw, sh, visual, depth, cmap); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; +@@ -1573,7 +1582,7 @@ setup(void) + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) +- scheme[i] = drw_scm_create(drw, colors[i], 3); ++ scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1810,16 +1819,18 @@ updatebars(void) + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, +- .background_pixmap = ParentRelative, ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); +@@ -2116,6 +2127,43 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++void ++xinitvisual() ++{ ++ XVisualInfo *infos; ++ XRenderPictFormat *fmt; ++ int nitems; ++ int i; ++ ++ XVisualInfo tpl = { ++ .screen = screen, ++ .depth = 32, ++ .class = TrueColor ++ }; ++ long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; ++ ++ infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); ++ visual = NULL; ++ for(i = 0; i < nitems; i ++) { ++ fmt = XRenderFindVisualFormat(dpy, infos[i].visual); ++ if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { ++ visual = infos[i].visual; ++ depth = infos[i].depth; ++ cmap = XCreateColormap(dpy, root, visual, AllocNone); ++ useargb = 1; ++ break; ++ } ++ } ++ ++ XFree(infos); ++ ++ if (! visual) { ++ visual = DefaultVisual(dpy, screen); ++ depth = DefaultDepth(dpy, screen); ++ cmap = DefaultColormap(dpy, screen); ++ } ++} ++ + void + zoom(const Arg *arg) + { +-- +2.19.1 + + +From 14803952902c1cbde814bc1233a6c0d766a0febf Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:45:19 +0100 +Subject: [PATCH 2/2] Adding systray patch + +Refer to https://dwm.suckless.org/patches/systray/ +--- + config.def.h | 4 + + dwm.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 390 insertions(+), 24 deletions(-) + +diff --git a/config.def.h b/config.def.h +index ca7a9ba..0b82307 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,10 @@ 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 */ ++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ ++static const int showsystray = 1; /* 0 means no systray */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/dwm.c b/dwm.c +index 34b9a32..2ae3e2b 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -59,12 +59,30 @@ + + #define OPAQUE 0xffU + ++#define SYSTEM_TRAY_REQUEST_DOCK 0 ++#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 ++ ++/* XEMBED messages */ ++#define XEMBED_EMBEDDED_NOTIFY 0 ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_FOCUS_IN 4 ++#define XEMBED_MODALITY_ON 10 ++ ++#define XEMBED_MAPPED (1 << 0) ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_WINDOW_DEACTIVATE 2 ++ ++#define VERSION_MAJOR 0 ++#define VERSION_MINOR 0 ++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR ++ + /* enums */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +-enum { NetSupported, NetWMName, NetWMState, NetWMCheck, +- NetWMFullscreen, NetActiveWindow, NetWMWindowType, +- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++enum { NetSupported, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayVisual, ++ NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDock, ++ NetSystemTrayOrientationHorz, NetWMWindowTypeDialog, NetClientList, NetWMCheck, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +@@ -143,6 +161,12 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++}; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -175,6 +199,7 @@ static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); ++static unsigned int getsystraywidth(); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); + static void incnmaster(const Arg *arg); +@@ -194,10 +219,12 @@ static Monitor *recttomon(int x, int y, int w, int h); + static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); ++static void removesystrayicon(Client *i); ++static void resizerequest(XEvent *e); + static void restack(Monitor *m); + static void run(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); +@@ -209,6 +236,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 Monitor *systraytomon(Monitor *m); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +@@ -226,12 +254,16 @@ static int updategeom(void); + static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); ++static void updatesystray(int updatebar); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); + static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); ++static Client *wintosystrayicon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); +@@ -261,9 +293,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -272,6 +305,9 @@ static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; + ++static Systray *systray = NULL; ++static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; ++ + static int useargb = 0; + static Visual *visual; + static int depth; +@@ -448,7 +484,7 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth()) + click = ClkStatusText; + else + click = ClkWinTitle; +@@ -491,6 +527,13 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ if (showsystray) { ++ while (systray->icons) ++ removesystrayicon(systray->icons); ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ free(systray); ++ } + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -521,9 +564,49 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ if (!(c->win = cme->data.l[2])) { ++ free(c); ++ return; ++ } ++ ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ XGetWindowAttributes(dpy, c->win, &wa); ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ setclientstate(c, NormalState); ++ updatesystray(1); ++ } ++ return; ++ } ++ + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -661,6 +744,10 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if (showsystray && (c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ updatesystray(1); ++ } + } + + void +@@ -704,7 +791,7 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int x, w, tw = 0, stw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; +@@ -713,11 +800,17 @@ drawbar(Monitor *m) + if (!m->showbar) + return; + ++ if (showsystray && m == systraytomon(m)) { ++ stw = getsystraywidth(); ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, m->ww - stw, 0, stw, bh, 1, 1); ++ } ++ + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ drw_text(drw, m->ww - tw - stw, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { +@@ -740,7 +833,7 @@ drawbar(Monitor *m) + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + +- if ((w = m->ww - tw - x) > bh) { ++ if ((w = m->ww - tw - stw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +@@ -761,6 +854,9 @@ drawbars(void) + + for (m = mons; m; m = m->next) + drawbar(m); ++ ++ if (showsystray && !systraypinning) ++ updatesystray(0); + } + + void +@@ -788,8 +884,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 (showsystray && m == systraytomon(m)) ++ updatesystray(0); ++ } + } + + void +@@ -875,9 +975,17 @@ getatomprop(Client *c, Atom prop) + unsigned char *p = NULL; + Atom da, atom = None; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ /* FIXME getatomprop should return the number of items and a pointer to ++ * the stored data instead of this workaround */ ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; ++ ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; +@@ -911,6 +1019,16 @@ getstate(Window w) + return result; + } + ++unsigned int ++getsystraywidth() ++{ ++ unsigned int w = 0; ++ Client *i; ++ if (showsystray) ++ for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next); ++ return w ? w + systrayspacing : 0; ++} ++ + int + gettextprop(Window w, Atom atom, char *text, unsigned int size) + { +@@ -1015,7 +1133,7 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1104,6 +1222,12 @@ maprequest(XEvent *e) + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + ++ Client *i; ++ if (showsystray && (i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ updatesystray(1); ++ } ++ + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if (wa.override_redirect) +@@ -1227,6 +1351,16 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + ++ if (showsystray && (c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } ++ else ++ updatesystrayiconstate(c, ev); ++ updatesystray(1); ++ } ++ + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) +@@ -1277,6 +1411,19 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removesystrayicon(Client *i) ++{ ++ Client **ii; ++ ++ if (!showsystray || !i) ++ return; ++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); ++ if (ii) ++ *ii = i->next; ++ free(i); ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1356,6 +1503,18 @@ resizemouse(const Arg *arg) + } + } + ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ updatesystray(1); ++ } ++} ++ + void + restack(Monitor *m) + { +@@ -1445,26 +1604,35 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; +- Atom *protocols; ++ Atom *protocols, mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ } else { ++ exists = True; ++ mt = proto; + } + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1478,7 +1646,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1568,13 +1736,22 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); ++ netatom[NetSystemTrayVisual] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_VISUAL", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); ++ netatom[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); +@@ -1583,6 +1760,9 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); ++ /* init system tray */ ++ if (showsystray) ++ updatesystray(0); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1665,6 +1845,23 @@ spawn(const Arg *arg) + } + } + ++Monitor * ++systraytomon(Monitor *m) ++{ ++ Monitor *t; ++ int i, n; ++ if (!systraypinning) { ++ if (!m) ++ return selmon; ++ return m == selmon ? m : NULL; ++ } ++ for (n = 1, t = mons; t && t->next; n++, t = t->next); ++ for (i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next); ++ if (systraypinningfailfirst && n < systraypinning) ++ return mons; ++ return t; ++} ++ + void + tag(const Arg *arg) + { +@@ -1717,6 +1914,23 @@ togglebar(const Arg *arg) + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ if (showsystray) { ++ XWindowChanges wc; ++ if (!selmon->showbar) ++ wc.y = -bh; ++ else if (selmon->showbar) { ++ #if BARPADDING_PATCH ++ wc.y = vp; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh + vp; ++ #else ++ wc.y = 0; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh; ++ #endif // BARPADDING_PATCH ++ } ++ XConfigureWindow(dpy, systray->win, CWY, &wc); ++ } + arrange(selmon); + } + +@@ -1810,6 +2024,11 @@ unmapnotify(XEvent *e) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); ++ } else if (showsystray && (c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ updatesystray(1); + } + } + +@@ -1832,6 +2051,8 @@ updatebars(void) + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); ++ if (showsystray && m == systraytomon(m)) ++ XMapRaised(dpy, systray->win); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +@@ -2009,6 +2230,137 @@ updatestatus(void) + drawbar(selmon); + } + ++void ++updatesystray(int updatebar) ++{ ++ XSetWindowAttributes wa; ++ XWindowChanges wc; ++ Client *i; ++ Monitor *m = systraytomon(NULL); ++ unsigned int x = m->mx + m->mw; ++ unsigned int w = 1, xpad = 0, ypad = 0; ++ #if BARPADDING_PATCH ++ xpad = sp; ++ ypad = vp; ++ #endif // BARPADDING_PATCH ++ ++ if (!showsystray) ++ return; ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ ++ wa.override_redirect = True; ++ wa.event_mask = ButtonPressMask|ExposureMask; ++ wa.background_pixel = 0; ++ wa.border_pixel = 0; ++ wa.colormap = cmap; ++ systray->win = XCreateWindow(dpy, root, x - xpad, m->by + ypad, w, bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&systrayorientation, 1); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayVisual], XA_VISUALID, 32, ++ PropModeReplace, (unsigned char *)&visual->visualid, 1); ++ XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1); ++ XMapRaised(dpy, systray->win); ++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); ++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { ++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); ++ XSync(dpy, False); ++ } ++ else { ++ fprintf(stderr, "dwm: unable to obtain system tray.\n"); ++ free(systray); ++ systray = NULL; ++ return; ++ } ++ } ++ ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ wa.background_pixel = 0; ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ w += systrayspacing; ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->mon != m) ++ i->mon = m; ++ } ++ w = w ? w + systrayspacing : 1; ++ x -= w; ++ XMoveResizeWindow(dpy, systray->win, x - xpad, m->by + ypad, w, bh); ++ wc.x = x - xpad; ++ wc.y = m->by + ypad; ++ wc.width = w; ++ wc.height = bh; ++ wc.stack_mode = Above; wc.sibling = m->barwin; ++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); ++ XMapWindow(dpy, systray->win); ++ XMapSubwindows(dpy, systray->win); ++ XSync(dpy, False); ++ ++ if (updatebar) ++ drawbar(m); ++} ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((float)w / (float)h)); ++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); ++ /* force icons into the systray dimensions if they don't want to */ ++ if (i->h > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ if (i->w > 2*bh) ++ i->w = bh; ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !i || ev->atom != xatom[XembedInfo] || ++ !(flags = getatomprop(i, xatom[XembedInfo]))) ++ return; ++ ++ if (flags & XEMBED_MAPPED && !i->tags) { ++ i->tags = 1; ++ code = XEMBED_WINDOW_ACTIVATE; ++ XMapRaised(dpy, i->win); ++ setclientstate(i, NormalState); ++ } ++ else if (!(flags & XEMBED_MAPPED) && i->tags) { ++ i->tags = 0; ++ code = XEMBED_WINDOW_DEACTIVATE; ++ XUnmapWindow(dpy, i->win); ++ setclientstate(i, WithdrawnState); ++ } ++ else ++ return; ++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, ++ systray->win, XEMBED_EMBEDDED_VERSION); ++} ++ + void + updatetitle(Client *c) + { +@@ -2091,6 +2443,16 @@ wintomon(Window w) + return selmon; + } + ++Client * ++wintosystrayicon(Window w) { ++ Client *i = NULL; ++ ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next); ++ return i; ++} ++ + /* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +-- +2.19.1 + diff --git a/dwm/dwm-alpha_deck_layout-6.3.diff b/dwm/dwm-alpha_deck_layout-6.3.diff new file mode 100644 index 0000000..37b9aee --- /dev/null +++ b/dwm/dwm-alpha_deck_layout-6.3.diff @@ -0,0 +1,99 @@ +From d59d1fcbaacacc8fffa8cbf6212e1013f95d4e0e Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:09:11 +0100 +Subject: [PATCH] alpha deck layout: only show the currently focused window + (rather than all windows stacked on top of each other) + +--- + config.def.h | 2 ++ + dwm.c | 40 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..a52bbfa 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -42,6 +42,7 @@ static const Layout layouts[] = { + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "[D]", deck }, + }; + + /* key definitions */ +@@ -77,6 +78,7 @@ static Key keys[] = { + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY, XK_c, setlayout, {.v = &layouts[3]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..4d79804 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -157,6 +157,7 @@ static void configure(Client *c); + static void configurenotify(XEvent *e); + static void configurerequest(XEvent *e); + static Monitor *createmon(void); ++static void deck(Monitor *m); + static void destroynotify(XEvent *e); + static void detach(Client *c); + static void detachstack(Client *c); +@@ -645,6 +646,43 @@ createmon(void) + return m; + } + ++void ++deck(Monitor *m) { ++ unsigned int i, n, h, mw, my; ++ Client *c, *s; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (n == 0) ++ return; ++ ++ if (n > m->nmaster) { ++ mw = m->nmaster ? m->ww * m->mfact : 0; ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n - m->nmaster); ++ } ++ else ++ mw = m->ww; ++ for (i = my = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), False); ++ my += HEIGHT(c); ++ } ++ else ++ XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); ++ ++ for (s = m->stack; s; s = s->snext) { ++ if (!ISVISIBLE(s) || s->isfloating) ++ continue; ++ ++ for (i = my = 0, c = nexttiled(m->clients); c && c != s; c = nexttiled(c->next), i++); ++ if (i < m->nmaster) ++ continue; ++ XMoveWindow(dpy, s->win, c->x, c->y); ++ resize(s, m->wx + mw, m->wy, m->ww - mw - (2*s->bw), m->wh - (2*s->bw), False); ++ break; ++ } ++} ++ + void + destroynotify(XEvent *e) + { +@@ -806,6 +844,8 @@ focus(Client *c) + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; ++ if (selmon->lt[selmon->sellt]->arrange == deck) ++ arrangemon(selmon); + drawbars(); + } + +-- +2.19.1 + diff --git a/dwm/dwm-alpha_monocle_layout-6.3.diff b/dwm/dwm-alpha_monocle_layout-6.3.diff new file mode 100644 index 0000000..9415268 --- /dev/null +++ b/dwm/dwm-alpha_monocle_layout-6.3.diff @@ -0,0 +1,43 @@ +From 945f4d84a9617c26769d2ee9c36d96dc39a5856f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:08:27 +0100 +Subject: [PATCH] alpha monocle layout: only show the currently focused window + (rather than all windows stacked on top of each other) + +--- + dwm.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..05f9edb 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -806,6 +806,8 @@ focus(Client *c) + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; ++ if (selmon->lt[selmon->sellt]->arrange == monocle) ++ arrangemon(selmon); + drawbars(); + } + +@@ -1115,8 +1117,15 @@ monocle(Monitor *m) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); +- for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) ++ for (c = m->stack; c && (!ISVISIBLE(c) || c->isfloating); c = c->snext); ++ if (c && !c->isfloating) { ++ XMoveWindow(dpy, c->win, m->wx, m->wy); + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); ++ c = c->snext; ++ } ++ for (; c; c = c->snext) ++ if (!c->isfloating && ISVISIBLE(c)) ++ XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } + + void +-- +2.19.1 + diff --git a/dwm/dwm-attachbottom-6.3.diff b/dwm/dwm-attachbottom-6.3.diff new file mode 100644 index 0000000..dde5b27 --- /dev/null +++ b/dwm/dwm-attachbottom-6.3.diff @@ -0,0 +1,71 @@ +From 2b0aa06acb2ce04d8761cfd993adf069e9abb9ab Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:11:00 +0100 +Subject: [PATCH] attachbottom patch + +New clients attach at the bottom of the stack instead of the top. +--- + dwm.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..15b0138 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -147,6 +147,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachbottom(Client *c); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -407,6 +408,18 @@ attach(Client *c) + c->mon->clients = c; + } + ++void ++attachbottom(Client *c) ++{ ++ Client *below = c->mon->clients; ++ for (; below && below->next; below = below->next); ++ c->next = NULL; ++ if (below) ++ below->next = c; ++ else ++ c->mon->clients = c; ++} ++ + void + attachstack(Client *c) + { +@@ -1066,7 +1079,7 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ attachbottom(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1421,7 +1434,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachbottom(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1903,7 +1916,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachbottom(c); + attachstack(c); + } + if (m == selmon) +-- +2.19.1 + diff --git a/dwm/dwm-attachx-6.3.diff b/dwm/dwm-attachx-6.3.diff new file mode 100644 index 0000000..1fd10d7 --- /dev/null +++ b/dwm/dwm-attachx-6.3.diff @@ -0,0 +1,137 @@ +From 8481cf0e5f3879a19c69385917f4775f529f1ff4 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:12:29 +0100 +Subject: [PATCH] Adding attachx patch (combined attach + above/below/bottom/aside/master patch) + +This includes the following attach modes: + +0 - master (default behaviour): new windows become the new master +1 - attachabove: new window is placed above selected client +2 - attachaside: new window is placed on top of the stack +3 - attachbelow: new window is placed below selected client +4 - attachbottom: new window is placed at the bottom of the stack + +Refer to: + https://dwm.suckless.org/patches/attachabove/ + https://dwm.suckless.org/patches/attachaside/ + https://dwm.suckless.org/patches/attachbelow/ + https://dwm.suckless.org/patches/attachbottom/ +--- + config.def.h | 1 + + dwm.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 57 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..774c5f0 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,7 @@ 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 */ ++static const int attachmode = 0; /* 0 master (default), 1 = above, 2 = aside, 3 = below, 4 = bottom */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/dwm.c b/dwm.c +index a96f33c..10fbeef 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -49,7 +49,8 @@ + #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 ISVISIBLEONTAG(C, T) ((C->tags & T)) ++#define ISVISIBLE(C) ISVISIBLEONTAG(C, 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) +@@ -148,6 +149,7 @@ static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); + static void attachstack(Client *c); ++static void attachx(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); + static void cleanup(void); +@@ -400,6 +402,57 @@ arrangemon(Monitor *m) + m->lt[m->sellt]->arrange(m); + } + ++void ++attachx(Client *c) ++{ ++ Client *at; ++ unsigned int n; ++ ++ switch (attachmode) { ++ case 1: // above ++ if (c->mon->sel == NULL || c->mon->sel == c->mon->clients || c->mon->sel->isfloating) ++ break; ++ ++ for (at = c->mon->clients; at->next != c->mon->sel; at = at->next); ++ c->next = at->next; ++ at->next = c; ++ return; ++ ++ case 2: // aside ++ for (at = c->mon->clients, n = 0; at; at = at->next) ++ if (!at->isfloating && ISVISIBLEONTAG(at, c->tags)) ++ if (++n >= c->mon->nmaster) ++ break; ++ ++ if (!at || !c->mon->nmaster) ++ break; ++ ++ c->next = at->next; ++ at->next = c; ++ return; ++ ++ case 3: // below ++ if (c->mon->sel == NULL || c->mon->sel->isfloating) ++ break; ++ ++ c->next = c->mon->sel->next; ++ c->mon->sel->next = c; ++ return; ++ ++ case 4: // bottom ++ for (at = c->mon->clients; at && at->next; at = at->next); ++ if (!at) ++ break; ++ ++ at->next = c; ++ c->next = NULL; ++ return; ++ } ++ ++ /* master (default) */ ++ attach(c); ++} ++ + void + attach(Client *c) + { +@@ -1066,7 +1119,7 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ attachx(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1421,7 +1474,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachx(c); + attachstack(c); + focus(NULL); + arrange(NULL); +-- +2.19.1 + diff --git a/dwm/dwm-autostart-6.3.diff b/dwm/dwm-autostart-6.3.diff new file mode 100644 index 0000000..7afd101 --- /dev/null +++ b/dwm/dwm-autostart-6.3.diff @@ -0,0 +1,51 @@ +From b27822d126334b6fd5bf5265fff201e54f4d8ede Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:13:09 +0100 +Subject: [PATCH] Adding autostart patch + +Refer to https://dwm.suckless.org/patches/autostart/ +--- + dwm.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/dwm.c b/dwm.c +index a96f33c..0fad144 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -194,6 +194,7 @@ static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); + static void run(void); ++static void runAutostart(void); + static void scan(void); + static int sendevent(Client *c, Atom proto); + static void sendmon(Client *c, Monitor *m); +@@ -1384,6 +1385,17 @@ run(void) + handler[ev.type](&ev); /* call handler */ + } + ++void ++runAutostart(void) { ++ ++ int ret; ++ ++ ret = system("cd ~/.config/dwm; ./autostart_blocking.sh"); ++ ret = system("cd ~/.config/dwm; ./autostart.sh &"); ++ ++ if (ret); // ignore, hide compilation warnings ++} ++ + void + scan(void) + { +@@ -2148,6 +2160,7 @@ main(int argc, char *argv[]) + die("pledge"); + #endif /* __OpenBSD__ */ + scan(); ++ runAutostart(); + run(); + cleanup(); + XCloseDisplay(dpy); +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-6.3.diff b/dwm/dwm-barmodules-6.3.diff new file mode 100644 index 0000000..7c79f7e --- /dev/null +++ b/dwm/dwm-barmodules-6.3.diff @@ -0,0 +1,897 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH] Bar Modules - splits the bar functionality into individual + segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-awesomebar-6.3.diff b/dwm/dwm-barmodules-awesomebar-6.3.diff new file mode 100644 index 0000000..6a3d0f6 --- /dev/null +++ b/dwm/dwm-barmodules-awesomebar-6.3.diff @@ -0,0 +1,180 @@ +From bef83ecfe66bb83a8420239f8c068cf60d2c3e0c Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:07:02 +0100 +Subject: [PATCH 2/2] Adding awesomebar barmodules patch. + +Note that this patch does not come with bound ClkWinTitle button click +actions for extra behaviour when clicking on the window name in the bar. + +The original bartabgroups patch included hardcoded focus on click which +has been replaced with a generic click for barmodules. + +The intention here is that this is combined with the wintitleactions +patch that allow clients to be focused, zoomed and hidden - functionality +that has been separated from the original awesomebar patch. + +In other words, all this patch does is showing all window titles in the +title bar with an even split between them. +--- + config.def.h | 2 +- + patch/bar_awesomebar.c | 94 ++++++++++++++++++++++++++++++++++++++++++ + patch/bar_awesomebar.h | 3 ++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 5 files changed, 102 insertions(+), 3 deletions(-) + create mode 100644 patch/bar_awesomebar.c + create mode 100644 patch/bar_awesomebar.h + +diff --git a/config.def.h b/config.def.h +index f870c41..74ad870 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -48,7 +48,7 @@ static const BarRule barrules[] = { + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, +- { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++ { -1, 0, BAR_ALIGN_NONE, width_awesomebar, draw_awesomebar, click_awesomebar, "awesomebar" }, + }; + + /* layout(s) */ +diff --git a/patch/bar_awesomebar.c b/patch/bar_awesomebar.c +new file mode 100644 +index 0000000..aa2e2e5 +--- /dev/null ++++ b/patch/bar_awesomebar.c +@@ -0,0 +1,94 @@ ++int ++width_awesomebar(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_awesomebar(Bar *bar, BarDrawArg *a) ++{ ++ int n = 0, scm, remainder = 0, tabw, pad; ++ unsigned int i; ++ #if BAR_TITLE_LEFT_PAD && BAR_TITLE_RIGHT_PAD ++ int x = a->x + lrpad / 2, w = a->w - lrpad; ++ #elif BAR_TITLE_LEFT_PAD ++ int x = a->x + lrpad / 2, w = a->w - lrpad / 2; ++ #elif BAR_TITLE_RIGHT_PAD ++ int x = a->x, w = a->w - lrpad / 2; ++ #else ++ int x = a->x, w = a->w; ++ #endif // BAR_TITLE_LEFT_PAD | BAR_TITLE_RIGHT_PAD ++ ++ Client *c; ++ for (c = bar->mon->clients; c; c = c->next) ++ if (ISVISIBLE(c)) ++ n++; ++ ++ if (n > 0) { ++ remainder = w % n; ++ tabw = w / n; ++ for (i = 0, c = bar->mon->clients; c; c = c->next, i++) { ++ if (!ISVISIBLE(c)) ++ continue; ++ if (bar->mon->sel == c) ++ #if BAR_VTCOLORS_PATCH ++ scm = SchemeTitleSel; ++ #elif BAR_TITLECOLOR_PATCH ++ scm = SchemeTitle; ++ #else ++ scm = SchemeSel; ++ #endif // BAR_VTCOLORS_PATCH / BAR_TITLECOLOR_PATCH ++ #ifdef HIDDEN ++ else if (HIDDEN(c)) ++ scm = SchemeHid; ++ #endif ++ else ++ #if BAR_VTCOLORS_PATCH ++ scm = SchemeTitleNorm; ++ #else ++ scm = SchemeNorm; ++ #endif // BAR_VTCOLORS_PATCH ++ ++ pad = lrpad / 2; ++ #if BAR_CENTEREDWINDOWNAME_PATCH ++ if (TEXTW(c->name) < tabw) ++ pad = (tabw - TEXTW(c->name) + lrpad) / 2; ++ #endif // BAR_CENTEREDWINDOWNAME_PATCH ++ ++ drw_setscheme(drw, scheme[scm]); ++ #if BAR_PANGO_PATCH ++ drw_text(drw, x, 0, tabw + (i < remainder ? 1 : 0), bh, pad, c->name, 0, False); ++ #else ++ drw_text(drw, x, 0, tabw + (i < remainder ? 1 : 0), bh, pad, c->name, 0); ++ #endif // BAR_PANGO_PATCH ++ x += tabw + (i < remainder ? 1 : 0); ++ } ++ } ++ return a->x + a->w; ++} ++ ++int ++click_awesomebar(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int x = 0, n = 0; ++ Client *c; ++ ++ for (c = bar->mon->clients; c; c = c->next) ++ if (ISVISIBLE(c)) ++ n++; ++ ++ c = bar->mon->clients; ++ ++ do { ++ if (!c || !ISVISIBLE(c)) ++ continue; ++ else ++ x += (1.0 / (double)n) * a->rel_w; ++ } while (c && a->rel_x > x && (c = c->next)); ++ ++ if (c) { ++ arg->v = c; ++ return ClkWinTitle; ++ } ++ return -1; ++} +\ No newline at end of file +diff --git a/patch/bar_awesomebar.h b/patch/bar_awesomebar.h +new file mode 100644 +index 0000000..3269954 +--- /dev/null ++++ b/patch/bar_awesomebar.h +@@ -0,0 +1,3 @@ ++static int width_awesomebar(Bar *bar, BarWidthArg *a); ++static int draw_awesomebar(Bar *bar, BarDrawArg *a); ++static int click_awesomebar(Bar *bar, Arg *arg, BarClickArg *a); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..6ae83ed 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++//#include "bar_wintitle.c" ++#include "bar_awesomebar.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..de77a11 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++//#include "bar_wintitle.h" ++#include "bar_awesomebar.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-awesomebar-6.3_full.diff b/dwm/dwm-barmodules-awesomebar-6.3_full.diff new file mode 100644 index 0000000..1187dd3 --- /dev/null +++ b/dwm/dwm-barmodules-awesomebar-6.3_full.diff @@ -0,0 +1,1078 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From bef83ecfe66bb83a8420239f8c068cf60d2c3e0c Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:07:02 +0100 +Subject: [PATCH 2/2] Adding awesomebar barmodules patch. + +Note that this patch does not come with bound ClkWinTitle button click +actions for extra behaviour when clicking on the window name in the bar. + +The original bartabgroups patch included hardcoded focus on click which +has been replaced with a generic click for barmodules. + +The intention here is that this is combined with the wintitleactions +patch that allow clients to be focused, zoomed and hidden - functionality +that has been separated from the original awesomebar patch. + +In other words, all this patch does is showing all window titles in the +title bar with an even split between them. +--- + config.def.h | 2 +- + patch/bar_awesomebar.c | 94 ++++++++++++++++++++++++++++++++++++++++++ + patch/bar_awesomebar.h | 3 ++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 5 files changed, 102 insertions(+), 3 deletions(-) + create mode 100644 patch/bar_awesomebar.c + create mode 100644 patch/bar_awesomebar.h + +diff --git a/config.def.h b/config.def.h +index f870c41..74ad870 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -48,7 +48,7 @@ static const BarRule barrules[] = { + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, +- { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++ { -1, 0, BAR_ALIGN_NONE, width_awesomebar, draw_awesomebar, click_awesomebar, "awesomebar" }, + }; + + /* layout(s) */ +diff --git a/patch/bar_awesomebar.c b/patch/bar_awesomebar.c +new file mode 100644 +index 0000000..aa2e2e5 +--- /dev/null ++++ b/patch/bar_awesomebar.c +@@ -0,0 +1,94 @@ ++int ++width_awesomebar(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_awesomebar(Bar *bar, BarDrawArg *a) ++{ ++ int n = 0, scm, remainder = 0, tabw, pad; ++ unsigned int i; ++ #if BAR_TITLE_LEFT_PAD && BAR_TITLE_RIGHT_PAD ++ int x = a->x + lrpad / 2, w = a->w - lrpad; ++ #elif BAR_TITLE_LEFT_PAD ++ int x = a->x + lrpad / 2, w = a->w - lrpad / 2; ++ #elif BAR_TITLE_RIGHT_PAD ++ int x = a->x, w = a->w - lrpad / 2; ++ #else ++ int x = a->x, w = a->w; ++ #endif // BAR_TITLE_LEFT_PAD | BAR_TITLE_RIGHT_PAD ++ ++ Client *c; ++ for (c = bar->mon->clients; c; c = c->next) ++ if (ISVISIBLE(c)) ++ n++; ++ ++ if (n > 0) { ++ remainder = w % n; ++ tabw = w / n; ++ for (i = 0, c = bar->mon->clients; c; c = c->next, i++) { ++ if (!ISVISIBLE(c)) ++ continue; ++ if (bar->mon->sel == c) ++ #if BAR_VTCOLORS_PATCH ++ scm = SchemeTitleSel; ++ #elif BAR_TITLECOLOR_PATCH ++ scm = SchemeTitle; ++ #else ++ scm = SchemeSel; ++ #endif // BAR_VTCOLORS_PATCH / BAR_TITLECOLOR_PATCH ++ #ifdef HIDDEN ++ else if (HIDDEN(c)) ++ scm = SchemeHid; ++ #endif ++ else ++ #if BAR_VTCOLORS_PATCH ++ scm = SchemeTitleNorm; ++ #else ++ scm = SchemeNorm; ++ #endif // BAR_VTCOLORS_PATCH ++ ++ pad = lrpad / 2; ++ #if BAR_CENTEREDWINDOWNAME_PATCH ++ if (TEXTW(c->name) < tabw) ++ pad = (tabw - TEXTW(c->name) + lrpad) / 2; ++ #endif // BAR_CENTEREDWINDOWNAME_PATCH ++ ++ drw_setscheme(drw, scheme[scm]); ++ #if BAR_PANGO_PATCH ++ drw_text(drw, x, 0, tabw + (i < remainder ? 1 : 0), bh, pad, c->name, 0, False); ++ #else ++ drw_text(drw, x, 0, tabw + (i < remainder ? 1 : 0), bh, pad, c->name, 0); ++ #endif // BAR_PANGO_PATCH ++ x += tabw + (i < remainder ? 1 : 0); ++ } ++ } ++ return a->x + a->w; ++} ++ ++int ++click_awesomebar(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int x = 0, n = 0; ++ Client *c; ++ ++ for (c = bar->mon->clients; c; c = c->next) ++ if (ISVISIBLE(c)) ++ n++; ++ ++ c = bar->mon->clients; ++ ++ do { ++ if (!c || !ISVISIBLE(c)) ++ continue; ++ else ++ x += (1.0 / (double)n) * a->rel_w; ++ } while (c && a->rel_x > x && (c = c->next)); ++ ++ if (c) { ++ arg->v = c; ++ return ClkWinTitle; ++ } ++ return -1; ++} +\ No newline at end of file +diff --git a/patch/bar_awesomebar.h b/patch/bar_awesomebar.h +new file mode 100644 +index 0000000..3269954 +--- /dev/null ++++ b/patch/bar_awesomebar.h +@@ -0,0 +1,3 @@ ++static int width_awesomebar(Bar *bar, BarWidthArg *a); ++static int draw_awesomebar(Bar *bar, BarDrawArg *a); ++static int click_awesomebar(Bar *bar, Arg *arg, BarClickArg *a); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..6ae83ed 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++//#include "bar_wintitle.c" ++#include "bar_awesomebar.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..de77a11 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++//#include "bar_wintitle.h" ++#include "bar_awesomebar.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-bartabgroups-6.3.diff b/dwm/dwm-barmodules-bartabgroups-6.3.diff new file mode 100644 index 0000000..9ebf6a1 --- /dev/null +++ b/dwm/dwm-barmodules-bartabgroups-6.3.diff @@ -0,0 +1,351 @@ +From d8f276471e66eb7a4d9f5bcf23964f0e37be1db9 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:07:45 +0100 +Subject: [PATCH 2/2] Adding bartabgroups barmodules patch. + +Note that this patch does not come with bound ClkWinTitle button click +actions for extra behaviour when clicking on the window name in the bar. + +The original bartabgroups patch included hardcoded focus on click which +has been replaced with a generic click for barmodules. + +The intention here is that this is combined with the wintitleactions +patch that adds options such as allowing clients to be focused and +hidden - functionality that has been separated from the awesomebar patch. +--- + config.def.h | 5 +- + dwm.c | 2 +- + patch/bar_tabgroups.c | 233 ++++++++++++++++++++++++++++++++++++++++++ + patch/bar_tabgroups.h | 7 ++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 6 files changed, 249 insertions(+), 4 deletions(-) + create mode 100644 patch/bar_tabgroups.c + create mode 100644 patch/bar_tabgroups.h + +diff --git a/config.def.h b/config.def.h +index f870c41..1324c30 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -7,6 +7,7 @@ static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; ++static void (*bartabmonfns[])(Monitor *) = { monocle /* , customlayoutfn */ }; + static const char col_gray1[] = "#222222"; + static const char col_gray2[] = "#444444"; + static const char col_gray3[] = "#bbbbbb"; +@@ -16,6 +17,8 @@ static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeTabActive] = { col_gray2, col_gray3, col_gray2 }, ++ [SchemeTabInactive] = { col_gray1, col_gray3, col_gray1 }, + }; + + /* tagging */ +@@ -48,7 +51,7 @@ static const BarRule barrules[] = { + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, +- { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++ { -1, 0, BAR_ALIGN_NONE, width_bartabgroups, draw_bartabgroups, click_bartabgroups, "bartabgroups" }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index 86763d8..ba80bc9 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -60,7 +60,7 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeTabActive, SchemeTabInactive }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +diff --git a/patch/bar_tabgroups.c b/patch/bar_tabgroups.c +new file mode 100644 +index 0000000..116ddc7 +--- /dev/null ++++ b/patch/bar_tabgroups.c +@@ -0,0 +1,233 @@ ++/* Bartabgroups properties, you can override these in your config.h if you want. */ ++#ifndef BARTAB_BORDERS ++#define BARTAB_BORDERS 1 // 0 = off, 1 = on ++#endif ++#ifndef BARTAB_TAGSINDICATOR ++#define BARTAB_TAGSINDICATOR 1 // 0 = off, 1 = on if >1 client/view tag, 2 = always on ++#endif ++#ifndef BARTAB_TAGSPX ++#define BARTAB_TAGSPX 5 // # pixels for tag grid boxes ++#endif ++#ifndef BARTAB_TAGSROWS ++#define BARTAB_TAGSROWS 3 // # rows in tag grid (9 tags, e.g. 3x3) ++#endif ++#ifndef BARTAB_SHOWFLOATING ++#define BARTAB_SHOWFLOATING 0 // whether to show titles for floating windows, hidden clients are always shown ++#endif ++#ifndef BARTAB_STACKWEIGHT ++#define BARTAB_STACKWEIGHT 1 // stack weight compared to hidden and floating window titles ++#endif ++#ifndef BARTAB_HIDDENWEIGHT ++#define BARTAB_HIDDENWEIGHT 1 // hidden window title weight ++#endif ++#ifndef BARTAB_FLOATWEIGHT ++#define BARTAB_FLOATWEIGHT 1 // floating window title weight, set to 0 to not show floating windows ++#endif ++ ++int ++width_bartabgroups(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_bartabgroups(Bar *bar, BarDrawArg *a) ++{ ++ drw_rect(drw, a->x, 0, a->w, bh, 1, 1); ++ bartabcalculate(bar->mon, a->x, a->w, -1, bartabdraw, NULL); ++ return a->x + a->w; ++} ++ ++int ++click_bartabgroups(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ bartabcalculate(bar->mon, 0, a->rel_w, a->rel_x, bartabclick, arg); ++ return ClkWinTitle; ++} ++ ++void ++bartabdraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive, Arg *arg) ++{ ++ if (!c) ++ return; ++ int i, nclienttags = 0, nviewtags = 0; ++ drw_setscheme(drw, scheme[ ++ m->sel == c ++ ? SchemeSel ++ #ifdef HIDDEN ++ : HIDDEN(c) ++ ? SchemeHid ++ #endif ++ : groupactive ++ ? SchemeTabActive ++ : SchemeTabInactive ++ ]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, c->name, 0); ++ if (c->isfloating) ++ drw_rect(drw, x + 2, 2, 5, 5, 0, 0); ++ ++ if (BARTAB_BORDERS) { ++ XSetForeground(drw->dpy, drw->gc, scheme[SchemeSel][ColBorder].pixel); ++ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, 0, 1, bh); ++ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x + w, 0, 1, bh); ++ } ++ /* Optional tags icons */ ++ for (i = 0; i < LENGTH(tags); i++) { ++ if ((m->tagset[m->seltags] >> i) & 1) ++ nviewtags++; ++ if ((c->tags >> i) & 1) ++ nclienttags++; ++ } ++ ++ if (BARTAB_TAGSINDICATOR == 2 || nclienttags > 1 || nviewtags > 1) { ++ for (i = 0; i < LENGTH(tags); i++) { ++ drw_rect(drw, ++ ( x + w - 2 - ((LENGTH(tags) / BARTAB_TAGSROWS) * BARTAB_TAGSPX) ++ - (i % (LENGTH(tags)/BARTAB_TAGSROWS)) + ((i % (LENGTH(tags) / BARTAB_TAGSROWS)) * BARTAB_TAGSPX) ++ ), ++ ( 2 + ((i / (LENGTH(tags)/BARTAB_TAGSROWS)) * BARTAB_TAGSPX) ++ - ((i / (LENGTH(tags)/BARTAB_TAGSROWS))) ++ ), ++ BARTAB_TAGSPX, BARTAB_TAGSPX, (c->tags >> i) & 1, 0 ++ ); ++ } ++ } ++} ++ ++#ifndef HIDDEN ++#define HIDDEN(C) 0 ++#endif ++ ++void ++bartabclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg) ++{ ++ if (passx >= x && passx <= x + w) ++ arg->v = c; ++} ++ ++void ++bartabcalculate( ++ Monitor *m, int offx, int tabw, int passx, ++ void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg), Arg *arg ++) { ++ Client *c; ++ int ++ i, clientsnmaster = 0, clientsnstack = 0, clientsnfloating = 0, clientsnhidden = 0, ++ masteractive = 0, fulllayout = 0, ++ x = offx, w, r, num = 0, den, tgactive; ++ ++ for (i = 0; i < LENGTH(bartabmonfns); i++) ++ if (m ->lt[m->sellt]->arrange == bartabmonfns[i]) { ++ fulllayout = 1; ++ break; ++ } ++ ++ for (i = 0, c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ if (HIDDEN(c)) { ++ clientsnhidden++; ++ continue; ++ } ++ if (c->isfloating) { ++ clientsnfloating++; ++ continue; ++ } ++ if (m->sel == c) ++ masteractive = i < m->nmaster; ++ if (i < m->nmaster) ++ clientsnmaster++; ++ else ++ clientsnstack++; ++ i++; ++ } ++ ++ if (clientsnmaster + clientsnstack + clientsnfloating + clientsnhidden == 0) ++ return; ++ ++ tgactive = 1; ++ num = tabw; ++ /* floating mode */ ++ if ((fulllayout && BARTAB_FLOATWEIGHT) || clientsnmaster + clientsnstack == 0 || !m->lt[m->sellt]->arrange) { ++ den = clientsnmaster + clientsnstack + clientsnfloating + clientsnhidden; ++ r = num % den; ++ w = num / den; ++ for (c = m->clients, i = 0; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg); ++ x += w + (i < r ? 1 : 0); ++ i++; ++ } ++ /* no master and stack mode, e.g. monocole, grid layouts, fibonacci */ ++ } else if (fulllayout) { ++ den = clientsnmaster + clientsnstack + clientsnhidden; ++ r = num % den; ++ w = num / den; ++ for (c = m->clients, i = 0; c; c = c->next) { ++ if (!ISVISIBLE(c) || (c->isfloating && !HIDDEN(c))) ++ continue; ++ tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg); ++ x += w + (i < r ? 1 : 0); ++ i++; ++ } ++ /* tiled mode */ ++ } else { ++ den = clientsnmaster; ++ c = m->clients; ++ i = 0; ++ if (den) { ++ if (clientsnstack + clientsnfloating * BARTAB_FLOATWEIGHT + clientsnhidden) { ++ tgactive = masteractive; ++ num = tabw * m->mfact; ++ } ++ r = num % den; ++ w = num / den; ++ for (; c && i < m->nmaster; c = c->next) { // tiled master ++ if (!ISVISIBLE(c) || c->isfloating || HIDDEN(c)) ++ continue; ++ tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg); ++ x += w + (i < r ? 1 : 0); ++ i++; ++ } ++ tgactive = !tgactive; ++ num = tabw - num; ++ } ++ ++ den = clientsnstack * BARTAB_STACKWEIGHT + clientsnfloating * BARTAB_FLOATWEIGHT + clientsnhidden * BARTAB_HIDDENWEIGHT; ++ if (!den) ++ return; ++ ++ r = num % den; ++ w = num / den; ++ #if BARTAB_STACKWEIGHT ++ for (; c; c = c->next) { // tiled stack ++ if (!ISVISIBLE(c) || HIDDEN(c) || c->isfloating) ++ continue; ++ tabfn(m, c, passx, x, w * BARTAB_STACKWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg); ++ x += w * BARTAB_STACKWEIGHT + (i - m->nmaster < r ? 1 : 0); ++ i++; ++ } ++ #endif // BARTAB_STACKWEIGHT ++ ++ #if BARTAB_HIDDENWEIGHT ++ for (c = m->clients; c; c = c->next) { // hidden windows ++ if (!ISVISIBLE(c) || !HIDDEN(c)) ++ continue; ++ tabfn(m, c, passx, x, w * BARTAB_HIDDENWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg); ++ x += w * BARTAB_HIDDENWEIGHT + (i - m->nmaster < r ? 1 : 0); ++ i++; ++ } ++ #endif // BARTAB_HIDDENWEIGHT ++ ++ #if BARTAB_FLOATWEIGHT ++ for (c = m->clients; c; c = c->next) { // floating windows ++ if (!ISVISIBLE(c) || HIDDEN(c) || !c->isfloating) ++ continue; ++ tabfn(m, c, passx, x, w * BARTAB_FLOATWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg); ++ x += w * BARTAB_FLOATWEIGHT + (i - m->nmaster < r ? 1 : 0); ++ i++; ++ } ++ #endif // BARTAB_FLOATWEIGHT ++ } ++} +\ No newline at end of file +diff --git a/patch/bar_tabgroups.h b/patch/bar_tabgroups.h +new file mode 100644 +index 0000000..15f6876 +--- /dev/null ++++ b/patch/bar_tabgroups.h +@@ -0,0 +1,7 @@ ++static int width_bartabgroups(Bar *bar, BarWidthArg *a); ++static int draw_bartabgroups(Bar *bar, BarDrawArg *a); ++static int click_bartabgroups(Bar *bar, Arg *arg, BarClickArg *a); ++ ++static void bartabdraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive, Arg *arg); ++static void bartabclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg); ++static void bartabcalculate(Monitor *m, int offx, int w, int passx, void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg), Arg *arg); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..a1885ed 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++// #include "bar_wintitle.c" ++#include "bar_tabgroups.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..a024a16 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++// #include "bar_wintitle.h" ++#include "bar_tabgroups.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-bartabgroups-6.3_full.diff b/dwm/dwm-barmodules-bartabgroups-6.3_full.diff new file mode 100644 index 0000000..e2764d5 --- /dev/null +++ b/dwm/dwm-barmodules-bartabgroups-6.3_full.diff @@ -0,0 +1,1249 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From d8f276471e66eb7a4d9f5bcf23964f0e37be1db9 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:07:45 +0100 +Subject: [PATCH 2/2] Adding bartabgroups barmodules patch. + +Note that this patch does not come with bound ClkWinTitle button click +actions for extra behaviour when clicking on the window name in the bar. + +The original bartabgroups patch included hardcoded focus on click which +has been replaced with a generic click for barmodules. + +The intention here is that this is combined with the wintitleactions +patch that adds options such as allowing clients to be focused and +hidden - functionality that has been separated from the awesomebar patch. +--- + config.def.h | 5 +- + dwm.c | 2 +- + patch/bar_tabgroups.c | 233 ++++++++++++++++++++++++++++++++++++++++++ + patch/bar_tabgroups.h | 7 ++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 6 files changed, 249 insertions(+), 4 deletions(-) + create mode 100644 patch/bar_tabgroups.c + create mode 100644 patch/bar_tabgroups.h + +diff --git a/config.def.h b/config.def.h +index f870c41..1324c30 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -7,6 +7,7 @@ static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; ++static void (*bartabmonfns[])(Monitor *) = { monocle /* , customlayoutfn */ }; + static const char col_gray1[] = "#222222"; + static const char col_gray2[] = "#444444"; + static const char col_gray3[] = "#bbbbbb"; +@@ -16,6 +17,8 @@ static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeTabActive] = { col_gray2, col_gray3, col_gray2 }, ++ [SchemeTabInactive] = { col_gray1, col_gray3, col_gray1 }, + }; + + /* tagging */ +@@ -48,7 +51,7 @@ static const BarRule barrules[] = { + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, +- { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++ { -1, 0, BAR_ALIGN_NONE, width_bartabgroups, draw_bartabgroups, click_bartabgroups, "bartabgroups" }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index 86763d8..ba80bc9 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -60,7 +60,7 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeTabActive, SchemeTabInactive }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +diff --git a/patch/bar_tabgroups.c b/patch/bar_tabgroups.c +new file mode 100644 +index 0000000..116ddc7 +--- /dev/null ++++ b/patch/bar_tabgroups.c +@@ -0,0 +1,233 @@ ++/* Bartabgroups properties, you can override these in your config.h if you want. */ ++#ifndef BARTAB_BORDERS ++#define BARTAB_BORDERS 1 // 0 = off, 1 = on ++#endif ++#ifndef BARTAB_TAGSINDICATOR ++#define BARTAB_TAGSINDICATOR 1 // 0 = off, 1 = on if >1 client/view tag, 2 = always on ++#endif ++#ifndef BARTAB_TAGSPX ++#define BARTAB_TAGSPX 5 // # pixels for tag grid boxes ++#endif ++#ifndef BARTAB_TAGSROWS ++#define BARTAB_TAGSROWS 3 // # rows in tag grid (9 tags, e.g. 3x3) ++#endif ++#ifndef BARTAB_SHOWFLOATING ++#define BARTAB_SHOWFLOATING 0 // whether to show titles for floating windows, hidden clients are always shown ++#endif ++#ifndef BARTAB_STACKWEIGHT ++#define BARTAB_STACKWEIGHT 1 // stack weight compared to hidden and floating window titles ++#endif ++#ifndef BARTAB_HIDDENWEIGHT ++#define BARTAB_HIDDENWEIGHT 1 // hidden window title weight ++#endif ++#ifndef BARTAB_FLOATWEIGHT ++#define BARTAB_FLOATWEIGHT 1 // floating window title weight, set to 0 to not show floating windows ++#endif ++ ++int ++width_bartabgroups(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_bartabgroups(Bar *bar, BarDrawArg *a) ++{ ++ drw_rect(drw, a->x, 0, a->w, bh, 1, 1); ++ bartabcalculate(bar->mon, a->x, a->w, -1, bartabdraw, NULL); ++ return a->x + a->w; ++} ++ ++int ++click_bartabgroups(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ bartabcalculate(bar->mon, 0, a->rel_w, a->rel_x, bartabclick, arg); ++ return ClkWinTitle; ++} ++ ++void ++bartabdraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive, Arg *arg) ++{ ++ if (!c) ++ return; ++ int i, nclienttags = 0, nviewtags = 0; ++ drw_setscheme(drw, scheme[ ++ m->sel == c ++ ? SchemeSel ++ #ifdef HIDDEN ++ : HIDDEN(c) ++ ? SchemeHid ++ #endif ++ : groupactive ++ ? SchemeTabActive ++ : SchemeTabInactive ++ ]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, c->name, 0); ++ if (c->isfloating) ++ drw_rect(drw, x + 2, 2, 5, 5, 0, 0); ++ ++ if (BARTAB_BORDERS) { ++ XSetForeground(drw->dpy, drw->gc, scheme[SchemeSel][ColBorder].pixel); ++ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, 0, 1, bh); ++ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x + w, 0, 1, bh); ++ } ++ /* Optional tags icons */ ++ for (i = 0; i < LENGTH(tags); i++) { ++ if ((m->tagset[m->seltags] >> i) & 1) ++ nviewtags++; ++ if ((c->tags >> i) & 1) ++ nclienttags++; ++ } ++ ++ if (BARTAB_TAGSINDICATOR == 2 || nclienttags > 1 || nviewtags > 1) { ++ for (i = 0; i < LENGTH(tags); i++) { ++ drw_rect(drw, ++ ( x + w - 2 - ((LENGTH(tags) / BARTAB_TAGSROWS) * BARTAB_TAGSPX) ++ - (i % (LENGTH(tags)/BARTAB_TAGSROWS)) + ((i % (LENGTH(tags) / BARTAB_TAGSROWS)) * BARTAB_TAGSPX) ++ ), ++ ( 2 + ((i / (LENGTH(tags)/BARTAB_TAGSROWS)) * BARTAB_TAGSPX) ++ - ((i / (LENGTH(tags)/BARTAB_TAGSROWS))) ++ ), ++ BARTAB_TAGSPX, BARTAB_TAGSPX, (c->tags >> i) & 1, 0 ++ ); ++ } ++ } ++} ++ ++#ifndef HIDDEN ++#define HIDDEN(C) 0 ++#endif ++ ++void ++bartabclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg) ++{ ++ if (passx >= x && passx <= x + w) ++ arg->v = c; ++} ++ ++void ++bartabcalculate( ++ Monitor *m, int offx, int tabw, int passx, ++ void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg), Arg *arg ++) { ++ Client *c; ++ int ++ i, clientsnmaster = 0, clientsnstack = 0, clientsnfloating = 0, clientsnhidden = 0, ++ masteractive = 0, fulllayout = 0, ++ x = offx, w, r, num = 0, den, tgactive; ++ ++ for (i = 0; i < LENGTH(bartabmonfns); i++) ++ if (m ->lt[m->sellt]->arrange == bartabmonfns[i]) { ++ fulllayout = 1; ++ break; ++ } ++ ++ for (i = 0, c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ if (HIDDEN(c)) { ++ clientsnhidden++; ++ continue; ++ } ++ if (c->isfloating) { ++ clientsnfloating++; ++ continue; ++ } ++ if (m->sel == c) ++ masteractive = i < m->nmaster; ++ if (i < m->nmaster) ++ clientsnmaster++; ++ else ++ clientsnstack++; ++ i++; ++ } ++ ++ if (clientsnmaster + clientsnstack + clientsnfloating + clientsnhidden == 0) ++ return; ++ ++ tgactive = 1; ++ num = tabw; ++ /* floating mode */ ++ if ((fulllayout && BARTAB_FLOATWEIGHT) || clientsnmaster + clientsnstack == 0 || !m->lt[m->sellt]->arrange) { ++ den = clientsnmaster + clientsnstack + clientsnfloating + clientsnhidden; ++ r = num % den; ++ w = num / den; ++ for (c = m->clients, i = 0; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg); ++ x += w + (i < r ? 1 : 0); ++ i++; ++ } ++ /* no master and stack mode, e.g. monocole, grid layouts, fibonacci */ ++ } else if (fulllayout) { ++ den = clientsnmaster + clientsnstack + clientsnhidden; ++ r = num % den; ++ w = num / den; ++ for (c = m->clients, i = 0; c; c = c->next) { ++ if (!ISVISIBLE(c) || (c->isfloating && !HIDDEN(c))) ++ continue; ++ tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg); ++ x += w + (i < r ? 1 : 0); ++ i++; ++ } ++ /* tiled mode */ ++ } else { ++ den = clientsnmaster; ++ c = m->clients; ++ i = 0; ++ if (den) { ++ if (clientsnstack + clientsnfloating * BARTAB_FLOATWEIGHT + clientsnhidden) { ++ tgactive = masteractive; ++ num = tabw * m->mfact; ++ } ++ r = num % den; ++ w = num / den; ++ for (; c && i < m->nmaster; c = c->next) { // tiled master ++ if (!ISVISIBLE(c) || c->isfloating || HIDDEN(c)) ++ continue; ++ tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg); ++ x += w + (i < r ? 1 : 0); ++ i++; ++ } ++ tgactive = !tgactive; ++ num = tabw - num; ++ } ++ ++ den = clientsnstack * BARTAB_STACKWEIGHT + clientsnfloating * BARTAB_FLOATWEIGHT + clientsnhidden * BARTAB_HIDDENWEIGHT; ++ if (!den) ++ return; ++ ++ r = num % den; ++ w = num / den; ++ #if BARTAB_STACKWEIGHT ++ for (; c; c = c->next) { // tiled stack ++ if (!ISVISIBLE(c) || HIDDEN(c) || c->isfloating) ++ continue; ++ tabfn(m, c, passx, x, w * BARTAB_STACKWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg); ++ x += w * BARTAB_STACKWEIGHT + (i - m->nmaster < r ? 1 : 0); ++ i++; ++ } ++ #endif // BARTAB_STACKWEIGHT ++ ++ #if BARTAB_HIDDENWEIGHT ++ for (c = m->clients; c; c = c->next) { // hidden windows ++ if (!ISVISIBLE(c) || !HIDDEN(c)) ++ continue; ++ tabfn(m, c, passx, x, w * BARTAB_HIDDENWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg); ++ x += w * BARTAB_HIDDENWEIGHT + (i - m->nmaster < r ? 1 : 0); ++ i++; ++ } ++ #endif // BARTAB_HIDDENWEIGHT ++ ++ #if BARTAB_FLOATWEIGHT ++ for (c = m->clients; c; c = c->next) { // floating windows ++ if (!ISVISIBLE(c) || HIDDEN(c) || !c->isfloating) ++ continue; ++ tabfn(m, c, passx, x, w * BARTAB_FLOATWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg); ++ x += w * BARTAB_FLOATWEIGHT + (i - m->nmaster < r ? 1 : 0); ++ i++; ++ } ++ #endif // BARTAB_FLOATWEIGHT ++ } ++} +\ No newline at end of file +diff --git a/patch/bar_tabgroups.h b/patch/bar_tabgroups.h +new file mode 100644 +index 0000000..15f6876 +--- /dev/null ++++ b/patch/bar_tabgroups.h +@@ -0,0 +1,7 @@ ++static int width_bartabgroups(Bar *bar, BarWidthArg *a); ++static int draw_bartabgroups(Bar *bar, BarDrawArg *a); ++static int click_bartabgroups(Bar *bar, Arg *arg, BarClickArg *a); ++ ++static void bartabdraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive, Arg *arg); ++static void bartabclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg); ++static void bartabcalculate(Monitor *m, int offx, int w, int passx, void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg), Arg *arg); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..a1885ed 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++// #include "bar_wintitle.c" ++#include "bar_tabgroups.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..a024a16 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++// #include "bar_wintitle.h" ++#include "bar_tabgroups.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-fancybar-6.3.diff b/dwm/dwm-barmodules-fancybar-6.3.diff new file mode 100644 index 0000000..9939446 --- /dev/null +++ b/dwm/dwm-barmodules-fancybar-6.3.diff @@ -0,0 +1,157 @@ +From 01bd1d40399c4ee4b73216ab894d65bf29c01d93 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:08:23 +0100 +Subject: [PATCH 2/2] Adding fancybar module + +--- + config.def.h | 2 +- + patch/bar_fancybar.c | 84 ++++++++++++++++++++++++++++++++++++++++++++ + patch/bar_fancybar.h | 3 ++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 5 files changed, 92 insertions(+), 3 deletions(-) + create mode 100644 patch/bar_fancybar.c + create mode 100644 patch/bar_fancybar.h + +diff --git a/config.def.h b/config.def.h +index f870c41..59c1b42 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -48,7 +48,7 @@ static const BarRule barrules[] = { + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, +- { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++ { -1, 0, BAR_ALIGN_NONE, width_fancybar, draw_fancybar, click_fancybar, "fancybar" }, + }; + + /* layout(s) */ +diff --git a/patch/bar_fancybar.c b/patch/bar_fancybar.c +new file mode 100644 +index 0000000..810026d +--- /dev/null ++++ b/patch/bar_fancybar.c +@@ -0,0 +1,84 @@ ++int ++width_fancybar(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_fancybar(Bar *bar, BarDrawArg *a) ++{ ++ int ftw, mw, ew = 0, n = 0; ++ unsigned int i; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ #if BAR_TITLE_LEFT_PAD && BAR_TITLE_RIGHT_PAD ++ int x = a->x + lrpad / 2, w = a->w - lrpad; ++ #elif BAR_TITLE_LEFT_PAD ++ int x = a->x + lrpad / 2, w = a->w - lrpad / 2; ++ #elif BAR_TITLE_RIGHT_PAD ++ int x = a->x, w = a->w - lrpad / 2; ++ #else ++ int x = a->x, w = a->w; ++ #endif // BAR_TITLE_LEFT_PAD | BAR_TITLE_RIGHT_PAD ++ ++ for (c = m->clients; c; c = c->next) { ++ if (ISVISIBLE(c)) ++ n++; ++ } ++ ++ if (n > 0) { ++ ftw = TEXTW(m->sel->name); ++ mw = (ftw >= w || n == 1) ? 0 : (w - ftw) / (n - 1); ++ ++ i = 0; ++ ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c) || c == m->sel) ++ continue; ++ ftw = TEXTW(c->name); ++ if (ftw < mw) ++ ew += (mw - ftw); ++ else ++ i++; ++ } ++ ++ if (i > 0) ++ mw += ew / i; ++ ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ ftw = MIN(m->sel == c ? w : mw, TEXTW(c->name)); ++ ++ #if BAR_VTCOLORS_PATCH ++ drw_setscheme(drw, scheme[m->sel == c ? SchemeTitleSel : SchemeTitleNorm]); ++ #elif BAR_TITLECOLOR_PATCH ++ drw_setscheme(drw, scheme[m->sel == c ? SchemeTitle : SchemeNorm]); ++ #else ++ drw_setscheme(drw, scheme[m->sel == c ? SchemeSel : SchemeNorm]); ++ #endif // BAR_VTCOLORS_PATCH / BAR_TITLECOLOR_PATCH ++ if (ftw > 0) /* trap special handling of 0 in drw_text */ ++ #if BAR_PANGO_PATCH ++ drw_text(drw, x, 0, ftw, bh, lrpad / 2, c->name, 0, False); ++ #else ++ drw_text(drw, x, 0, ftw, bh, lrpad / 2, c->name, 0); ++ #endif // BAR_PANGO_PATCH ++ if (c->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, c->isfixed, 0); ++ x += ftw; ++ w -= ftw; ++ } ++ } ++ return x + w; ++} ++ ++int ++click_fancybar(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} ++ ++ +diff --git a/patch/bar_fancybar.h b/patch/bar_fancybar.h +new file mode 100644 +index 0000000..c90d189 +--- /dev/null ++++ b/patch/bar_fancybar.h +@@ -0,0 +1,3 @@ ++static int width_fancybar(Bar *bar, BarWidthArg *a); ++static int draw_fancybar(Bar *bar, BarDrawArg *a); ++static int click_fancybar(Bar *bar, Arg *arg, BarClickArg *a); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..2ab4594 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++//#include "bar_wintitle.c" ++#include "bar_fancybar.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..4eab55d 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++//#include "bar_wintitle.h" ++#include "bar_fancybar.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-fancybar-6.3_full.diff b/dwm/dwm-barmodules-fancybar-6.3_full.diff new file mode 100644 index 0000000..139b433 --- /dev/null +++ b/dwm/dwm-barmodules-fancybar-6.3_full.diff @@ -0,0 +1,1055 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From 01bd1d40399c4ee4b73216ab894d65bf29c01d93 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:08:23 +0100 +Subject: [PATCH 2/2] Adding fancybar module + +--- + config.def.h | 2 +- + patch/bar_fancybar.c | 84 ++++++++++++++++++++++++++++++++++++++++++++ + patch/bar_fancybar.h | 3 ++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 5 files changed, 92 insertions(+), 3 deletions(-) + create mode 100644 patch/bar_fancybar.c + create mode 100644 patch/bar_fancybar.h + +diff --git a/config.def.h b/config.def.h +index f870c41..59c1b42 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -48,7 +48,7 @@ static const BarRule barrules[] = { + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, +- { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++ { -1, 0, BAR_ALIGN_NONE, width_fancybar, draw_fancybar, click_fancybar, "fancybar" }, + }; + + /* layout(s) */ +diff --git a/patch/bar_fancybar.c b/patch/bar_fancybar.c +new file mode 100644 +index 0000000..810026d +--- /dev/null ++++ b/patch/bar_fancybar.c +@@ -0,0 +1,84 @@ ++int ++width_fancybar(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_fancybar(Bar *bar, BarDrawArg *a) ++{ ++ int ftw, mw, ew = 0, n = 0; ++ unsigned int i; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ #if BAR_TITLE_LEFT_PAD && BAR_TITLE_RIGHT_PAD ++ int x = a->x + lrpad / 2, w = a->w - lrpad; ++ #elif BAR_TITLE_LEFT_PAD ++ int x = a->x + lrpad / 2, w = a->w - lrpad / 2; ++ #elif BAR_TITLE_RIGHT_PAD ++ int x = a->x, w = a->w - lrpad / 2; ++ #else ++ int x = a->x, w = a->w; ++ #endif // BAR_TITLE_LEFT_PAD | BAR_TITLE_RIGHT_PAD ++ ++ for (c = m->clients; c; c = c->next) { ++ if (ISVISIBLE(c)) ++ n++; ++ } ++ ++ if (n > 0) { ++ ftw = TEXTW(m->sel->name); ++ mw = (ftw >= w || n == 1) ? 0 : (w - ftw) / (n - 1); ++ ++ i = 0; ++ ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c) || c == m->sel) ++ continue; ++ ftw = TEXTW(c->name); ++ if (ftw < mw) ++ ew += (mw - ftw); ++ else ++ i++; ++ } ++ ++ if (i > 0) ++ mw += ew / i; ++ ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ ftw = MIN(m->sel == c ? w : mw, TEXTW(c->name)); ++ ++ #if BAR_VTCOLORS_PATCH ++ drw_setscheme(drw, scheme[m->sel == c ? SchemeTitleSel : SchemeTitleNorm]); ++ #elif BAR_TITLECOLOR_PATCH ++ drw_setscheme(drw, scheme[m->sel == c ? SchemeTitle : SchemeNorm]); ++ #else ++ drw_setscheme(drw, scheme[m->sel == c ? SchemeSel : SchemeNorm]); ++ #endif // BAR_VTCOLORS_PATCH / BAR_TITLECOLOR_PATCH ++ if (ftw > 0) /* trap special handling of 0 in drw_text */ ++ #if BAR_PANGO_PATCH ++ drw_text(drw, x, 0, ftw, bh, lrpad / 2, c->name, 0, False); ++ #else ++ drw_text(drw, x, 0, ftw, bh, lrpad / 2, c->name, 0); ++ #endif // BAR_PANGO_PATCH ++ if (c->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, c->isfixed, 0); ++ x += ftw; ++ w -= ftw; ++ } ++ } ++ return x + w; ++} ++ ++int ++click_fancybar(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} ++ ++ +diff --git a/patch/bar_fancybar.h b/patch/bar_fancybar.h +new file mode 100644 +index 0000000..c90d189 +--- /dev/null ++++ b/patch/bar_fancybar.h +@@ -0,0 +1,3 @@ ++static int width_fancybar(Bar *bar, BarWidthArg *a); ++static int draw_fancybar(Bar *bar, BarDrawArg *a); ++static int click_fancybar(Bar *bar, Arg *arg, BarClickArg *a); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..2ab4594 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++//#include "bar_wintitle.c" ++#include "bar_fancybar.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..4eab55d 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++//#include "bar_wintitle.h" ++#include "bar_fancybar.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-powerline-6.3.diff b/dwm/dwm-barmodules-powerline-6.3.diff new file mode 100644 index 0000000..298afd6 --- /dev/null +++ b/dwm/dwm-barmodules-powerline-6.3.diff @@ -0,0 +1,479 @@ +From 97343b7b17a3bdc111768f026e8532b93143d409 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:08:58 +0100 +Subject: [PATCH 2/2] Adding powerline module + +--- + config.def.h | 10 ++- + drw.c | 41 +++++++++++ + drw.h | 2 + + dwm.c | 7 ++ + patch/bar_powerline_status.c | 121 +++++++++++++++++++++++++++++++++ + patch/bar_powerline_status.h | 11 +++ + patch/bar_powerline_tags.c | 127 +++++++++++++++++++++++++++++++++++ + patch/bar_powerline_tags.h | 3 + + patch/include.c | 8 ++- + patch/include.h | 8 ++- + 10 files changed, 330 insertions(+), 8 deletions(-) + create mode 100644 patch/bar_powerline_status.c + create mode 100644 patch/bar_powerline_status.h + create mode 100644 patch/bar_powerline_tags.c + create mode 100644 patch/bar_powerline_tags.h + +diff --git a/config.def.h b/config.def.h +index f870c41..1187340 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -18,6 +18,12 @@ static const char *colors[][3] = { + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; + ++static const char *statuscolors[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, ++ [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++}; ++ + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +@@ -45,9 +51,9 @@ static const Rule rules[] = { + */ + static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ +- { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_pwrl_tags, draw_pwrl_tags, click_pwrl_tags, "powerline_tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, +- { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_pwrl_status, draw_pwrl_status, click_pwrl_status, "powerline_status" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, + }; + +diff --git a/drw.c b/drw.c +index 4cdbcbe..522fc79 100644 +--- a/drw.c ++++ b/drw.c +@@ -15,6 +15,7 @@ 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}; ++Clr transcheme[3]; + + static long + utf8decodebyte(const char c, size_t *i) +@@ -236,6 +237,15 @@ drw_setscheme(Drw *drw, Clr *scm) + drw->scheme = scm; + } + ++void ++drw_settrans(Drw *drw, Clr *psc, Clr *nsc) ++{ ++ if (drw) { ++ transcheme[0] = psc[ColBg]; transcheme[1] = nsc[ColBg]; transcheme[2] = psc[ColBorder]; ++ drw->scheme = transcheme; ++ } ++} ++ + void + drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) + { +@@ -379,6 +389,37 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp + return x + (render ? w : 0); + } + ++void ++drw_arrow(Drw *drw, int x, int y, unsigned int w, unsigned int h, int direction, int slash) ++{ ++ if (!drw || !drw->scheme) ++ return; ++ ++ /* direction=1 draws right arrow */ ++ x = direction ? x : x + w; ++ w = direction ? w : -w; ++ /* slash=1 draws slash instead of arrow */ ++ unsigned int hh = slash ? (direction ? 0 : h) : h/2; ++ ++ XPoint points[] = { ++ {x , y }, ++ {x + w, y + hh }, ++ {x , y + h }, ++ }; ++ ++ XPoint bg[] = { ++ {x , y }, ++ {x + w, y }, ++ {x + w, y + h}, ++ {x , y + h}, ++ }; ++ ++ XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel); ++ XFillPolygon(drw->dpy, drw->drawable, drw->gc, bg, 4, Convex, CoordModeOrigin); ++ XSetForeground(drw->dpy, drw->gc, drw->scheme[ColFg].pixel); ++ XFillPolygon(drw->dpy, drw->drawable, drw->gc, points, 3, Nonconvex, CoordModeOrigin); ++} ++ + void + drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) + { +diff --git a/drw.h b/drw.h +index 4bcd5ad..4a42557 100644 +--- a/drw.h ++++ b/drw.h +@@ -48,10 +48,12 @@ void drw_cur_free(Drw *drw, Cur *cursor); + /* Drawing context manipulation */ + void drw_setfontset(Drw *drw, Fnt *set); + void drw_setscheme(Drw *drw, Clr *scm); ++void drw_settrans(Drw *drw, Clr *psc, Clr *nsc); + + /* Drawing functions */ + void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); + int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); ++void drw_arrow(Drw *drw, int x, int y, unsigned int w, unsigned int h, int direction, int slash); + + /* Map functions */ + void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); +diff --git a/dwm.c b/dwm.c +index 86763d8..48905e3 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1730,6 +1730,13 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); ++ statusscheme = ecalloc(LENGTH(statuscolors), sizeof(Clr *)); ++ for (i = 0; i < LENGTH(statuscolors); i++) ++ #if BAR_ALPHA_PATCH ++ statusscheme[i] = drw_scm_create(drw, statuscolors[i], alphas[0], ColCount); ++ #else ++ statusscheme[i] = drw_scm_create(drw, statuscolors[i], 3); ++ #endif // BAR_ALPHA_PATCH + /* init bars */ + updatebars(); + updatestatus(); +diff --git a/patch/bar_powerline_status.c b/patch/bar_powerline_status.c +new file mode 100644 +index 0000000..3e2ee6a +--- /dev/null ++++ b/patch/bar_powerline_status.c +@@ -0,0 +1,121 @@ ++static Clr **statusscheme; ++ ++int ++width_pwrl_status(Bar *bar, BarWidthArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return widthpowerlinestatus(rawstext); ++ #else ++ return widthpowerlinestatus(stext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++ ++#if BAR_EXTRASTATUS_PATCH ++int ++width_pwrl_status_es(Bar *bar, BarWidthArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return widthpowerlinestatus(rawestext); ++ #else ++ return widthpowerlinestatus(estext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++#endif // BAR_EXTRASTATUS_PATCH ++ ++int ++draw_pwrl_status(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return drawpowerlinestatus(a->x + a->w, rawstext); ++ #else ++ return drawpowerlinestatus(a->x + a->w, stext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++ ++#if BAR_EXTRASTATUS_PATCH ++int ++draw_pwrl_status_es(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return drawpowerlinestatus(a->x + a->w, rawestext); ++ #else ++ return drawpowerlinestatus(a->x + a->w, estext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++#endif // BAR_EXTRASTATUS_PATCH ++ ++int ++click_pwrl_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} ++ ++int ++widthpowerlinestatus(char *stext) ++{ ++ char status[512]; ++ int w = 0, i, n = strlen(stext); ++ int plw = drw->fonts->h / 2 + 1; ++ char *bs, bp = '|'; ++ strcpy(status, stext); ++ ++ for (i = n, bs = &status[n-1]; i >= 0; i--, bs--) { ++ if (*bs == '<' || *bs == '/' || *bs == '\\' || *bs == '>' || *bs == '|') { /* block start */ ++ if (bp != '|') ++ w += plw; ++ w += TEXTW(bs+2); ++ bp = *bs; ++ *bs = 0; ++ } ++ } ++ if (bp != '|') ++ w += plw * 2; ++ ++ return w; ++} ++ ++int ++drawpowerlinestatus(int xpos, char *stext) ++{ ++ char status[512]; ++ int i, n = strlen(stext), cn = 0; ++ int x = xpos, w = 0; ++ int plw = drw->fonts->h / 2 + 1; ++ char *bs, bp = '|'; ++ Clr *prevscheme = statusscheme[0], *nxtscheme; ++ strcpy(status, stext); ++ ++ for (i = n, bs = &status[n-1]; i >= 0; i--, bs--) { ++ if (*bs == '<' || *bs == '/' || *bs == '\\' || *bs == '>' || *bs == '|') { /* block start */ ++ cn = ((int) *(bs+1)) - 1; ++ ++ if (cn < LENGTH(statuscolors)) { ++ drw_settrans(drw, prevscheme, (nxtscheme = statusscheme[cn])); ++ } else { ++ drw_settrans(drw, prevscheme, (nxtscheme = statusscheme[0])); ++ } ++ ++ if (bp != '|') { ++ drw_arrow(drw, x - plw, 0, plw, bh, bp == '\\' || bp == '>' ? 1 : 0, bp == '<' ? 0 : 1); ++ x -= plw; ++ } ++ ++ drw_setscheme(drw, nxtscheme); ++ w = TEXTW(bs+2); ++ drw_text(drw, x - w, 0, w, bh, lrpad / 2, bs+2, 0); ++ x -= w; ++ ++ bp = *bs; ++ *bs = 0; ++ prevscheme = nxtscheme; ++ } ++ } ++ if (bp != '|') { ++ drw_settrans(drw, prevscheme, scheme[SchemeNorm]); ++ drw_arrow(drw, x - plw, 0, plw, bh, bp == '\\' || bp == '>' ? 1 : 0, bp == '<' ? 0 : 1); ++ drw_rect(drw, x - 2 * plw, 0, plw, bh, 1, 1); ++ x -= plw * 2; ++ } ++ ++ return xpos - x; ++} +\ No newline at end of file +diff --git a/patch/bar_powerline_status.h b/patch/bar_powerline_status.h +new file mode 100644 +index 0000000..2ff5ad2 +--- /dev/null ++++ b/patch/bar_powerline_status.h +@@ -0,0 +1,11 @@ ++static int width_pwrl_status(Bar *bar, BarWidthArg *a); ++#if BAR_EXTRASTATUS_PATCH ++static int width_pwrl_status_es(Bar *bar, BarWidthArg *a); ++#endif // BAR_EXTRASTATUS_PATCH ++static int draw_pwrl_status(Bar *bar, BarDrawArg *a); ++#if BAR_EXTRASTATUS_PATCH ++static int draw_pwrl_status_es(Bar *bar, BarDrawArg *a); ++#endif // BAR_EXTRASTATUS_PATCH ++static int click_pwrl_status(Bar *bar, Arg *arg, BarClickArg *a); ++static int drawpowerlinestatus(int x, char *stext); ++static int widthpowerlinestatus(char *stext); +\ No newline at end of file +diff --git a/patch/bar_powerline_tags.c b/patch/bar_powerline_tags.c +new file mode 100644 +index 0000000..a85559b +--- /dev/null ++++ b/patch/bar_powerline_tags.c +@@ -0,0 +1,127 @@ ++int ++width_pwrl_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ int plw = drw->fonts->h / 2 + 1; ++ #if BAR_HIDEVACANTTAGS_PATCH ++ Client *c; ++ unsigned int occ = 0; ++ for (c = bar->mon->clients; c; c = c->next) ++ occ |= c->tags == 255 ? 0 : c->tags; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ #if BAR_HIDEVACANTTAGS_PATCH ++ if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) ++ continue; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ #if BAR_ALTERNATIVE_TAGS_PATCH ++ w += selmon->alttag ? TEXTW(tagsalt[i]) : TEXTW(tags[i]) + plw; ++ #else ++ w += TEXTW(tags[i]) + plw; ++ #endif // BAR_ALTERNATIVE_TAGS_PATCH ++ } ++ return w + lrpad; ++} ++ ++int ++draw_pwrl_tags(Bar *bar, BarDrawArg *a) ++{ ++ int x, w; ++ int invert; ++ int plw = drw->fonts->h / 2 + 1; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Clr *prevscheme, *nxtscheme; ++ #if !BAR_HIDEVACANTTAGS_PATCH ++ #if !BAR_ACTIVETAGINDICATORBAR_PATCH && !BAR_ACTIVETAGINDICATORBAR_ALT1_PATCH ++ int boxs = drw->fonts->h / 9; ++ #endif // BAR_ACTIVETAGINDICATORBAR_PATCH | BAR_ACTIVETAGINDICATORBAR_ALT1_PATCH ++ int boxw = drw->fonts->h / 6 + 2; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ ++ for (c = bar->mon->clients; c; c = c->next) { ++ #if BAR_HIDEVACANTTAGS_PATCH ++ occ |= c->tags == 255 ? 0 : c->tags; ++ #else ++ occ |= c->tags; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ x = a->x; ++ prevscheme = scheme[SchemeNorm]; ++ for (i = 0; i < LENGTH(tags); i++) { ++ #if BAR_HIDEVACANTTAGS_PATCH ++ /* do not draw vacant tags */ ++ if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) ++ continue; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ #if URGENTBORDER_PATCH ++ invert = 0; ++ #else ++ invert = urg & 1 << i; ++ #endif // URGENTBORDER_PATCH ++ w = TEXTW(tags[i]); ++ drw_settrans(drw, prevscheme, (nxtscheme = scheme[bar->mon->tagset[bar->mon->seltags] & 1 << i ? SchemeSel : SchemeNorm])); ++ #if BAR_POWERLINE_TAGS_SLASH_PATCH ++ drw_arrow(drw, x, 0, plw, bh, 1, 1); ++ #else ++ drw_arrow(drw, x, 0, plw, bh, 1, 1); ++ #endif // BAR_POWERLINE_TAGS_SLASH_PATCH ++ x += plw; ++ drw_setscheme(drw, nxtscheme); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ #if !BAR_HIDEVACANTTAGS_PATCH ++ if (occ & 1 << i) ++ #if BAR_ACTIVETAGINDICATORBAR_PATCH ++ drw_rect(drw, x + boxw, 0, w - ( 2 * boxw + 1), boxw, ++ #elif BAR_ACTIVETAGINDICATORBAR_ALT1_PATCH ++ drw_rect(drw, x + boxw, bh - boxw/2, w - ( 2 * boxw + 1), boxw/2, ++ #else ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ #endif // BAR_ACTIVETAGINDICATORBAR_PATCH ++ bar->mon == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ x += w; ++ prevscheme = nxtscheme; ++ } ++ nxtscheme = scheme[SchemeNorm]; ++ ++ drw_settrans(drw, prevscheme, nxtscheme); ++ #if BAR_POWERLINE_TAGS_SLASH_PATCH ++ drw_arrow(drw, x, 0, plw, bh, 1, 1); ++ #else ++ drw_arrow(drw, x, 0, plw, bh, 1, 0); ++ #endif // BAR_POWERLINE_TAGS_SLASH_PATCH ++ return a->x + a->w; ++} ++ ++int ++click_pwrl_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ int plw = drw->fonts->h / 2 + 1; ++ #if BAR_HIDEVACANTTAGS_PATCH ++ Client *c; ++ unsigned int occ = 0; ++ for (c = bar->mon->clients; c; c = c->next) ++ occ |= c->tags == 255 ? 0 : c->tags; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ ++ do { ++ #if BAR_HIDEVACANTTAGS_PATCH ++ if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) ++ continue; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ #if BAR_ALTERNATIVE_TAGS_PATCH ++ x += selmon->alttag ? TEXTW(tagsalt[i]) : TEXTW(tags[i]) + plw; ++ #else ++ x += TEXTW(tags[i]) + plw; ++ #endif ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +\ No newline at end of file +diff --git a/patch/bar_powerline_tags.h b/patch/bar_powerline_tags.h +new file mode 100644 +index 0000000..b1e0389 +--- /dev/null ++++ b/patch/bar_powerline_tags.h +@@ -0,0 +1,3 @@ ++static int width_pwrl_tags(Bar *bar, BarWidthArg *a); ++static int draw_pwrl_tags(Bar *bar, BarDrawArg *a); ++static int click_pwrl_tags(Bar *bar, Arg *arg, BarClickArg *a); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..48ce1cb 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -1,5 +1,7 @@ + /* Bar functionality */ + #include "bar_ltsymbol.c" +-#include "bar_status.c" +-#include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++//#include "bar_status.c" ++//#include "bar_tags.c" ++#include "bar_wintitle.c" ++#include "bar_powerline_tags.c" ++#include "bar_powerline_status.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..b313ca4 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -1,5 +1,7 @@ + /* Bar functionality */ + #include "bar_ltsymbol.h" +-#include "bar_status.h" +-#include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++//#include "bar_status.h" ++//#include "bar_tags.h" ++#include "bar_wintitle.h" ++#include "bar_powerline_tags.h" ++#include "bar_powerline_status.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-powerline-6.3_full.diff b/dwm/dwm-barmodules-powerline-6.3_full.diff new file mode 100644 index 0000000..b687930 --- /dev/null +++ b/dwm/dwm-barmodules-powerline-6.3_full.diff @@ -0,0 +1,1377 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From 97343b7b17a3bdc111768f026e8532b93143d409 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:08:58 +0100 +Subject: [PATCH 2/2] Adding powerline module + +--- + config.def.h | 10 ++- + drw.c | 41 +++++++++++ + drw.h | 2 + + dwm.c | 7 ++ + patch/bar_powerline_status.c | 121 +++++++++++++++++++++++++++++++++ + patch/bar_powerline_status.h | 11 +++ + patch/bar_powerline_tags.c | 127 +++++++++++++++++++++++++++++++++++ + patch/bar_powerline_tags.h | 3 + + patch/include.c | 8 ++- + patch/include.h | 8 ++- + 10 files changed, 330 insertions(+), 8 deletions(-) + create mode 100644 patch/bar_powerline_status.c + create mode 100644 patch/bar_powerline_status.h + create mode 100644 patch/bar_powerline_tags.c + create mode 100644 patch/bar_powerline_tags.h + +diff --git a/config.def.h b/config.def.h +index f870c41..1187340 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -18,6 +18,12 @@ static const char *colors[][3] = { + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; + ++static const char *statuscolors[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, ++ [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++}; ++ + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +@@ -45,9 +51,9 @@ static const Rule rules[] = { + */ + static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ +- { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_pwrl_tags, draw_pwrl_tags, click_pwrl_tags, "powerline_tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, +- { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_pwrl_status, draw_pwrl_status, click_pwrl_status, "powerline_status" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, + }; + +diff --git a/drw.c b/drw.c +index 4cdbcbe..522fc79 100644 +--- a/drw.c ++++ b/drw.c +@@ -15,6 +15,7 @@ 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}; ++Clr transcheme[3]; + + static long + utf8decodebyte(const char c, size_t *i) +@@ -236,6 +237,15 @@ drw_setscheme(Drw *drw, Clr *scm) + drw->scheme = scm; + } + ++void ++drw_settrans(Drw *drw, Clr *psc, Clr *nsc) ++{ ++ if (drw) { ++ transcheme[0] = psc[ColBg]; transcheme[1] = nsc[ColBg]; transcheme[2] = psc[ColBorder]; ++ drw->scheme = transcheme; ++ } ++} ++ + void + drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) + { +@@ -379,6 +389,37 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp + return x + (render ? w : 0); + } + ++void ++drw_arrow(Drw *drw, int x, int y, unsigned int w, unsigned int h, int direction, int slash) ++{ ++ if (!drw || !drw->scheme) ++ return; ++ ++ /* direction=1 draws right arrow */ ++ x = direction ? x : x + w; ++ w = direction ? w : -w; ++ /* slash=1 draws slash instead of arrow */ ++ unsigned int hh = slash ? (direction ? 0 : h) : h/2; ++ ++ XPoint points[] = { ++ {x , y }, ++ {x + w, y + hh }, ++ {x , y + h }, ++ }; ++ ++ XPoint bg[] = { ++ {x , y }, ++ {x + w, y }, ++ {x + w, y + h}, ++ {x , y + h}, ++ }; ++ ++ XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel); ++ XFillPolygon(drw->dpy, drw->drawable, drw->gc, bg, 4, Convex, CoordModeOrigin); ++ XSetForeground(drw->dpy, drw->gc, drw->scheme[ColFg].pixel); ++ XFillPolygon(drw->dpy, drw->drawable, drw->gc, points, 3, Nonconvex, CoordModeOrigin); ++} ++ + void + drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) + { +diff --git a/drw.h b/drw.h +index 4bcd5ad..4a42557 100644 +--- a/drw.h ++++ b/drw.h +@@ -48,10 +48,12 @@ void drw_cur_free(Drw *drw, Cur *cursor); + /* Drawing context manipulation */ + void drw_setfontset(Drw *drw, Fnt *set); + void drw_setscheme(Drw *drw, Clr *scm); ++void drw_settrans(Drw *drw, Clr *psc, Clr *nsc); + + /* Drawing functions */ + void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); + int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); ++void drw_arrow(Drw *drw, int x, int y, unsigned int w, unsigned int h, int direction, int slash); + + /* Map functions */ + void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); +diff --git a/dwm.c b/dwm.c +index 86763d8..48905e3 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1730,6 +1730,13 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); ++ statusscheme = ecalloc(LENGTH(statuscolors), sizeof(Clr *)); ++ for (i = 0; i < LENGTH(statuscolors); i++) ++ #if BAR_ALPHA_PATCH ++ statusscheme[i] = drw_scm_create(drw, statuscolors[i], alphas[0], ColCount); ++ #else ++ statusscheme[i] = drw_scm_create(drw, statuscolors[i], 3); ++ #endif // BAR_ALPHA_PATCH + /* init bars */ + updatebars(); + updatestatus(); +diff --git a/patch/bar_powerline_status.c b/patch/bar_powerline_status.c +new file mode 100644 +index 0000000..3e2ee6a +--- /dev/null ++++ b/patch/bar_powerline_status.c +@@ -0,0 +1,121 @@ ++static Clr **statusscheme; ++ ++int ++width_pwrl_status(Bar *bar, BarWidthArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return widthpowerlinestatus(rawstext); ++ #else ++ return widthpowerlinestatus(stext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++ ++#if BAR_EXTRASTATUS_PATCH ++int ++width_pwrl_status_es(Bar *bar, BarWidthArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return widthpowerlinestatus(rawestext); ++ #else ++ return widthpowerlinestatus(estext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++#endif // BAR_EXTRASTATUS_PATCH ++ ++int ++draw_pwrl_status(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return drawpowerlinestatus(a->x + a->w, rawstext); ++ #else ++ return drawpowerlinestatus(a->x + a->w, stext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++ ++#if BAR_EXTRASTATUS_PATCH ++int ++draw_pwrl_status_es(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return drawpowerlinestatus(a->x + a->w, rawestext); ++ #else ++ return drawpowerlinestatus(a->x + a->w, estext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++#endif // BAR_EXTRASTATUS_PATCH ++ ++int ++click_pwrl_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} ++ ++int ++widthpowerlinestatus(char *stext) ++{ ++ char status[512]; ++ int w = 0, i, n = strlen(stext); ++ int plw = drw->fonts->h / 2 + 1; ++ char *bs, bp = '|'; ++ strcpy(status, stext); ++ ++ for (i = n, bs = &status[n-1]; i >= 0; i--, bs--) { ++ if (*bs == '<' || *bs == '/' || *bs == '\\' || *bs == '>' || *bs == '|') { /* block start */ ++ if (bp != '|') ++ w += plw; ++ w += TEXTW(bs+2); ++ bp = *bs; ++ *bs = 0; ++ } ++ } ++ if (bp != '|') ++ w += plw * 2; ++ ++ return w; ++} ++ ++int ++drawpowerlinestatus(int xpos, char *stext) ++{ ++ char status[512]; ++ int i, n = strlen(stext), cn = 0; ++ int x = xpos, w = 0; ++ int plw = drw->fonts->h / 2 + 1; ++ char *bs, bp = '|'; ++ Clr *prevscheme = statusscheme[0], *nxtscheme; ++ strcpy(status, stext); ++ ++ for (i = n, bs = &status[n-1]; i >= 0; i--, bs--) { ++ if (*bs == '<' || *bs == '/' || *bs == '\\' || *bs == '>' || *bs == '|') { /* block start */ ++ cn = ((int) *(bs+1)) - 1; ++ ++ if (cn < LENGTH(statuscolors)) { ++ drw_settrans(drw, prevscheme, (nxtscheme = statusscheme[cn])); ++ } else { ++ drw_settrans(drw, prevscheme, (nxtscheme = statusscheme[0])); ++ } ++ ++ if (bp != '|') { ++ drw_arrow(drw, x - plw, 0, plw, bh, bp == '\\' || bp == '>' ? 1 : 0, bp == '<' ? 0 : 1); ++ x -= plw; ++ } ++ ++ drw_setscheme(drw, nxtscheme); ++ w = TEXTW(bs+2); ++ drw_text(drw, x - w, 0, w, bh, lrpad / 2, bs+2, 0); ++ x -= w; ++ ++ bp = *bs; ++ *bs = 0; ++ prevscheme = nxtscheme; ++ } ++ } ++ if (bp != '|') { ++ drw_settrans(drw, prevscheme, scheme[SchemeNorm]); ++ drw_arrow(drw, x - plw, 0, plw, bh, bp == '\\' || bp == '>' ? 1 : 0, bp == '<' ? 0 : 1); ++ drw_rect(drw, x - 2 * plw, 0, plw, bh, 1, 1); ++ x -= plw * 2; ++ } ++ ++ return xpos - x; ++} +\ No newline at end of file +diff --git a/patch/bar_powerline_status.h b/patch/bar_powerline_status.h +new file mode 100644 +index 0000000..2ff5ad2 +--- /dev/null ++++ b/patch/bar_powerline_status.h +@@ -0,0 +1,11 @@ ++static int width_pwrl_status(Bar *bar, BarWidthArg *a); ++#if BAR_EXTRASTATUS_PATCH ++static int width_pwrl_status_es(Bar *bar, BarWidthArg *a); ++#endif // BAR_EXTRASTATUS_PATCH ++static int draw_pwrl_status(Bar *bar, BarDrawArg *a); ++#if BAR_EXTRASTATUS_PATCH ++static int draw_pwrl_status_es(Bar *bar, BarDrawArg *a); ++#endif // BAR_EXTRASTATUS_PATCH ++static int click_pwrl_status(Bar *bar, Arg *arg, BarClickArg *a); ++static int drawpowerlinestatus(int x, char *stext); ++static int widthpowerlinestatus(char *stext); +\ No newline at end of file +diff --git a/patch/bar_powerline_tags.c b/patch/bar_powerline_tags.c +new file mode 100644 +index 0000000..a85559b +--- /dev/null ++++ b/patch/bar_powerline_tags.c +@@ -0,0 +1,127 @@ ++int ++width_pwrl_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ int plw = drw->fonts->h / 2 + 1; ++ #if BAR_HIDEVACANTTAGS_PATCH ++ Client *c; ++ unsigned int occ = 0; ++ for (c = bar->mon->clients; c; c = c->next) ++ occ |= c->tags == 255 ? 0 : c->tags; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ #if BAR_HIDEVACANTTAGS_PATCH ++ if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) ++ continue; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ #if BAR_ALTERNATIVE_TAGS_PATCH ++ w += selmon->alttag ? TEXTW(tagsalt[i]) : TEXTW(tags[i]) + plw; ++ #else ++ w += TEXTW(tags[i]) + plw; ++ #endif // BAR_ALTERNATIVE_TAGS_PATCH ++ } ++ return w + lrpad; ++} ++ ++int ++draw_pwrl_tags(Bar *bar, BarDrawArg *a) ++{ ++ int x, w; ++ int invert; ++ int plw = drw->fonts->h / 2 + 1; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Clr *prevscheme, *nxtscheme; ++ #if !BAR_HIDEVACANTTAGS_PATCH ++ #if !BAR_ACTIVETAGINDICATORBAR_PATCH && !BAR_ACTIVETAGINDICATORBAR_ALT1_PATCH ++ int boxs = drw->fonts->h / 9; ++ #endif // BAR_ACTIVETAGINDICATORBAR_PATCH | BAR_ACTIVETAGINDICATORBAR_ALT1_PATCH ++ int boxw = drw->fonts->h / 6 + 2; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ ++ for (c = bar->mon->clients; c; c = c->next) { ++ #if BAR_HIDEVACANTTAGS_PATCH ++ occ |= c->tags == 255 ? 0 : c->tags; ++ #else ++ occ |= c->tags; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ x = a->x; ++ prevscheme = scheme[SchemeNorm]; ++ for (i = 0; i < LENGTH(tags); i++) { ++ #if BAR_HIDEVACANTTAGS_PATCH ++ /* do not draw vacant tags */ ++ if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) ++ continue; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ #if URGENTBORDER_PATCH ++ invert = 0; ++ #else ++ invert = urg & 1 << i; ++ #endif // URGENTBORDER_PATCH ++ w = TEXTW(tags[i]); ++ drw_settrans(drw, prevscheme, (nxtscheme = scheme[bar->mon->tagset[bar->mon->seltags] & 1 << i ? SchemeSel : SchemeNorm])); ++ #if BAR_POWERLINE_TAGS_SLASH_PATCH ++ drw_arrow(drw, x, 0, plw, bh, 1, 1); ++ #else ++ drw_arrow(drw, x, 0, plw, bh, 1, 1); ++ #endif // BAR_POWERLINE_TAGS_SLASH_PATCH ++ x += plw; ++ drw_setscheme(drw, nxtscheme); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ #if !BAR_HIDEVACANTTAGS_PATCH ++ if (occ & 1 << i) ++ #if BAR_ACTIVETAGINDICATORBAR_PATCH ++ drw_rect(drw, x + boxw, 0, w - ( 2 * boxw + 1), boxw, ++ #elif BAR_ACTIVETAGINDICATORBAR_ALT1_PATCH ++ drw_rect(drw, x + boxw, bh - boxw/2, w - ( 2 * boxw + 1), boxw/2, ++ #else ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ #endif // BAR_ACTIVETAGINDICATORBAR_PATCH ++ bar->mon == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ x += w; ++ prevscheme = nxtscheme; ++ } ++ nxtscheme = scheme[SchemeNorm]; ++ ++ drw_settrans(drw, prevscheme, nxtscheme); ++ #if BAR_POWERLINE_TAGS_SLASH_PATCH ++ drw_arrow(drw, x, 0, plw, bh, 1, 1); ++ #else ++ drw_arrow(drw, x, 0, plw, bh, 1, 0); ++ #endif // BAR_POWERLINE_TAGS_SLASH_PATCH ++ return a->x + a->w; ++} ++ ++int ++click_pwrl_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ int plw = drw->fonts->h / 2 + 1; ++ #if BAR_HIDEVACANTTAGS_PATCH ++ Client *c; ++ unsigned int occ = 0; ++ for (c = bar->mon->clients; c; c = c->next) ++ occ |= c->tags == 255 ? 0 : c->tags; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ ++ do { ++ #if BAR_HIDEVACANTTAGS_PATCH ++ if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) ++ continue; ++ #endif // BAR_HIDEVACANTTAGS_PATCH ++ #if BAR_ALTERNATIVE_TAGS_PATCH ++ x += selmon->alttag ? TEXTW(tagsalt[i]) : TEXTW(tags[i]) + plw; ++ #else ++ x += TEXTW(tags[i]) + plw; ++ #endif ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +\ No newline at end of file +diff --git a/patch/bar_powerline_tags.h b/patch/bar_powerline_tags.h +new file mode 100644 +index 0000000..b1e0389 +--- /dev/null ++++ b/patch/bar_powerline_tags.h +@@ -0,0 +1,3 @@ ++static int width_pwrl_tags(Bar *bar, BarWidthArg *a); ++static int draw_pwrl_tags(Bar *bar, BarDrawArg *a); ++static int click_pwrl_tags(Bar *bar, Arg *arg, BarClickArg *a); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..48ce1cb 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -1,5 +1,7 @@ + /* Bar functionality */ + #include "bar_ltsymbol.c" +-#include "bar_status.c" +-#include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++//#include "bar_status.c" ++//#include "bar_tags.c" ++#include "bar_wintitle.c" ++#include "bar_powerline_tags.c" ++#include "bar_powerline_status.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..b313ca4 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -1,5 +1,7 @@ + /* Bar functionality */ + #include "bar_ltsymbol.h" +-#include "bar_status.h" +-#include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++//#include "bar_status.h" ++//#include "bar_tags.h" ++#include "bar_wintitle.h" ++#include "bar_powerline_tags.h" ++#include "bar_powerline_status.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-status2d-6.3.diff b/dwm/dwm-barmodules-status2d-6.3.diff new file mode 100644 index 0000000..67d2b8c --- /dev/null +++ b/dwm/dwm-barmodules-status2d-6.3.diff @@ -0,0 +1,346 @@ +From 8592c59216ccdbaf3bfbc028130e116057517d0d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:09:50 +0100 +Subject: [PATCH 2/2] Adding status2d module + +--- + config.def.h | 2 +- + dwm.c | 7 +- + patch/bar_status2d.c | 225 +++++++++++++++++++++++++++++++++++++++++++ + patch/bar_status2d.h | 13 +++ + patch/include.c | 5 +- + patch/include.h | 5 +- + 6 files changed, 249 insertions(+), 8 deletions(-) + create mode 100644 patch/bar_status2d.c + create mode 100644 patch/bar_status2d.h + +diff --git a/config.def.h b/config.def.h +index f870c41..299974a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -47,7 +47,7 @@ static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, +- { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status2d, draw_status2d, click_status2d, "status2d" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, + }; + +diff --git a/dwm.c b/dwm.c +index 86763d8..10f9220 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -290,7 +290,7 @@ static void zoom(const Arg *arg); + #include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; +-static char stext[256]; ++static char stext[1024]; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh; /* bar geometry */ +@@ -558,7 +558,7 @@ cleanup(void) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); +- for (i = 0; i < LENGTH(colors); i++) ++ for (i = 0; i < LENGTH(colors) + 1; i++) + free(scheme[i]); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); +@@ -1727,7 +1727,8 @@ setup(void) + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ +- scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); ++ scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *)); ++ scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], 3); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ +diff --git a/patch/bar_status2d.c b/patch/bar_status2d.c +new file mode 100644 +index 0000000..5863d44 +--- /dev/null ++++ b/patch/bar_status2d.c +@@ -0,0 +1,225 @@ ++int ++width_status2d(Bar *bar, BarWidthArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return status2dtextlength(rawstext) + lrpad; ++ #else ++ return status2dtextlength(stext) + lrpad; ++ #endif // BAR_STATUSCMD_PATCH ++} ++ ++#if BAR_EXTRASTATUS_PATCH ++int ++width_status2d_es(Bar *bar, BarWidthArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return status2dtextlength(rawestext); ++ #else ++ return status2dtextlength(estext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++#endif // BAR_EXTRASTATUS_PATCH ++ ++int ++draw_status2d(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return drawstatusbar(a->x, rawstext); ++ #else ++ return drawstatusbar(a->x, stext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++ ++#if BAR_EXTRASTATUS_PATCH ++int ++draw_status2d_es(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return drawstatusbar(a->x, rawestext); ++ #else ++ return drawstatusbar(a->x, estext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++#endif // BAR_EXTRASTATUS_PATCH ++ ++#if !BAR_STATUSCMD_PATCH ++int ++click_status2d(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} ++#endif // BAR_STATUSCMD_PATCH ++ ++int ++drawstatusbar(int x, char* stext) ++{ ++ int i, w, len; ++ short isCode = 0; ++ char *text; ++ char *p; ++ ++ len = strlen(stext); ++ if (!(text = (char*) malloc(sizeof(char)*(len + 1)))) ++ die("malloc"); ++ p = text; ++ #if BAR_STATUSCMD_PATCH ++ copyvalidchars(text, stext); ++ #else ++ memcpy(text, stext, len); ++ #endif // BAR_STATUSCMD_PATCH ++ text[len] = '\0'; ++ ++ x += lrpad / 2; ++ drw_setscheme(drw, scheme[LENGTH(colors)]); ++ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; ++ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; ++ ++ /* process status text */ ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^' && !isCode) { ++ isCode = 1; ++ ++ text[i] = '\0'; ++ #if BAR_PANGO_PATCH ++ w = TEXTWM(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0, True); ++ #else ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ #endif // BAR_PANGO_PATCH ++ ++ x += w; ++ ++ /* process code */ ++ while (text[++i] != '^') { ++ if (text[i] == 'c') { ++ char buf[8]; ++ if (i + 7 > len - 1) { ++ i += 7; ++ len = 0; ++ break; ++ } ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColFg], buf, 0xff); ++ #elif BAR_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColFg], buf, alphas[SchemeNorm][ColFg]); ++ #else ++ drw_clr_create(drw, &drw->scheme[ColFg], buf); ++ #endif // BAR_ALPHA_PATCH ++ i += 7; ++ } else if (text[i] == 'b') { ++ char buf[8]; ++ if (i + 7 > len - 1) { ++ i += 7; ++ len = 0; ++ break; ++ } ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColBg], buf, 0xff); ++ #elif BAR_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColBg], buf, alphas[SchemeNorm][ColBg]); ++ #else ++ drw_clr_create(drw, &drw->scheme[ColBg], buf); ++ #endif // BAR_ALPHA_PATCH ++ i += 7; ++ } else if (text[i] == 'd') { ++ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; ++ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; ++ } else if (text[i] == 'r') { ++ int rx = atoi(text + ++i); ++ while (text[++i] != ','); ++ int ry = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rw = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rh = atoi(text + ++i); ++ ++ if (ry < 0) ++ ry = 0; ++ if (rx < 0) ++ rx = 0; ++ ++ drw_rect(drw, rx + x, ry, rw, rh, 1, 0); ++ } else if (text[i] == 'f') { ++ x += atoi(text + ++i); ++ } ++ } ++ ++ text = text + i + 1; ++ len -= i + 1; ++ i=-1; ++ isCode = 0; ++ } ++ } ++ if (!isCode && len) { ++ #if BAR_PANGO_PATCH ++ w = TEXTWM(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0, True); ++ #else ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ #endif // BAR_PANGO_PATCH ++ x += w; ++ } ++ free(p); ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ ++ return x; ++} ++ ++int ++status2dtextlength(char* stext) ++{ ++ int i, w, len; ++ short isCode = 0; ++ char *text; ++ char *p; ++ ++ len = strlen(stext) + 1; ++ if (!(text = (char*) malloc(sizeof(char)*len))) ++ die("malloc"); ++ p = text; ++ #if BAR_STATUSCMD_PATCH ++ copyvalidchars(text, stext); ++ #else ++ memcpy(text, stext, len); ++ #endif // BAR_STATUSCMD_PATCH ++ ++ /* compute width of the status text */ ++ w = 0; ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^') { ++ if (!isCode) { ++ isCode = 1; ++ text[i] = '\0'; ++ #if BAR_PANGO_PATCH ++ w += TEXTWM(text) - lrpad; ++ #else ++ w += TEXTW(text) - lrpad; ++ #endif // BAR_PANGO_PATCH ++ text[i] = '^'; ++ if (text[++i] == 'f') ++ w += atoi(text + ++i); ++ } else { ++ isCode = 0; ++ text = text + i + 1; ++ i = -1; ++ } ++ } ++ } ++ if (!isCode) ++ #if BAR_PANGO_PATCH ++ w += TEXTWM(text) - lrpad; ++ #else ++ w += TEXTW(text) - lrpad; ++ #endif // BAR_PANGO_PATCH ++ free(p); ++ return w; ++} +\ No newline at end of file +diff --git a/patch/bar_status2d.h b/patch/bar_status2d.h +new file mode 100644 +index 0000000..ea48dd3 +--- /dev/null ++++ b/patch/bar_status2d.h +@@ -0,0 +1,13 @@ ++static int width_status2d(Bar *bar, BarWidthArg *a); ++#if BAR_EXTRASTATUS_PATCH ++static int width_status2d_es(Bar *bar, BarWidthArg *a); ++#endif // BAR_EXTRASTATUS_PATCH ++static int draw_status2d(Bar *bar, BarDrawArg *a); ++#if BAR_EXTRASTATUS_PATCH ++static int draw_status2d_es(Bar *bar, BarDrawArg *a); ++#endif // BAR_EXTRASTATUS_PATCH ++#if !BAR_STATUSCMD_PATCH ++static int click_status2d(Bar *bar, Arg *arg, BarClickArg *a); ++#endif // BAR_STATUSCMD_PATCH ++static int drawstatusbar(int x, char *text); ++static int status2dtextlength(char *stext); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..77eafa3 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -1,5 +1,6 @@ + /* Bar functionality */ + #include "bar_ltsymbol.c" +-#include "bar_status.c" ++//#include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_status2d.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..2927238 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -1,5 +1,6 @@ + /* Bar functionality */ + #include "bar_ltsymbol.h" +-#include "bar_status.h" ++//#include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_status2d.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-status2d-6.3_full.diff b/dwm/dwm-barmodules-status2d-6.3_full.diff new file mode 100644 index 0000000..c7652ac --- /dev/null +++ b/dwm/dwm-barmodules-status2d-6.3_full.diff @@ -0,0 +1,1244 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From 8592c59216ccdbaf3bfbc028130e116057517d0d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:09:50 +0100 +Subject: [PATCH 2/2] Adding status2d module + +--- + config.def.h | 2 +- + dwm.c | 7 +- + patch/bar_status2d.c | 225 +++++++++++++++++++++++++++++++++++++++++++ + patch/bar_status2d.h | 13 +++ + patch/include.c | 5 +- + patch/include.h | 5 +- + 6 files changed, 249 insertions(+), 8 deletions(-) + create mode 100644 patch/bar_status2d.c + create mode 100644 patch/bar_status2d.h + +diff --git a/config.def.h b/config.def.h +index f870c41..299974a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -47,7 +47,7 @@ static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, +- { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status2d, draw_status2d, click_status2d, "status2d" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, + }; + +diff --git a/dwm.c b/dwm.c +index 86763d8..10f9220 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -290,7 +290,7 @@ static void zoom(const Arg *arg); + #include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; +-static char stext[256]; ++static char stext[1024]; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh; /* bar geometry */ +@@ -558,7 +558,7 @@ cleanup(void) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); +- for (i = 0; i < LENGTH(colors); i++) ++ for (i = 0; i < LENGTH(colors) + 1; i++) + free(scheme[i]); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); +@@ -1727,7 +1727,8 @@ setup(void) + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ +- scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); ++ scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *)); ++ scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], 3); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ +diff --git a/patch/bar_status2d.c b/patch/bar_status2d.c +new file mode 100644 +index 0000000..5863d44 +--- /dev/null ++++ b/patch/bar_status2d.c +@@ -0,0 +1,225 @@ ++int ++width_status2d(Bar *bar, BarWidthArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return status2dtextlength(rawstext) + lrpad; ++ #else ++ return status2dtextlength(stext) + lrpad; ++ #endif // BAR_STATUSCMD_PATCH ++} ++ ++#if BAR_EXTRASTATUS_PATCH ++int ++width_status2d_es(Bar *bar, BarWidthArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return status2dtextlength(rawestext); ++ #else ++ return status2dtextlength(estext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++#endif // BAR_EXTRASTATUS_PATCH ++ ++int ++draw_status2d(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return drawstatusbar(a->x, rawstext); ++ #else ++ return drawstatusbar(a->x, stext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++ ++#if BAR_EXTRASTATUS_PATCH ++int ++draw_status2d_es(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_STATUSCMD_PATCH ++ return drawstatusbar(a->x, rawestext); ++ #else ++ return drawstatusbar(a->x, estext); ++ #endif // BAR_STATUSCMD_PATCH ++} ++#endif // BAR_EXTRASTATUS_PATCH ++ ++#if !BAR_STATUSCMD_PATCH ++int ++click_status2d(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} ++#endif // BAR_STATUSCMD_PATCH ++ ++int ++drawstatusbar(int x, char* stext) ++{ ++ int i, w, len; ++ short isCode = 0; ++ char *text; ++ char *p; ++ ++ len = strlen(stext); ++ if (!(text = (char*) malloc(sizeof(char)*(len + 1)))) ++ die("malloc"); ++ p = text; ++ #if BAR_STATUSCMD_PATCH ++ copyvalidchars(text, stext); ++ #else ++ memcpy(text, stext, len); ++ #endif // BAR_STATUSCMD_PATCH ++ text[len] = '\0'; ++ ++ x += lrpad / 2; ++ drw_setscheme(drw, scheme[LENGTH(colors)]); ++ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; ++ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; ++ ++ /* process status text */ ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^' && !isCode) { ++ isCode = 1; ++ ++ text[i] = '\0'; ++ #if BAR_PANGO_PATCH ++ w = TEXTWM(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0, True); ++ #else ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ #endif // BAR_PANGO_PATCH ++ ++ x += w; ++ ++ /* process code */ ++ while (text[++i] != '^') { ++ if (text[i] == 'c') { ++ char buf[8]; ++ if (i + 7 > len - 1) { ++ i += 7; ++ len = 0; ++ break; ++ } ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColFg], buf, 0xff); ++ #elif BAR_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColFg], buf, alphas[SchemeNorm][ColFg]); ++ #else ++ drw_clr_create(drw, &drw->scheme[ColFg], buf); ++ #endif // BAR_ALPHA_PATCH ++ i += 7; ++ } else if (text[i] == 'b') { ++ char buf[8]; ++ if (i + 7 > len - 1) { ++ i += 7; ++ len = 0; ++ break; ++ } ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColBg], buf, 0xff); ++ #elif BAR_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColBg], buf, alphas[SchemeNorm][ColBg]); ++ #else ++ drw_clr_create(drw, &drw->scheme[ColBg], buf); ++ #endif // BAR_ALPHA_PATCH ++ i += 7; ++ } else if (text[i] == 'd') { ++ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; ++ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; ++ } else if (text[i] == 'r') { ++ int rx = atoi(text + ++i); ++ while (text[++i] != ','); ++ int ry = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rw = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rh = atoi(text + ++i); ++ ++ if (ry < 0) ++ ry = 0; ++ if (rx < 0) ++ rx = 0; ++ ++ drw_rect(drw, rx + x, ry, rw, rh, 1, 0); ++ } else if (text[i] == 'f') { ++ x += atoi(text + ++i); ++ } ++ } ++ ++ text = text + i + 1; ++ len -= i + 1; ++ i=-1; ++ isCode = 0; ++ } ++ } ++ if (!isCode && len) { ++ #if BAR_PANGO_PATCH ++ w = TEXTWM(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0, True); ++ #else ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ #endif // BAR_PANGO_PATCH ++ x += w; ++ } ++ free(p); ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ ++ return x; ++} ++ ++int ++status2dtextlength(char* stext) ++{ ++ int i, w, len; ++ short isCode = 0; ++ char *text; ++ char *p; ++ ++ len = strlen(stext) + 1; ++ if (!(text = (char*) malloc(sizeof(char)*len))) ++ die("malloc"); ++ p = text; ++ #if BAR_STATUSCMD_PATCH ++ copyvalidchars(text, stext); ++ #else ++ memcpy(text, stext, len); ++ #endif // BAR_STATUSCMD_PATCH ++ ++ /* compute width of the status text */ ++ w = 0; ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^') { ++ if (!isCode) { ++ isCode = 1; ++ text[i] = '\0'; ++ #if BAR_PANGO_PATCH ++ w += TEXTWM(text) - lrpad; ++ #else ++ w += TEXTW(text) - lrpad; ++ #endif // BAR_PANGO_PATCH ++ text[i] = '^'; ++ if (text[++i] == 'f') ++ w += atoi(text + ++i); ++ } else { ++ isCode = 0; ++ text = text + i + 1; ++ i = -1; ++ } ++ } ++ } ++ if (!isCode) ++ #if BAR_PANGO_PATCH ++ w += TEXTWM(text) - lrpad; ++ #else ++ w += TEXTW(text) - lrpad; ++ #endif // BAR_PANGO_PATCH ++ free(p); ++ return w; ++} +\ No newline at end of file +diff --git a/patch/bar_status2d.h b/patch/bar_status2d.h +new file mode 100644 +index 0000000..ea48dd3 +--- /dev/null ++++ b/patch/bar_status2d.h +@@ -0,0 +1,13 @@ ++static int width_status2d(Bar *bar, BarWidthArg *a); ++#if BAR_EXTRASTATUS_PATCH ++static int width_status2d_es(Bar *bar, BarWidthArg *a); ++#endif // BAR_EXTRASTATUS_PATCH ++static int draw_status2d(Bar *bar, BarDrawArg *a); ++#if BAR_EXTRASTATUS_PATCH ++static int draw_status2d_es(Bar *bar, BarDrawArg *a); ++#endif // BAR_EXTRASTATUS_PATCH ++#if !BAR_STATUSCMD_PATCH ++static int click_status2d(Bar *bar, Arg *arg, BarClickArg *a); ++#endif // BAR_STATUSCMD_PATCH ++static int drawstatusbar(int x, char *text); ++static int status2dtextlength(char *stext); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..77eafa3 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -1,5 +1,6 @@ + /* Bar functionality */ + #include "bar_ltsymbol.c" +-#include "bar_status.c" ++//#include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_status2d.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..2927238 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -1,5 +1,6 @@ + /* Bar functionality */ + #include "bar_ltsymbol.h" +-#include "bar_status.h" ++//#include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_status2d.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-status2d-statuscmd-dwmblocks-extrastatus-6.3.diff b/dwm/dwm-barmodules-status2d-statuscmd-dwmblocks-extrastatus-6.3.diff new file mode 100644 index 0000000..3b91830 --- /dev/null +++ b/dwm/dwm-barmodules-status2d-statuscmd-dwmblocks-extrastatus-6.3.diff @@ -0,0 +1,459 @@ +From 69ba99292c4eac2ffa2eb1f1962f55ce463b35db Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:10:28 +0100 +Subject: [PATCH 2/2] Adding status2d + statuscmd + dwmblocks + extrastatus + module + +--- + config.def.h | 11 ++- + dwm.c | 23 +++++- + patch/bar_dwmblocks.c | 31 ++++++++ + patch/bar_dwmblocks.h | 2 + + patch/bar_status2d.c | 171 ++++++++++++++++++++++++++++++++++++++++++ + patch/bar_status2d.h | 6 ++ + patch/bar_statuscmd.c | 49 ++++++++++++ + patch/bar_statuscmd.h | 4 + + patch/include.c | 7 +- + patch/include.h | 7 +- + 10 files changed, 301 insertions(+), 10 deletions(-) + create mode 100644 patch/bar_dwmblocks.c + create mode 100644 patch/bar_dwmblocks.h + create mode 100644 patch/bar_status2d.c + create mode 100644 patch/bar_status2d.h + create mode 100644 patch/bar_statuscmd.c + create mode 100644 patch/bar_statuscmd.h + +diff --git a/config.def.h b/config.def.h +index f870c41..af2aa90 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,7 @@ 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 */ ++static const char statussep = ';'; /* separator between status bars */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -47,8 +48,10 @@ static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, +- { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status2d, draw_status2d, click_statuscmd, "status2d" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++ { 0, 1, BAR_ALIGN_CENTER, width_status2d_es, draw_status2d_es, click_statuscmd_es, "status2d_es" }, ++ + }; + + /* layout(s) */ +@@ -124,7 +127,11 @@ static Button buttons[] = { + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, +- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, ++ { ClkStatusText, 0, Button1, sigdwmblocks, {.i = 1 } }, ++ { ClkStatusText, 0, Button2, sigdwmblocks, {.i = 2 } }, ++ { ClkStatusText, 0, Button3, sigdwmblocks, {.i = 3 } }, ++ { ClkStatusText, 0, Button4, sigdwmblocks, {.i = 4 } }, ++ { ClkStatusText, 0, Button5, sigdwmblocks, {.i = 5 } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, +diff --git a/dwm.c b/dwm.c +index 86763d8..9fd8b67 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -290,7 +290,10 @@ static void zoom(const Arg *arg); + #include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; +-static char stext[256]; ++static char stext[1024]; ++static char rawstext[1024]; ++static char estext[1024]; ++static char rawestext[1024]; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh; /* bar geometry */ +@@ -558,7 +561,7 @@ cleanup(void) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); +- for (i = 0; i < LENGTH(colors); i++) ++ for (i = 0; i < LENGTH(colors) + 1; i++) + free(scheme[i]); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); +@@ -1727,7 +1730,8 @@ setup(void) + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ +- scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); ++ scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *)); ++ scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], 3); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ +@@ -2185,8 +2189,19 @@ void + updatestatus(void) + { + Monitor *m; +- if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) ++ if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) { + strcpy(stext, "dwm-"VERSION); ++ estext[0] = '\0'; ++ } else { ++ char *e = strchr(rawstext, statussep); ++ if (e) { ++ *e = '\0'; e++; ++ strncpy(rawestext, e, sizeof(estext) - 1); ++ copyvalidchars(estext, rawestext); ++ } else ++ estext[0] = '\0'; ++ copyvalidchars(stext, rawstext); ++ } + for (m = mons; m; m = m->next) + drawbar(m); + } +diff --git a/patch/bar_dwmblocks.c b/patch/bar_dwmblocks.c +new file mode 100644 +index 0000000..442b0bc +--- /dev/null ++++ b/patch/bar_dwmblocks.c +@@ -0,0 +1,31 @@ ++static int dwmblockssig; ++pid_t dwmblockspid = 0; ++ ++int ++getdwmblockspid() ++{ ++ char buf[16]; ++ FILE *fp = popen("pidof -s dwmblocks", "r"); ++ if (fgets(buf, sizeof(buf), fp)); ++ pid_t pid = strtoul(buf, NULL, 10); ++ pclose(fp); ++ dwmblockspid = pid; ++ return pid != 0 ? 0 : -1; ++} ++ ++void ++sigdwmblocks(const Arg *arg) ++{ ++ union sigval sv; ++ sv.sival_int = (dwmblockssig << 8) | arg->i; ++ if (!dwmblockspid) ++ if (getdwmblockspid() == -1) ++ return; ++ ++ if (sigqueue(dwmblockspid, SIGUSR1, sv) == -1) { ++ if (errno == ESRCH) { ++ if (!getdwmblockspid()) ++ sigqueue(dwmblockspid, SIGUSR1, sv); ++ } ++ } ++} +\ No newline at end of file +diff --git a/patch/bar_dwmblocks.h b/patch/bar_dwmblocks.h +new file mode 100644 +index 0000000..f08f1d5 +--- /dev/null ++++ b/patch/bar_dwmblocks.h +@@ -0,0 +1,2 @@ ++static int getdwmblockspid(); ++static void sigdwmblocks(const Arg *arg); +\ No newline at end of file +diff --git a/patch/bar_status2d.c b/patch/bar_status2d.c +new file mode 100644 +index 0000000..6111a51 +--- /dev/null ++++ b/patch/bar_status2d.c +@@ -0,0 +1,171 @@ ++int ++width_status2d(Bar *bar, BarWidthArg *a) ++{ ++ return status2dtextlength(rawstext) + lrpad; ++} ++ ++int ++width_status2d_es(Bar *bar, BarWidthArg *a) ++{ ++ return status2dtextlength(rawestext); ++} ++ ++int ++draw_status2d(Bar *bar, BarDrawArg *a) ++{ ++ return drawstatusbar(a->x, rawstext); ++} ++ ++int ++draw_status2d_es(Bar *bar, BarDrawArg *a) ++{ ++ return drawstatusbar(a->x, rawestext); ++} ++ ++int ++drawstatusbar(int x, char* stext) ++{ ++ int i, w, len; ++ short isCode = 0; ++ char *text; ++ char *p; ++ ++ len = strlen(stext); ++ if (!(text = (char*) malloc(sizeof(char)*(len + 1)))) ++ die("malloc"); ++ p = text; ++ copyvalidchars(text, stext); ++ text[len] = '\0'; ++ ++ x += lrpad / 2; ++ drw_setscheme(drw, scheme[LENGTH(colors)]); ++ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; ++ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; ++ ++ /* process status text */ ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^' && !isCode) { ++ isCode = 1; ++ ++ text[i] = '\0'; ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ ++ x += w; ++ ++ /* process code */ ++ while (text[++i] != '^') { ++ if (text[i] == 'c') { ++ char buf[8]; ++ if (i + 7 > len - 1) { ++ i += 7; ++ len = 0; ++ break; ++ } ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColFg], buf, 0xff); ++ #elif BAR_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColFg], buf, alphas[SchemeNorm][ColFg]); ++ #else ++ drw_clr_create(drw, &drw->scheme[ColFg], buf); ++ #endif // BAR_ALPHA_PATCH ++ i += 7; ++ } else if (text[i] == 'b') { ++ char buf[8]; ++ if (i + 7 > len - 1) { ++ i += 7; ++ len = 0; ++ break; ++ } ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColBg], buf, 0xff); ++ #elif BAR_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColBg], buf, alphas[SchemeNorm][ColBg]); ++ #else ++ drw_clr_create(drw, &drw->scheme[ColBg], buf); ++ #endif // BAR_ALPHA_PATCH ++ i += 7; ++ } else if (text[i] == 'd') { ++ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; ++ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; ++ } else if (text[i] == 'r') { ++ int rx = atoi(text + ++i); ++ while (text[++i] != ','); ++ int ry = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rw = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rh = atoi(text + ++i); ++ ++ if (ry < 0) ++ ry = 0; ++ if (rx < 0) ++ rx = 0; ++ ++ drw_rect(drw, rx + x, ry, rw, rh, 1, 0); ++ } else if (text[i] == 'f') { ++ x += atoi(text + ++i); ++ } ++ } ++ ++ text = text + i + 1; ++ len -= i + 1; ++ i=-1; ++ isCode = 0; ++ } ++ } ++ if (!isCode && len) { ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ x += w; ++ } ++ free(p); ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ ++ return x; ++} ++ ++int ++status2dtextlength(char* stext) ++{ ++ int i, w, len; ++ short isCode = 0; ++ char *text; ++ char *p; ++ ++ len = strlen(stext) + 1; ++ if (!(text = (char*) malloc(sizeof(char)*len))) ++ die("malloc"); ++ p = text; ++ copyvalidchars(text, stext); ++ ++ /* compute width of the status text */ ++ w = 0; ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^') { ++ if (!isCode) { ++ isCode = 1; ++ text[i] = '\0'; ++ w += TEXTW(text) - lrpad; ++ text[i] = '^'; ++ if (text[++i] == 'f') ++ w += atoi(text + ++i); ++ } else { ++ isCode = 0; ++ text = text + i + 1; ++ i = -1; ++ } ++ } ++ } ++ if (!isCode) ++ w += TEXTW(text) - lrpad; ++ free(p); ++ return w; ++} +\ No newline at end of file +diff --git a/patch/bar_status2d.h b/patch/bar_status2d.h +new file mode 100644 +index 0000000..0830025 +--- /dev/null ++++ b/patch/bar_status2d.h +@@ -0,0 +1,6 @@ ++static int width_status2d(Bar *bar, BarWidthArg *a); ++static int width_status2d_es(Bar *bar, BarWidthArg *a); ++static int draw_status2d(Bar *bar, BarDrawArg *a); ++static int draw_status2d_es(Bar *bar, BarDrawArg *a); ++static int drawstatusbar(int x, char *text); ++static int status2dtextlength(char *stext); +\ No newline at end of file +diff --git a/patch/bar_statuscmd.c b/patch/bar_statuscmd.c +new file mode 100644 +index 0000000..28ce120 +--- /dev/null ++++ b/patch/bar_statuscmd.c +@@ -0,0 +1,49 @@ ++int ++click_statuscmd(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return click_statuscmd_text(arg, a->rel_x, rawstext); ++} ++ ++int ++click_statuscmd_es(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return click_statuscmd_text(arg, a->rel_x, rawestext); ++} ++ ++int ++click_statuscmd_text(Arg *arg, int rel_x, char *text) ++{ ++ int i = -1; ++ int x = 0; ++ char ch; ++ dwmblockssig = -1; ++ while (text[++i]) { ++ if ((unsigned char)text[i] < ' ') { ++ ch = text[i]; ++ text[i] = '\0'; ++ x += status2dtextlength(text); ++ text[i] = ch; ++ text += i+1; ++ i = -1; ++ if (x >= rel_x && dwmblockssig != -1) ++ break; ++ dwmblockssig = ch; ++ } ++ } ++ if (dwmblockssig == -1) ++ dwmblockssig = 0; ++ return ClkStatusText; ++} ++ ++void ++copyvalidchars(char *text, char *rawtext) ++{ ++ int i = -1, j = 0; ++ ++ while (rawtext[++i]) { ++ if ((unsigned char)rawtext[i] >= ' ') { ++ text[j++] = rawtext[i]; ++ } ++ } ++ text[j] = '\0'; ++} +diff --git a/patch/bar_statuscmd.h b/patch/bar_statuscmd.h +new file mode 100644 +index 0000000..d6d428e +--- /dev/null ++++ b/patch/bar_statuscmd.h +@@ -0,0 +1,4 @@ ++static int click_statuscmd(Bar *bar, Arg *arg, BarClickArg *a); ++static int click_statuscmd_es(Bar *bar, Arg *arg, BarClickArg *a); ++static int click_statuscmd_text(Arg *arg, int rel_x, char *text); ++static void copyvalidchars(char *text, char *rawtext); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..84bbd19 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -1,5 +1,8 @@ + /* Bar functionality */ + #include "bar_ltsymbol.c" +-#include "bar_status.c" ++//#include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_status2d.c" ++#include "bar_dwmblocks.c" ++#include "bar_statuscmd.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..78f7520 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -1,5 +1,8 @@ + /* Bar functionality */ + #include "bar_ltsymbol.h" +-#include "bar_status.h" ++//#include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_status2d.h" ++#include "bar_dwmblocks.h" ++#include "bar_statuscmd.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-status2d-statuscmd-dwmblocks-extrastatus-6.3_full.diff b/dwm/dwm-barmodules-status2d-statuscmd-dwmblocks-extrastatus-6.3_full.diff new file mode 100644 index 0000000..034677e --- /dev/null +++ b/dwm/dwm-barmodules-status2d-statuscmd-dwmblocks-extrastatus-6.3_full.diff @@ -0,0 +1,1357 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From 69ba99292c4eac2ffa2eb1f1962f55ce463b35db Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:10:28 +0100 +Subject: [PATCH 2/2] Adding status2d + statuscmd + dwmblocks + extrastatus + module + +--- + config.def.h | 11 ++- + dwm.c | 23 +++++- + patch/bar_dwmblocks.c | 31 ++++++++ + patch/bar_dwmblocks.h | 2 + + patch/bar_status2d.c | 171 ++++++++++++++++++++++++++++++++++++++++++ + patch/bar_status2d.h | 6 ++ + patch/bar_statuscmd.c | 49 ++++++++++++ + patch/bar_statuscmd.h | 4 + + patch/include.c | 7 +- + patch/include.h | 7 +- + 10 files changed, 301 insertions(+), 10 deletions(-) + create mode 100644 patch/bar_dwmblocks.c + create mode 100644 patch/bar_dwmblocks.h + create mode 100644 patch/bar_status2d.c + create mode 100644 patch/bar_status2d.h + create mode 100644 patch/bar_statuscmd.c + create mode 100644 patch/bar_statuscmd.h + +diff --git a/config.def.h b/config.def.h +index f870c41..af2aa90 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,7 @@ 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 */ ++static const char statussep = ';'; /* separator between status bars */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -47,8 +48,10 @@ static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, +- { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status2d, draw_status2d, click_statuscmd, "status2d" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++ { 0, 1, BAR_ALIGN_CENTER, width_status2d_es, draw_status2d_es, click_statuscmd_es, "status2d_es" }, ++ + }; + + /* layout(s) */ +@@ -124,7 +127,11 @@ static Button buttons[] = { + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, +- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, ++ { ClkStatusText, 0, Button1, sigdwmblocks, {.i = 1 } }, ++ { ClkStatusText, 0, Button2, sigdwmblocks, {.i = 2 } }, ++ { ClkStatusText, 0, Button3, sigdwmblocks, {.i = 3 } }, ++ { ClkStatusText, 0, Button4, sigdwmblocks, {.i = 4 } }, ++ { ClkStatusText, 0, Button5, sigdwmblocks, {.i = 5 } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, +diff --git a/dwm.c b/dwm.c +index 86763d8..9fd8b67 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -290,7 +290,10 @@ static void zoom(const Arg *arg); + #include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; +-static char stext[256]; ++static char stext[1024]; ++static char rawstext[1024]; ++static char estext[1024]; ++static char rawestext[1024]; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh; /* bar geometry */ +@@ -558,7 +561,7 @@ cleanup(void) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); +- for (i = 0; i < LENGTH(colors); i++) ++ for (i = 0; i < LENGTH(colors) + 1; i++) + free(scheme[i]); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); +@@ -1727,7 +1730,8 @@ setup(void) + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ +- scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); ++ scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *)); ++ scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], 3); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ +@@ -2185,8 +2189,19 @@ void + updatestatus(void) + { + Monitor *m; +- if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) ++ if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) { + strcpy(stext, "dwm-"VERSION); ++ estext[0] = '\0'; ++ } else { ++ char *e = strchr(rawstext, statussep); ++ if (e) { ++ *e = '\0'; e++; ++ strncpy(rawestext, e, sizeof(estext) - 1); ++ copyvalidchars(estext, rawestext); ++ } else ++ estext[0] = '\0'; ++ copyvalidchars(stext, rawstext); ++ } + for (m = mons; m; m = m->next) + drawbar(m); + } +diff --git a/patch/bar_dwmblocks.c b/patch/bar_dwmblocks.c +new file mode 100644 +index 0000000..442b0bc +--- /dev/null ++++ b/patch/bar_dwmblocks.c +@@ -0,0 +1,31 @@ ++static int dwmblockssig; ++pid_t dwmblockspid = 0; ++ ++int ++getdwmblockspid() ++{ ++ char buf[16]; ++ FILE *fp = popen("pidof -s dwmblocks", "r"); ++ if (fgets(buf, sizeof(buf), fp)); ++ pid_t pid = strtoul(buf, NULL, 10); ++ pclose(fp); ++ dwmblockspid = pid; ++ return pid != 0 ? 0 : -1; ++} ++ ++void ++sigdwmblocks(const Arg *arg) ++{ ++ union sigval sv; ++ sv.sival_int = (dwmblockssig << 8) | arg->i; ++ if (!dwmblockspid) ++ if (getdwmblockspid() == -1) ++ return; ++ ++ if (sigqueue(dwmblockspid, SIGUSR1, sv) == -1) { ++ if (errno == ESRCH) { ++ if (!getdwmblockspid()) ++ sigqueue(dwmblockspid, SIGUSR1, sv); ++ } ++ } ++} +\ No newline at end of file +diff --git a/patch/bar_dwmblocks.h b/patch/bar_dwmblocks.h +new file mode 100644 +index 0000000..f08f1d5 +--- /dev/null ++++ b/patch/bar_dwmblocks.h +@@ -0,0 +1,2 @@ ++static int getdwmblockspid(); ++static void sigdwmblocks(const Arg *arg); +\ No newline at end of file +diff --git a/patch/bar_status2d.c b/patch/bar_status2d.c +new file mode 100644 +index 0000000..6111a51 +--- /dev/null ++++ b/patch/bar_status2d.c +@@ -0,0 +1,171 @@ ++int ++width_status2d(Bar *bar, BarWidthArg *a) ++{ ++ return status2dtextlength(rawstext) + lrpad; ++} ++ ++int ++width_status2d_es(Bar *bar, BarWidthArg *a) ++{ ++ return status2dtextlength(rawestext); ++} ++ ++int ++draw_status2d(Bar *bar, BarDrawArg *a) ++{ ++ return drawstatusbar(a->x, rawstext); ++} ++ ++int ++draw_status2d_es(Bar *bar, BarDrawArg *a) ++{ ++ return drawstatusbar(a->x, rawestext); ++} ++ ++int ++drawstatusbar(int x, char* stext) ++{ ++ int i, w, len; ++ short isCode = 0; ++ char *text; ++ char *p; ++ ++ len = strlen(stext); ++ if (!(text = (char*) malloc(sizeof(char)*(len + 1)))) ++ die("malloc"); ++ p = text; ++ copyvalidchars(text, stext); ++ text[len] = '\0'; ++ ++ x += lrpad / 2; ++ drw_setscheme(drw, scheme[LENGTH(colors)]); ++ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; ++ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; ++ ++ /* process status text */ ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^' && !isCode) { ++ isCode = 1; ++ ++ text[i] = '\0'; ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ ++ x += w; ++ ++ /* process code */ ++ while (text[++i] != '^') { ++ if (text[i] == 'c') { ++ char buf[8]; ++ if (i + 7 > len - 1) { ++ i += 7; ++ len = 0; ++ break; ++ } ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColFg], buf, 0xff); ++ #elif BAR_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColFg], buf, alphas[SchemeNorm][ColFg]); ++ #else ++ drw_clr_create(drw, &drw->scheme[ColFg], buf); ++ #endif // BAR_ALPHA_PATCH ++ i += 7; ++ } else if (text[i] == 'b') { ++ char buf[8]; ++ if (i + 7 > len - 1) { ++ i += 7; ++ len = 0; ++ break; ++ } ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColBg], buf, 0xff); ++ #elif BAR_ALPHA_PATCH ++ drw_clr_create(drw, &drw->scheme[ColBg], buf, alphas[SchemeNorm][ColBg]); ++ #else ++ drw_clr_create(drw, &drw->scheme[ColBg], buf); ++ #endif // BAR_ALPHA_PATCH ++ i += 7; ++ } else if (text[i] == 'd') { ++ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; ++ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; ++ } else if (text[i] == 'r') { ++ int rx = atoi(text + ++i); ++ while (text[++i] != ','); ++ int ry = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rw = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rh = atoi(text + ++i); ++ ++ if (ry < 0) ++ ry = 0; ++ if (rx < 0) ++ rx = 0; ++ ++ drw_rect(drw, rx + x, ry, rw, rh, 1, 0); ++ } else if (text[i] == 'f') { ++ x += atoi(text + ++i); ++ } ++ } ++ ++ text = text + i + 1; ++ len -= i + 1; ++ i=-1; ++ isCode = 0; ++ } ++ } ++ if (!isCode && len) { ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ x += w; ++ } ++ free(p); ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ ++ return x; ++} ++ ++int ++status2dtextlength(char* stext) ++{ ++ int i, w, len; ++ short isCode = 0; ++ char *text; ++ char *p; ++ ++ len = strlen(stext) + 1; ++ if (!(text = (char*) malloc(sizeof(char)*len))) ++ die("malloc"); ++ p = text; ++ copyvalidchars(text, stext); ++ ++ /* compute width of the status text */ ++ w = 0; ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^') { ++ if (!isCode) { ++ isCode = 1; ++ text[i] = '\0'; ++ w += TEXTW(text) - lrpad; ++ text[i] = '^'; ++ if (text[++i] == 'f') ++ w += atoi(text + ++i); ++ } else { ++ isCode = 0; ++ text = text + i + 1; ++ i = -1; ++ } ++ } ++ } ++ if (!isCode) ++ w += TEXTW(text) - lrpad; ++ free(p); ++ return w; ++} +\ No newline at end of file +diff --git a/patch/bar_status2d.h b/patch/bar_status2d.h +new file mode 100644 +index 0000000..0830025 +--- /dev/null ++++ b/patch/bar_status2d.h +@@ -0,0 +1,6 @@ ++static int width_status2d(Bar *bar, BarWidthArg *a); ++static int width_status2d_es(Bar *bar, BarWidthArg *a); ++static int draw_status2d(Bar *bar, BarDrawArg *a); ++static int draw_status2d_es(Bar *bar, BarDrawArg *a); ++static int drawstatusbar(int x, char *text); ++static int status2dtextlength(char *stext); +\ No newline at end of file +diff --git a/patch/bar_statuscmd.c b/patch/bar_statuscmd.c +new file mode 100644 +index 0000000..28ce120 +--- /dev/null ++++ b/patch/bar_statuscmd.c +@@ -0,0 +1,49 @@ ++int ++click_statuscmd(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return click_statuscmd_text(arg, a->rel_x, rawstext); ++} ++ ++int ++click_statuscmd_es(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return click_statuscmd_text(arg, a->rel_x, rawestext); ++} ++ ++int ++click_statuscmd_text(Arg *arg, int rel_x, char *text) ++{ ++ int i = -1; ++ int x = 0; ++ char ch; ++ dwmblockssig = -1; ++ while (text[++i]) { ++ if ((unsigned char)text[i] < ' ') { ++ ch = text[i]; ++ text[i] = '\0'; ++ x += status2dtextlength(text); ++ text[i] = ch; ++ text += i+1; ++ i = -1; ++ if (x >= rel_x && dwmblockssig != -1) ++ break; ++ dwmblockssig = ch; ++ } ++ } ++ if (dwmblockssig == -1) ++ dwmblockssig = 0; ++ return ClkStatusText; ++} ++ ++void ++copyvalidchars(char *text, char *rawtext) ++{ ++ int i = -1, j = 0; ++ ++ while (rawtext[++i]) { ++ if ((unsigned char)rawtext[i] >= ' ') { ++ text[j++] = rawtext[i]; ++ } ++ } ++ text[j] = '\0'; ++} +diff --git a/patch/bar_statuscmd.h b/patch/bar_statuscmd.h +new file mode 100644 +index 0000000..d6d428e +--- /dev/null ++++ b/patch/bar_statuscmd.h +@@ -0,0 +1,4 @@ ++static int click_statuscmd(Bar *bar, Arg *arg, BarClickArg *a); ++static int click_statuscmd_es(Bar *bar, Arg *arg, BarClickArg *a); ++static int click_statuscmd_text(Arg *arg, int rel_x, char *text); ++static void copyvalidchars(char *text, char *rawtext); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..84bbd19 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -1,5 +1,8 @@ + /* Bar functionality */ + #include "bar_ltsymbol.c" +-#include "bar_status.c" ++//#include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_status2d.c" ++#include "bar_dwmblocks.c" ++#include "bar_statuscmd.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..78f7520 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -1,5 +1,8 @@ + /* Bar functionality */ + #include "bar_ltsymbol.h" +-#include "bar_status.h" ++//#include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_status2d.h" ++#include "bar_dwmblocks.h" ++#include "bar_statuscmd.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-statusbutton-6.3.diff b/dwm/dwm-barmodules-statusbutton-6.3.diff new file mode 100644 index 0000000..67ef2a9 --- /dev/null +++ b/dwm/dwm-barmodules-statusbutton-6.3.diff @@ -0,0 +1,127 @@ +From 941d14787a5f49e5fe1be516a2b30858110ec35d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:11:09 +0100 +Subject: [PATCH 2/2] Adding statusbutton module + +--- + config.def.h | 5 ++++- + dwm.c | 2 +- + patch/bar_statusbutton.c | 21 +++++++++++++++++++++ + patch/bar_statusbutton.h | 3 +++ + patch/include.c | 3 ++- + patch/include.h | 3 ++- + 6 files changed, 33 insertions(+), 4 deletions(-) + create mode 100644 patch/bar_statusbutton.c + create mode 100644 patch/bar_statusbutton.h + +diff --git a/config.def.h b/config.def.h +index f870c41..14d47ad 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,7 @@ 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 */ ++static const char buttonbar[] = ""; + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -45,6 +46,7 @@ static const Rule rules[] = { + */ + static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_stbutton, draw_stbutton, click_stbutton, "statusbutton" }, + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, +@@ -118,9 +120,10 @@ static Key keys[] = { + }; + + /* button definitions */ +-/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ ++/* click can be ClkButton, ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ + static Button buttons[] = { + /* click event mask button function argument */ ++ { ClkButton, 0, Button1, spawn, {.v = dmenucmd } }, + { 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 86763d8..de465bc 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -65,7 +65,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 { ClkButton, ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + enum { + BAR_ALIGN_LEFT, +diff --git a/patch/bar_statusbutton.c b/patch/bar_statusbutton.c +new file mode 100644 +index 0000000..f5c9c9d +--- /dev/null ++++ b/patch/bar_statusbutton.c +@@ -0,0 +1,21 @@ ++int ++width_stbutton(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(buttonbar); ++} ++ ++int ++draw_stbutton(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_PANGO_PATCH ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, buttonbar, 0, False); ++ #else ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, buttonbar, 0); ++ #endif // BAR_PANGO_PATCH ++} ++ ++int ++click_stbutton(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkButton; ++} +diff --git a/patch/bar_statusbutton.h b/patch/bar_statusbutton.h +new file mode 100644 +index 0000000..8e9d6fe +--- /dev/null ++++ b/patch/bar_statusbutton.h +@@ -0,0 +1,3 @@ ++static int width_stbutton(Bar *bar, BarWidthArg *a); ++static int draw_stbutton(Bar *bar, BarDrawArg *a); ++static int click_stbutton(Bar *bar, Arg *arg, BarClickArg *a); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..b731f2e 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_statusbutton.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..c2a1127 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_statusbutton.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-statusbutton-6.3_full.diff b/dwm/dwm-barmodules-statusbutton-6.3_full.diff new file mode 100644 index 0000000..13f3836 --- /dev/null +++ b/dwm/dwm-barmodules-statusbutton-6.3_full.diff @@ -0,0 +1,1025 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From 941d14787a5f49e5fe1be516a2b30858110ec35d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:11:09 +0100 +Subject: [PATCH 2/2] Adding statusbutton module + +--- + config.def.h | 5 ++++- + dwm.c | 2 +- + patch/bar_statusbutton.c | 21 +++++++++++++++++++++ + patch/bar_statusbutton.h | 3 +++ + patch/include.c | 3 ++- + patch/include.h | 3 ++- + 6 files changed, 33 insertions(+), 4 deletions(-) + create mode 100644 patch/bar_statusbutton.c + create mode 100644 patch/bar_statusbutton.h + +diff --git a/config.def.h b/config.def.h +index f870c41..14d47ad 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,7 @@ 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 */ ++static const char buttonbar[] = ""; + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -45,6 +46,7 @@ static const Rule rules[] = { + */ + static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_stbutton, draw_stbutton, click_stbutton, "statusbutton" }, + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, +@@ -118,9 +120,10 @@ static Key keys[] = { + }; + + /* button definitions */ +-/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ ++/* click can be ClkButton, ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ + static Button buttons[] = { + /* click event mask button function argument */ ++ { ClkButton, 0, Button1, spawn, {.v = dmenucmd } }, + { 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 86763d8..de465bc 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -65,7 +65,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 { ClkButton, ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + enum { + BAR_ALIGN_LEFT, +diff --git a/patch/bar_statusbutton.c b/patch/bar_statusbutton.c +new file mode 100644 +index 0000000..f5c9c9d +--- /dev/null ++++ b/patch/bar_statusbutton.c +@@ -0,0 +1,21 @@ ++int ++width_stbutton(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(buttonbar); ++} ++ ++int ++draw_stbutton(Bar *bar, BarDrawArg *a) ++{ ++ #if BAR_PANGO_PATCH ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, buttonbar, 0, False); ++ #else ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, buttonbar, 0); ++ #endif // BAR_PANGO_PATCH ++} ++ ++int ++click_stbutton(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkButton; ++} +diff --git a/patch/bar_statusbutton.h b/patch/bar_statusbutton.h +new file mode 100644 +index 0000000..8e9d6fe +--- /dev/null ++++ b/patch/bar_statusbutton.h +@@ -0,0 +1,3 @@ ++static int width_stbutton(Bar *bar, BarWidthArg *a); ++static int draw_stbutton(Bar *bar, BarDrawArg *a); ++static int click_stbutton(Bar *bar, Arg *arg, BarClickArg *a); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..b731f2e 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_statusbutton.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..c2a1127 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_statusbutton.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-systray-6.3.diff b/dwm/dwm-barmodules-systray-6.3.diff new file mode 100644 index 0000000..23db74b --- /dev/null +++ b/dwm/dwm-barmodules-systray-6.3.diff @@ -0,0 +1,561 @@ +From a44a11168d6823a5b725242dfb5480b5be2f4b91 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:11:51 +0100 +Subject: [PATCH 2/2] Adding systray module + +--- + config.def.h | 3 + + dwm.c | 131 ++++++++++++++++++++++++++---- + patch/bar_systray.c | 190 ++++++++++++++++++++++++++++++++++++++++++++ + patch/bar_systray.h | 40 ++++++++++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 6 files changed, 353 insertions(+), 17 deletions(-) + create mode 100644 patch/bar_systray.c + create mode 100644 patch/bar_systray.h + +diff --git a/config.def.h b/config.def.h +index f870c41..a22f507 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ 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 */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int showsystray = 1; /* 0 means no systray */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -47,6 +49,7 @@ static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_systray, draw_systray, click_systray, "systray" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, + }; +diff --git a/dwm.c b/dwm.c +index 86763d8..6875b06 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -63,6 +63,8 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, ++ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, ++ NetSystemTrayVisual, NetWMWindowTypeDock, NetSystemTrayOrientationHorz, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, +@@ -247,7 +249,7 @@ static void resizemouse(const Arg *arg); + static void restack(Monitor *m); + static void run(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); +@@ -311,9 +313,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -556,6 +559,15 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ if (showsystray && systray) { ++ while (systray->icons) ++ removesystrayicon(systray->icons); ++ if (systray->win) { ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ } ++ free(systray); ++ } + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -591,9 +603,48 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && systray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ if (!(c->win = cme->data.l[2])) { ++ free(c); ++ return; ++ } ++ ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ XGetWindowAttributes(dpy, c->win, &wa); ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ setclientstate(c, NormalState); ++ } ++ return; ++ } ++ + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -756,6 +807,10 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if (showsystray && (c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ drawbarwin(systray->bar); ++ } + } + + void +@@ -1022,9 +1077,15 @@ getatomprop(Client *c, Atom prop) + unsigned char *p = NULL; + Atom da, atom = None; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; ++ ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; +@@ -1162,7 +1223,7 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1248,9 +1309,15 @@ mappingnotify(XEvent *e) + void + maprequest(XEvent *e) + { ++ Client *i; + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + ++ if (showsystray && systray && (i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ drawbarwin(systray->bar); ++ } ++ + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if (wa.override_redirect) +@@ -1374,6 +1441,16 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + ++ if (showsystray && (c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } ++ else ++ updatesystrayiconstate(c, ev); ++ drawbarwin(systray->bar); ++ } ++ + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) +@@ -1593,26 +1670,36 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; + Atom *protocols; ++ Atom mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ } else { ++ exists = True; ++ mt = proto; + } + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1626,7 +1713,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1715,6 +1802,15 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); ++ netatom[NetSystemTrayVisual] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_VISUAL", False); ++ netatom[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); +@@ -1959,6 +2055,11 @@ unmapnotify(XEvent *e) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); ++ } else if (showsystray && (c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ drawbarwin(systray->bar); + } + } + +diff --git a/patch/bar_systray.c b/patch/bar_systray.c +new file mode 100644 +index 0000000..3ae2e56 +--- /dev/null ++++ b/patch/bar_systray.c +@@ -0,0 +1,190 @@ ++static Systray *systray = NULL; ++static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; ++ ++int ++width_systray(Bar *bar, BarWidthArg *a) ++{ ++ unsigned int w = 0; ++ Client *i; ++ if (!systray) ++ return 1; ++ if (showsystray) ++ for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next); ++ return w ? w + lrpad - systrayspacing : 0; ++} ++ ++int ++draw_systray(Bar *bar, BarDrawArg *a) ++{ ++ if (!showsystray) { ++ if (systray) ++ XMoveWindow(dpy, systray->win, -500, bar->by); ++ return a->x; ++ } ++ ++ XSetWindowAttributes wa; ++ Client *i; ++ unsigned int w; ++ ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ ++ wa.override_redirect = True; ++ wa.event_mask = ButtonPressMask|ExposureMask; ++ wa.border_pixel = 0; ++ #if BAR_ALPHA_PATCH ++ wa.background_pixel = 0; ++ wa.colormap = cmap; ++ systray->win = XCreateWindow(dpy, root, bar->bx + a->x + lrpad / 2, bar->by, MAX(a->w + 40, 1), bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBorderPixel|CWBackPixel|CWColormap|CWEventMask, &wa); // CWBackPixmap ++ #else ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ systray->win = XCreateSimpleWindow(dpy, root, bar->bx + a->x + lrpad / 2, bar->by, MIN(a->w, 1), bar->bh, 0, 0, scheme[SchemeNorm][ColBg].pixel); ++ XChangeWindowAttributes(dpy, systray->win, CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&systrayorientation, 1); ++ #if BAR_ALPHA_PATCH ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayVisual], XA_VISUALID, 32, ++ PropModeReplace, (unsigned char *)&visual->visualid, 1); ++ #endif // BAR_ALPHA_PATCH ++ XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1); ++ XMapRaised(dpy, systray->win); ++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); ++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { ++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); ++ XSync(dpy, False); ++ } else { ++ fprintf(stderr, "dwm: unable to obtain system tray.\n"); ++ free(systray); ++ systray = NULL; ++ return a->x; ++ } ++ } ++ ++ systray->bar = bar; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ #if BAR_ALPHA_PATCH ++ wa.background_pixel = 0; ++ #else ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ #endif // BAR_ALPHA_PATCH ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->next) ++ w += systrayspacing; ++ if (i->mon != bar->mon) ++ i->mon = bar->mon; ++ } ++ ++ XMoveResizeWindow(dpy, systray->win, bar->bx + a->x + lrpad / 2, (w ? bar->by : -bar->by), MAX(w, 1), bar->bh); ++ return a->x + a->w; ++} ++ ++int ++click_systray(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return -1; ++} ++ ++void ++removesystrayicon(Client *i) ++{ ++ Client **ii; ++ ++ if (!showsystray || !i) ++ return; ++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); ++ if (ii) ++ *ii = i->next; ++ free(i); ++ drawbarwin(systray->bar); ++} ++ ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ drawbarwin(systray->bar); ++ } ++} ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((float)w / (float)h)); ++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); ++ /* force icons into the systray dimensions if they don't want to */ ++ if (i->h > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ if (i->w > 2*bh) ++ i->w = bh; ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !systray || !i || ev->atom != xatom[XembedInfo] || ++ !(flags = getatomprop(i, xatom[XembedInfo]))) ++ return; ++ ++ if (flags & XEMBED_MAPPED && !i->tags) { ++ i->tags = 1; ++ code = XEMBED_WINDOW_ACTIVATE; ++ XMapRaised(dpy, i->win); ++ setclientstate(i, NormalState); ++ } ++ else if (!(flags & XEMBED_MAPPED) && i->tags) { ++ i->tags = 0; ++ code = XEMBED_WINDOW_DEACTIVATE; ++ XUnmapWindow(dpy, i->win); ++ setclientstate(i, WithdrawnState); ++ } ++ else ++ return; ++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, ++ systray->win, XEMBED_EMBEDDED_VERSION); ++} ++ ++Client * ++wintosystrayicon(Window w) ++{ ++ if (!systray) ++ return NULL; ++ Client *i = NULL; ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next); ++ return i; ++} +diff --git a/patch/bar_systray.h b/patch/bar_systray.h +new file mode 100644 +index 0000000..5123a73 +--- /dev/null ++++ b/patch/bar_systray.h +@@ -0,0 +1,40 @@ ++#define SYSTEM_TRAY_REQUEST_DOCK 0 ++#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 ++ ++/* XEMBED messages */ ++#define XEMBED_EMBEDDED_NOTIFY 0 ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_FOCUS_IN 4 ++#define XEMBED_MODALITY_ON 10 ++ ++#define XEMBED_MAPPED (1 << 0) ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_WINDOW_DEACTIVATE 2 ++ ++#define VERSION_MAJOR 0 ++#define VERSION_MINOR 0 ++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR ++ ++/* enums */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ ++ ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++ Bar *bar; ++}; ++ ++/* bar integration */ ++static int width_systray(Bar *bar, BarWidthArg *a); ++static int draw_systray(Bar *bar, BarDrawArg *a); ++static int click_systray(Bar *bar, Arg *arg, BarClickArg *a); ++ ++/* function declarations */ ++static Atom getatomprop(Client *c, Atom prop); ++static void removesystrayicon(Client *i); ++static void resizerequest(XEvent *e); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); ++static Client *wintosystrayicon(Window w); ++ +diff --git a/patch/include.c b/patch/include.c +index d422f56..c82b392 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_systray.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..c01916a 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_systray.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-systray-6.3_full.diff b/dwm/dwm-barmodules-systray-6.3_full.diff new file mode 100644 index 0000000..42a371c --- /dev/null +++ b/dwm/dwm-barmodules-systray-6.3_full.diff @@ -0,0 +1,1459 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From a44a11168d6823a5b725242dfb5480b5be2f4b91 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:11:51 +0100 +Subject: [PATCH 2/2] Adding systray module + +--- + config.def.h | 3 + + dwm.c | 131 ++++++++++++++++++++++++++---- + patch/bar_systray.c | 190 ++++++++++++++++++++++++++++++++++++++++++++ + patch/bar_systray.h | 40 ++++++++++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 6 files changed, 353 insertions(+), 17 deletions(-) + create mode 100644 patch/bar_systray.c + create mode 100644 patch/bar_systray.h + +diff --git a/config.def.h b/config.def.h +index f870c41..a22f507 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ 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 */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int showsystray = 1; /* 0 means no systray */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -47,6 +49,7 @@ static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_systray, draw_systray, click_systray, "systray" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, + }; +diff --git a/dwm.c b/dwm.c +index 86763d8..6875b06 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -63,6 +63,8 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, ++ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, ++ NetSystemTrayVisual, NetWMWindowTypeDock, NetSystemTrayOrientationHorz, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, +@@ -247,7 +249,7 @@ static void resizemouse(const Arg *arg); + static void restack(Monitor *m); + static void run(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); +@@ -311,9 +313,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -556,6 +559,15 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ if (showsystray && systray) { ++ while (systray->icons) ++ removesystrayicon(systray->icons); ++ if (systray->win) { ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ } ++ free(systray); ++ } + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -591,9 +603,48 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && systray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ if (!(c->win = cme->data.l[2])) { ++ free(c); ++ return; ++ } ++ ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ XGetWindowAttributes(dpy, c->win, &wa); ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ setclientstate(c, NormalState); ++ } ++ return; ++ } ++ + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -756,6 +807,10 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if (showsystray && (c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ drawbarwin(systray->bar); ++ } + } + + void +@@ -1022,9 +1077,15 @@ getatomprop(Client *c, Atom prop) + unsigned char *p = NULL; + Atom da, atom = None; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; ++ ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; +@@ -1162,7 +1223,7 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1248,9 +1309,15 @@ mappingnotify(XEvent *e) + void + maprequest(XEvent *e) + { ++ Client *i; + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + ++ if (showsystray && systray && (i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ drawbarwin(systray->bar); ++ } ++ + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if (wa.override_redirect) +@@ -1374,6 +1441,16 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + ++ if (showsystray && (c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } ++ else ++ updatesystrayiconstate(c, ev); ++ drawbarwin(systray->bar); ++ } ++ + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) +@@ -1593,26 +1670,36 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; + Atom *protocols; ++ Atom mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ } else { ++ exists = True; ++ mt = proto; + } + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1626,7 +1713,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1715,6 +1802,15 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); ++ netatom[NetSystemTrayVisual] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_VISUAL", False); ++ netatom[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); +@@ -1959,6 +2055,11 @@ unmapnotify(XEvent *e) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); ++ } else if (showsystray && (c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ drawbarwin(systray->bar); + } + } + +diff --git a/patch/bar_systray.c b/patch/bar_systray.c +new file mode 100644 +index 0000000..3ae2e56 +--- /dev/null ++++ b/patch/bar_systray.c +@@ -0,0 +1,190 @@ ++static Systray *systray = NULL; ++static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; ++ ++int ++width_systray(Bar *bar, BarWidthArg *a) ++{ ++ unsigned int w = 0; ++ Client *i; ++ if (!systray) ++ return 1; ++ if (showsystray) ++ for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next); ++ return w ? w + lrpad - systrayspacing : 0; ++} ++ ++int ++draw_systray(Bar *bar, BarDrawArg *a) ++{ ++ if (!showsystray) { ++ if (systray) ++ XMoveWindow(dpy, systray->win, -500, bar->by); ++ return a->x; ++ } ++ ++ XSetWindowAttributes wa; ++ Client *i; ++ unsigned int w; ++ ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ ++ wa.override_redirect = True; ++ wa.event_mask = ButtonPressMask|ExposureMask; ++ wa.border_pixel = 0; ++ #if BAR_ALPHA_PATCH ++ wa.background_pixel = 0; ++ wa.colormap = cmap; ++ systray->win = XCreateWindow(dpy, root, bar->bx + a->x + lrpad / 2, bar->by, MAX(a->w + 40, 1), bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBorderPixel|CWBackPixel|CWColormap|CWEventMask, &wa); // CWBackPixmap ++ #else ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ systray->win = XCreateSimpleWindow(dpy, root, bar->bx + a->x + lrpad / 2, bar->by, MIN(a->w, 1), bar->bh, 0, 0, scheme[SchemeNorm][ColBg].pixel); ++ XChangeWindowAttributes(dpy, systray->win, CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&systrayorientation, 1); ++ #if BAR_ALPHA_PATCH ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayVisual], XA_VISUALID, 32, ++ PropModeReplace, (unsigned char *)&visual->visualid, 1); ++ #endif // BAR_ALPHA_PATCH ++ XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1); ++ XMapRaised(dpy, systray->win); ++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); ++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { ++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); ++ XSync(dpy, False); ++ } else { ++ fprintf(stderr, "dwm: unable to obtain system tray.\n"); ++ free(systray); ++ systray = NULL; ++ return a->x; ++ } ++ } ++ ++ systray->bar = bar; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ #if BAR_ALPHA_PATCH ++ wa.background_pixel = 0; ++ #else ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ #endif // BAR_ALPHA_PATCH ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->next) ++ w += systrayspacing; ++ if (i->mon != bar->mon) ++ i->mon = bar->mon; ++ } ++ ++ XMoveResizeWindow(dpy, systray->win, bar->bx + a->x + lrpad / 2, (w ? bar->by : -bar->by), MAX(w, 1), bar->bh); ++ return a->x + a->w; ++} ++ ++int ++click_systray(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return -1; ++} ++ ++void ++removesystrayicon(Client *i) ++{ ++ Client **ii; ++ ++ if (!showsystray || !i) ++ return; ++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); ++ if (ii) ++ *ii = i->next; ++ free(i); ++ drawbarwin(systray->bar); ++} ++ ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ drawbarwin(systray->bar); ++ } ++} ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((float)w / (float)h)); ++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); ++ /* force icons into the systray dimensions if they don't want to */ ++ if (i->h > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ if (i->w > 2*bh) ++ i->w = bh; ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !systray || !i || ev->atom != xatom[XembedInfo] || ++ !(flags = getatomprop(i, xatom[XembedInfo]))) ++ return; ++ ++ if (flags & XEMBED_MAPPED && !i->tags) { ++ i->tags = 1; ++ code = XEMBED_WINDOW_ACTIVATE; ++ XMapRaised(dpy, i->win); ++ setclientstate(i, NormalState); ++ } ++ else if (!(flags & XEMBED_MAPPED) && i->tags) { ++ i->tags = 0; ++ code = XEMBED_WINDOW_DEACTIVATE; ++ XUnmapWindow(dpy, i->win); ++ setclientstate(i, WithdrawnState); ++ } ++ else ++ return; ++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, ++ systray->win, XEMBED_EMBEDDED_VERSION); ++} ++ ++Client * ++wintosystrayicon(Window w) ++{ ++ if (!systray) ++ return NULL; ++ Client *i = NULL; ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next); ++ return i; ++} +diff --git a/patch/bar_systray.h b/patch/bar_systray.h +new file mode 100644 +index 0000000..5123a73 +--- /dev/null ++++ b/patch/bar_systray.h +@@ -0,0 +1,40 @@ ++#define SYSTEM_TRAY_REQUEST_DOCK 0 ++#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 ++ ++/* XEMBED messages */ ++#define XEMBED_EMBEDDED_NOTIFY 0 ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_FOCUS_IN 4 ++#define XEMBED_MODALITY_ON 10 ++ ++#define XEMBED_MAPPED (1 << 0) ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_WINDOW_DEACTIVATE 2 ++ ++#define VERSION_MAJOR 0 ++#define VERSION_MINOR 0 ++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR ++ ++/* enums */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ ++ ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++ Bar *bar; ++}; ++ ++/* bar integration */ ++static int width_systray(Bar *bar, BarWidthArg *a); ++static int draw_systray(Bar *bar, BarDrawArg *a); ++static int click_systray(Bar *bar, Arg *arg, BarClickArg *a); ++ ++/* function declarations */ ++static Atom getatomprop(Client *c, Atom prop); ++static void removesystrayicon(Client *i); ++static void resizerequest(XEvent *e); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); ++static Client *wintosystrayicon(Window w); ++ +diff --git a/patch/include.c b/patch/include.c +index d422f56..c82b392 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_systray.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..c01916a 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_systray.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-taggrid-6.3.diff b/dwm/dwm-barmodules-taggrid-6.3.diff new file mode 100644 index 0000000..20b80eb --- /dev/null +++ b/dwm/dwm-barmodules-taggrid-6.3.diff @@ -0,0 +1,260 @@ +From 3b3064354ad90f18beb933b3d2b5ecf892201633 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:12:28 +0100 +Subject: [PATCH 2/2] Adding taggrid module + +--- + config.def.h | 25 ++++++++ + patch/bar_taggrid.c | 148 ++++++++++++++++++++++++++++++++++++++++++++ + patch/bar_taggrid.h | 4 ++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 5 files changed, 181 insertions(+), 2 deletions(-) + create mode 100644 patch/bar_taggrid.c + create mode 100644 patch/bar_taggrid.h + +diff --git a/config.def.h b/config.def.h +index f870c41..6d5e172 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -21,6 +21,22 @@ static const char *colors[][3] = { + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + ++/* grid of tags */ ++#define DRAWCLASSICTAGS 1 << 0 ++#define DRAWTAGGRID 1 << 1 ++ ++#define SWITCHTAG_UP 1 << 0 ++#define SWITCHTAG_DOWN 1 << 1 ++#define SWITCHTAG_LEFT 1 << 2 ++#define SWITCHTAG_RIGHT 1 << 3 ++#define SWITCHTAG_TOGGLETAG 1 << 4 ++#define SWITCHTAG_TAG 1 << 5 ++#define SWITCHTAG_VIEW 1 << 6 ++#define SWITCHTAG_TOGGLEVIEW 1 << 7 ++ ++static const unsigned int drawtagmask = DRAWTAGGRID; /* | DRAWCLASSICTAGS to show classic row of tags */ ++static const int tagrows = 2; ++ + static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class +@@ -46,6 +62,7 @@ static const Rule rules[] = { + static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_taggrid, draw_taggrid, click_taggrid, "taggrid" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, +@@ -101,6 +118,14 @@ static Key keys[] = { + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, ++ { MODKEY|ControlMask, XK_Up, switchtag, { .ui = SWITCHTAG_UP | SWITCHTAG_VIEW } }, ++ { MODKEY|ControlMask, XK_Down, switchtag, { .ui = SWITCHTAG_DOWN | SWITCHTAG_VIEW } }, ++ { MODKEY|ControlMask, XK_Right, switchtag, { .ui = SWITCHTAG_RIGHT | SWITCHTAG_VIEW } }, ++ { MODKEY|ControlMask, XK_Left, switchtag, { .ui = SWITCHTAG_LEFT | SWITCHTAG_VIEW } }, ++ { MODKEY|Mod4Mask, XK_Up, switchtag, { .ui = SWITCHTAG_UP | SWITCHTAG_TAG | SWITCHTAG_VIEW } }, ++ { MODKEY|Mod4Mask, XK_Down, switchtag, { .ui = SWITCHTAG_DOWN | SWITCHTAG_TAG | SWITCHTAG_VIEW } }, ++ { MODKEY|Mod4Mask, XK_Right, switchtag, { .ui = SWITCHTAG_RIGHT | SWITCHTAG_TAG | SWITCHTAG_VIEW } }, ++ { MODKEY|Mod4Mask, XK_Left, switchtag, { .ui = SWITCHTAG_LEFT | SWITCHTAG_TAG | SWITCHTAG_VIEW } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, +diff --git a/patch/bar_taggrid.c b/patch/bar_taggrid.c +new file mode 100644 +index 0000000..53046fa +--- /dev/null ++++ b/patch/bar_taggrid.c +@@ -0,0 +1,148 @@ ++int ++width_taggrid(Bar *bar, BarWidthArg *a) ++{ ++ return (bh / 2) * (LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0)) + lrpad; ++} ++ ++int ++draw_taggrid(Bar *bar, BarDrawArg *a) ++{ ++ unsigned int x, y, h, max_x = 0, columns, occ = 0; ++ int invert, i,j, k; ++ Client *c; ++ ++ for (c = bar->mon->clients; c; c = c->next) ++ occ |= c->tags; ++ ++ max_x = x = a->x + lrpad / 2; ++ h = bh / tagrows; ++ y = 0; ++ columns = LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0); ++ ++ /* Firstly we will fill the borders of squares */ ++ XSetForeground(drw->dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, drw->drawable, drw->gc, x, y, h*columns + 1, bh); ++ ++ /* We will draw LENGTH(tags) squares in tagraws raws. */ ++ for (j = 0, i = 0; j < tagrows; j++) { ++ x = a->x + lrpad / 2; ++ for (k = 0; k < columns; k++, i++) { ++ if (i < LENGTH(tags)) { ++ invert = bar->mon->tagset[bar->mon->seltags] & 1 << i ? 0 : 1; ++ ++ /* Select active color for current square */ ++ XSetForeground(drw->dpy, drw->gc, !invert ? scheme[SchemeSel][ColBg].pixel : ++ scheme[SchemeNorm][ColFg].pixel); ++ XFillRectangle(dpy, drw->drawable, drw->gc, x+1, y+1, h-1, h-1); ++ ++ /* Mark square if tag has client */ ++ if (occ & 1 << i) { ++ XSetForeground(drw->dpy, drw->gc, !invert ? scheme[SchemeSel][ColFg].pixel : ++ scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, drw->drawable, drw->gc, x + 1, y + 1, ++ h / 2, h / 2); ++ } ++ } else { ++ XSetForeground(drw->dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, drw->drawable, drw->gc, x+1, y+1, h-1, h); ++ } ++ x += h; ++ if (x > max_x) { ++ max_x = x; ++ } ++ } ++ y += h; ++ } ++ return max_x; ++} ++ ++int ++click_taggrid(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ unsigned int i, columns; ++ ++ columns = LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0); ++ i = (a->rel_x - lrpad / 2) / (bh / tagrows) + columns * (a->rel_y / (bh / tagrows)); ++ if (i >= LENGTH(tags)) { ++ i = LENGTH(tags) - 1; ++ } ++ arg->ui = 1 << i; ++ return ClkTagBar; ++} ++ ++void ++switchtag(const Arg *arg) ++{ ++ unsigned int columns; ++ unsigned int new_tagset = 0; ++ unsigned int pos, i; ++ int col, row; ++ Arg new_arg; ++ ++ columns = LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0); ++ ++ for (i = 0; i < LENGTH(tags); ++i) { ++ if (!(selmon->tagset[selmon->seltags] & 1 << i)) { ++ continue; ++ } ++ pos = i; ++ row = pos / columns; ++ col = pos % columns; ++ if (arg->ui & SWITCHTAG_UP) { /* UP */ ++ row --; ++ if (row < 0) { ++ row = tagrows - 1; ++ } ++ do { ++ pos = row * columns + col; ++ row --; ++ } while (pos >= LENGTH(tags)); ++ } ++ if (arg->ui & SWITCHTAG_DOWN) { /* DOWN */ ++ row ++; ++ if (row >= tagrows) { ++ row = 0; ++ } ++ pos = row * columns + col; ++ if (pos >= LENGTH(tags)) { ++ row = 0; ++ } ++ pos = row * columns + col; ++ } ++ if (arg->ui & SWITCHTAG_LEFT) { /* LEFT */ ++ col --; ++ if (col < 0) { ++ col = columns - 1; ++ } ++ do { ++ pos = row * columns + col; ++ col --; ++ } while (pos >= LENGTH(tags)); ++ } ++ if (arg->ui & SWITCHTAG_RIGHT) { /* RIGHT */ ++ col ++; ++ if (col >= columns) { ++ col = 0; ++ } ++ pos = row * columns + col; ++ if (pos >= LENGTH(tags)) { ++ col = 0; ++ pos = row * columns + col; ++ } ++ } ++ new_tagset |= 1 << pos; ++ } ++ new_arg.ui = new_tagset; ++ if (arg->ui & SWITCHTAG_TOGGLETAG) { ++ toggletag(&new_arg); ++ } ++ if (arg->ui & SWITCHTAG_TAG) { ++ tag(&new_arg); ++ } ++ if (arg->ui & SWITCHTAG_VIEW) { ++ view (&new_arg); ++ } ++ if (arg->ui & SWITCHTAG_TOGGLEVIEW) { ++ toggleview (&new_arg); ++ } ++} +\ No newline at end of file +diff --git a/patch/bar_taggrid.h b/patch/bar_taggrid.h +new file mode 100644 +index 0000000..c35b337 +--- /dev/null ++++ b/patch/bar_taggrid.h +@@ -0,0 +1,4 @@ ++static int width_taggrid(Bar *bar, BarWidthArg *a); ++static int draw_taggrid(Bar *bar, BarDrawArg *a); ++static int click_taggrid(Bar *bar, Arg *arg, BarClickArg *a); ++static void switchtag(const Arg *arg); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..f6455d8 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_taggrid.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..98ac046 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_taggrid.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-taggrid-6.3_full.diff b/dwm/dwm-barmodules-taggrid-6.3_full.diff new file mode 100644 index 0000000..dac58ab --- /dev/null +++ b/dwm/dwm-barmodules-taggrid-6.3_full.diff @@ -0,0 +1,1158 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From 3b3064354ad90f18beb933b3d2b5ecf892201633 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:12:28 +0100 +Subject: [PATCH 2/2] Adding taggrid module + +--- + config.def.h | 25 ++++++++ + patch/bar_taggrid.c | 148 ++++++++++++++++++++++++++++++++++++++++++++ + patch/bar_taggrid.h | 4 ++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 5 files changed, 181 insertions(+), 2 deletions(-) + create mode 100644 patch/bar_taggrid.c + create mode 100644 patch/bar_taggrid.h + +diff --git a/config.def.h b/config.def.h +index f870c41..6d5e172 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -21,6 +21,22 @@ static const char *colors[][3] = { + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + ++/* grid of tags */ ++#define DRAWCLASSICTAGS 1 << 0 ++#define DRAWTAGGRID 1 << 1 ++ ++#define SWITCHTAG_UP 1 << 0 ++#define SWITCHTAG_DOWN 1 << 1 ++#define SWITCHTAG_LEFT 1 << 2 ++#define SWITCHTAG_RIGHT 1 << 3 ++#define SWITCHTAG_TOGGLETAG 1 << 4 ++#define SWITCHTAG_TAG 1 << 5 ++#define SWITCHTAG_VIEW 1 << 6 ++#define SWITCHTAG_TOGGLEVIEW 1 << 7 ++ ++static const unsigned int drawtagmask = DRAWTAGGRID; /* | DRAWCLASSICTAGS to show classic row of tags */ ++static const int tagrows = 2; ++ + static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class +@@ -46,6 +62,7 @@ static const Rule rules[] = { + static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_taggrid, draw_taggrid, click_taggrid, "taggrid" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, +@@ -101,6 +118,14 @@ static Key keys[] = { + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, ++ { MODKEY|ControlMask, XK_Up, switchtag, { .ui = SWITCHTAG_UP | SWITCHTAG_VIEW } }, ++ { MODKEY|ControlMask, XK_Down, switchtag, { .ui = SWITCHTAG_DOWN | SWITCHTAG_VIEW } }, ++ { MODKEY|ControlMask, XK_Right, switchtag, { .ui = SWITCHTAG_RIGHT | SWITCHTAG_VIEW } }, ++ { MODKEY|ControlMask, XK_Left, switchtag, { .ui = SWITCHTAG_LEFT | SWITCHTAG_VIEW } }, ++ { MODKEY|Mod4Mask, XK_Up, switchtag, { .ui = SWITCHTAG_UP | SWITCHTAG_TAG | SWITCHTAG_VIEW } }, ++ { MODKEY|Mod4Mask, XK_Down, switchtag, { .ui = SWITCHTAG_DOWN | SWITCHTAG_TAG | SWITCHTAG_VIEW } }, ++ { MODKEY|Mod4Mask, XK_Right, switchtag, { .ui = SWITCHTAG_RIGHT | SWITCHTAG_TAG | SWITCHTAG_VIEW } }, ++ { MODKEY|Mod4Mask, XK_Left, switchtag, { .ui = SWITCHTAG_LEFT | SWITCHTAG_TAG | SWITCHTAG_VIEW } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, +diff --git a/patch/bar_taggrid.c b/patch/bar_taggrid.c +new file mode 100644 +index 0000000..53046fa +--- /dev/null ++++ b/patch/bar_taggrid.c +@@ -0,0 +1,148 @@ ++int ++width_taggrid(Bar *bar, BarWidthArg *a) ++{ ++ return (bh / 2) * (LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0)) + lrpad; ++} ++ ++int ++draw_taggrid(Bar *bar, BarDrawArg *a) ++{ ++ unsigned int x, y, h, max_x = 0, columns, occ = 0; ++ int invert, i,j, k; ++ Client *c; ++ ++ for (c = bar->mon->clients; c; c = c->next) ++ occ |= c->tags; ++ ++ max_x = x = a->x + lrpad / 2; ++ h = bh / tagrows; ++ y = 0; ++ columns = LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0); ++ ++ /* Firstly we will fill the borders of squares */ ++ XSetForeground(drw->dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, drw->drawable, drw->gc, x, y, h*columns + 1, bh); ++ ++ /* We will draw LENGTH(tags) squares in tagraws raws. */ ++ for (j = 0, i = 0; j < tagrows; j++) { ++ x = a->x + lrpad / 2; ++ for (k = 0; k < columns; k++, i++) { ++ if (i < LENGTH(tags)) { ++ invert = bar->mon->tagset[bar->mon->seltags] & 1 << i ? 0 : 1; ++ ++ /* Select active color for current square */ ++ XSetForeground(drw->dpy, drw->gc, !invert ? scheme[SchemeSel][ColBg].pixel : ++ scheme[SchemeNorm][ColFg].pixel); ++ XFillRectangle(dpy, drw->drawable, drw->gc, x+1, y+1, h-1, h-1); ++ ++ /* Mark square if tag has client */ ++ if (occ & 1 << i) { ++ XSetForeground(drw->dpy, drw->gc, !invert ? scheme[SchemeSel][ColFg].pixel : ++ scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, drw->drawable, drw->gc, x + 1, y + 1, ++ h / 2, h / 2); ++ } ++ } else { ++ XSetForeground(drw->dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, drw->drawable, drw->gc, x+1, y+1, h-1, h); ++ } ++ x += h; ++ if (x > max_x) { ++ max_x = x; ++ } ++ } ++ y += h; ++ } ++ return max_x; ++} ++ ++int ++click_taggrid(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ unsigned int i, columns; ++ ++ columns = LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0); ++ i = (a->rel_x - lrpad / 2) / (bh / tagrows) + columns * (a->rel_y / (bh / tagrows)); ++ if (i >= LENGTH(tags)) { ++ i = LENGTH(tags) - 1; ++ } ++ arg->ui = 1 << i; ++ return ClkTagBar; ++} ++ ++void ++switchtag(const Arg *arg) ++{ ++ unsigned int columns; ++ unsigned int new_tagset = 0; ++ unsigned int pos, i; ++ int col, row; ++ Arg new_arg; ++ ++ columns = LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0); ++ ++ for (i = 0; i < LENGTH(tags); ++i) { ++ if (!(selmon->tagset[selmon->seltags] & 1 << i)) { ++ continue; ++ } ++ pos = i; ++ row = pos / columns; ++ col = pos % columns; ++ if (arg->ui & SWITCHTAG_UP) { /* UP */ ++ row --; ++ if (row < 0) { ++ row = tagrows - 1; ++ } ++ do { ++ pos = row * columns + col; ++ row --; ++ } while (pos >= LENGTH(tags)); ++ } ++ if (arg->ui & SWITCHTAG_DOWN) { /* DOWN */ ++ row ++; ++ if (row >= tagrows) { ++ row = 0; ++ } ++ pos = row * columns + col; ++ if (pos >= LENGTH(tags)) { ++ row = 0; ++ } ++ pos = row * columns + col; ++ } ++ if (arg->ui & SWITCHTAG_LEFT) { /* LEFT */ ++ col --; ++ if (col < 0) { ++ col = columns - 1; ++ } ++ do { ++ pos = row * columns + col; ++ col --; ++ } while (pos >= LENGTH(tags)); ++ } ++ if (arg->ui & SWITCHTAG_RIGHT) { /* RIGHT */ ++ col ++; ++ if (col >= columns) { ++ col = 0; ++ } ++ pos = row * columns + col; ++ if (pos >= LENGTH(tags)) { ++ col = 0; ++ pos = row * columns + col; ++ } ++ } ++ new_tagset |= 1 << pos; ++ } ++ new_arg.ui = new_tagset; ++ if (arg->ui & SWITCHTAG_TOGGLETAG) { ++ toggletag(&new_arg); ++ } ++ if (arg->ui & SWITCHTAG_TAG) { ++ tag(&new_arg); ++ } ++ if (arg->ui & SWITCHTAG_VIEW) { ++ view (&new_arg); ++ } ++ if (arg->ui & SWITCHTAG_TOGGLEVIEW) { ++ toggleview (&new_arg); ++ } ++} +\ No newline at end of file +diff --git a/patch/bar_taggrid.h b/patch/bar_taggrid.h +new file mode 100644 +index 0000000..c35b337 +--- /dev/null ++++ b/patch/bar_taggrid.h +@@ -0,0 +1,4 @@ ++static int width_taggrid(Bar *bar, BarWidthArg *a); ++static int draw_taggrid(Bar *bar, BarDrawArg *a); ++static int click_taggrid(Bar *bar, Arg *arg, BarClickArg *a); ++static void switchtag(const Arg *arg); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..f6455d8 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_taggrid.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..98ac046 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_taggrid.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-wintitleactions-6.3.diff b/dwm/dwm-barmodules-wintitleactions-6.3.diff new file mode 100644 index 0000000..27a597c --- /dev/null +++ b/dwm/dwm-barmodules-wintitleactions-6.3.diff @@ -0,0 +1,266 @@ +From 54674a344665e893a72a81319b7caf9226a2180f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:13:41 +0100 +Subject: [PATCH 2/2] Adding wintitleactions patch + +--- + config.def.h | 4 ++ + dwm.c | 24 +++++----- + patch/bar_wintitleactions.c | 90 +++++++++++++++++++++++++++++++++++++ + patch/bar_wintitleactions.h | 7 +++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 6 files changed, 119 insertions(+), 12 deletions(-) + create mode 100644 patch/bar_wintitleactions.c + create mode 100644 patch/bar_wintitleactions.h + +diff --git a/config.def.h b/config.def.h +index f870c41..b8cbed0 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -16,6 +16,7 @@ static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeHid] = { col_cyan, col_gray1, col_cyan }, + }; + + /* tagging */ +@@ -93,6 +94,7 @@ static Key keys[] = { + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, ++ { MODKEY|ControlMask, XK_z, showhideclient, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, +@@ -123,7 +125,9 @@ static Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, ++ { ClkWinTitle, 0, Button1, togglewin, {0} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, ++ { ClkWinTitle, 0, Button3, showhideclient, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, +diff --git a/dwm.c b/dwm.c +index 86763d8..5de793d 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -60,7 +60,7 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeHid }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +@@ -524,7 +524,7 @@ 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 == ClkWinTitle) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); + } + } + } +@@ -996,16 +996,16 @@ focusstack(const Arg *arg) + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { +- for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); ++ for (c = selmon->sel->next; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next); + if (!c) +- for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); ++ for (c = selmon->clients; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) +- if (ISVISIBLE(i)) ++ if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(i))) + c = i; + if (!c) + for (; i; i = i->next) +- if (ISVISIBLE(i)) ++ if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(i))) + c = i; + } + if (c) { +@@ -1226,12 +1226,14 @@ manage(Window w, XWindowAttributes *wa) + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ +- setclientstate(c, NormalState); ++ if (!HIDDEN(c)) ++ setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); +- XMapWindow(dpy, c->win); ++ if (!HIDDEN(c)) ++ XMapWindow(dpy, c->win); + focus(NULL); + } + +@@ -1354,7 +1356,7 @@ movemouse(const Arg *arg) + Client * + nexttiled(Client *c) + { +- for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); ++ for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(c)); c = c->next); + return c; + } + +@@ -2314,7 +2316,9 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + void + zoom(const Arg *arg) + { +- Client *c = selmon->sel; ++ Client *c = (Client*)arg->v; ++ if (!c) ++ c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange + || (selmon->sel && selmon->sel->isfloating)) +diff --git a/patch/bar_wintitleactions.c b/patch/bar_wintitleactions.c +new file mode 100644 +index 0000000..a9ae3e1 +--- /dev/null ++++ b/patch/bar_wintitleactions.c +@@ -0,0 +1,90 @@ ++void ++hide(Client *c) { ++ ++ Client *n; ++ if (!c || HIDDEN(c)) ++ return; ++ ++ Window w = c->win; ++ static XWindowAttributes ra, ca; ++ ++ // more or less taken directly from blackbox's hide() function ++ XGrabServer(dpy); ++ XGetWindowAttributes(dpy, root, &ra); ++ XGetWindowAttributes(dpy, w, &ca); ++ // prevent UnmapNotify events ++ XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); ++ XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); ++ XUnmapWindow(dpy, w); ++ setclientstate(c, IconicState); ++ XSelectInput(dpy, root, ra.your_event_mask); ++ XSelectInput(dpy, w, ca.your_event_mask); ++ XUngrabServer(dpy); ++ ++ if (c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { ++ for (n = c->snext; n && (!ISVISIBLE(n) || HIDDEN(n)); n = n->snext); ++ if (!n) ++ for (n = c->mon->stack; n && (!ISVISIBLE(n) || HIDDEN(n)); n = n->snext); ++ } else { ++ n = nexttiled(c); ++ if (!n) ++ n = prevtiled(c); ++ } ++ focus(n); ++ arrange(c->mon); ++} ++ ++void ++show(Client *c) ++{ ++ if (!c || !HIDDEN(c)) ++ return; ++ ++ XMapWindow(dpy, c->win); ++ setclientstate(c, NormalState); ++ arrange(c->mon); ++} ++ ++void ++togglewin(const Arg *arg) ++{ ++ Client *c = (Client*)arg->v; ++ if (!c) ++ return; ++ if (c == selmon->sel) ++ hide(c); ++ else { ++ if (HIDDEN(c)) ++ show(c); ++ focus(c); ++ restack(c->mon); ++ } ++} ++ ++Client * ++prevtiled(Client *c) ++{ ++ Client *p, *i; ++ for (p = NULL, i = c->mon->clients; c && i != c; i = i->next) ++ if (ISVISIBLE(i) && !HIDDEN(i)) ++ p = i; ++ return p; ++} ++ ++void ++showhideclient(const Arg *arg) ++{ ++ Client *c = (Client*)arg->v; ++ if (!c) ++ c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (HIDDEN(c)) { ++ show(c); ++ focus(c); ++ restack(c->mon); ++ } else { ++ hide(c); ++ } ++} +\ No newline at end of file +diff --git a/patch/bar_wintitleactions.h b/patch/bar_wintitleactions.h +new file mode 100644 +index 0000000..e37201e +--- /dev/null ++++ b/patch/bar_wintitleactions.h +@@ -0,0 +1,7 @@ ++#define HIDDEN(C) ((getstate(C->win) == IconicState)) ++ ++static void hide(Client *c); ++static void show(Client *c); ++static void togglewin(const Arg *arg); ++static Client * prevtiled(Client *c); ++static void showhideclient(const Arg *arg); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..8008552 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_wintitleactions.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..be075b5 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_wintitleactions.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barmodules-wintitleactions-6.3_full.diff b/dwm/dwm-barmodules-wintitleactions-6.3_full.diff new file mode 100644 index 0000000..53e2196 --- /dev/null +++ b/dwm/dwm-barmodules-wintitleactions-6.3_full.diff @@ -0,0 +1,1164 @@ +From 2433e0499e4607d39a3f511449f9466886055914 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:06:10 +0100 +Subject: [PATCH 1/2] Bar Modules - splits the bar functionality into + individual segments that can be re-arranged + +--- + config.def.h | 20 +++ + dwm.c | 393 ++++++++++++++++++++++++++++++++----------- + patch/bar_ltsymbol.c | 17 ++ + patch/bar_ltsymbol.h | 3 + + patch/bar_status.c | 19 +++ + patch/bar_status.h | 3 + + patch/bar_tags.c | 55 ++++++ + patch/bar_tags.h | 3 + + patch/bar_wintitle.c | 31 ++++ + patch/bar_wintitle.h | 3 + + patch/include.c | 5 + + patch/include.h | 5 + + 12 files changed, 458 insertions(+), 99 deletions(-) + create mode 100644 patch/bar_ltsymbol.c + create mode 100644 patch/bar_ltsymbol.h + create mode 100644 patch/bar_status.c + create mode 100644 patch/bar_status.h + create mode 100644 patch/bar_tags.c + create mode 100644 patch/bar_tags.h + create mode 100644 patch/bar_wintitle.c + create mode 100644 patch/bar_wintitle.h + create mode 100644 patch/include.c + create mode 100644 patch/include.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f870c41 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,26 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++/* Bar rules allow you to configure what is shown where on the bar, as well as ++ * introducing your own bar modules. ++ * ++ * monitor: ++ * -1 show on all monitors ++ * 0 show on monitor 0 ++ * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) ++ * bar - bar index, 0 is default, 1 is extrabar ++ * alignment - how the module is aligned compared to other modules ++ * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions ++ * name - does nothing, intended for visual clue and for logging / debugging ++ */ ++static const BarRule barrules[] = { ++ /* monitor bar alignment widthfunc drawfunc clickfunc name */ ++ { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, ++ { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, ++ { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, ++ { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..86763d8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define BARRULES 20 + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -66,6 +67,19 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ BAR_ALIGN_LEFT, ++ BAR_ALIGN_CENTER, ++ BAR_ALIGN_RIGHT, ++ BAR_ALIGN_LEFT_LEFT, ++ BAR_ALIGN_LEFT_RIGHT, ++ BAR_ALIGN_LEFT_CENTER, ++ BAR_ALIGN_NONE, ++ BAR_ALIGN_RIGHT_LEFT, ++ BAR_ALIGN_RIGHT_RIGHT, ++ BAR_ALIGN_RIGHT_CENTER, ++ BAR_ALIGN_LAST ++}; /* bar alignment */ + + typedef union { + int i; +@@ -74,6 +88,46 @@ typedef union { + const void *v; + } Arg; + ++typedef struct Monitor Monitor; ++typedef struct Bar Bar; ++struct Bar { ++ Window win; ++ Monitor *mon; ++ Bar *next; ++ int idx; ++ int topbar; ++ int bx, by, bw, bh; /* bar geometry */ ++ int w[BARRULES]; // module width ++ int x[BARRULES]; // module position ++}; ++ ++typedef struct { ++ int max_width; ++} BarWidthArg; ++ ++typedef struct { ++ int x; ++ int w; ++} BarDrawArg; ++ ++typedef struct { ++ int rel_x; ++ int rel_y; ++ int rel_w; ++ int rel_h; ++} BarClickArg; ++ ++typedef struct { ++ int monitor; ++ int bar; ++ int alignment; // see bar alignment enum ++ int (*widthfunc)(Bar *bar, BarWidthArg *a); ++ int (*drawfunc)(Bar *bar, BarDrawArg *a); ++ int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a); ++ char *name; // for debugging ++ int x, w; // position, width for internal use ++} BarRule; ++ + typedef struct { + unsigned int click; + unsigned int mask; +@@ -82,7 +136,6 @@ typedef struct { + const Arg arg; + } Button; + +-typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { + char name[256]; +@@ -116,19 +169,17 @@ 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 showbar; +- int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; +- Window barwin; ++ Bar *bar; + const Layout *lt[2]; + }; + +@@ -163,6 +214,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void drawbarwin(Bar *bar); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -235,12 +287,13 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "patch/include.h" + /* variables */ + static const char broken[] = "broken"; + 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 bh; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -272,6 +325,8 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++#include "patch/include.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -417,43 +472,61 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ int click, i, r, mi; + Arg arg = {0}; + Client *c; + Monitor *m; ++ Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; ++ const BarRule *br; ++ BarClickArg carg = { 0, 0, 0, 0 }; + + click = ClkRootWin; + /* focus monitor if necessary */ +- if ((m = wintomon(ev->window)) && m != selmon) { ++ if ((m = wintomon(ev->window)) && m != selmon ++ ) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } +- 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 - (int)TEXTW(stext)) +- click = ClkStatusText; +- else +- click = ClkWinTitle; +- } else if ((c = wintoclient(ev->window))) { ++ ++ for (mi = 0, m = mons; m && m != selmon; m = m->next, mi++); // get the monitor index ++ for (bar = selmon->bar; bar; bar = bar->next) { ++ if (ev->window == bar->win) { ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { ++ carg.rel_x = ev->x - bar->x[r]; ++ carg.rel_y = ev->y; ++ carg.rel_w = bar->w[r]; ++ carg.rel_h = bar->bh; ++ click = br->clickfunc(bar, &arg, &carg); ++ if (click < 0) ++ return; ++ break; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (click == ClkRootWin && (c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } +- for (i = 0; i < LENGTH(buttons); i++) ++ ++ 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)) ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++ } ++ } + } + + void +@@ -498,6 +571,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ Bar *bar; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +579,12 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } +- XUnmapWindow(dpy, mon->barwin); +- XDestroyWindow(dpy, mon->barwin); ++ for (bar = mon->bar; bar; bar = mon->bar) { ++ XUnmapWindow(dpy, bar->win); ++ XDestroyWindow(dpy, bar->win); ++ mon->bar = bar->next; ++ free(bar); ++ } + free(mon); + } + +@@ -552,6 +630,7 @@ void + configurenotify(XEvent *e) + { + Monitor *m; ++ Bar *bar; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; +@@ -568,7 +647,8 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ for (bar = m->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); +@@ -631,17 +711,40 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ int i, n, mi, max_bars = 2, istopbar = topbar; ++ ++ const BarRule *br; ++ Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +- m->topbar = topbar; ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ /* Derive the number of bars for this monitor based on bar rules */ ++ for (n = -1, i = 0; i < LENGTH(barrules); i++) { ++ br = &barrules[i]; ++ if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) ++ n = MAX(br->bar, n); ++ } ++ ++ for (i = 0; i <= n && i < max_bars; i++) { ++ bar = ecalloc(1, sizeof(Bar)); ++ bar->mon = m; ++ bar->idx = i; ++ bar->next = m->bar; ++ bar->topbar = istopbar; ++ m->bar = bar; ++ istopbar = !istopbar; ++ } ++ + return m; + } + +@@ -696,65 +799,117 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; +- Client *c; +- +- if (!m->showbar) +- return; +- +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } +- +- for (c = m->clients; c; c = c->next) { +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); +- x += w; +- } +- w = blw = TEXTW(m->ltsymbol); +- drw_setscheme(drw, scheme[SchemeNorm]); +- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); +- } +- } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ Bar *bar; ++ for (bar = m->bar; bar; bar = bar->next) ++ drawbarwin(bar); + } + + void + drawbars(void) + { + Monitor *m; +- + for (m = mons; m; m = m->next) + drawbar(m); + } + ++void ++drawbarwin(Bar *bar) ++{ ++ if (!bar->win) ++ return; ++ Monitor *mon; ++ int r, w, mi; ++ int rx, lx, rw, lw; // bar size, split between left and right if a center module is added ++ const BarRule *br; ++ BarWidthArg warg = { 0 }; ++ BarDrawArg darg = { 0, 0 }; ++ ++ for (mi = 0, mon = mons; mon && mon != bar->mon; mon = mon->next, mi++); // get the monitor index ++ rw = lw = bar->bw; ++ rx = lx = 0; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, lx, 0, lw, bh, 1, 1); ++ for (r = 0; r < LENGTH(barrules); r++) { ++ br = &barrules[r]; ++ if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon)) ++ continue; ++ if (br->monitor != 'A' && br->monitor != -1 && br->monitor != mi) ++ continue; ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); ++ w = br->widthfunc(bar, &warg); ++ w = MIN(warg.max_width, w); ++ ++ if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa ++ lw = rw; ++ lx = rx; ++ } else if (rw <= 0) { ++ rw = lw; ++ rx = lx; ++ } ++ ++ switch(br->alignment) { ++ default: ++ case BAR_ALIGN_NONE: ++ case BAR_ALIGN_LEFT_LEFT: ++ case BAR_ALIGN_LEFT: ++ bar->x[r] = lx; ++ if (lx == rx) { ++ rx += w; ++ rw -= w; ++ } ++ lx += w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_RIGHT: ++ case BAR_ALIGN_RIGHT: ++ bar->x[r] = lx + lw - w; ++ if (lx == rx) ++ rw -= w; ++ lw -= w; ++ break; ++ case BAR_ALIGN_LEFT_CENTER: ++ case BAR_ALIGN_CENTER: ++ bar->x[r] = lx + lw / 2 - w / 2; ++ if (lx == rx) { ++ rw = rx + rw - bar->x[r] - w; ++ rx = bar->x[r] + w; ++ } ++ lw = bar->x[r] - lx; ++ break; ++ case BAR_ALIGN_RIGHT_LEFT: ++ bar->x[r] = rx; ++ if (lx == rx) { ++ lx += w; ++ lw -= w; ++ } ++ rx += w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_RIGHT: ++ bar->x[r] = rx + rw - w; ++ if (lx == rx) ++ lw -= w; ++ rw -= w; ++ break; ++ case BAR_ALIGN_RIGHT_CENTER: ++ bar->x[r] = rx + rw / 2 - w / 2; ++ if (lx == rx) { ++ lw = lx + lw - bar->x[r] + w; ++ lx = bar->x[r] + w; ++ } ++ rw = bar->x[r] - rx; ++ break; ++ } ++ bar->w[r] = w; ++ darg.x = bar->x[r]; ++ darg.w = bar->w[r]; ++ br->drawfunc(bar, &darg); ++ } ++ drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); ++} ++ + void + enternotify(XEvent *e) + { +@@ -1049,7 +1204,7 @@ manage(Window w, XWindowAttributes *wa) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ +- c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + +@@ -1236,7 +1391,8 @@ propertynotify(XEvent *e) + break; + case XA_WM_HINTS: + updatewmhints(c); +- drawbars(); ++ if (c->isurgent) ++ drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { +@@ -1362,7 +1518,7 @@ restack(Monitor *m) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; +- wc.sibling = m->barwin; ++ wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); +@@ -1705,9 +1861,11 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { ++ Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ for (bar = selmon->bar; bar; bar = bar->next) ++ XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + +@@ -1807,22 +1965,37 @@ unmapnotify(XEvent *e) + void + updatebars(void) + { ++ Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, ++ #if BAR_ALPHA_PATCH ++ .background_pixel = 0, ++ .border_pixel = 0, ++ .colormap = cmap, ++ #else + .background_pixmap = ParentRelative, ++ #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { +- if (m->barwin) +- continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +- CopyFromParent, DefaultVisual(dpy, screen), +- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); +- XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +- XMapRaised(dpy, m->barwin); +- XSetClassHint(dpy, m->barwin, &ch); ++ for (bar = m->bar; bar; bar = bar->next) { ++ if (!bar->win) { ++ #if BAR_ALPHA_PATCH ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, ++ InputOutput, visual, ++ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); ++ #else ++ bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ #endif // BAR_ALPHA_PATCH ++ XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, bar->win); ++ XSetClassHint(dpy, bar->win, &ch); ++ } ++ } + } + } + +@@ -1831,12 +2004,30 @@ updatebarpos(Monitor *m) + { + 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 +- m->by = -bh; ++ int num_bars; ++ Bar *bar; ++ int y_pad = 0; ++ int x_pad = 0; ++ ++ for (bar = m->bar; bar; bar = bar->next) { ++ bar->bx = m->mx + x_pad; ++ bar->bw = m->ww - 2 * x_pad; ++ bar->bh = bh; ++ } ++ ++ if (!m->showbar) { ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = -bh - y_pad; ++ return; ++ } ++ ++ for (num_bars = 0, bar = m->bar; bar; bar = bar->next, num_bars++) ++ if (bar->topbar) ++ m->wy = m->my + bh + y_pad; ++ m->wh = m->wh - y_pad * num_bars - bh * num_bars; ++ ++ for (bar = m->bar; bar; bar = bar->next) ++ bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh); + } + + void +@@ -1993,9 +2184,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for (m = mons; m; m = m->next) ++ drawbar(m); + } + + void +@@ -2069,12 +2262,14 @@ wintomon(Window w) + int x, y; + Client *c; + Monitor *m; ++ Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) +- if (w == m->barwin) +- return m; ++ for (bar = m->bar; bar; bar = bar->next) ++ if (w == bar->win) ++ return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c +new file mode 100644 +index 0000000..6676a2a +--- /dev/null ++++ b/patch/bar_ltsymbol.c +@@ -0,0 +1,17 @@ ++int ++width_ltsymbol(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(bar->mon->ltsymbol); ++} ++ ++int ++draw_ltsymbol(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, bar->mon->ltsymbol, 0); ++} ++ ++int ++click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkLtSymbol; ++} +diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h +new file mode 100644 +index 0000000..d9c79bf +--- /dev/null ++++ b/patch/bar_ltsymbol.h +@@ -0,0 +1,3 @@ ++static int width_ltsymbol(Bar *bar, BarWidthArg *a); ++static int draw_ltsymbol(Bar *bar, BarDrawArg *a); ++static int click_ltsymbol(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_status.c b/patch/bar_status.c +new file mode 100644 +index 0000000..7d27282 +--- /dev/null ++++ b/patch/bar_status.c +@@ -0,0 +1,19 @@ ++int ++width_status(Bar *bar, BarWidthArg *a) ++{ ++ return TEXTW(stext); ++} ++ ++ ++int ++draw_status(Bar *bar, BarDrawArg *a) ++{ ++ return drw_text(drw, a->x, 0, a->w, bh, lrpad / 2, stext, 0); ++} ++ ++ ++int ++click_status(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkStatusText; ++} +diff --git a/patch/bar_status.h b/patch/bar_status.h +new file mode 100644 +index 0000000..b02a4b8 +--- /dev/null ++++ b/patch/bar_status.h +@@ -0,0 +1,3 @@ ++static int width_status(Bar *bar, BarWidthArg *a); ++static int draw_status(Bar *bar, BarDrawArg *a); ++static int click_status(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_tags.c b/patch/bar_tags.c +new file mode 100644 +index 0000000..680e1fe +--- /dev/null ++++ b/patch/bar_tags.c +@@ -0,0 +1,55 @@ ++int ++width_tags(Bar *bar, BarWidthArg *a) ++{ ++ int w, i; ++ ++ for (w = 0, i = 0; i < LENGTH(tags); i++) { ++ w += TEXTW(tags[i]); ++ } ++ return w; ++} ++ ++int ++draw_tags(Bar *bar, BarDrawArg *a) ++{ ++ int invert; ++ int w, x = a->x; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ Monitor *m = bar->mon; ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ invert = urg & 1 << i; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], invert); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, invert); ++ x += w; ++ } ++ ++ return x; ++} ++ ++int ++click_tags(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ int i = 0, x = lrpad / 2; ++ ++ do { ++ x += TEXTW(tags[i]); ++ } while (a->rel_x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ arg->ui = 1 << i; ++ } ++ return ClkTagBar; ++} +diff --git a/patch/bar_tags.h b/patch/bar_tags.h +new file mode 100644 +index 0000000..7ac04d8 +--- /dev/null ++++ b/patch/bar_tags.h +@@ -0,0 +1,3 @@ ++static int width_tags(Bar *bar, BarWidthArg *a); ++static int draw_tags(Bar *bar, BarDrawArg *a); ++static int click_tags(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c +new file mode 100644 +index 0000000..3c11b75 +--- /dev/null ++++ b/patch/bar_wintitle.c +@@ -0,0 +1,31 @@ ++int ++width_wintitle(Bar *bar, BarWidthArg *a) ++{ ++ return a->max_width; ++} ++ ++int ++draw_wintitle(Bar *bar, BarDrawArg *a) ++{ ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ int x = a->x, w = a->w; ++ Monitor *m = bar->mon; ++ ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ return x + w; ++} ++ ++int ++click_wintitle(Bar *bar, Arg *arg, BarClickArg *a) ++{ ++ return ClkWinTitle; ++} +diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h +new file mode 100644 +index 0000000..266404c +--- /dev/null ++++ b/patch/bar_wintitle.h +@@ -0,0 +1,3 @@ ++static int width_wintitle(Bar *bar, BarWidthArg *a); ++static int draw_wintitle(Bar *bar, BarDrawArg *a); ++static int click_wintitle(Bar *bar, Arg *arg, BarClickArg *a); +diff --git a/patch/include.c b/patch/include.c +new file mode 100644 +index 0000000..d422f56 +--- /dev/null ++++ b/patch/include.c +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.c" ++#include "bar_status.c" ++#include "bar_tags.c" ++#include "bar_wintitle.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +new file mode 100644 +index 0000000..5f9a3fe +--- /dev/null ++++ b/patch/include.h +@@ -0,0 +1,5 @@ ++/* Bar functionality */ ++#include "bar_ltsymbol.h" ++#include "bar_status.h" ++#include "bar_tags.h" ++#include "bar_wintitle.h" +\ No newline at end of file +-- +2.19.1 + + +From 54674a344665e893a72a81319b7caf9226a2180f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:13:41 +0100 +Subject: [PATCH 2/2] Adding wintitleactions patch + +--- + config.def.h | 4 ++ + dwm.c | 24 +++++----- + patch/bar_wintitleactions.c | 90 +++++++++++++++++++++++++++++++++++++ + patch/bar_wintitleactions.h | 7 +++ + patch/include.c | 3 +- + patch/include.h | 3 +- + 6 files changed, 119 insertions(+), 12 deletions(-) + create mode 100644 patch/bar_wintitleactions.c + create mode 100644 patch/bar_wintitleactions.h + +diff --git a/config.def.h b/config.def.h +index f870c41..b8cbed0 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -16,6 +16,7 @@ static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeHid] = { col_cyan, col_gray1, col_cyan }, + }; + + /* tagging */ +@@ -93,6 +94,7 @@ static Key keys[] = { + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, ++ { MODKEY|ControlMask, XK_z, showhideclient, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, +@@ -123,7 +125,9 @@ static Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, ++ { ClkWinTitle, 0, Button1, togglewin, {0} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, ++ { ClkWinTitle, 0, Button3, showhideclient, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, +diff --git a/dwm.c b/dwm.c +index 86763d8..5de793d 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -60,7 +60,7 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeHid }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +@@ -524,7 +524,7 @@ 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 == ClkWinTitle) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); + } + } + } +@@ -996,16 +996,16 @@ focusstack(const Arg *arg) + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { +- for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); ++ for (c = selmon->sel->next; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next); + if (!c) +- for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); ++ for (c = selmon->clients; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) +- if (ISVISIBLE(i)) ++ if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(i))) + c = i; + if (!c) + for (; i; i = i->next) +- if (ISVISIBLE(i)) ++ if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(i))) + c = i; + } + if (c) { +@@ -1226,12 +1226,14 @@ manage(Window w, XWindowAttributes *wa) + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ +- setclientstate(c, NormalState); ++ if (!HIDDEN(c)) ++ setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); +- XMapWindow(dpy, c->win); ++ if (!HIDDEN(c)) ++ XMapWindow(dpy, c->win); + focus(NULL); + } + +@@ -1354,7 +1356,7 @@ movemouse(const Arg *arg) + Client * + nexttiled(Client *c) + { +- for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); ++ for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(c)); c = c->next); + return c; + } + +@@ -2314,7 +2316,9 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + void + zoom(const Arg *arg) + { +- Client *c = selmon->sel; ++ Client *c = (Client*)arg->v; ++ if (!c) ++ c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange + || (selmon->sel && selmon->sel->isfloating)) +diff --git a/patch/bar_wintitleactions.c b/patch/bar_wintitleactions.c +new file mode 100644 +index 0000000..a9ae3e1 +--- /dev/null ++++ b/patch/bar_wintitleactions.c +@@ -0,0 +1,90 @@ ++void ++hide(Client *c) { ++ ++ Client *n; ++ if (!c || HIDDEN(c)) ++ return; ++ ++ Window w = c->win; ++ static XWindowAttributes ra, ca; ++ ++ // more or less taken directly from blackbox's hide() function ++ XGrabServer(dpy); ++ XGetWindowAttributes(dpy, root, &ra); ++ XGetWindowAttributes(dpy, w, &ca); ++ // prevent UnmapNotify events ++ XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); ++ XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); ++ XUnmapWindow(dpy, w); ++ setclientstate(c, IconicState); ++ XSelectInput(dpy, root, ra.your_event_mask); ++ XSelectInput(dpy, w, ca.your_event_mask); ++ XUngrabServer(dpy); ++ ++ if (c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { ++ for (n = c->snext; n && (!ISVISIBLE(n) || HIDDEN(n)); n = n->snext); ++ if (!n) ++ for (n = c->mon->stack; n && (!ISVISIBLE(n) || HIDDEN(n)); n = n->snext); ++ } else { ++ n = nexttiled(c); ++ if (!n) ++ n = prevtiled(c); ++ } ++ focus(n); ++ arrange(c->mon); ++} ++ ++void ++show(Client *c) ++{ ++ if (!c || !HIDDEN(c)) ++ return; ++ ++ XMapWindow(dpy, c->win); ++ setclientstate(c, NormalState); ++ arrange(c->mon); ++} ++ ++void ++togglewin(const Arg *arg) ++{ ++ Client *c = (Client*)arg->v; ++ if (!c) ++ return; ++ if (c == selmon->sel) ++ hide(c); ++ else { ++ if (HIDDEN(c)) ++ show(c); ++ focus(c); ++ restack(c->mon); ++ } ++} ++ ++Client * ++prevtiled(Client *c) ++{ ++ Client *p, *i; ++ for (p = NULL, i = c->mon->clients; c && i != c; i = i->next) ++ if (ISVISIBLE(i) && !HIDDEN(i)) ++ p = i; ++ return p; ++} ++ ++void ++showhideclient(const Arg *arg) ++{ ++ Client *c = (Client*)arg->v; ++ if (!c) ++ c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (HIDDEN(c)) { ++ show(c); ++ focus(c); ++ restack(c->mon); ++ } else { ++ hide(c); ++ } ++} +\ No newline at end of file +diff --git a/patch/bar_wintitleactions.h b/patch/bar_wintitleactions.h +new file mode 100644 +index 0000000..e37201e +--- /dev/null ++++ b/patch/bar_wintitleactions.h +@@ -0,0 +1,7 @@ ++#define HIDDEN(C) ((getstate(C->win) == IconicState)) ++ ++static void hide(Client *c); ++static void show(Client *c); ++static void togglewin(const Arg *arg); ++static Client * prevtiled(Client *c); ++static void showhideclient(const Arg *arg); +\ No newline at end of file +diff --git a/patch/include.c b/patch/include.c +index d422f56..8008552 100644 +--- a/patch/include.c ++++ b/patch/include.c +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.c" + #include "bar_status.c" + #include "bar_tags.c" +-#include "bar_wintitle.c" +\ No newline at end of file ++#include "bar_wintitle.c" ++#include "bar_wintitleactions.c" +\ No newline at end of file +diff --git a/patch/include.h b/patch/include.h +index 5f9a3fe..be075b5 100644 +--- a/patch/include.h ++++ b/patch/include.h +@@ -2,4 +2,5 @@ + #include "bar_ltsymbol.h" + #include "bar_status.h" + #include "bar_tags.h" +-#include "bar_wintitle.h" +\ No newline at end of file ++#include "bar_wintitle.h" ++#include "bar_wintitleactions.h" +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-barpadding-6.3.diff b/dwm/dwm-barpadding-6.3.diff new file mode 100644 index 0000000..2b3423e --- /dev/null +++ b/dwm/dwm-barpadding-6.3.diff @@ -0,0 +1,117 @@ +From a3e3bd001c4b9b4d5bc6b26ed0cef6f2ec78a205 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:49:22 +0100 +Subject: [PATCH] barpadding: multi-monitor fix + +Ref. +https://www.reddit.com/r/suckless/comments/nfc3xn/gaps_problem_with_multiple_monitors/ +--- + config.def.h | 2 ++ + dwm.c | 24 ++++++++++++++---------- + 2 files changed, 16 insertions(+), 10 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..f0b739f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ 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 */ ++static const int vertpad = 10; /* vertical padding of bar */ ++static const int sidepad = 10; /* horizontal padding of bar */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/dwm.c b/dwm.c +index a96f33c..5728f92 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -242,6 +242,8 @@ static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ ++static int vp; /* vertical padding for bar */ ++static int sp; /* side padding for bar */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; + static void (*handler[LASTEvent]) (XEvent *) = { +@@ -568,7 +570,7 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh); + } + focus(NULL); + arrange(NULL); +@@ -709,7 +711,7 @@ drawbar(Monitor *m) + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ drw_text(drw, m->ww - tw - 2 * sp, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { +@@ -735,12 +737,12 @@ drawbar(Monitor *m) + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ drw_text(drw, x, 0, w - 2 * sp, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); ++ drw_rect(drw, x, 0, w - 2 * sp, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +@@ -1550,6 +1552,8 @@ setup(void) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; ++ sp = sidepad; ++ vp = (topbar == 1) ? vertpad : - vertpad; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); +@@ -1707,7 +1711,7 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, bh); + arrange(selmon); + } + +@@ -1817,7 +1821,7 @@ updatebars(void) + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), ++ m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +@@ -1832,11 +1836,11 @@ updatebarpos(Monitor *m) + 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; ++ m->wh = m->wh - vertpad - bh; ++ m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; ++ m->wy = m->topbar ? m->wy + bh + vp : m->wy; + } else +- m->by = -bh; ++ m->by = -bh - vp; + } + + void +-- +2.19.1 + diff --git a/dwm/dwm-center-6.3.diff b/dwm/dwm-center-6.3.diff new file mode 100644 index 0000000..b732c8c --- /dev/null +++ b/dwm/dwm-center-6.3.diff @@ -0,0 +1,92 @@ +From 687f8c8ff590302462e629f8313b8fc6371e09ca Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:17:58 +0100 +Subject: [PATCH] Adding 6.3 center patch with multi-monitor fix and + auto-centering of floating popup windows + +Refer to https://dwm.suckless.org/patches/center/ +--- + config.def.h | 6 +++--- + dwm.c | 13 +++++++++++-- + 2 files changed, 14 insertions(+), 5 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..dd91fa6 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 iscentered isfloating monitor */ ++ { "Gimp", NULL, NULL, 0, 0, 1, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1 }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index a96f33c..b7b97e3 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -92,7 +92,7 @@ struct Client { + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; +- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int isfixed, iscentered, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + Client *next; + Client *snext; + Monitor *mon; +@@ -137,6 +137,7 @@ typedef struct { + const char *instance; + const char *title; + unsigned int tags; ++ int iscentered; + int isfloating; + int monitor; + } Rule; +@@ -286,6 +287,7 @@ applyrules(Client *c) + XClassHint ch = { NULL, NULL }; + + /* rule matching */ ++ c->iscentered = 0; + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); +@@ -298,6 +300,7 @@ applyrules(Client *c) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { ++ c->iscentered = r->iscentered; + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); +@@ -1060,6 +1063,10 @@ manage(Window w, XWindowAttributes *wa) + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); ++ if (c->iscentered) { ++ c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; ++ c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; ++ } + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) +@@ -2015,8 +2022,10 @@ updatewindowtype(Client *c) + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); +- if (wtype == netatom[NetWMWindowTypeDialog]) ++ if (wtype == netatom[NetWMWindowTypeDialog]) { ++ c->iscentered = 1; + c->isfloating = 1; ++ } + } + + void +-- +2.19.1 + diff --git a/dwm/dwm-centeredsizehints-6.3.diff b/dwm/dwm-centeredsizehints-6.3.diff new file mode 100644 index 0000000..ae94de9 --- /dev/null +++ b/dwm/dwm-centeredsizehints-6.3.diff @@ -0,0 +1,79 @@ +From b38b2f665b587add976cd9572ad0bfb748f1b143 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:19:17 +0100 +Subject: [PATCH] Center clients within their allocated tile based on size + hints + +--- + dwm.c | 30 ++++++++++++++++++++++++++---- + 1 file changed, 26 insertions(+), 4 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..bc7c7ef 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -191,6 +191,7 @@ static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); + static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); ++static void resizeclientpad(Client *c, int x, int y, int w, int h, int xpad, int ypad); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); + static void run(void); +@@ -1233,6 +1234,7 @@ propertynotify(XEvent *e) + break; + case XA_WM_NORMAL_HINTS: + updatesizehints(c); ++ arrangemon(c->mon); + break; + case XA_WM_HINTS: + updatewmhints(c); +@@ -1270,21 +1272,41 @@ recttomon(int x, int y, int w, int h) + } + + void +-resize(Client *c, int x, int y, int w, int h, int interact) ++resize(Client *c, int tx, int ty, int tw, int th, int interact) + { +- if (applysizehints(c, &x, &y, &w, &h, interact)) +- resizeclient(c, x, y, w, h); ++ int wh = tw, hh = th; ++ if (applysizehints(c, &tx, &ty, &wh, &hh, interact)) ++ resizeclientpad(c, tx, ty, wh, hh, tw, th); + } + ++/* This wrapper is just for compatibility with other patches that may call resizeclient */ + void + resizeclient(Client *c, int x, int y, int w, int h) + { +- XWindowChanges wc; ++ resizeclientpad(c, x, y, w, h, w, h); ++} + ++/* This is essentially the resizeclient function renamed with two ++ * additional parameters, tw and th (for tile width and height). */ ++void ++resizeclientpad(Client *c, int x, int y, int w, int h, int tw, int th) ++{ ++ XWindowChanges wc; + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; ++ if (!c->isfloating) { ++ if (w != tw) { ++ wc.x += (tw - w) / 2; ++ c->w = tw; ++ } ++ if (h != th) { ++ wc.y += (th - h) / 2; ++ c->h = th; ++ } ++ } ++ + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); +-- +2.19.1 + diff --git a/dwm/dwm-cfacts-6.3.diff b/dwm/dwm-cfacts-6.3.diff new file mode 100644 index 0000000..bd475f2 --- /dev/null +++ b/dwm/dwm-cfacts-6.3.diff @@ -0,0 +1,119 @@ +From 112abfec73e12282e4b0288f2e7739000ad135b6 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:21:06 +0100 +Subject: [PATCH] Adding cfacts patch which provides the ability to assign + different weights to clients in their respective stack in tiled layout. + +Refer to https://dwm.suckless.org/patches/cfacts/ +--- + config.def.h | 3 +++ + dwm.c | 35 ++++++++++++++++++++++++++++++++--- + 2 files changed, 35 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..5b0cfde 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -71,6 +71,9 @@ static Key keys[] = { + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { MODKEY|ShiftMask, XK_h, setcfact, {.f = +0.25} }, ++ { MODKEY|ShiftMask, XK_l, setcfact, {.f = -0.25} }, ++ { MODKEY|ShiftMask, XK_o, setcfact, {.f = 0.00} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..bcb155d 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -87,6 +87,7 @@ typedef struct Client Client; + struct Client { + char name[256]; + float mina, maxa; ++ float cfact; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; +@@ -201,6 +202,7 @@ static void setclientstate(Client *c, long state); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); + static void setlayout(const Arg *arg); ++static void setcfact(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); + static void seturgent(Client *c, int urg); +@@ -1033,6 +1035,7 @@ manage(Window w, XWindowAttributes *wa) + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; ++ c->cfact = 1.0; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { +@@ -1515,6 +1518,24 @@ setlayout(const Arg *arg) + drawbar(selmon); + } + ++void ++setcfact(const Arg *arg) { ++ float f; ++ Client *c; ++ ++ c = selmon->sel; ++ ++ if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ f = arg->f + c->cfact; ++ if(arg->f == 0.0) ++ f = 1.0; ++ else if(f < 0.25 || f > 4.0) ++ return; ++ c->cfact = f; ++ arrange(selmon); ++} ++ + /* arg > 1.0 will set mfact absolutely */ + void + setmfact(const Arg *arg) +@@ -1678,9 +1699,15 @@ void + tile(Monitor *m) + { + unsigned int i, n, h, mw, my, ty; ++ float mfacts = 0, sfacts = 0; + Client *c; + +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ if (n < m->nmaster) ++ mfacts += c->cfact; ++ else ++ sfacts += c->cfact; ++ } + if (n == 0) + return; + +@@ -1690,15 +1717,17 @@ tile(Monitor *m) + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { +- h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ h = (m->wh - my) * (c->cfact / mfacts); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); ++ mfacts -= c->cfact; + } else { +- h = (m->wh - ty) / (n - i); ++ h = (m->wh - ty) * (c->cfact / sfacts); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); ++ sfacts -= c->cfact; + } + } + +-- +2.19.1 + diff --git a/dwm/dwm-cfacts-dragcfact-6.3.diff b/dwm/dwm-cfacts-dragcfact-6.3.diff new file mode 100644 index 0000000..e5472fe --- /dev/null +++ b/dwm/dwm-cfacts-dragcfact-6.3.diff @@ -0,0 +1,152 @@ +From 39647ff8b155bdff072e32d04ca4e1c5e27873d5 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:22:21 +0100 +Subject: [PATCH 2/2] The dragcfact patch allow you resize clients' size (i.e. + modify cfact) by holding modkey + shift + right-click and dragging the mouse. + +--- + config.def.h | 1 + + dwm.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 89 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 5b0cfde..595cf43 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -111,6 +111,7 @@ static Button buttons[] = { + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, ++ { ClkClientWin, MODKEY|ShiftMask, Button3, dragcfact, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, +diff --git a/dwm.c b/dwm.c +index bcb155d..54f7320 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -162,6 +162,7 @@ static void destroynotify(XEvent *e); + static void detach(Client *c); + static void detachstack(Client *c); + static Monitor *dirtomon(int dir); ++static void dragcfact(const Arg *arg); + static void drawbar(Monitor *m); + static void drawbars(void); + static void enternotify(XEvent *e); +@@ -695,6 +696,81 @@ dirtomon(int dir) + return m; + } + ++void ++dragcfact(const Arg *arg) ++{ ++ int prev_x, prev_y, dist_x, dist_y; ++ float fact; ++ Client *c; ++ XEvent ev; ++ Time lasttime = 0; ++ ++ if (!(c = selmon->sel)) ++ return; ++ if (c->isfloating) { ++ resizemouse(arg); ++ return; ++ } ++ #if !FAKEFULLSCREEN_PATCH ++ #if FAKEFULLSCREEN_CLIENT_PATCH ++ if (c->isfullscreen && !c->fakefullscreen) /* no support resizing fullscreen windows by mouse */ ++ return; ++ #else ++ if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ return; ++ #endif // FAKEFULLSCREEN_CLIENT_PATCH ++ #endif // !FAKEFULLSCREEN_PATCH ++ restack(selmon); ++ ++ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, ++ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) ++ return; ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); ++ ++ prev_x = prev_y = -999999; ++ ++ do { ++ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); ++ switch(ev.type) { ++ case ConfigureRequest: ++ case Expose: ++ case MapRequest: ++ handler[ev.type](&ev); ++ break; ++ case MotionNotify: ++ if ((ev.xmotion.time - lasttime) <= (1000 / 60)) ++ continue; ++ lasttime = ev.xmotion.time; ++ if (prev_x == -999999) { ++ prev_x = ev.xmotion.x_root; ++ prev_y = ev.xmotion.y_root; ++ } ++ ++ dist_x = ev.xmotion.x - prev_x; ++ dist_y = ev.xmotion.y - prev_y; ++ ++ if (abs(dist_x) > abs(dist_y)) { ++ fact = (float) 4.0 * dist_x / c->mon->ww; ++ } else { ++ fact = (float) -4.0 * dist_y / c->mon->wh; ++ } ++ ++ if (fact) ++ setcfact(&((Arg) { .f = fact })); ++ ++ prev_x = ev.xmotion.x; ++ prev_y = ev.xmotion.y; ++ break; ++ } ++ } while (ev.type != ButtonRelease); ++ ++ ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); ++ ++ XUngrabPointer(dpy, CurrentTime); ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); ++} ++ + void + drawbar(Monitor *m) + { +@@ -1519,19 +1595,25 @@ setlayout(const Arg *arg) + } + + void +-setcfact(const Arg *arg) { ++setcfact(const Arg *arg) ++{ + float f; + Client *c; + + c = selmon->sel; + +- if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) ++ if (!arg || !c || !selmon->lt[selmon->sellt]->arrange) + return; +- f = arg->f + c->cfact; +- if(arg->f == 0.0) ++ if (!arg->f) + f = 1.0; +- else if(f < 0.25 || f > 4.0) +- return; ++ else if (arg->f > 4.0) // set fact absolutely ++ f = arg->f - 4.0; ++ else ++ f = arg->f + c->cfact; ++ if (f < 0.25) ++ f = 0.25; ++ else if (f > 4.0) ++ f = 4.0; + c->cfact = f; + arrange(selmon); + } +-- +2.19.1 + diff --git a/dwm/dwm-cfacts-dragcfact-6.3_full.diff b/dwm/dwm-cfacts-dragcfact-6.3_full.diff new file mode 100644 index 0000000..964d8d5 --- /dev/null +++ b/dwm/dwm-cfacts-dragcfact-6.3_full.diff @@ -0,0 +1,272 @@ +From 112abfec73e12282e4b0288f2e7739000ad135b6 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:21:06 +0100 +Subject: [PATCH 1/2] Adding cfacts patch which provides the ability to assign + different weights to clients in their respective stack in tiled layout. + +Refer to https://dwm.suckless.org/patches/cfacts/ +--- + config.def.h | 3 +++ + dwm.c | 35 ++++++++++++++++++++++++++++++++--- + 2 files changed, 35 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..5b0cfde 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -71,6 +71,9 @@ static Key keys[] = { + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { MODKEY|ShiftMask, XK_h, setcfact, {.f = +0.25} }, ++ { MODKEY|ShiftMask, XK_l, setcfact, {.f = -0.25} }, ++ { MODKEY|ShiftMask, XK_o, setcfact, {.f = 0.00} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..bcb155d 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -87,6 +87,7 @@ typedef struct Client Client; + struct Client { + char name[256]; + float mina, maxa; ++ float cfact; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; +@@ -201,6 +202,7 @@ static void setclientstate(Client *c, long state); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); + static void setlayout(const Arg *arg); ++static void setcfact(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); + static void seturgent(Client *c, int urg); +@@ -1033,6 +1035,7 @@ manage(Window w, XWindowAttributes *wa) + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; ++ c->cfact = 1.0; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { +@@ -1515,6 +1518,24 @@ setlayout(const Arg *arg) + drawbar(selmon); + } + ++void ++setcfact(const Arg *arg) { ++ float f; ++ Client *c; ++ ++ c = selmon->sel; ++ ++ if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ f = arg->f + c->cfact; ++ if(arg->f == 0.0) ++ f = 1.0; ++ else if(f < 0.25 || f > 4.0) ++ return; ++ c->cfact = f; ++ arrange(selmon); ++} ++ + /* arg > 1.0 will set mfact absolutely */ + void + setmfact(const Arg *arg) +@@ -1678,9 +1699,15 @@ void + tile(Monitor *m) + { + unsigned int i, n, h, mw, my, ty; ++ float mfacts = 0, sfacts = 0; + Client *c; + +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ if (n < m->nmaster) ++ mfacts += c->cfact; ++ else ++ sfacts += c->cfact; ++ } + if (n == 0) + return; + +@@ -1690,15 +1717,17 @@ tile(Monitor *m) + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { +- h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ h = (m->wh - my) * (c->cfact / mfacts); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); ++ mfacts -= c->cfact; + } else { +- h = (m->wh - ty) / (n - i); ++ h = (m->wh - ty) * (c->cfact / sfacts); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); ++ sfacts -= c->cfact; + } + } + +-- +2.19.1 + + +From 39647ff8b155bdff072e32d04ca4e1c5e27873d5 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:22:21 +0100 +Subject: [PATCH 2/2] The dragcfact patch allow you resize clients' size (i.e. + modify cfact) by holding modkey + shift + right-click and dragging the mouse. + +--- + config.def.h | 1 + + dwm.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 89 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 5b0cfde..595cf43 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -111,6 +111,7 @@ static Button buttons[] = { + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, ++ { ClkClientWin, MODKEY|ShiftMask, Button3, dragcfact, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, +diff --git a/dwm.c b/dwm.c +index bcb155d..54f7320 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -162,6 +162,7 @@ static void destroynotify(XEvent *e); + static void detach(Client *c); + static void detachstack(Client *c); + static Monitor *dirtomon(int dir); ++static void dragcfact(const Arg *arg); + static void drawbar(Monitor *m); + static void drawbars(void); + static void enternotify(XEvent *e); +@@ -695,6 +696,81 @@ dirtomon(int dir) + return m; + } + ++void ++dragcfact(const Arg *arg) ++{ ++ int prev_x, prev_y, dist_x, dist_y; ++ float fact; ++ Client *c; ++ XEvent ev; ++ Time lasttime = 0; ++ ++ if (!(c = selmon->sel)) ++ return; ++ if (c->isfloating) { ++ resizemouse(arg); ++ return; ++ } ++ #if !FAKEFULLSCREEN_PATCH ++ #if FAKEFULLSCREEN_CLIENT_PATCH ++ if (c->isfullscreen && !c->fakefullscreen) /* no support resizing fullscreen windows by mouse */ ++ return; ++ #else ++ if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ return; ++ #endif // FAKEFULLSCREEN_CLIENT_PATCH ++ #endif // !FAKEFULLSCREEN_PATCH ++ restack(selmon); ++ ++ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, ++ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) ++ return; ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); ++ ++ prev_x = prev_y = -999999; ++ ++ do { ++ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); ++ switch(ev.type) { ++ case ConfigureRequest: ++ case Expose: ++ case MapRequest: ++ handler[ev.type](&ev); ++ break; ++ case MotionNotify: ++ if ((ev.xmotion.time - lasttime) <= (1000 / 60)) ++ continue; ++ lasttime = ev.xmotion.time; ++ if (prev_x == -999999) { ++ prev_x = ev.xmotion.x_root; ++ prev_y = ev.xmotion.y_root; ++ } ++ ++ dist_x = ev.xmotion.x - prev_x; ++ dist_y = ev.xmotion.y - prev_y; ++ ++ if (abs(dist_x) > abs(dist_y)) { ++ fact = (float) 4.0 * dist_x / c->mon->ww; ++ } else { ++ fact = (float) -4.0 * dist_y / c->mon->wh; ++ } ++ ++ if (fact) ++ setcfact(&((Arg) { .f = fact })); ++ ++ prev_x = ev.xmotion.x; ++ prev_y = ev.xmotion.y; ++ break; ++ } ++ } while (ev.type != ButtonRelease); ++ ++ ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); ++ ++ XUngrabPointer(dpy, CurrentTime); ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); ++} ++ + void + drawbar(Monitor *m) + { +@@ -1519,19 +1595,25 @@ setlayout(const Arg *arg) + } + + void +-setcfact(const Arg *arg) { ++setcfact(const Arg *arg) ++{ + float f; + Client *c; + + c = selmon->sel; + +- if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) ++ if (!arg || !c || !selmon->lt[selmon->sellt]->arrange) + return; +- f = arg->f + c->cfact; +- if(arg->f == 0.0) ++ if (!arg->f) + f = 1.0; +- else if(f < 0.25 || f > 4.0) +- return; ++ else if (arg->f > 4.0) // set fact absolutely ++ f = arg->f - 4.0; ++ else ++ f = arg->f + c->cfact; ++ if (f < 0.25) ++ f = 0.25; ++ else if (f > 4.0) ++ f = 4.0; + c->cfact = f; + arrange(selmon); + } +-- +2.19.1 + diff --git a/dwm/dwm-cfacts-vanitygaps-6.3.diff b/dwm/dwm-cfacts-vanitygaps-6.3.diff new file mode 100644 index 0000000..9c9a2e5 --- /dev/null +++ b/dwm/dwm-cfacts-vanitygaps-6.3.diff @@ -0,0 +1,994 @@ +From 3786a63796c661a8a66b4a10aa45dcf77cdf68ac Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:26:11 +0100 +Subject: [PATCH 2/2] vanitygaps - adds gaps to layouts + +This patch differentiates between inner and outer gaps as well as +horizontal and vertical gaps. + +The logic of these layouts also aims to be pixel perfect by ensuring +an even split of the available space and re-distributing the remainder +among the available clients. +--- + config.def.h | 38 ++- + dwm.c | 45 +-- + vanitygaps.c | 822 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 867 insertions(+), 38 deletions(-) + create mode 100644 vanitygaps.c + +diff --git a/config.def.h b/config.def.h +index 5b0cfde..b9c2e61 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,11 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const unsigned int gappih = 20; /* horiz inner gap between windows */ ++static const unsigned int gappiv = 10; /* vert inner gap between windows */ ++static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ ++static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ ++static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -37,11 +42,26 @@ static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ + static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + ++#define FORCE_VSPLIT 1 /* nrowgrid layout: force two clients to always split vertically */ ++#include "vanitygaps.c" ++ + static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ +- { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "[@]", spiral }, ++ { "[\\]", dwindle }, ++ { "D[]", deck }, ++ { "TTT", bstack }, ++ { "===", bstackhoriz }, ++ { "HHH", grid }, ++ { "###", nrowgrid }, ++ { "---", horizgrid }, ++ { ":::", gaplessgrid }, ++ { "|M|", centeredmaster }, ++ { ">M>", centeredfloatingmaster }, ++ { "><>", NULL }, /* no layout function means floating behavior */ ++ { NULL, NULL }, + }; + + /* key definitions */ +@@ -75,6 +95,22 @@ static Key keys[] = { + { MODKEY|ShiftMask, XK_l, setcfact, {.f = -0.25} }, + { MODKEY|ShiftMask, XK_o, setcfact, {.f = 0.00} }, + { MODKEY, XK_Return, zoom, {0} }, ++ { MODKEY|Mod4Mask, XK_u, incrgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_u, incrgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_i, incrigaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_i, incrigaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_o, incrogaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_o, incrogaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_6, incrihgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_6, incrihgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_7, incrivgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_7, incrivgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_8, incrohgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_8, incrohgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_9, incrovgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_9, incrovgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_0, togglegaps, {0} }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_0, defaultgaps, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +diff --git a/dwm.c b/dwm.c +index bcb155d..36582e1 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -120,6 +120,10 @@ struct Monitor { + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ ++ int gappih; /* horizontal gap between windows */ ++ int gappiv; /* vertical gap between windows */ ++ int gappoh; /* horizontal outer gaps */ ++ int gappov; /* vertical outer gaps */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; +@@ -211,7 +215,6 @@ 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 tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -641,6 +644,10 @@ createmon(void) + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; ++ m->gappih = gappih; ++ m->gappiv = gappiv; ++ m->gappoh = gappoh; ++ m->gappov = gappov; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); +@@ -1695,42 +1702,6 @@ tagmon(const Arg *arg) + sendmon(selmon->sel, dirtomon(arg->i)); + } + +-void +-tile(Monitor *m) +-{ +- unsigned int i, n, h, mw, my, ty; +- float mfacts = 0, sfacts = 0; +- Client *c; +- +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { +- if (n < m->nmaster) +- mfacts += c->cfact; +- else +- sfacts += c->cfact; +- } +- if (n == 0) +- return; +- +- if (n > m->nmaster) +- mw = m->nmaster ? m->ww * m->mfact : 0; +- else +- mw = m->ww; +- for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) +- if (i < m->nmaster) { +- h = (m->wh - my) * (c->cfact / mfacts); +- resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); +- if (my + HEIGHT(c) < m->wh) +- my += HEIGHT(c); +- mfacts -= c->cfact; +- } else { +- h = (m->wh - ty) * (c->cfact / sfacts); +- resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); +- if (ty + HEIGHT(c) < m->wh) +- ty += HEIGHT(c); +- sfacts -= c->cfact; +- } +-} +- + void + togglebar(const Arg *arg) + { +diff --git a/vanitygaps.c b/vanitygaps.c +new file mode 100644 +index 0000000..1a816b6 +--- /dev/null ++++ b/vanitygaps.c +@@ -0,0 +1,822 @@ ++/* Key binding functions */ ++static void defaultgaps(const Arg *arg); ++static void incrgaps(const Arg *arg); ++static void incrigaps(const Arg *arg); ++static void incrogaps(const Arg *arg); ++static void incrohgaps(const Arg *arg); ++static void incrovgaps(const Arg *arg); ++static void incrihgaps(const Arg *arg); ++static void incrivgaps(const Arg *arg); ++static void togglegaps(const Arg *arg); ++/* Layouts (delete the ones you do not need) */ ++static void bstack(Monitor *m); ++static void bstackhoriz(Monitor *m); ++static void centeredmaster(Monitor *m); ++static void centeredfloatingmaster(Monitor *m); ++static void deck(Monitor *m); ++static void dwindle(Monitor *m); ++static void fibonacci(Monitor *m, int s); ++static void grid(Monitor *m); ++static void nrowgrid(Monitor *m); ++static void spiral(Monitor *m); ++static void tile(Monitor *m); ++/* Internals */ ++static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); ++static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr); ++static void setgaps(int oh, int ov, int ih, int iv); ++ ++/* Settings */ ++#if !PERTAG_PATCH ++static int enablegaps = 1; ++#endif // PERTAG_PATCH ++ ++void ++setgaps(int oh, int ov, int ih, int iv) ++{ ++ if (oh < 0) oh = 0; ++ if (ov < 0) ov = 0; ++ if (ih < 0) ih = 0; ++ if (iv < 0) iv = 0; ++ ++ selmon->gappoh = oh; ++ selmon->gappov = ov; ++ selmon->gappih = ih; ++ selmon->gappiv = iv; ++ arrange(selmon); ++} ++ ++void ++togglegaps(const Arg *arg) ++{ ++ #if PERTAG_PATCH ++ selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag]; ++ #else ++ enablegaps = !enablegaps; ++ #endif // PERTAG_PATCH ++ arrange(NULL); ++} ++ ++void ++defaultgaps(const Arg *arg) ++{ ++ setgaps(gappoh, gappov, gappih, gappiv); ++} ++ ++void ++incrgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh + arg->i, ++ selmon->gappov + arg->i, ++ selmon->gappih + arg->i, ++ selmon->gappiv + arg->i ++ ); ++} ++ ++void ++incrigaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov, ++ selmon->gappih + arg->i, ++ selmon->gappiv + arg->i ++ ); ++} ++ ++void ++incrogaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh + arg->i, ++ selmon->gappov + arg->i, ++ selmon->gappih, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrohgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh + arg->i, ++ selmon->gappov, ++ selmon->gappih, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrovgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov + arg->i, ++ selmon->gappih, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrihgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov, ++ selmon->gappih + arg->i, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrivgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov, ++ selmon->gappih, ++ selmon->gappiv + arg->i ++ ); ++} ++ ++void ++getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) ++{ ++ unsigned int n, oe, ie; ++ #if PERTAG_PATCH ++ oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag]; ++ #else ++ oe = ie = enablegaps; ++ #endif // PERTAG_PATCH ++ Client *c; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (smartgaps && n == 1) { ++ oe = 0; // outer gaps disabled when only one client ++ } ++ ++ *oh = m->gappoh*oe; // outer horizontal gap ++ *ov = m->gappov*oe; // outer vertical gap ++ *ih = m->gappih*ie; // inner horizontal gap ++ *iv = m->gappiv*ie; // inner vertical gap ++ *nc = n; // number of clients ++} ++ ++void ++getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) ++{ ++ unsigned int n; ++ float mfacts = 0, sfacts = 0; ++ int mtotal = 0, stotal = 0; ++ Client *c; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ++ if (n < m->nmaster) ++ mfacts += c->cfact; ++ else ++ sfacts += c->cfact; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ++ if (n < m->nmaster) ++ mtotal += msize * (c->cfact / mfacts); ++ else ++ stotal += ssize * (c->cfact / sfacts); ++ ++ *mf = mfacts; // total factor of master area ++ *sf = sfacts; // total factor of stack area ++ *mr = msize - mtotal; // the remainder (rest) of pixels after a cfacts master split ++ *sr = ssize - stotal; // the remainder (rest) of pixels after a cfacts stack split ++} ++ ++/*** ++ * Layouts ++ */ ++ ++/* ++ * Bottomstack layout + gaps ++ * https://dwm.suckless.org/patches/bottomstack/ ++ */ ++static void ++bstack(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh; ++ mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); ++ sw = m->ww - 2*ov - iv * (n - m->nmaster - 1); ++ ++ if (m->nmaster && n > m->nmaster) { ++ sh = (mh - ih) * (1 - m->mfact); ++ mh = mh - ih - sh; ++ sx = mx; ++ sy = my + mh + ih; ++ } ++ ++ getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv; ++ } else { ++ resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); ++ sx += WIDTH(c) + iv; ++ } ++ } ++} ++ ++static void ++bstackhoriz(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ mh = m->wh - 2*oh; ++ sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); ++ mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); ++ sw = m->ww - 2*ov; ++ ++ if (m->nmaster && n > m->nmaster) { ++ sh = (mh - ih) * (1 - m->mfact); ++ mh = mh - ih - sh; ++ sy = my + mh + ih; ++ sh = m->wh - mh - 2*oh - ih * (n - m->nmaster); ++ } ++ ++ getfacts(m, mw, sh, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv; ++ } else { ++ resize(c, sx, sy, sw - (2*c->bw), sh * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); ++ sy += HEIGHT(c) + ih; ++ } ++ } ++} ++ ++/* ++ * Centred master layout + gaps ++ * https://dwm.suckless.org/patches/centeredmaster/ ++ */ ++void ++centeredmaster(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int lx = 0, ly = 0, lw = 0, lh = 0; ++ int rx = 0, ry = 0, rw = 0, rh = 0; ++ float mfacts = 0, lfacts = 0, rfacts = 0; ++ int mtotal = 0, ltotal = 0, rtotal = 0; ++ int mrest = 0, lrest = 0, rrest = 0; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ /* initialize areas */ ++ mx = m->wx + ov; ++ my = m->wy + oh; ++ mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1); ++ mw = m->ww - 2*ov; ++ lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1); ++ rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1)); ++ ++ if (m->nmaster && n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ if (n - m->nmaster > 1) { ++ /* ||<-S->|<---M--->|<-S->|| */ ++ mw = (m->ww - 2*ov - 2*iv) * m->mfact; ++ lw = (m->ww - mw - 2*ov - 2*iv) / 2; ++ rw = (m->ww - mw - 2*ov - 2*iv) - lw; ++ mx += lw + iv; ++ } else { ++ /* ||<---M--->|<-S->|| */ ++ mw = (mw - iv) * m->mfact; ++ lw = 0; ++ rw = m->ww - mw - iv - 2*ov; ++ } ++ lx = m->wx + ov; ++ ly = m->wy + oh; ++ rx = mx + mw + iv; ++ ry = m->wy + oh; ++ } ++ ++ /* calculate facts */ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ if (!m->nmaster || n < m->nmaster) ++ mfacts += c->cfact; ++ else if ((n - m->nmaster) % 2) ++ lfacts += c->cfact; // total factor of left hand stack area ++ else ++ rfacts += c->cfact; // total factor of right hand stack area ++ } ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ++ if (!m->nmaster || n < m->nmaster) ++ mtotal += mh * (c->cfact / mfacts); ++ else if ((n - m->nmaster) % 2) ++ ltotal += lh * (c->cfact / lfacts); ++ else ++ rtotal += rh * (c->cfact / rfacts); ++ ++ mrest = mh - mtotal; ++ lrest = lh - ltotal; ++ rrest = rh - rtotal; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (!m->nmaster || i < m->nmaster) { ++ /* nmaster clients are stacked vertically, in the center of the screen */ ++ resize(c, mx, my, mw - (2*c->bw), mh * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); ++ my += HEIGHT(c) + ih; ++ } else { ++ /* stack clients are stacked vertically */ ++ if ((i - m->nmaster) % 2 ) { ++ resize(c, lx, ly, lw - (2*c->bw), lh * (c->cfact / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); ++ ly += HEIGHT(c) + ih; ++ } else { ++ resize(c, rx, ry, rw - (2*c->bw), rh * (c->cfact / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); ++ ry += HEIGHT(c) + ih; ++ } ++ } ++ } ++} ++ ++void ++centeredfloatingmaster(Monitor *m) ++{ ++ unsigned int i, n; ++ float mfacts, sfacts; ++ float mivf = 1.0; // master inner vertical gap factor ++ int oh, ov, ih, iv, mrest, srest; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh; ++ mw = m->ww - 2*ov - iv*(n - 1); ++ sw = m->ww - 2*ov - iv*(n - m->nmaster - 1); ++ ++ if (m->nmaster && n > m->nmaster) { ++ mivf = 0.8; ++ /* go mfact box in the center if more than nmaster clients */ ++ if (m->ww > m->wh) { ++ mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1); ++ mh = m->wh * 0.9; ++ } else { ++ mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1); ++ mh = m->wh * m->mfact; ++ } ++ mx = m->wx + (m->ww - mw) / 2; ++ my = m->wy + (m->wh - mh - 2*oh) / 2; ++ ++ sx = m->wx + ov; ++ sy = m->wy + oh; ++ sh = m->wh - 2*oh; ++ } ++ ++ getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ /* nmaster clients are stacked horizontally, in the center of the screen */ ++ resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv*mivf; ++ } else { ++ /* stack clients are stacked horizontally */ ++ resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); ++ sx += WIDTH(c) + iv; ++ } ++} ++ ++/* ++ * Deck layout + gaps ++ * https://dwm.suckless.org/patches/deck/ ++ */ ++void ++deck(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); ++ sw = mw = m->ww - 2*ov; ++ ++ if (m->nmaster && n > m->nmaster) { ++ sw = (mw - iv) * (1 - m->mfact); ++ mw = mw - iv - sw; ++ sx = mx + mw + iv; ++ sh = m->wh - 2*oh; ++ } ++ ++ getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); ++ ++ if (n - m->nmaster > 0) /* override layout symbol */ ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw - (2*c->bw), mh * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); ++ my += HEIGHT(c) + ih; ++ } else { ++ resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0); ++ } ++} ++ ++/* ++ * Fibonacci layout + gaps ++ * https://dwm.suckless.org/patches/fibonacci/ ++ */ ++void ++fibonacci(Monitor *m, int s) ++{ ++ unsigned int i, n; ++ int nx, ny, nw, nh; ++ int oh, ov, ih, iv; ++ int nv, hrest = 0, wrest = 0, r = 1; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ nx = m->wx + ov; ++ ny = m->wy + oh; ++ nw = m->ww - 2*ov; ++ nh = m->wh - 2*oh; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++ if (r) { ++ if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw)) ++ || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) { ++ r = 0; ++ } ++ if (r && i < n - 1) { ++ if (i % 2) { ++ nv = (nh - ih) / 2; ++ hrest = nh - 2*nv - ih; ++ nh = nv; ++ } else { ++ nv = (nw - iv) / 2; ++ wrest = nw - 2*nv - iv; ++ nw = nv; ++ } ++ ++ if ((i % 4) == 2 && !s) ++ nx += nw + iv; ++ else if ((i % 4) == 3 && !s) ++ ny += nh + ih; ++ } ++ ++ if ((i % 4) == 0) { ++ if (s) { ++ ny += nh + ih; ++ nh += hrest; ++ } ++ else { ++ nh -= hrest; ++ ny -= nh + ih; ++ } ++ } ++ else if ((i % 4) == 1) { ++ nx += nw + iv; ++ nw += wrest; ++ } ++ else if ((i % 4) == 2) { ++ ny += nh + ih; ++ nh += hrest; ++ if (i < n - 1) ++ nw += wrest; ++ } ++ else if ((i % 4) == 3) { ++ if (s) { ++ nx += nw + iv; ++ nw -= wrest; ++ } else { ++ nw -= wrest; ++ nx -= nw + iv; ++ nh += hrest; ++ } ++ } ++ if (i == 0) { ++ if (n != 1) { ++ nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact); ++ wrest = 0; ++ } ++ ny = m->wy + oh; ++ } ++ else if (i == 1) ++ nw = m->ww - nw - iv - 2*ov; ++ i++; ++ } ++ ++ resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); ++ } ++} ++ ++void ++dwindle(Monitor *m) ++{ ++ fibonacci(m, 1); ++} ++ ++void ++spiral(Monitor *m) ++{ ++ fibonacci(m, 0); ++} ++ ++/* ++ * Gappless grid layout + gaps (ironically) ++ * https://dwm.suckless.org/patches/gaplessgrid/ ++ */ ++void ++gaplessgrid(Monitor *m) ++{ ++ unsigned int i, n; ++ int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters ++ int oh, ov, ih, iv; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ /* grid dimensions */ ++ for (cols = 0; cols <= n/2; cols++) ++ if (cols*cols >= n) ++ break; ++ if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ ++ cols = 2; ++ rows = n/cols; ++ cn = rn = 0; // reset column no, row no, client count ++ ++ ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; ++ cw = (m->ww - 2*ov - iv * (cols - 1)) / cols; ++ rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; ++ crest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; ++ x = m->wx + ov; ++ y = m->wy + oh; ++ ++ for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { ++ if (i/rows + 1 > cols - n%cols) { ++ rows = n/cols + 1; ++ ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; ++ rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; ++ } ++ resize(c, ++ x, ++ y + rn*(ch + ih) + MIN(rn, rrest), ++ cw + (cn < crest ? 1 : 0) - 2*c->bw, ++ ch + (rn < rrest ? 1 : 0) - 2*c->bw, ++ 0); ++ rn++; ++ if (rn >= rows) { ++ rn = 0; ++ x += cw + ih + (cn < crest ? 1 : 0); ++ cn++; ++ } ++ } ++} ++ ++/* ++ * Gridmode layout + gaps ++ * https://dwm.suckless.org/patches/gridmode/ ++ */ ++void ++grid(Monitor *m) ++{ ++ unsigned int i, n; ++ int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows; ++ int oh, ov, ih, iv; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ ++ /* grid dimensions */ ++ for (rows = 0; rows <= n/2; rows++) ++ if (rows*rows >= n) ++ break; ++ cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; ++ ++ /* window geoms (cell height/width) */ ++ ch = (m->wh - 2*oh - ih * (rows - 1)) / (rows ? rows : 1); ++ cw = (m->ww - 2*ov - iv * (cols - 1)) / (cols ? cols : 1); ++ chrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; ++ cwrest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ cc = i / rows; ++ cr = i % rows; ++ cx = m->wx + ov + cc * (cw + iv) + MIN(cc, cwrest); ++ cy = m->wy + oh + cr * (ch + ih) + MIN(cr, chrest); ++ resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); ++ } ++} ++ ++/* ++ * Horizontal grid layout + gaps ++ * https://dwm.suckless.org/patches/horizgrid/ ++ */ ++void ++horizgrid(Monitor *m) { ++ Client *c; ++ unsigned int n, i; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ int ntop, nbottom = 1; ++ float mfacts = 0, sfacts = 0; ++ int mrest, srest, mtotal = 0, stotal = 0; ++ ++ /* Count windows */ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ if (n <= 2) ++ ntop = n; ++ else { ++ ntop = n / 2; ++ nbottom = n - ntop; ++ } ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh; ++ sw = mw = m->ww - 2*ov; ++ ++ if (n > ntop) { ++ sh = (mh - ih) / 2; ++ mh = mh - ih - sh; ++ sy = my + mh + ih; ++ mw = m->ww - 2*ov - iv * (ntop - 1); ++ sw = m->ww - 2*ov - iv * (nbottom - 1); ++ } ++ ++ /* calculate facts */ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < ntop) ++ mfacts += c->cfact; ++ else ++ sfacts += c->cfact; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < ntop) ++ mtotal += mh * (c->cfact / mfacts); ++ else ++ stotal += sw * (c->cfact / sfacts); ++ ++ mrest = mh - mtotal; ++ srest = sw - stotal; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < ntop) { ++ resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv; ++ } else { ++ resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); ++ sx += WIDTH(c) + iv; ++ } ++} ++ ++/* ++ * nrowgrid layout + gaps ++ * https://dwm.suckless.org/patches/nrowgrid/ ++ */ ++void ++nrowgrid(Monitor *m) ++{ ++ unsigned int n; ++ int ri = 0, ci = 0; /* counters */ ++ int oh, ov, ih, iv; /* vanitygap settings */ ++ unsigned int cx, cy, cw, ch; /* client geometry */ ++ unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */ ++ unsigned int cols, rows = m->nmaster + 1; ++ Client *c; ++ ++ /* count clients */ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ ++ /* nothing to do here */ ++ if (n == 0) ++ return; ++ ++ /* force 2 clients to always split vertically */ ++ if (FORCE_VSPLIT && n == 2) ++ rows = 1; ++ ++ /* never allow empty rows */ ++ if (n < rows) ++ rows = n; ++ ++ /* define first row */ ++ cols = n / rows; ++ uc = cols; ++ cy = m->wy + oh; ++ ch = (m->wh - 2*oh - ih*(rows - 1)) / rows; ++ uh = ch; ++ ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next), ci++) { ++ if (ci == cols) { ++ uw = 0; ++ ci = 0; ++ ri++; ++ ++ /* next row */ ++ cols = (n - uc) / (rows - ri); ++ uc += cols; ++ cy = m->wy + oh + uh + ih; ++ uh += ch + ih; ++ } ++ ++ cx = m->wx + ov + uw; ++ cw = (m->ww - 2*ov - uw) / (cols - ci); ++ uw += cw + iv; ++ ++ resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0); ++ } ++} ++ ++/* ++ * Default tile layout + gaps ++ */ ++static void ++tile(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); ++ sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); ++ sw = mw = m->ww - 2*ov; ++ ++ if (m->nmaster && n > m->nmaster) { ++ sw = (mw - iv) * (1 - m->mfact); ++ mw = mw - iv - sw; ++ sx = mx + mw + iv; ++ } ++ ++ getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw - (2*c->bw), mh * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); ++ my += HEIGHT(c) + ih; ++ } else { ++ resize(c, sx, sy, sw - (2*c->bw), sh * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); ++ sy += HEIGHT(c) + ih; ++ } ++} +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-cfacts-vanitygaps-6.3_full.diff b/dwm/dwm-cfacts-vanitygaps-6.3_full.diff new file mode 100644 index 0000000..5bbfae8 --- /dev/null +++ b/dwm/dwm-cfacts-vanitygaps-6.3_full.diff @@ -0,0 +1,1114 @@ +From 112abfec73e12282e4b0288f2e7739000ad135b6 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:21:06 +0100 +Subject: [PATCH 1/2] Adding cfacts patch which provides the ability to assign + different weights to clients in their respective stack in tiled layout. + +Refer to https://dwm.suckless.org/patches/cfacts/ +--- + config.def.h | 3 +++ + dwm.c | 35 ++++++++++++++++++++++++++++++++--- + 2 files changed, 35 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..5b0cfde 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -71,6 +71,9 @@ static Key keys[] = { + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { MODKEY|ShiftMask, XK_h, setcfact, {.f = +0.25} }, ++ { MODKEY|ShiftMask, XK_l, setcfact, {.f = -0.25} }, ++ { MODKEY|ShiftMask, XK_o, setcfact, {.f = 0.00} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..bcb155d 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -87,6 +87,7 @@ typedef struct Client Client; + struct Client { + char name[256]; + float mina, maxa; ++ float cfact; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; +@@ -201,6 +202,7 @@ static void setclientstate(Client *c, long state); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); + static void setlayout(const Arg *arg); ++static void setcfact(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); + static void seturgent(Client *c, int urg); +@@ -1033,6 +1035,7 @@ manage(Window w, XWindowAttributes *wa) + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; ++ c->cfact = 1.0; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { +@@ -1515,6 +1518,24 @@ setlayout(const Arg *arg) + drawbar(selmon); + } + ++void ++setcfact(const Arg *arg) { ++ float f; ++ Client *c; ++ ++ c = selmon->sel; ++ ++ if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ f = arg->f + c->cfact; ++ if(arg->f == 0.0) ++ f = 1.0; ++ else if(f < 0.25 || f > 4.0) ++ return; ++ c->cfact = f; ++ arrange(selmon); ++} ++ + /* arg > 1.0 will set mfact absolutely */ + void + setmfact(const Arg *arg) +@@ -1678,9 +1699,15 @@ void + tile(Monitor *m) + { + unsigned int i, n, h, mw, my, ty; ++ float mfacts = 0, sfacts = 0; + Client *c; + +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ if (n < m->nmaster) ++ mfacts += c->cfact; ++ else ++ sfacts += c->cfact; ++ } + if (n == 0) + return; + +@@ -1690,15 +1717,17 @@ tile(Monitor *m) + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { +- h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ h = (m->wh - my) * (c->cfact / mfacts); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); ++ mfacts -= c->cfact; + } else { +- h = (m->wh - ty) / (n - i); ++ h = (m->wh - ty) * (c->cfact / sfacts); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); ++ sfacts -= c->cfact; + } + } + +-- +2.19.1 + + +From 3786a63796c661a8a66b4a10aa45dcf77cdf68ac Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:26:11 +0100 +Subject: [PATCH 2/2] vanitygaps - adds gaps to layouts + +This patch differentiates between inner and outer gaps as well as +horizontal and vertical gaps. + +The logic of these layouts also aims to be pixel perfect by ensuring +an even split of the available space and re-distributing the remainder +among the available clients. +--- + config.def.h | 38 ++- + dwm.c | 45 +-- + vanitygaps.c | 822 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 867 insertions(+), 38 deletions(-) + create mode 100644 vanitygaps.c + +diff --git a/config.def.h b/config.def.h +index 5b0cfde..b9c2e61 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,11 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const unsigned int gappih = 20; /* horiz inner gap between windows */ ++static const unsigned int gappiv = 10; /* vert inner gap between windows */ ++static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ ++static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ ++static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -37,11 +42,26 @@ static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ + static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + ++#define FORCE_VSPLIT 1 /* nrowgrid layout: force two clients to always split vertically */ ++#include "vanitygaps.c" ++ + static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ +- { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "[@]", spiral }, ++ { "[\\]", dwindle }, ++ { "D[]", deck }, ++ { "TTT", bstack }, ++ { "===", bstackhoriz }, ++ { "HHH", grid }, ++ { "###", nrowgrid }, ++ { "---", horizgrid }, ++ { ":::", gaplessgrid }, ++ { "|M|", centeredmaster }, ++ { ">M>", centeredfloatingmaster }, ++ { "><>", NULL }, /* no layout function means floating behavior */ ++ { NULL, NULL }, + }; + + /* key definitions */ +@@ -75,6 +95,22 @@ static Key keys[] = { + { MODKEY|ShiftMask, XK_l, setcfact, {.f = -0.25} }, + { MODKEY|ShiftMask, XK_o, setcfact, {.f = 0.00} }, + { MODKEY, XK_Return, zoom, {0} }, ++ { MODKEY|Mod4Mask, XK_u, incrgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_u, incrgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_i, incrigaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_i, incrigaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_o, incrogaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_o, incrogaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_6, incrihgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_6, incrihgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_7, incrivgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_7, incrivgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_8, incrohgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_8, incrohgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_9, incrovgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_9, incrovgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_0, togglegaps, {0} }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_0, defaultgaps, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +diff --git a/dwm.c b/dwm.c +index bcb155d..36582e1 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -120,6 +120,10 @@ struct Monitor { + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ ++ int gappih; /* horizontal gap between windows */ ++ int gappiv; /* vertical gap between windows */ ++ int gappoh; /* horizontal outer gaps */ ++ int gappov; /* vertical outer gaps */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; +@@ -211,7 +215,6 @@ 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 tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -641,6 +644,10 @@ createmon(void) + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; ++ m->gappih = gappih; ++ m->gappiv = gappiv; ++ m->gappoh = gappoh; ++ m->gappov = gappov; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); +@@ -1695,42 +1702,6 @@ tagmon(const Arg *arg) + sendmon(selmon->sel, dirtomon(arg->i)); + } + +-void +-tile(Monitor *m) +-{ +- unsigned int i, n, h, mw, my, ty; +- float mfacts = 0, sfacts = 0; +- Client *c; +- +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { +- if (n < m->nmaster) +- mfacts += c->cfact; +- else +- sfacts += c->cfact; +- } +- if (n == 0) +- return; +- +- if (n > m->nmaster) +- mw = m->nmaster ? m->ww * m->mfact : 0; +- else +- mw = m->ww; +- for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) +- if (i < m->nmaster) { +- h = (m->wh - my) * (c->cfact / mfacts); +- resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); +- if (my + HEIGHT(c) < m->wh) +- my += HEIGHT(c); +- mfacts -= c->cfact; +- } else { +- h = (m->wh - ty) * (c->cfact / sfacts); +- resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); +- if (ty + HEIGHT(c) < m->wh) +- ty += HEIGHT(c); +- sfacts -= c->cfact; +- } +-} +- + void + togglebar(const Arg *arg) + { +diff --git a/vanitygaps.c b/vanitygaps.c +new file mode 100644 +index 0000000..1a816b6 +--- /dev/null ++++ b/vanitygaps.c +@@ -0,0 +1,822 @@ ++/* Key binding functions */ ++static void defaultgaps(const Arg *arg); ++static void incrgaps(const Arg *arg); ++static void incrigaps(const Arg *arg); ++static void incrogaps(const Arg *arg); ++static void incrohgaps(const Arg *arg); ++static void incrovgaps(const Arg *arg); ++static void incrihgaps(const Arg *arg); ++static void incrivgaps(const Arg *arg); ++static void togglegaps(const Arg *arg); ++/* Layouts (delete the ones you do not need) */ ++static void bstack(Monitor *m); ++static void bstackhoriz(Monitor *m); ++static void centeredmaster(Monitor *m); ++static void centeredfloatingmaster(Monitor *m); ++static void deck(Monitor *m); ++static void dwindle(Monitor *m); ++static void fibonacci(Monitor *m, int s); ++static void grid(Monitor *m); ++static void nrowgrid(Monitor *m); ++static void spiral(Monitor *m); ++static void tile(Monitor *m); ++/* Internals */ ++static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); ++static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr); ++static void setgaps(int oh, int ov, int ih, int iv); ++ ++/* Settings */ ++#if !PERTAG_PATCH ++static int enablegaps = 1; ++#endif // PERTAG_PATCH ++ ++void ++setgaps(int oh, int ov, int ih, int iv) ++{ ++ if (oh < 0) oh = 0; ++ if (ov < 0) ov = 0; ++ if (ih < 0) ih = 0; ++ if (iv < 0) iv = 0; ++ ++ selmon->gappoh = oh; ++ selmon->gappov = ov; ++ selmon->gappih = ih; ++ selmon->gappiv = iv; ++ arrange(selmon); ++} ++ ++void ++togglegaps(const Arg *arg) ++{ ++ #if PERTAG_PATCH ++ selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag]; ++ #else ++ enablegaps = !enablegaps; ++ #endif // PERTAG_PATCH ++ arrange(NULL); ++} ++ ++void ++defaultgaps(const Arg *arg) ++{ ++ setgaps(gappoh, gappov, gappih, gappiv); ++} ++ ++void ++incrgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh + arg->i, ++ selmon->gappov + arg->i, ++ selmon->gappih + arg->i, ++ selmon->gappiv + arg->i ++ ); ++} ++ ++void ++incrigaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov, ++ selmon->gappih + arg->i, ++ selmon->gappiv + arg->i ++ ); ++} ++ ++void ++incrogaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh + arg->i, ++ selmon->gappov + arg->i, ++ selmon->gappih, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrohgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh + arg->i, ++ selmon->gappov, ++ selmon->gappih, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrovgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov + arg->i, ++ selmon->gappih, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrihgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov, ++ selmon->gappih + arg->i, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrivgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov, ++ selmon->gappih, ++ selmon->gappiv + arg->i ++ ); ++} ++ ++void ++getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) ++{ ++ unsigned int n, oe, ie; ++ #if PERTAG_PATCH ++ oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag]; ++ #else ++ oe = ie = enablegaps; ++ #endif // PERTAG_PATCH ++ Client *c; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (smartgaps && n == 1) { ++ oe = 0; // outer gaps disabled when only one client ++ } ++ ++ *oh = m->gappoh*oe; // outer horizontal gap ++ *ov = m->gappov*oe; // outer vertical gap ++ *ih = m->gappih*ie; // inner horizontal gap ++ *iv = m->gappiv*ie; // inner vertical gap ++ *nc = n; // number of clients ++} ++ ++void ++getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) ++{ ++ unsigned int n; ++ float mfacts = 0, sfacts = 0; ++ int mtotal = 0, stotal = 0; ++ Client *c; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ++ if (n < m->nmaster) ++ mfacts += c->cfact; ++ else ++ sfacts += c->cfact; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ++ if (n < m->nmaster) ++ mtotal += msize * (c->cfact / mfacts); ++ else ++ stotal += ssize * (c->cfact / sfacts); ++ ++ *mf = mfacts; // total factor of master area ++ *sf = sfacts; // total factor of stack area ++ *mr = msize - mtotal; // the remainder (rest) of pixels after a cfacts master split ++ *sr = ssize - stotal; // the remainder (rest) of pixels after a cfacts stack split ++} ++ ++/*** ++ * Layouts ++ */ ++ ++/* ++ * Bottomstack layout + gaps ++ * https://dwm.suckless.org/patches/bottomstack/ ++ */ ++static void ++bstack(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh; ++ mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); ++ sw = m->ww - 2*ov - iv * (n - m->nmaster - 1); ++ ++ if (m->nmaster && n > m->nmaster) { ++ sh = (mh - ih) * (1 - m->mfact); ++ mh = mh - ih - sh; ++ sx = mx; ++ sy = my + mh + ih; ++ } ++ ++ getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv; ++ } else { ++ resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); ++ sx += WIDTH(c) + iv; ++ } ++ } ++} ++ ++static void ++bstackhoriz(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ mh = m->wh - 2*oh; ++ sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); ++ mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); ++ sw = m->ww - 2*ov; ++ ++ if (m->nmaster && n > m->nmaster) { ++ sh = (mh - ih) * (1 - m->mfact); ++ mh = mh - ih - sh; ++ sy = my + mh + ih; ++ sh = m->wh - mh - 2*oh - ih * (n - m->nmaster); ++ } ++ ++ getfacts(m, mw, sh, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv; ++ } else { ++ resize(c, sx, sy, sw - (2*c->bw), sh * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); ++ sy += HEIGHT(c) + ih; ++ } ++ } ++} ++ ++/* ++ * Centred master layout + gaps ++ * https://dwm.suckless.org/patches/centeredmaster/ ++ */ ++void ++centeredmaster(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int lx = 0, ly = 0, lw = 0, lh = 0; ++ int rx = 0, ry = 0, rw = 0, rh = 0; ++ float mfacts = 0, lfacts = 0, rfacts = 0; ++ int mtotal = 0, ltotal = 0, rtotal = 0; ++ int mrest = 0, lrest = 0, rrest = 0; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ /* initialize areas */ ++ mx = m->wx + ov; ++ my = m->wy + oh; ++ mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1); ++ mw = m->ww - 2*ov; ++ lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1); ++ rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1)); ++ ++ if (m->nmaster && n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ if (n - m->nmaster > 1) { ++ /* ||<-S->|<---M--->|<-S->|| */ ++ mw = (m->ww - 2*ov - 2*iv) * m->mfact; ++ lw = (m->ww - mw - 2*ov - 2*iv) / 2; ++ rw = (m->ww - mw - 2*ov - 2*iv) - lw; ++ mx += lw + iv; ++ } else { ++ /* ||<---M--->|<-S->|| */ ++ mw = (mw - iv) * m->mfact; ++ lw = 0; ++ rw = m->ww - mw - iv - 2*ov; ++ } ++ lx = m->wx + ov; ++ ly = m->wy + oh; ++ rx = mx + mw + iv; ++ ry = m->wy + oh; ++ } ++ ++ /* calculate facts */ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ if (!m->nmaster || n < m->nmaster) ++ mfacts += c->cfact; ++ else if ((n - m->nmaster) % 2) ++ lfacts += c->cfact; // total factor of left hand stack area ++ else ++ rfacts += c->cfact; // total factor of right hand stack area ++ } ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ++ if (!m->nmaster || n < m->nmaster) ++ mtotal += mh * (c->cfact / mfacts); ++ else if ((n - m->nmaster) % 2) ++ ltotal += lh * (c->cfact / lfacts); ++ else ++ rtotal += rh * (c->cfact / rfacts); ++ ++ mrest = mh - mtotal; ++ lrest = lh - ltotal; ++ rrest = rh - rtotal; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (!m->nmaster || i < m->nmaster) { ++ /* nmaster clients are stacked vertically, in the center of the screen */ ++ resize(c, mx, my, mw - (2*c->bw), mh * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); ++ my += HEIGHT(c) + ih; ++ } else { ++ /* stack clients are stacked vertically */ ++ if ((i - m->nmaster) % 2 ) { ++ resize(c, lx, ly, lw - (2*c->bw), lh * (c->cfact / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); ++ ly += HEIGHT(c) + ih; ++ } else { ++ resize(c, rx, ry, rw - (2*c->bw), rh * (c->cfact / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); ++ ry += HEIGHT(c) + ih; ++ } ++ } ++ } ++} ++ ++void ++centeredfloatingmaster(Monitor *m) ++{ ++ unsigned int i, n; ++ float mfacts, sfacts; ++ float mivf = 1.0; // master inner vertical gap factor ++ int oh, ov, ih, iv, mrest, srest; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh; ++ mw = m->ww - 2*ov - iv*(n - 1); ++ sw = m->ww - 2*ov - iv*(n - m->nmaster - 1); ++ ++ if (m->nmaster && n > m->nmaster) { ++ mivf = 0.8; ++ /* go mfact box in the center if more than nmaster clients */ ++ if (m->ww > m->wh) { ++ mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1); ++ mh = m->wh * 0.9; ++ } else { ++ mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1); ++ mh = m->wh * m->mfact; ++ } ++ mx = m->wx + (m->ww - mw) / 2; ++ my = m->wy + (m->wh - mh - 2*oh) / 2; ++ ++ sx = m->wx + ov; ++ sy = m->wy + oh; ++ sh = m->wh - 2*oh; ++ } ++ ++ getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ /* nmaster clients are stacked horizontally, in the center of the screen */ ++ resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv*mivf; ++ } else { ++ /* stack clients are stacked horizontally */ ++ resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); ++ sx += WIDTH(c) + iv; ++ } ++} ++ ++/* ++ * Deck layout + gaps ++ * https://dwm.suckless.org/patches/deck/ ++ */ ++void ++deck(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); ++ sw = mw = m->ww - 2*ov; ++ ++ if (m->nmaster && n > m->nmaster) { ++ sw = (mw - iv) * (1 - m->mfact); ++ mw = mw - iv - sw; ++ sx = mx + mw + iv; ++ sh = m->wh - 2*oh; ++ } ++ ++ getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); ++ ++ if (n - m->nmaster > 0) /* override layout symbol */ ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw - (2*c->bw), mh * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); ++ my += HEIGHT(c) + ih; ++ } else { ++ resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0); ++ } ++} ++ ++/* ++ * Fibonacci layout + gaps ++ * https://dwm.suckless.org/patches/fibonacci/ ++ */ ++void ++fibonacci(Monitor *m, int s) ++{ ++ unsigned int i, n; ++ int nx, ny, nw, nh; ++ int oh, ov, ih, iv; ++ int nv, hrest = 0, wrest = 0, r = 1; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ nx = m->wx + ov; ++ ny = m->wy + oh; ++ nw = m->ww - 2*ov; ++ nh = m->wh - 2*oh; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++ if (r) { ++ if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw)) ++ || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) { ++ r = 0; ++ } ++ if (r && i < n - 1) { ++ if (i % 2) { ++ nv = (nh - ih) / 2; ++ hrest = nh - 2*nv - ih; ++ nh = nv; ++ } else { ++ nv = (nw - iv) / 2; ++ wrest = nw - 2*nv - iv; ++ nw = nv; ++ } ++ ++ if ((i % 4) == 2 && !s) ++ nx += nw + iv; ++ else if ((i % 4) == 3 && !s) ++ ny += nh + ih; ++ } ++ ++ if ((i % 4) == 0) { ++ if (s) { ++ ny += nh + ih; ++ nh += hrest; ++ } ++ else { ++ nh -= hrest; ++ ny -= nh + ih; ++ } ++ } ++ else if ((i % 4) == 1) { ++ nx += nw + iv; ++ nw += wrest; ++ } ++ else if ((i % 4) == 2) { ++ ny += nh + ih; ++ nh += hrest; ++ if (i < n - 1) ++ nw += wrest; ++ } ++ else if ((i % 4) == 3) { ++ if (s) { ++ nx += nw + iv; ++ nw -= wrest; ++ } else { ++ nw -= wrest; ++ nx -= nw + iv; ++ nh += hrest; ++ } ++ } ++ if (i == 0) { ++ if (n != 1) { ++ nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact); ++ wrest = 0; ++ } ++ ny = m->wy + oh; ++ } ++ else if (i == 1) ++ nw = m->ww - nw - iv - 2*ov; ++ i++; ++ } ++ ++ resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); ++ } ++} ++ ++void ++dwindle(Monitor *m) ++{ ++ fibonacci(m, 1); ++} ++ ++void ++spiral(Monitor *m) ++{ ++ fibonacci(m, 0); ++} ++ ++/* ++ * Gappless grid layout + gaps (ironically) ++ * https://dwm.suckless.org/patches/gaplessgrid/ ++ */ ++void ++gaplessgrid(Monitor *m) ++{ ++ unsigned int i, n; ++ int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters ++ int oh, ov, ih, iv; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ /* grid dimensions */ ++ for (cols = 0; cols <= n/2; cols++) ++ if (cols*cols >= n) ++ break; ++ if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ ++ cols = 2; ++ rows = n/cols; ++ cn = rn = 0; // reset column no, row no, client count ++ ++ ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; ++ cw = (m->ww - 2*ov - iv * (cols - 1)) / cols; ++ rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; ++ crest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; ++ x = m->wx + ov; ++ y = m->wy + oh; ++ ++ for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { ++ if (i/rows + 1 > cols - n%cols) { ++ rows = n/cols + 1; ++ ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; ++ rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; ++ } ++ resize(c, ++ x, ++ y + rn*(ch + ih) + MIN(rn, rrest), ++ cw + (cn < crest ? 1 : 0) - 2*c->bw, ++ ch + (rn < rrest ? 1 : 0) - 2*c->bw, ++ 0); ++ rn++; ++ if (rn >= rows) { ++ rn = 0; ++ x += cw + ih + (cn < crest ? 1 : 0); ++ cn++; ++ } ++ } ++} ++ ++/* ++ * Gridmode layout + gaps ++ * https://dwm.suckless.org/patches/gridmode/ ++ */ ++void ++grid(Monitor *m) ++{ ++ unsigned int i, n; ++ int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows; ++ int oh, ov, ih, iv; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ ++ /* grid dimensions */ ++ for (rows = 0; rows <= n/2; rows++) ++ if (rows*rows >= n) ++ break; ++ cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; ++ ++ /* window geoms (cell height/width) */ ++ ch = (m->wh - 2*oh - ih * (rows - 1)) / (rows ? rows : 1); ++ cw = (m->ww - 2*ov - iv * (cols - 1)) / (cols ? cols : 1); ++ chrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; ++ cwrest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ cc = i / rows; ++ cr = i % rows; ++ cx = m->wx + ov + cc * (cw + iv) + MIN(cc, cwrest); ++ cy = m->wy + oh + cr * (ch + ih) + MIN(cr, chrest); ++ resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); ++ } ++} ++ ++/* ++ * Horizontal grid layout + gaps ++ * https://dwm.suckless.org/patches/horizgrid/ ++ */ ++void ++horizgrid(Monitor *m) { ++ Client *c; ++ unsigned int n, i; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ int ntop, nbottom = 1; ++ float mfacts = 0, sfacts = 0; ++ int mrest, srest, mtotal = 0, stotal = 0; ++ ++ /* Count windows */ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ if (n <= 2) ++ ntop = n; ++ else { ++ ntop = n / 2; ++ nbottom = n - ntop; ++ } ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh; ++ sw = mw = m->ww - 2*ov; ++ ++ if (n > ntop) { ++ sh = (mh - ih) / 2; ++ mh = mh - ih - sh; ++ sy = my + mh + ih; ++ mw = m->ww - 2*ov - iv * (ntop - 1); ++ sw = m->ww - 2*ov - iv * (nbottom - 1); ++ } ++ ++ /* calculate facts */ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < ntop) ++ mfacts += c->cfact; ++ else ++ sfacts += c->cfact; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < ntop) ++ mtotal += mh * (c->cfact / mfacts); ++ else ++ stotal += sw * (c->cfact / sfacts); ++ ++ mrest = mh - mtotal; ++ srest = sw - stotal; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < ntop) { ++ resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv; ++ } else { ++ resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); ++ sx += WIDTH(c) + iv; ++ } ++} ++ ++/* ++ * nrowgrid layout + gaps ++ * https://dwm.suckless.org/patches/nrowgrid/ ++ */ ++void ++nrowgrid(Monitor *m) ++{ ++ unsigned int n; ++ int ri = 0, ci = 0; /* counters */ ++ int oh, ov, ih, iv; /* vanitygap settings */ ++ unsigned int cx, cy, cw, ch; /* client geometry */ ++ unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */ ++ unsigned int cols, rows = m->nmaster + 1; ++ Client *c; ++ ++ /* count clients */ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ ++ /* nothing to do here */ ++ if (n == 0) ++ return; ++ ++ /* force 2 clients to always split vertically */ ++ if (FORCE_VSPLIT && n == 2) ++ rows = 1; ++ ++ /* never allow empty rows */ ++ if (n < rows) ++ rows = n; ++ ++ /* define first row */ ++ cols = n / rows; ++ uc = cols; ++ cy = m->wy + oh; ++ ch = (m->wh - 2*oh - ih*(rows - 1)) / rows; ++ uh = ch; ++ ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next), ci++) { ++ if (ci == cols) { ++ uw = 0; ++ ci = 0; ++ ri++; ++ ++ /* next row */ ++ cols = (n - uc) / (rows - ri); ++ uc += cols; ++ cy = m->wy + oh + uh + ih; ++ uh += ch + ih; ++ } ++ ++ cx = m->wx + ov + uw; ++ cw = (m->ww - 2*ov - uw) / (cols - ci); ++ uw += cw + iv; ++ ++ resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0); ++ } ++} ++ ++/* ++ * Default tile layout + gaps ++ */ ++static void ++tile(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); ++ sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); ++ sw = mw = m->ww - 2*ov; ++ ++ if (m->nmaster && n > m->nmaster) { ++ sw = (mw - iv) * (1 - m->mfact); ++ mw = mw - iv - sw; ++ sx = mx + mw + iv; ++ } ++ ++ getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw - (2*c->bw), mh * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); ++ my += HEIGHT(c) + ih; ++ } else { ++ resize(c, sx, sy, sw - (2*c->bw), sh * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); ++ sy += HEIGHT(c) + ih; ++ } ++} +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-cyclelayouts-6.3.diff b/dwm/dwm-cyclelayouts-6.3.diff new file mode 100644 index 0000000..12915c5 --- /dev/null +++ b/dwm/dwm-cyclelayouts-6.3.diff @@ -0,0 +1,95 @@ +From 48d8560319e18a0325007a1f765f0589fb2dbfc8 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:26:41 +0100 +Subject: [PATCH] cyclelayout, function to cycle through available layouts. + +MOD-CTRL-, and MOD-CTRL-. +cycle backwards and forwards through available layouts. +Probably only useful if you have a lot of additional layouts. +The NULL, NULL layout should always be the last layout in your list, +in order to guarantee consistent behavior. + +Refer to https://dwm.suckless.org/patches/cyclelayouts/ +--- + config.def.h | 3 +++ + dwm.1 | 6 ++++++ + dwm.c | 18 ++++++++++++++++++ + 3 files changed, 27 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..938fd60 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -42,6 +42,7 @@ static const Layout layouts[] = { + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { NULL, NULL }, + }; + + /* key definitions */ +@@ -85,6 +86,8 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY|ControlMask, XK_comma, cyclelayout, {.i = -1 } }, ++ { MODKEY|ControlMask, XK_period, cyclelayout, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/dwm.1 b/dwm.1 +index ddc8321..829047b 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -92,6 +92,12 @@ Sets monocle layout. + .B Mod1\-space + Toggles between current and previous layout. + .TP ++.B Mod1\-Control\-, ++Cycles backwards in layout list. ++.TP ++.B Mod1\-Control\-. ++Cycles forwards in layout list. ++.TP + .B Mod1\-j + Focus next window. + .TP +diff --git a/dwm.c b/dwm.c +index a96f33c..2d98c2b 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -157,6 +157,7 @@ static void configure(Client *c); + static void configurenotify(XEvent *e); + static void configurerequest(XEvent *e); + static Monitor *createmon(void); ++static void cyclelayout(const Arg *arg); + static void destroynotify(XEvent *e); + static void detach(Client *c); + static void detachstack(Client *c); +@@ -645,6 +646,23 @@ createmon(void) + return m; + } + ++void ++cyclelayout(const Arg *arg) { ++ Layout *l; ++ for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++); ++ if(arg->i > 0) { ++ if(l->symbol && (l + 1)->symbol) ++ setlayout(&((Arg) { .v = (l + 1) })); ++ else ++ setlayout(&((Arg) { .v = layouts })); ++ } else { ++ if(l != layouts && (l - 1)->symbol) ++ setlayout(&((Arg) { .v = (l - 1) })); ++ else ++ setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] })); ++ } ++} ++ + void + destroynotify(XEvent *e) + { +-- +2.19.1 + diff --git a/dwm/dwm-desktop-6.3.diff b/dwm/dwm-desktop-6.3.diff new file mode 100644 index 0000000..138acfa --- /dev/null +++ b/dwm/dwm-desktop-6.3.diff @@ -0,0 +1,53 @@ +From 4c76c1e41ce0f3d48bac61993e71adbc6239bba1 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:28:06 +0100 +Subject: [PATCH] Adding desktop patch + +--- + dwm.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..5c76f0b 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1743,12 +1743,14 @@ toggletag(const Arg *arg) + void + toggleview(const Arg *arg) + { ++ Monitor *m; + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { +- selmon->tagset[selmon->seltags] = newtagset; ++ for (m = mons; m; m = m->next) ++ m->tagset[m->seltags] = newtagset; + focus(NULL); +- arrange(selmon); ++ arrange(NULL); + } + } + +@@ -2041,13 +2043,16 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { ++ Monitor *m; + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; +- selmon->seltags ^= 1; /* toggle sel tagset */ ++ for (m = mons; m; m = m->next) ++ m->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) +- selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ for (m = mons; m; m = m->next) ++ m->tagset[m->seltags] = arg->ui & TAGMASK; + focus(NULL); +- arrange(selmon); ++ arrange(NULL); + } + + Client * +-- +2.19.1 + diff --git a/dwm/dwm-desktop_icons-6.3.diff b/dwm/dwm-desktop_icons-6.3.diff new file mode 100644 index 0000000..34f6d14 --- /dev/null +++ b/dwm/dwm-desktop_icons-6.3.diff @@ -0,0 +1,59 @@ +From 7014c3f566124fc3612cc2e720efbf8678a046eb Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:29:24 +0100 +Subject: [PATCH] desktop icons patch + +New windows that has the _NET_WM_WINDOW_TYPE_DESKTOP window type +will not be managed by dwm. + +Example programs that this applies to: + - pcmanfm --desktop + - nautilus -n (legacy) + - nemo-desktop + - caja --force-desktop --no-default-window + +Note that these applications have a tendency of managing the +background wallpaper as well, the exception being nemo-desktop +where it appears to just be transparent if a compositor is used. +--- + dwm.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/dwm.c b/dwm.c +index a96f33c..efe966f 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -62,6 +62,7 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, ++ NetWMWindowTypeDesktop, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, +@@ -1027,6 +1028,14 @@ manage(Window w, XWindowAttributes *wa) + + c = ecalloc(1, sizeof(Client)); + c->win = w; ++ ++ if (getatomprop(c, netatom[NetWMWindowType]) == netatom[NetWMWindowTypeDesktop]) { ++ XMapWindow(dpy, c->win); ++ XLowerWindow(dpy, c->win); ++ free(c); ++ return; ++ } ++ + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; +@@ -1565,6 +1574,7 @@ setup(void) + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); ++ netatom[NetWMWindowTypeDesktop] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); +-- +2.19.1 + diff --git a/dwm/dwm-dragmfact-6.3.diff b/dwm/dwm-dragmfact-6.3.diff new file mode 100644 index 0000000..935ac48 --- /dev/null +++ b/dwm/dwm-dragmfact-6.3.diff @@ -0,0 +1,286 @@ +From 8643834a556125bb8c68583c46d4cac6a82ebe87 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:29:56 +0100 +Subject: [PATCH] dragmfact patch with smooth sliding and support for multiple + layouts + +--- + config.def.h | 1 + + dwm.c | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 228 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..f20315f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -108,6 +108,7 @@ static Button buttons[] = { + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, ++ { ClkClientWin, MODKEY|ShiftMask, Button1, dragmfact, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..02283e7 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -58,7 +58,7 @@ + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +-enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ ++enum { CurNormal, CurResize, CurMove, CurResizeHorzArrow, CurResizeVertArrow, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, +@@ -161,6 +161,7 @@ static void destroynotify(XEvent *e); + static void detach(Client *c); + static void detachstack(Client *c); + static Monitor *dirtomon(int dir); ++static void dragmfact(const Arg *arg); + static void drawbar(Monitor *m); + static void drawbars(void); + static void enternotify(XEvent *e); +@@ -693,6 +694,229 @@ dirtomon(int dir) + return m; + } + ++void ++dragmfact(const Arg *arg) ++{ ++ unsigned int n; ++ int py, px; // pointer coordinates ++ int ax, ay, aw, ah; // area position, width and height ++ int center = 0, horizontal = 0, mirror = 0, fixed = 0; // layout configuration ++ double fact; ++ Monitor *m; ++ XEvent ev; ++ Time lasttime = 0; ++ ++ m = selmon; ++ ++ #if VANITYGAPS_PATCH ++ int oh, ov, ih, iv; ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ #else ++ Client *c; ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ #endif // VANITYGAPS_PATCH ++ ++ ax = m->wx; ++ ay = m->wy; ++ ah = m->wh; ++ aw = m->ww; ++ ++ if (!n) ++ return; ++ #if FLEXTILE_DELUXE_LAYOUT ++ else if (m->lt[m->sellt]->arrange == &flextile) { ++ int layout = m->ltaxis[LAYOUT]; ++ if (layout < 0) { ++ mirror = 1; ++ layout *= -1; ++ } ++ if (layout > FLOATING_MASTER) { ++ layout -= FLOATING_MASTER; ++ fixed = 1; ++ } ++ ++ if (layout == SPLIT_HORIZONTAL || layout == SPLIT_HORIZONTAL_DUAL_STACK) ++ horizontal = 1; ++ else if (layout == SPLIT_CENTERED_VERTICAL && (fixed || n - m->nmaster > 1)) ++ center = 1; ++ else if (layout == FLOATING_MASTER) { ++ center = 1; ++ if (aw < ah) ++ horizontal = 1; ++ } ++ else if (layout == SPLIT_CENTERED_HORIZONTAL) { ++ if (fixed || n - m->nmaster > 1) ++ center = 1; ++ horizontal = 1; ++ } ++ } ++ #endif // FLEXTILE_DELUXE_LAYOUT ++ #if CENTEREDMASTER_LAYOUT ++ else if (m->lt[m->sellt]->arrange == ¢eredmaster && (fixed || n - m->nmaster > 1)) ++ center = 1; ++ #endif // CENTEREDMASTER_LAYOUT ++ #if CENTEREDFLOATINGMASTER_LAYOUT ++ else if (m->lt[m->sellt]->arrange == ¢eredfloatingmaster) ++ center = 1; ++ #endif // CENTEREDFLOATINGMASTER_LAYOUT ++ #if BSTACK_LAYOUT ++ else if (m->lt[m->sellt]->arrange == &bstack) ++ horizontal = 1; ++ #endif // BSTACK_LAYOUT ++ #if BSTACKHORIZ_LAYOUT ++ else if (m->lt[m->sellt]->arrange == &bstackhoriz) ++ horizontal = 1; ++ #endif // BSTACKHORIZ_LAYOUT ++ ++ /* do not allow mfact to be modified under certain conditions */ ++ if (!m->lt[m->sellt]->arrange // floating layout ++ || (!fixed && m->nmaster && n <= m->nmaster) // no master ++ #if MONOCLE_LAYOUT ++ || m->lt[m->sellt]->arrange == &monocle ++ #endif // MONOCLE_LAYOUT ++ #if GRIDMODE_LAYOUT ++ || m->lt[m->sellt]->arrange == &grid ++ #endif // GRIDMODE_LAYOUT ++ #if HORIZGRID_LAYOUT ++ || m->lt[m->sellt]->arrange == &horizgrid ++ #endif // HORIZGRID_LAYOUT ++ #if GAPPLESSGRID_LAYOUT ++ || m->lt[m->sellt]->arrange == &gaplessgrid ++ #endif // GAPPLESSGRID_LAYOUT ++ #if NROWGRID_LAYOUT ++ || m->lt[m->sellt]->arrange == &nrowgrid ++ #endif // NROWGRID_LAYOUT ++ #if FLEXTILE_DELUXE_LAYOUT ++ || (m->lt[m->sellt]->arrange == &flextile && m->ltaxis[LAYOUT] == NO_SPLIT) ++ #endif // FLEXTILE_DELUXE_LAYOUT ++ ) ++ return; ++ ++ #if VANITYGAPS_PATCH ++ ay += oh; ++ ax += ov; ++ aw -= 2*ov; ++ ah -= 2*oh; ++ #endif // VANITYGAPS_PATCH ++ ++ if (center) { ++ if (horizontal) { ++ px = ax + aw / 2; ++ #if VANITYGAPS_PATCH ++ py = ay + ah / 2 + (ah - 2*ih) * (m->mfact / 2.0) + ih / 2; ++ #else ++ py = ay + ah / 2 + ah * m->mfact / 2.0; ++ #endif // VANITYGAPS_PATCH ++ } else { // vertical split ++ #if VANITYGAPS_PATCH ++ px = ax + aw / 2 + (aw - 2*iv) * m->mfact / 2.0 + iv / 2; ++ #else ++ px = ax + aw / 2 + aw * m->mfact / 2.0; ++ #endif // VANITYGAPS_PATCH ++ py = ay + ah / 2; ++ } ++ } else if (horizontal) { ++ px = ax + aw / 2; ++ if (mirror) ++ #if VANITYGAPS_PATCH ++ py = ay + (ah - ih) * (1.0 - m->mfact) + ih / 2; ++ #else ++ py = ay + (ah * (1.0 - m->mfact)); ++ #endif // VANITYGAPS_PATCH ++ else ++ #if VANITYGAPS_PATCH ++ py = ay + ((ah - ih) * m->mfact) + ih / 2; ++ #else ++ py = ay + (ah * m->mfact); ++ #endif // VANITYGAPS_PATCH ++ } else { // vertical split ++ if (mirror) ++ #if VANITYGAPS_PATCH ++ px = ax + (aw - iv) * (1.0 - m->mfact) + iv / 2; ++ #else ++ px = ax + (aw * m->mfact); ++ #endif // VANITYGAPS_PATCH ++ else ++ #if VANITYGAPS_PATCH ++ px = ax + ((aw - iv) * m->mfact) + iv / 2; ++ #else ++ px = ax + (aw * m->mfact); ++ #endif // VANITYGAPS_PATCH ++ py = ay + ah / 2; ++ } ++ ++ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, ++ None, cursor[horizontal ? CurResizeVertArrow : CurResizeHorzArrow]->cursor, CurrentTime) != GrabSuccess) ++ return; ++ XWarpPointer(dpy, None, root, 0, 0, 0, 0, px, py); ++ ++ do { ++ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); ++ switch(ev.type) { ++ case ConfigureRequest: ++ case Expose: ++ case MapRequest: ++ handler[ev.type](&ev); ++ break; ++ case MotionNotify: ++ if ((ev.xmotion.time - lasttime) <= (1000 / 40)) ++ continue; ++ if (lasttime != 0) { ++ px = ev.xmotion.x; ++ py = ev.xmotion.y; ++ } ++ lasttime = ev.xmotion.time; ++ ++ #if VANITYGAPS_PATCH ++ if (center) ++ if (horizontal) ++ if (py - ay > ah / 2) ++ fact = (double) 1.0 - (ay + ah - py - ih / 2) * 2 / (double) (ah - 2*ih); ++ else ++ fact = (double) 1.0 - (py - ay - ih / 2) * 2 / (double) (ah - 2*ih); ++ else ++ if (px - ax > aw / 2) ++ fact = (double) 1.0 - (ax + aw - px - iv / 2) * 2 / (double) (aw - 2*iv); ++ else ++ fact = (double) 1.0 - (px - ax - iv / 2) * 2 / (double) (aw - 2*iv); ++ else ++ if (horizontal) ++ fact = (double) (py - ay - ih / 2) / (double) (ah - ih); ++ else ++ fact = (double) (px - ax - iv / 2) / (double) (aw - iv); ++ #else ++ if (center) ++ if (horizontal) ++ if (py - ay > ah / 2) ++ fact = (double) 1.0 - (ay + ah - py) * 2 / (double) ah; ++ else ++ fact = (double) 1.0 - (py - ay) * 2 / (double) ah; ++ else ++ if (px - ax > aw / 2) ++ fact = (double) 1.0 - (ax + aw - px) * 2 / (double) aw; ++ else ++ fact = (double) 1.0 - (px - ax) * 2 / (double) aw; ++ else ++ if (horizontal) ++ fact = (double) (py - ay) / (double) ah; ++ else ++ fact = (double) (px - ax) / (double) aw; ++ #endif // VANITYGAPS_PATCH ++ ++ if (!center && mirror) ++ fact = 1.0 - fact; ++ ++ setmfact(&((Arg) { .f = 1.0 + fact })); ++ px = ev.xmotion.x; ++ py = ev.xmotion.y; ++ break; ++ } ++ } while (ev.type != ButtonRelease); ++ ++ XUngrabPointer(dpy, CurrentTime); ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); ++} ++ + void + drawbar(Monitor *m) + { +@@ -1570,6 +1794,8 @@ setup(void) + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); ++ cursor[CurResizeHorzArrow] = drw_cur_create(drw, XC_sb_h_double_arrow); ++ cursor[CurResizeVertArrow] = drw_cur_create(drw, XC_sb_v_double_arrow); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) +-- +2.19.1 + diff --git a/dwm/dwm-fakefullscreenclient-6.3.diff b/dwm/dwm-fakefullscreenclient-6.3.diff new file mode 100644 index 0000000..6c875e3 --- /dev/null +++ b/dwm/dwm-fakefullscreenclient-6.3.diff @@ -0,0 +1,161 @@ +From 7d6a517f890828b497fcfe28f8d10d5bced4bc3e Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:31:15 +0100 +Subject: [PATCH] fakefullscreenclient - enable fake fullscreen on a per client + basis + +--- + config.def.h | 1 + + dwm.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++------ + 2 files changed, 55 insertions(+), 7 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..7e6b2ff 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -78,6 +78,7 @@ static Key keys[] = { + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, ++ { MODKEY|ShiftMask, XK_f, togglefakefullscreen, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..b95f50f 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int fakefullscreen; + Client *next; + Client *snext; + Monitor *mon; +@@ -211,6 +212,7 @@ static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); ++static void togglefakefullscreen(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); +@@ -566,7 +568,7 @@ configurenotify(XEvent *e) + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) +- if (c->isfullscreen) ++ if (c->isfullscreen && c->fakefullscreen != 1) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } +@@ -1147,7 +1149,7 @@ movemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1288,7 +1290,10 @@ resizeclient(Client *c, int x, int y, int w, int h) + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); +- XSync(dpy, False); ++ if (c->fakefullscreen == 1) ++ XSync(dpy, True); ++ else ++ XSync(dpy, False); + } + + void +@@ -1302,7 +1307,7 @@ resizemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1480,8 +1485,10 @@ setfullscreen(Client *c, int fullscreen) + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; +- c->oldstate = c->isfloating; + c->oldbw = c->bw; ++ if (c->fakefullscreen == 1) ++ return; ++ c->oldstate = c->isfloating; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); +@@ -1490,8 +1497,12 @@ setfullscreen(Client *c, int fullscreen) + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; +- c->isfloating = c->oldstate; + c->bw = c->oldbw; ++ if (c->fakefullscreen == 1) ++ return; ++ if (c->fakefullscreen == 2) ++ c->fakefullscreen = 1; ++ c->isfloating = c->oldstate; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; +@@ -1711,12 +1722,48 @@ togglebar(const Arg *arg) + arrange(selmon); + } + ++void ++togglefakefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen) { ++ if (c->isfullscreen) { ++ if (c->isfloating && c->fakefullscreen == 1) { ++ c->oldstate = c->isfloating; ++ c->oldx = c->x; ++ c->oldy = c->y; ++ c->oldw = c->w; ++ c->oldh = c->h; ++ } ++ c->fakefullscreen = 0; ++ } ++ else ++ c->isfullscreen = 0; ++ } else { ++ c->fakefullscreen = 1; ++ if (c->isfullscreen) { ++ c->isfloating = c->oldstate; ++ c->bw = c->oldbw; ++ c->x = c->oldx; ++ c->y = c->oldy; ++ c->w = c->oldw; ++ c->h = c->oldh; ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ } ++ c->isfullscreen = 0; ++ } ++ setfullscreen(c, !c->isfullscreen); ++} ++ + void + togglefloating(const Arg *arg) + { + if (!selmon->sel) + return; +- if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ ++ if (selmon->sel->isfullscreen && selmon->sel->fakefullscreen != 1) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) +-- +2.19.1 + diff --git a/dwm/dwm-fancybar-6.3.diff b/dwm/dwm-fancybar-6.3.diff new file mode 100644 index 0000000..3d5a4a2 --- /dev/null +++ b/dwm/dwm-fancybar-6.3.diff @@ -0,0 +1,96 @@ +From 781cd03b7c3a0cf59701b9d6a928c1b81cd28cdd Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:36:07 +0100 +Subject: [PATCH] fancybar, shows the titles of all visible windows in the + status bar + +Inspired by the decorated tabbed layout of Xmonad this patch provides a status bar that shows +the titles of all visible windows (as opposed to showing just the selected one). When the +titles don't completely fit, they're cropped. The title of the selected window is inverted. + +Authors: + Mate Nagy + Jochen Sprickerhof + +Refer to https://dwm.suckless.org/patches/fancybar/ +--- + dwm.c | 46 ++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 36 insertions(+), 10 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..b8c6449 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -696,10 +696,10 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int x, w, tw = 0, tabw, mw, ew = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; ++ unsigned int i, occ = 0, urg = 0, n = 0; + Client *c; + + if (!m->showbar) +@@ -713,6 +713,8 @@ drawbar(Monitor *m) + } + + for (c = m->clients; c; c = c->next) { ++ if (ISVISIBLE(c)) ++ n++; + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; +@@ -733,15 +735,39 @@ drawbar(Monitor *m) + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); ++ if (n > 0) { ++ tabw = TEXTW(m->sel->name) + lrpad; ++ mw = (tabw >= w || n == 1) ? 0 : (w - tabw) / (n - 1); ++ ++ i = 0; ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c) || c == m->sel) ++ continue; ++ tw = TEXTW(c->name); ++ if (tabw < mw) ++ ew += (mw - tabw); ++ else ++ i++; ++ } ++ if (i > 0) ++ mw += ew / i; ++ ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ tabw = MIN(m->sel == c ? w : mw, TEXTW(c->name)); ++ ++ drw_setscheme(drw, scheme[m->sel == c ? SchemeSel : SchemeNorm]); ++ if (tabw > 0) /* trap special handling of 0 in drw_text */ ++ drw_text(drw, x, 0, tabw, bh, lrpad / 2, c->name, 0); ++ if (c->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, c->isfixed, 0); ++ x += tabw; ++ w -= tabw; ++ } + } ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); + } +-- +2.19.1 + diff --git a/dwm/dwm-floatpos-6.3.diff b/dwm/dwm-floatpos-6.3.diff new file mode 100644 index 0000000..2f14804 --- /dev/null +++ b/dwm/dwm-floatpos-6.3.diff @@ -0,0 +1,369 @@ +From 6639e81666ec1793956fa462282a85d3d046aa45 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:37:31 +0100 +Subject: [PATCH] floatpos: Control the size and position of floating windows + +This patch offers a comprehensive and monitor size agnostic way +of positioning new and existing floating windows. + +Refer to: +https://github.com/bakkeby/patches/wiki/floatpos/ +--- + config.def.h | 46 +++++++++++- + dwm.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 233 insertions(+), 5 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..91004b2 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ 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 */ ++static int floatposgrid_x = 5; /* float grid columns */ ++static int floatposgrid_y = 5; /* float grid rows */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -26,9 +28,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 floatpos monitor */ ++ { "Gimp", NULL, NULL, 0, 1, NULL, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, NULL, -1 }, + }; + + /* layout(s) */ +@@ -85,6 +87,44 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ /* Client position is limited to monitor window area */ ++ { Mod4Mask, XK_u, floatpos, {.v = "-26x -26y" } }, // ↖ ++ { Mod4Mask, XK_i, floatpos, {.v = " 0x -26y" } }, // ↑ ++ { Mod4Mask, XK_o, floatpos, {.v = " 26x -26y" } }, // ↗ ++ { Mod4Mask, XK_j, floatpos, {.v = "-26x 0y" } }, // ← ++ { Mod4Mask, XK_l, floatpos, {.v = " 26x 0y" } }, // → ++ { Mod4Mask, XK_m, floatpos, {.v = "-26x 26y" } }, // ↙ ++ { Mod4Mask, XK_comma, floatpos, {.v = " 0x 26y" } }, // ↓ ++ { Mod4Mask, XK_period, floatpos, {.v = " 26x 26y" } }, // ↘ ++ /* Absolute positioning (allows moving windows between monitors) */ ++ { Mod4Mask|ControlMask, XK_u, floatpos, {.v = "-26a -26a" } }, // ↖ ++ { Mod4Mask|ControlMask, XK_i, floatpos, {.v = " 0a -26a" } }, // ↑ ++ { Mod4Mask|ControlMask, XK_o, floatpos, {.v = " 26a -26a" } }, // ↗ ++ { Mod4Mask|ControlMask, XK_j, floatpos, {.v = "-26a 0a" } }, // ← ++ { Mod4Mask|ControlMask, XK_l, floatpos, {.v = " 26a 0a" } }, // → ++ { Mod4Mask|ControlMask, XK_m, floatpos, {.v = "-26a 26a" } }, // ↙ ++ { Mod4Mask|ControlMask, XK_comma, floatpos, {.v = " 0a 26a" } }, // ↓ ++ { Mod4Mask|ControlMask, XK_period, floatpos, {.v = " 26a 26a" } }, // ↘ ++ /* Resize client, client center position is fixed which means that client expands in all directions */ ++ { Mod4Mask|ShiftMask, XK_u, floatpos, {.v = "-26w -26h" } }, // ↖ ++ { Mod4Mask|ShiftMask, XK_i, floatpos, {.v = " 0w -26h" } }, // ↑ ++ { Mod4Mask|ShiftMask, XK_o, floatpos, {.v = " 26w -26h" } }, // ↗ ++ { Mod4Mask|ShiftMask, XK_j, floatpos, {.v = "-26w 0h" } }, // ← ++ { Mod4Mask|ShiftMask, XK_k, floatpos, {.v = "800W 800H" } }, // · ++ { Mod4Mask|ShiftMask, XK_l, floatpos, {.v = " 26w 0h" } }, // → ++ { Mod4Mask|ShiftMask, XK_m, floatpos, {.v = "-26w 26h" } }, // ↙ ++ { Mod4Mask|ShiftMask, XK_comma, floatpos, {.v = " 0w 26h" } }, // ↓ ++ { Mod4Mask|ShiftMask, XK_period, floatpos, {.v = " 26w 26h" } }, // ↘ ++ /* Client is positioned in a floating grid, movement is relative to client's current position */ ++ { Mod4Mask|Mod1Mask, XK_u, floatpos, {.v = "-1p -1p" } }, // ↖ ++ { Mod4Mask|Mod1Mask, XK_i, floatpos, {.v = " 0p -1p" } }, // ↑ ++ { Mod4Mask|Mod1Mask, XK_o, floatpos, {.v = " 1p -1p" } }, // ↗ ++ { Mod4Mask|Mod1Mask, XK_j, floatpos, {.v = "-1p 0p" } }, // ← ++ { Mod4Mask|Mod1Mask, XK_k, floatpos, {.v = " 0p 0p" } }, // · ++ { Mod4Mask|Mod1Mask, XK_l, floatpos, {.v = " 1p 0p" } }, // → ++ { Mod4Mask|Mod1Mask, XK_m, floatpos, {.v = "-1p 1p" } }, // ↙ ++ { Mod4Mask|Mod1Mask, XK_comma, floatpos, {.v = " 0p 1p" } }, // ↓ ++ { Mod4Mask|Mod1Mask, XK_period, floatpos, {.v = " 1p 1p" } }, // ↘ + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/dwm.c b/dwm.c +index a96f33c..bf9ba9a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int ignoresizehints; + Client *next; + Client *snext; + Monitor *mon; +@@ -138,6 +139,7 @@ typedef struct { + const char *title; + unsigned int tags; + int isfloating; ++ const char *floatpos; + int monitor; + } Rule; + +@@ -165,11 +167,13 @@ static void drawbar(Monitor *m); + static void drawbars(void); + static void enternotify(XEvent *e); + static void expose(XEvent *e); ++static void floatpos(const Arg *arg); + static void focus(Client *c); + static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); ++static void getfloatpos(int pos, char pCh, int size, char sCh, int min_p, int max_s, int cp, int cs, int cbw, int defgrid, int *out_p, int *out_s); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +@@ -198,6 +202,7 @@ static void scan(void); + static int sendevent(Client *c, Atom proto); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); ++static void setfloatpos(Client *c, const char *floatpos); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); + static void setlayout(const Arg *arg); +@@ -303,6 +308,8 @@ applyrules(Client *c) + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; ++ if (c->isfloating && r->floatpos) ++ setfloatpos(c, r->floatpos); + } + } + if (ch.res_class) +@@ -344,7 +351,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->ignoresizehints && (resizehints || c->isfloating || !c->mon->lt[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 */ +@@ -784,6 +791,21 @@ expose(XEvent *e) + drawbar(m); + } + ++void ++floatpos(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ ++ if (!c || (selmon->lt[selmon->sellt]->arrange && !c->isfloating)) ++ return; ++ ++ setfloatpos(c, (char *)arg->v); ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ ++ XRaiseWindow(dpy, c->win); ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); ++} ++ + void + focus(Client *c) + { +@@ -875,6 +897,124 @@ getatomprop(Client *c, Atom prop) + return atom; + } + ++void ++getfloatpos(int pos, char pCh, int size, char sCh, int min_p, int max_s, int cp, int cs, int cbw, int defgrid, int *out_p, int *out_s) ++{ ++ int abs_p, abs_s, i, delta, rest; ++ ++ abs_p = pCh == 'A' || pCh == 'a'; ++ abs_s = sCh == 'A' || sCh == 'a'; ++ ++ cs += 2*cbw; ++ ++ switch(pCh) { ++ case 'A': // absolute position ++ cp = pos; ++ break; ++ case 'a': // absolute relative position ++ cp += pos; ++ break; ++ case 'y': ++ case 'x': // client relative position ++ cp = MIN(cp + pos, min_p + max_s); ++ break; ++ case 'Y': ++ case 'X': // client position relative to monitor ++ cp = min_p + MIN(pos, max_s); ++ break; ++ case 'S': // fixed client position (sticky) ++ case 'C': // fixed client position (center) ++ case 'Z': // fixed client right-hand position (position + size) ++ if (pos == -1) ++ break; ++ pos = MAX(MIN(pos, max_s), 0); ++ if (pCh == 'Z') ++ cs = abs((cp + cs) - (min_p + pos)); ++ else if (pCh == 'C') ++ cs = abs((cp + cs / 2) - (min_p + pos)); ++ else ++ cs = abs(cp - (min_p + pos)); ++ cp = min_p + pos; ++ sCh = 0; // size determined by position, override defined size ++ break; ++ case 'G': // grid ++ if (pos <= 0) ++ pos = defgrid; // default configurable ++ if (size == 0 || pos < 2 || (sCh != 'p' && sCh != 'P')) ++ break; ++ delta = (max_s - cs) / (pos - 1); ++ rest = max_s - cs - delta * (pos - 1); ++ if (sCh == 'P') { ++ if (size < 1 || size > pos) ++ break; ++ cp = min_p + delta * (size - 1); ++ } else { ++ for (i = 0; i < pos && cp >= min_p + delta * i + (i > pos - rest ? i + rest - pos + 1 : 0); i++); ++ cp = min_p + delta * (MAX(MIN(i + size, pos), 1) - 1) + (i > pos - rest ? i + rest - pos + 1 : 0); ++ } ++ break; ++ } ++ ++ switch(sCh) { ++ case 'A': // absolute size ++ cs = size; ++ break; ++ case 'a': // absolute relative size ++ cs = MAX(1, cs + size); ++ break; ++ case '%': // client size percentage in relation to monitor window area size ++ if (size <= 0) ++ break; ++ size = max_s * MIN(size, 100) / 100; ++ /* falls through */ ++ case 'h': ++ case 'w': // size relative to client ++ if (sCh == 'w' || sCh == 'h') { ++ if (size == 0) ++ break; ++ size += cs; ++ } ++ /* falls through */ ++ case 'H': ++ case 'W': // normal size, position takes precedence ++ if (pCh == 'S' && cp + size > min_p + max_s) ++ size = min_p + max_s - cp; ++ else if (size > max_s) ++ size = max_s; ++ ++ if (pCh == 'C') { // fixed client center, expand or contract client ++ delta = size - cs; ++ if (delta < 0 || (cp - delta / 2 + size <= min_p + max_s)) ++ cp -= delta / 2; ++ else if (cp - delta / 2 < min_p) ++ cp = min_p; ++ else if (delta) ++ cp = min_p + max_s; ++ } else if (pCh == 'Z') ++ cp -= size - cs; ++ ++ cs = size; ++ break; ++ } ++ ++ if (pCh == '%') // client mid-point position in relation to monitor window area size ++ cp = min_p + max_s * MAX(MIN(pos, 100), 0) / 100 - (cs) / 2; ++ if (pCh == 'm' || pCh == 'M') ++ cp = pos - cs / 2; ++ ++ if (!abs_p && cp < min_p) ++ cp = min_p; ++ if (cp + cs > min_p + max_s && !(abs_p && abs_s)) { ++ if (abs_p || cp == min_p) ++ cs = min_p + max_s - cp; ++ else ++ cp = min_p + max_s - cs; ++ } ++ ++ *out_p = cp; ++ *out_s = MAX(cs - 2*cbw, 1); ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -1033,8 +1173,10 @@ manage(Window w, XWindowAttributes *wa) + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; ++ c->ignoresizehints = 0; + + updatetitle(c); ++ c->bw = borderpx; + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; +@@ -1051,7 +1193,6 @@ manage(Window w, XWindowAttributes *wa) + /* only fix client y-offset, if the client center might cover the bar */ + c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); +- c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); +@@ -1461,6 +1602,53 @@ sendevent(Client *c, Atom proto) + return exists; + } + ++void ++setfloatpos(Client *c, const char *floatpos) ++{ ++ char xCh, yCh, wCh, hCh; ++ int x, y, w, h, wx, ww, wy, wh; ++ ++ if (!c || !floatpos) ++ return; ++ if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) ++ return; ++ switch(sscanf(floatpos, "%d%c %d%c %d%c %d%c", &x, &xCh, &y, &yCh, &w, &wCh, &h, &hCh)) { ++ case 4: ++ if (xCh == 'w' || xCh == 'W') { ++ w = x; wCh = xCh; ++ h = y; hCh = yCh; ++ x = -1; xCh = 'C'; ++ y = -1; yCh = 'C'; ++ } else if (xCh == 'p' || xCh == 'P') { ++ w = x; wCh = xCh; ++ h = y; hCh = yCh; ++ x = 0; xCh = 'G'; ++ y = 0; yCh = 'G'; ++ } else if (xCh == 'm' || xCh == 'M') { ++ getrootptr(&x, &y); ++ } else { ++ w = 0; wCh = 0; ++ h = 0; hCh = 0; ++ } ++ break; ++ case 8: ++ if (xCh == 'm' || xCh == 'M') ++ getrootptr(&x, &y); ++ break; ++ default: ++ return; ++ } ++ ++ wx = c->mon->wx; ++ wy = c->mon->wy; ++ ww = c->mon->ww; ++ wh = c->mon->wh; ++ c->ignoresizehints = 1; ++ ++ getfloatpos(x, xCh, w, wCh, wx, ww, c->x, c->w, c->bw, floatposgrid_x, &c->x, &c->w); ++ getfloatpos(y, yCh, h, hCh, wy, wh, c->y, c->h, c->bw, floatposgrid_y, &c->y, &c->h); ++} ++ + void + setfocus(Client *c) + { +-- +2.19.1 + diff --git a/dwm/dwm-focusdir-6.3.diff b/dwm/dwm-focusdir-6.3.diff new file mode 100644 index 0000000..a8b8947 --- /dev/null +++ b/dwm/dwm-focusdir-6.3.diff @@ -0,0 +1,114 @@ +From 45957f20d45e99469e7f296231769966b016d11a Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:38:04 +0100 +Subject: [PATCH] focusdir: focus on the next client by direction (up, down, + left, right) + +--- + config.def.h | 4 ++++ + dwm.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 71 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..cd41574 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -67,6 +67,10 @@ static Key keys[] = { + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { MODKEY, XK_Left, focusdir, {.i = 0 } }, // left ++ { MODKEY, XK_Right, focusdir, {.i = 1 } }, // right ++ { MODKEY, XK_Up, focusdir, {.i = 2 } }, // up ++ { MODKEY, XK_Down, focusdir, {.i = 3 } }, // down + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..c3a868e 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -166,6 +166,7 @@ static void drawbars(void); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); ++static void focusdir(const Arg *arg); + static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); +@@ -809,6 +810,72 @@ focus(Client *c) + drawbars(); + } + ++void ++focusdir(const Arg *arg) ++{ ++ Client *s = selmon->sel, *f = NULL, *c, *next; ++ ++ if (!s) ++ return; ++ ++ unsigned int score = -1; ++ unsigned int client_score; ++ int dist; ++ int dirweight = 20; ++ int isfloating = s->isfloating; ++ ++ next = s->next; ++ if (!next) ++ next = s->mon->clients; ++ for (c = next; c != s; c = next) { ++ ++ next = c->next; ++ if (!next) ++ next = s->mon->clients; ++ ++ if (!ISVISIBLE(c) || c->isfloating != isfloating) // || HIDDEN(c) ++ continue; ++ ++ switch (arg->i) { ++ case 0: // left ++ dist = s->x - c->x - c->w; ++ client_score = ++ dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + ++ abs(s->y - c->y); ++ break; ++ case 1: // right ++ dist = c->x - s->x - s->w; ++ client_score = ++ dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + ++ abs(c->y - s->y); ++ break; ++ case 2: // up ++ dist = s->y - c->y - c->h; ++ client_score = ++ dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + ++ abs(s->x - c->x); ++ break; ++ default: ++ case 3: // down ++ dist = c->y - s->y - s->h; ++ client_score = ++ dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + ++ abs(c->x - s->x); ++ break; ++ } ++ ++ if (((arg->i == 0 || arg->i == 2) && client_score <= score) || client_score < score) { ++ score = client_score; ++ f = c; ++ } ++ } ++ ++ if (f && f != s) { ++ focus(f); ++ restack(f->mon); ++ } ++} ++ + /* there are some broken focus acquiring clients needing extra handling */ + void + focusin(XEvent *e) +-- +2.19.1 + diff --git a/dwm/dwm-focusedontop-6.3.diff b/dwm/dwm-focusedontop-6.3.diff new file mode 100644 index 0000000..5576ce8 --- /dev/null +++ b/dwm/dwm-focusedontop-6.3.diff @@ -0,0 +1,301 @@ +From 96b77618391c9feb45e91291fad274ca9dfeed11 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:43:28 +0100 +Subject: [PATCH] focusedontop: allow the currently focused client to display + on top of floating windows, but not on top of transient windows or dialog + boxes + +--- + config.def.h | 14 +++++++-- + dwm.c | 83 ++++++++++++++++++++++++++++++++++++++-------------- + 2 files changed, 72 insertions(+), 25 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..c964678 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,7 @@ 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 */ ++static const int focusedontop = 1; /* 1 means focused client is shown on top of floating windows */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -21,14 +22,21 @@ static const char *colors[][3] = { + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + ++#define WTYPE "_NET_WM_WINDOW_TYPE_" + static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title ++ * _NET_WM_WINDOW_TYPE(ATOM) = wintype + */ +- /* class instance title tags mask isfloating monitor */ +- { "Gimp", NULL, NULL, 0, 1, -1 }, +- { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ /* class role instance title wintype, tags mask isfloating alwaysontop monitor */ ++ { NULL, NULL, NULL, NULL, WTYPE "DIALOG", 0, 1, 1, -1 }, ++ { NULL, NULL, NULL, NULL, WTYPE "UTILITY", 0, 1, 1, -1 }, ++ { NULL, NULL, NULL, NULL, WTYPE "TOOLBAR", 0, 1, 1, -1 }, ++ { NULL, NULL, NULL, NULL, WTYPE "SPLASH", 0, 1, 1, -1 }, ++ { "Gimp", NULL, NULL, NULL, NULL, 0, 1, 0, -1 }, ++ { "Firefox", NULL, NULL, NULL, NULL, 1 << 8, 0, 0, -1 }, ++ { NULL, "pop-up", NULL, NULL, NULL, 0, 1, 1, -1 }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index a96f33c..0f7b60e 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -60,10 +60,10 @@ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +-enum { NetSupported, NetWMName, NetWMState, NetWMCheck, ++enum { NetSupported, NetWMName, NetWMState, NetWMStateAbove, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, +- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +-enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ ++ NetClientList, NetLast }; /* EWMH atoms */ ++enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMWindowRole, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int alwaysontop; + Client *next; + Client *snext; + Monitor *mon; +@@ -134,10 +135,13 @@ struct Monitor { + + typedef struct { + const char *class; ++ const char *role; + const char *instance; + const char *title; ++ const char *wintype; + unsigned int tags; + int isfloating; ++ int alwaysontop; + int monitor; + } Rule; + +@@ -225,7 +229,6 @@ static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); + static void updatetitle(Client *c); +-static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); + static Client *wintoclient(Window w); +@@ -280,6 +283,8 @@ void + applyrules(Client *c) + { + const char *class, *instance; ++ char role[64]; ++ Atom wintype; + unsigned int i; + const Rule *r; + Monitor *m; +@@ -291,15 +296,20 @@ applyrules(Client *c) + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; ++ gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role)); ++ wintype = getatomprop(c, netatom[NetWMWindowType]); + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) +- && (!r->instance || strstr(instance, r->instance))) ++ && (!r->role || strstr(role, r->role)) ++ && (!r->instance || strstr(instance, r->instance)) ++ && (!r->wintype || wintype == XInternAtom(dpy, r->wintype, False))) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ c->alwaysontop = r->alwaysontop; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -523,6 +533,9 @@ clientmessage(XEvent *e) + || cme->data.l[2] == netatom[NetWMFullscreen]) + 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->data.l[1] == netatom[NetWMStateAbove] ++ || cme->data.l[2] == netatom[NetWMStateAbove]) ++ c->alwaysontop = (cme->data.l[0] || cme->data.l[1]); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); +@@ -760,6 +773,7 @@ enternotify(XEvent *e) + { + Client *c; + Monitor *m; ++ XEvent xev; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) +@@ -772,6 +786,7 @@ enternotify(XEvent *e) + } else if (!c || c == selmon->sel) + return; + focus(c); ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &xev)); + } + + void +@@ -787,6 +802,8 @@ expose(XEvent *e) + void + focus(Client *c) + { ++ Client *f; ++ XWindowChanges wc; + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) +@@ -801,6 +818,31 @@ focus(Client *c) + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); ++ if (focusedontop && c->mon->lt[c->mon->sellt]->arrange) { ++ ++ /* Move all visible tiled clients that are not marked as on top below the bar window */ ++ wc.stack_mode = Below; ++ wc.sibling = c->mon->barwin; ++ for (f = c->mon->stack; f; f = f->snext) ++ if (f != c && !f->isfloating && ISVISIBLE(f) && !f->alwaysontop) { ++ XConfigureWindow(dpy, f->win, CWSibling|CWStackMode, &wc); ++ wc.sibling = f->win; ++ } ++ ++ /* Move the currently focused client above the bar window */ ++ wc.stack_mode = Above; ++ wc.sibling = c->mon->barwin; ++ XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); ++ ++ /* Move all visible floating windows that are not marked as on top below the current window */ ++ wc.stack_mode = Below; ++ wc.sibling = c->win; ++ for (f = c->mon->stack; f; f = f->snext) ++ if (f != c && f->isfloating && ISVISIBLE(f) && !f->alwaysontop) { ++ XConfigureWindow(dpy, f->win, CWSibling|CWStackMode, &wc); ++ wc.sibling = f->win; ++ } ++ } + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +@@ -837,6 +879,7 @@ void + focusstack(const Arg *arg) + { + Client *c = NULL, *i; ++ XEvent xev; + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; +@@ -855,8 +898,10 @@ focusstack(const Arg *arg) + } + if (c) { + focus(c); +- restack(selmon); ++ if (!focusedontop) ++ restack(selmon); + } ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &xev)); + } + + Atom +@@ -1024,6 +1069,7 @@ manage(Window w, XWindowAttributes *wa) + Client *c, *t = NULL; + Window trans = None; + XWindowChanges wc; ++ XEvent xev; + + c = ecalloc(1, sizeof(Client)); + c->win = w; +@@ -1038,6 +1084,7 @@ manage(Window w, XWindowAttributes *wa) + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; ++ c->alwaysontop = 1; + } else { + c->mon = selmon; + applyrules(c); +@@ -1057,7 +1104,10 @@ manage(Window w, XWindowAttributes *wa) + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ +- updatewindowtype(c); ++ if (getatomprop(c, netatom[NetWMState]) == netatom[NetWMStateAbove]) ++ c->alwaysontop = 1; ++ if (getatomprop(c, netatom[NetWMState]) == netatom[NetWMFullscreen]) ++ setfullscreen(c, 1); + updatesizehints(c); + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); +@@ -1077,7 +1127,9 @@ manage(Window w, XWindowAttributes *wa) + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); ++ + focus(NULL); ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &xev)); + } + + void +@@ -1244,8 +1296,6 @@ propertynotify(XEvent *e) + if (c == c->mon->sel) + drawbar(c->mon); + } +- if (ev->atom == netatom[NetWMWindowType]) +- updatewindowtype(c); + } + } + +@@ -1557,14 +1607,15 @@ setup(void) + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); ++ wmatom[WMWindowRole] = XInternAtom(dpy, "WM_WINDOW_ROLE", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); ++ netatom[NetWMStateAbove] = XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); +- netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); +@@ -2007,18 +2058,6 @@ updatetitle(Client *c) + strcpy(c->name, broken); + } + +-void +-updatewindowtype(Client *c) +-{ +- Atom state = getatomprop(c, netatom[NetWMState]); +- Atom wtype = getatomprop(c, netatom[NetWMWindowType]); +- +- if (state == netatom[NetWMFullscreen]) +- setfullscreen(c, 1); +- if (wtype == netatom[NetWMWindowTypeDialog]) +- c->isfloating = 1; +-} +- + void + updatewmhints(Client *c) + { +-- +2.19.1 + diff --git a/dwm/dwm-focusonnetactive-6.2.diff b/dwm/dwm-focusonnetactive-6.2.diff index 048f090..16bde58 100644 --- a/dwm/dwm-focusonnetactive-6.2.diff +++ b/dwm/dwm-focusonnetactive-6.2.diff @@ -1,6 +1,6 @@ From fd2e8fbc859d688521e50d98d5c47d1f98657afb Mon Sep 17 00:00:00 2001 From: bakkeby -Date: Tue, 7 Apr 2020 12.19.14 +0200 +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 diff --git a/dwm/dwm-focusonnetactive-6.3.diff b/dwm/dwm-focusonnetactive-6.3.diff new file mode 100644 index 0000000..935c615 --- /dev/null +++ b/dwm/dwm-focusonnetactive-6.3.diff @@ -0,0 +1,60 @@ +From 43b77da0e9125c66ac7585d79d2f0784cc38e86e Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:44:58 +0100 +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 + +Refer to: +https://dwm.suckless.org/patches/focusonnetactive/ +--- + dwm.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..44d55b1 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -515,6 +515,7 @@ clientmessage(XEvent *e) + { + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); ++ unsigned int i; + + if (!c) + return; +@@ -524,8 +525,15 @@ 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}; ++ unfocus(selmon->sel, 0); ++ selmon = c->mon; ++ view(&a); ++ focus(c); ++ restack(selmon); ++ } + } + } + +-- +2.19.1 + diff --git a/dwm/dwm-fullscreen-compilation-6.3.diff b/dwm/dwm-fullscreen-compilation-6.3.diff new file mode 100644 index 0000000..d1e4716 --- /dev/null +++ b/dwm/dwm-fullscreen-compilation-6.3.diff @@ -0,0 +1,356 @@ +From 26143b40a079f2bded51a6e01dba8530bec8e486 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:47:56 +0100 +Subject: [PATCH] Compilation of fullscreen patches for dwm. + +This aims to provide a comprehensive fullscreen solution with the following features: + - toggle fullscreen for any window using a single keybinding rather than having + to rely on per-application keybindings + - the ability to have windows go fullscreen within the size and position the + window is currently in (fake fullscreen) + - allow a fullscreen window to be moved to an adjacent monitor while remaining + fullscreen + - make fullscreen windows lose fullscreen if another (e.g. new) window on the + same monitor receives focus, while still allowing dialog boxes to show while + in fullscreen + - allow seamless transition between the two fullscreen modes + +The default keybindings are: + - MOD+f to make a window fullscreen + - MOD+Shift+f to make a window fake fullscreen + +This incorporates, and expands on, the following patches: + - fakefullscreenclient + - togglefullscreen (a.k.a. actualfullscreen) + - tagmonfixfs + - losefullscreen +--- + config.def.h | 5 +- + dwm.c | 174 ++++++++++++++++++++++++++++++++++++++++++--------- + 2 files changed, 147 insertions(+), 32 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..5f28f2c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -35,7 +35,6 @@ static const Rule rules[] = { + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +-static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + + static const Layout layouts[] = { + /* symbol arrange function */ +@@ -75,10 +74,12 @@ static Key keys[] = { + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, ++ { MODKEY, XK_e, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_f, togglefullscreen, {0} }, ++ { MODKEY|ShiftMask, XK_f, togglefakefullscreen, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..dbea07a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int fakefullscreen; + Client *next; + Client *snext; + Monitor *mon; +@@ -178,6 +179,7 @@ static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); ++static void losefullscreen(Client *next); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +@@ -211,7 +213,9 @@ static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); ++static void togglefakefullscreen(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -520,9 +524,12 @@ clientmessage(XEvent *e) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] +- || cme->data.l[2] == netatom[NetWMFullscreen]) ++ || cme->data.l[2] == netatom[NetWMFullscreen]) { ++ if (c->fakefullscreen == 2 && c->isfullscreen) ++ c->fakefullscreen = 3; + 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); +@@ -566,7 +573,7 @@ configurenotify(XEvent *e) + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) +- if (c->isfullscreen) ++ if (c->isfullscreen && c->fakefullscreen != 1) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } +@@ -789,8 +796,10 @@ focus(Client *c) + { + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); +- if (selmon->sel && selmon->sel != c) ++ if (selmon->sel && selmon->sel != c) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + if (c) { + if (c->mon != selmon) + selmon = c->mon; +@@ -838,7 +847,7 @@ focusstack(const Arg *arg) + { + Client *c = NULL, *i; + +- if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) ++ if (!selmon->sel || (selmon->sel->isfullscreen && selmon->sel->fakefullscreen != 1)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); +@@ -1018,6 +1027,16 @@ killclient(const Arg *arg) + } + } + ++void ++losefullscreen(Client *next) ++{ ++ Client *sel = selmon->sel; ++ if (!sel || !next) ++ return; ++ if (sel->isfullscreen && sel->fakefullscreen != 1 && ISVISIBLE(sel) && sel->mon == next->mon && !next->isfloating) ++ setfullscreen(sel, 0); ++} ++ + void + manage(Window w, XWindowAttributes *wa) + { +@@ -1072,8 +1091,10 @@ manage(Window w, XWindowAttributes *wa) + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); +- if (c->mon == selmon) ++ if (c->mon == selmon) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); +@@ -1147,7 +1168,7 @@ movemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1302,7 +1323,7 @@ resizemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1476,29 +1497,79 @@ setfocus(Client *c) + void + setfullscreen(Client *c, int fullscreen) + { +- if (fullscreen && !c->isfullscreen) { +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ XEvent ev; ++ int savestate = 0, restorestate = 0, restorefakefullscreen = 0; ++ ++ if ((c->fakefullscreen == 0 && fullscreen && !c->isfullscreen) // normal fullscreen ++ || (c->fakefullscreen == 2 && fullscreen)) // fake fullscreen --> actual fullscreen ++ savestate = 1; // go actual fullscreen ++ else if ((c->fakefullscreen == 0 && !fullscreen && c->isfullscreen) // normal fullscreen exit ++ || (c->fakefullscreen >= 2 && !fullscreen)) // fullscreen exit --> fake fullscreen ++ restorestate = 1; // go back into tiled ++ ++ /* If leaving fullscreen and the window was previously fake fullscreen (2), then restore ++ * that while staying in fullscreen. The exception to this is if we are in said state, but ++ * the client itself disables fullscreen (3) then we let the client go out of fullscreen ++ * while keeping fake fullscreen enabled (as otherwise there will be a mismatch between the ++ * client and the window manager's perception of the client's fullscreen state). */ ++ if (c->fakefullscreen == 2 && !fullscreen && c->isfullscreen) { ++ restorefakefullscreen = 1; + c->isfullscreen = 1; +- c->oldstate = c->isfloating; ++ fullscreen = 1; ++ } ++ ++ if (fullscreen != c->isfullscreen) { // only send property change if necessary ++ if (fullscreen) ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ else ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)0, 0); ++ } ++ ++ c->isfullscreen = fullscreen; ++ ++ /* Some clients, e.g. firefox, will send a client message informing the window manager ++ * that it is going into fullscreen after receiving the above signal. This has the side ++ * effect of this function (setfullscreen) sometimes being called twice when toggling ++ * fullscreen on and off via the window manager as opposed to the application itself. ++ * To protect against obscure issues where the client settings are stored or restored ++ * when they are not supposed to we add an additional bit-lock on the old state so that ++ * settings can only be stored and restored in that precise order. */ ++ if (savestate && !(c->oldstate & (1 << 1))) { + c->oldbw = c->bw; ++ c->oldstate = c->isfloating | (1 << 1); + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); +- } else if (!fullscreen && c->isfullscreen){ +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)0, 0); +- c->isfullscreen = 0; +- c->isfloating = c->oldstate; ++ } else if (restorestate && (c->oldstate & (1 << 1))) { + c->bw = c->oldbw; +- c->x = c->oldx; +- c->y = c->oldy; +- c->w = c->oldw; +- c->h = c->oldh; ++ c->isfloating = c->oldstate = c->oldstate & 1; ++ if (restorefakefullscreen || c->fakefullscreen == 3) ++ c->fakefullscreen = 1; ++ /* The client may have been moved to another monitor whilst in fullscreen which if tiled ++ * we address by doing a full arrange of tiled clients. If the client is floating then the ++ * height and width may be larger than the monitor's window area, so we cap that by ++ * ensuring max / min values. */ ++ if (c->isfloating) { ++ c->x = MAX(c->mon->wx, c->oldx); ++ c->y = MAX(c->mon->wy, c->oldy); ++ c->w = MIN(c->mon->ww - c->x - 2*c->bw, c->oldw); ++ c->h = MIN(c->mon->wh - c->y - 2*c->bw, c->oldh); ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ restack(c->mon); ++ } else ++ arrange(c->mon); ++ } else + resizeclient(c, c->x, c->y, c->w, c->h); +- arrange(c->mon); +- } ++ ++ /* Exception: if the client was in actual fullscreen and we exit out to fake fullscreen ++ * mode, then the focus would sometimes drift to whichever window is under the mouse cursor ++ * at the time. To avoid this we ask X for all EnterNotify events and just ignore them. ++ */ ++ if (!c->isfullscreen) ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + + void +@@ -1669,9 +1740,19 @@ tag(const Arg *arg) + void + tagmon(const Arg *arg) + { +- if (!selmon->sel || !mons->next) ++ Client *c = selmon->sel; ++ if (!c || !mons->next) + return; +- sendmon(selmon->sel, dirtomon(arg->i)); ++ if (c->isfullscreen) { ++ c->isfullscreen = 0; ++ sendmon(c, dirtomon(arg->i)); ++ c->isfullscreen = 1; ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } else ++ sendmon(c, dirtomon(arg->i)); + } + + void +@@ -1711,18 +1792,51 @@ togglebar(const Arg *arg) + arrange(selmon); + } + ++void ++togglefakefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen != 1 && c->isfullscreen) { // exit fullscreen --> fake fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 0); ++ } else if (c->fakefullscreen == 1) { ++ setfullscreen(c, 0); ++ c->fakefullscreen = 0; ++ } else { ++ c->fakefullscreen = 1; ++ setfullscreen(c, 1); ++ } ++} ++ + void + togglefloating(const Arg *arg) + { +- if (!selmon->sel) ++ Client *c = selmon->sel; ++ if (!c) + return; +- if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support for fullscreen windows */ + return; +- selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; ++ c->isfloating = !c->isfloating || c->isfixed; + if (selmon->sel->isfloating) +- resize(selmon->sel, selmon->sel->x, selmon->sel->y, +- selmon->sel->w, selmon->sel->h, 0); +- arrange(selmon); ++ resize(c, c->x, c->y, c->w, c->h, 0); ++ arrange(c->mon); ++} ++ ++void ++togglefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen == 1) { // fake fullscreen --> fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 1); ++ } else ++ setfullscreen(c, !c->isfullscreen); + } + + void +-- +2.19.1 + diff --git a/dwm/dwm-fullscreen-compilation-noborder-6.3.diff b/dwm/dwm-fullscreen-compilation-noborder-6.3.diff new file mode 100644 index 0000000..e6d5604 --- /dev/null +++ b/dwm/dwm-fullscreen-compilation-noborder-6.3.diff @@ -0,0 +1,32 @@ +From 7c7f76b5981d64c8309c3c9d91a6de881076c137 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:50:15 +0100 +Subject: [PATCH 2/2] Adding fake fullscreen compatible noborder patch + +--- + dwm.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/dwm.c b/dwm.c +index dbea07a..4990319 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1307,6 +1307,15 @@ resizeclient(Client *c, int x, int y, int w, int h) + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; ++ if (((nexttiled(c->mon->clients) == c && !nexttiled(c->next)) ++ || &monocle == c->mon->lt[c->mon->sellt]->arrange) ++ && (c->fakefullscreen == 1 || !c->isfullscreen) ++ && !c->isfloating ++ && c->mon->lt[c->mon->sellt]->arrange) { ++ c->w = wc.width += c->bw * 2; ++ c->h = wc.height += c->bw * 2; ++ wc.border_width = 0; ++ } + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +-- +2.19.1 + diff --git a/dwm/dwm-fullscreen-compilation-noborder-6.3_full.diff b/dwm/dwm-fullscreen-compilation-noborder-6.3_full.diff new file mode 100644 index 0000000..2905845 --- /dev/null +++ b/dwm/dwm-fullscreen-compilation-noborder-6.3_full.diff @@ -0,0 +1,389 @@ +From 26143b40a079f2bded51a6e01dba8530bec8e486 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:47:56 +0100 +Subject: [PATCH 1/2] Compilation of fullscreen patches for dwm. + +This aims to provide a comprehensive fullscreen solution with the following features: + - toggle fullscreen for any window using a single keybinding rather than having + to rely on per-application keybindings + - the ability to have windows go fullscreen within the size and position the + window is currently in (fake fullscreen) + - allow a fullscreen window to be moved to an adjacent monitor while remaining + fullscreen + - make fullscreen windows lose fullscreen if another (e.g. new) window on the + same monitor receives focus, while still allowing dialog boxes to show while + in fullscreen + - allow seamless transition between the two fullscreen modes + +The default keybindings are: + - MOD+f to make a window fullscreen + - MOD+Shift+f to make a window fake fullscreen + +This incorporates, and expands on, the following patches: + - fakefullscreenclient + - togglefullscreen (a.k.a. actualfullscreen) + - tagmonfixfs + - losefullscreen +--- + config.def.h | 5 +- + dwm.c | 174 ++++++++++++++++++++++++++++++++++++++++++--------- + 2 files changed, 147 insertions(+), 32 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..5f28f2c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -35,7 +35,6 @@ static const Rule rules[] = { + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +-static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + + static const Layout layouts[] = { + /* symbol arrange function */ +@@ -75,10 +74,12 @@ static Key keys[] = { + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, ++ { MODKEY, XK_e, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_f, togglefullscreen, {0} }, ++ { MODKEY|ShiftMask, XK_f, togglefakefullscreen, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..dbea07a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int fakefullscreen; + Client *next; + Client *snext; + Monitor *mon; +@@ -178,6 +179,7 @@ static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); ++static void losefullscreen(Client *next); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +@@ -211,7 +213,9 @@ static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); ++static void togglefakefullscreen(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -520,9 +524,12 @@ clientmessage(XEvent *e) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] +- || cme->data.l[2] == netatom[NetWMFullscreen]) ++ || cme->data.l[2] == netatom[NetWMFullscreen]) { ++ if (c->fakefullscreen == 2 && c->isfullscreen) ++ c->fakefullscreen = 3; + 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); +@@ -566,7 +573,7 @@ configurenotify(XEvent *e) + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) +- if (c->isfullscreen) ++ if (c->isfullscreen && c->fakefullscreen != 1) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } +@@ -789,8 +796,10 @@ focus(Client *c) + { + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); +- if (selmon->sel && selmon->sel != c) ++ if (selmon->sel && selmon->sel != c) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + if (c) { + if (c->mon != selmon) + selmon = c->mon; +@@ -838,7 +847,7 @@ focusstack(const Arg *arg) + { + Client *c = NULL, *i; + +- if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) ++ if (!selmon->sel || (selmon->sel->isfullscreen && selmon->sel->fakefullscreen != 1)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); +@@ -1018,6 +1027,16 @@ killclient(const Arg *arg) + } + } + ++void ++losefullscreen(Client *next) ++{ ++ Client *sel = selmon->sel; ++ if (!sel || !next) ++ return; ++ if (sel->isfullscreen && sel->fakefullscreen != 1 && ISVISIBLE(sel) && sel->mon == next->mon && !next->isfloating) ++ setfullscreen(sel, 0); ++} ++ + void + manage(Window w, XWindowAttributes *wa) + { +@@ -1072,8 +1091,10 @@ manage(Window w, XWindowAttributes *wa) + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); +- if (c->mon == selmon) ++ if (c->mon == selmon) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); +@@ -1147,7 +1168,7 @@ movemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1302,7 +1323,7 @@ resizemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1476,29 +1497,79 @@ setfocus(Client *c) + void + setfullscreen(Client *c, int fullscreen) + { +- if (fullscreen && !c->isfullscreen) { +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ XEvent ev; ++ int savestate = 0, restorestate = 0, restorefakefullscreen = 0; ++ ++ if ((c->fakefullscreen == 0 && fullscreen && !c->isfullscreen) // normal fullscreen ++ || (c->fakefullscreen == 2 && fullscreen)) // fake fullscreen --> actual fullscreen ++ savestate = 1; // go actual fullscreen ++ else if ((c->fakefullscreen == 0 && !fullscreen && c->isfullscreen) // normal fullscreen exit ++ || (c->fakefullscreen >= 2 && !fullscreen)) // fullscreen exit --> fake fullscreen ++ restorestate = 1; // go back into tiled ++ ++ /* If leaving fullscreen and the window was previously fake fullscreen (2), then restore ++ * that while staying in fullscreen. The exception to this is if we are in said state, but ++ * the client itself disables fullscreen (3) then we let the client go out of fullscreen ++ * while keeping fake fullscreen enabled (as otherwise there will be a mismatch between the ++ * client and the window manager's perception of the client's fullscreen state). */ ++ if (c->fakefullscreen == 2 && !fullscreen && c->isfullscreen) { ++ restorefakefullscreen = 1; + c->isfullscreen = 1; +- c->oldstate = c->isfloating; ++ fullscreen = 1; ++ } ++ ++ if (fullscreen != c->isfullscreen) { // only send property change if necessary ++ if (fullscreen) ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ else ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)0, 0); ++ } ++ ++ c->isfullscreen = fullscreen; ++ ++ /* Some clients, e.g. firefox, will send a client message informing the window manager ++ * that it is going into fullscreen after receiving the above signal. This has the side ++ * effect of this function (setfullscreen) sometimes being called twice when toggling ++ * fullscreen on and off via the window manager as opposed to the application itself. ++ * To protect against obscure issues where the client settings are stored or restored ++ * when they are not supposed to we add an additional bit-lock on the old state so that ++ * settings can only be stored and restored in that precise order. */ ++ if (savestate && !(c->oldstate & (1 << 1))) { + c->oldbw = c->bw; ++ c->oldstate = c->isfloating | (1 << 1); + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); +- } else if (!fullscreen && c->isfullscreen){ +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)0, 0); +- c->isfullscreen = 0; +- c->isfloating = c->oldstate; ++ } else if (restorestate && (c->oldstate & (1 << 1))) { + c->bw = c->oldbw; +- c->x = c->oldx; +- c->y = c->oldy; +- c->w = c->oldw; +- c->h = c->oldh; ++ c->isfloating = c->oldstate = c->oldstate & 1; ++ if (restorefakefullscreen || c->fakefullscreen == 3) ++ c->fakefullscreen = 1; ++ /* The client may have been moved to another monitor whilst in fullscreen which if tiled ++ * we address by doing a full arrange of tiled clients. If the client is floating then the ++ * height and width may be larger than the monitor's window area, so we cap that by ++ * ensuring max / min values. */ ++ if (c->isfloating) { ++ c->x = MAX(c->mon->wx, c->oldx); ++ c->y = MAX(c->mon->wy, c->oldy); ++ c->w = MIN(c->mon->ww - c->x - 2*c->bw, c->oldw); ++ c->h = MIN(c->mon->wh - c->y - 2*c->bw, c->oldh); ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ restack(c->mon); ++ } else ++ arrange(c->mon); ++ } else + resizeclient(c, c->x, c->y, c->w, c->h); +- arrange(c->mon); +- } ++ ++ /* Exception: if the client was in actual fullscreen and we exit out to fake fullscreen ++ * mode, then the focus would sometimes drift to whichever window is under the mouse cursor ++ * at the time. To avoid this we ask X for all EnterNotify events and just ignore them. ++ */ ++ if (!c->isfullscreen) ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + + void +@@ -1669,9 +1740,19 @@ tag(const Arg *arg) + void + tagmon(const Arg *arg) + { +- if (!selmon->sel || !mons->next) ++ Client *c = selmon->sel; ++ if (!c || !mons->next) + return; +- sendmon(selmon->sel, dirtomon(arg->i)); ++ if (c->isfullscreen) { ++ c->isfullscreen = 0; ++ sendmon(c, dirtomon(arg->i)); ++ c->isfullscreen = 1; ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } else ++ sendmon(c, dirtomon(arg->i)); + } + + void +@@ -1711,18 +1792,51 @@ togglebar(const Arg *arg) + arrange(selmon); + } + ++void ++togglefakefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen != 1 && c->isfullscreen) { // exit fullscreen --> fake fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 0); ++ } else if (c->fakefullscreen == 1) { ++ setfullscreen(c, 0); ++ c->fakefullscreen = 0; ++ } else { ++ c->fakefullscreen = 1; ++ setfullscreen(c, 1); ++ } ++} ++ + void + togglefloating(const Arg *arg) + { +- if (!selmon->sel) ++ Client *c = selmon->sel; ++ if (!c) + return; +- if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support for fullscreen windows */ + return; +- selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; ++ c->isfloating = !c->isfloating || c->isfixed; + if (selmon->sel->isfloating) +- resize(selmon->sel, selmon->sel->x, selmon->sel->y, +- selmon->sel->w, selmon->sel->h, 0); +- arrange(selmon); ++ resize(c, c->x, c->y, c->w, c->h, 0); ++ arrange(c->mon); ++} ++ ++void ++togglefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen == 1) { // fake fullscreen --> fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 1); ++ } else ++ setfullscreen(c, !c->isfullscreen); + } + + void +-- +2.19.1 + + +From 7c7f76b5981d64c8309c3c9d91a6de881076c137 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:50:15 +0100 +Subject: [PATCH 2/2] Adding fake fullscreen compatible noborder patch + +--- + dwm.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/dwm.c b/dwm.c +index dbea07a..4990319 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1307,6 +1307,15 @@ resizeclient(Client *c, int x, int y, int w, int h) + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; ++ if (((nexttiled(c->mon->clients) == c && !nexttiled(c->next)) ++ || &monocle == c->mon->lt[c->mon->sellt]->arrange) ++ && (c->fakefullscreen == 1 || !c->isfullscreen) ++ && !c->isfloating ++ && c->mon->lt[c->mon->sellt]->arrange) { ++ c->w = wc.width += c->bw * 2; ++ c->h = wc.height += c->bw * 2; ++ wc.border_width = 0; ++ } + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +-- +2.19.1 + diff --git a/dwm/dwm-fullscreen-compilation-rule-6.3.diff b/dwm/dwm-fullscreen-compilation-rule-6.3.diff new file mode 100644 index 0000000..22c221d --- /dev/null +++ b/dwm/dwm-fullscreen-compilation-rule-6.3.diff @@ -0,0 +1,50 @@ +From a3575ad7318c5da7f83ce08afc1e84acf63cdc1b Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:50:49 +0100 +Subject: [PATCH 2/2] Adding fake fullscreen client rule + +--- + config.def.h | 6 +++--- + dwm.c | 2 ++ + 2 files changed, 5 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 5f28f2c..dd98367 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 isfakefullscreen monitor */ ++ { "Gimp", NULL, NULL, 0, 1, 0, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1 }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index dbea07a..b0251d6 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -139,6 +139,7 @@ typedef struct { + const char *title; + unsigned int tags; + int isfloating; ++ int isfakefullscreen; + int monitor; + } Rule; + +@@ -304,6 +305,7 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ c->fakefullscreen = r->isfakefullscreen; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +-- +2.19.1 + diff --git a/dwm/dwm-fullscreen-compilation-rule-6.3_full.diff b/dwm/dwm-fullscreen-compilation-rule-6.3_full.diff new file mode 100644 index 0000000..0aea3f6 --- /dev/null +++ b/dwm/dwm-fullscreen-compilation-rule-6.3_full.diff @@ -0,0 +1,407 @@ +From 26143b40a079f2bded51a6e01dba8530bec8e486 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:47:56 +0100 +Subject: [PATCH 1/2] Compilation of fullscreen patches for dwm. + +This aims to provide a comprehensive fullscreen solution with the following features: + - toggle fullscreen for any window using a single keybinding rather than having + to rely on per-application keybindings + - the ability to have windows go fullscreen within the size and position the + window is currently in (fake fullscreen) + - allow a fullscreen window to be moved to an adjacent monitor while remaining + fullscreen + - make fullscreen windows lose fullscreen if another (e.g. new) window on the + same monitor receives focus, while still allowing dialog boxes to show while + in fullscreen + - allow seamless transition between the two fullscreen modes + +The default keybindings are: + - MOD+f to make a window fullscreen + - MOD+Shift+f to make a window fake fullscreen + +This incorporates, and expands on, the following patches: + - fakefullscreenclient + - togglefullscreen (a.k.a. actualfullscreen) + - tagmonfixfs + - losefullscreen +--- + config.def.h | 5 +- + dwm.c | 174 ++++++++++++++++++++++++++++++++++++++++++--------- + 2 files changed, 147 insertions(+), 32 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..5f28f2c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -35,7 +35,6 @@ static const Rule rules[] = { + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +-static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + + static const Layout layouts[] = { + /* symbol arrange function */ +@@ -75,10 +74,12 @@ static Key keys[] = { + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, ++ { MODKEY, XK_e, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_f, togglefullscreen, {0} }, ++ { MODKEY|ShiftMask, XK_f, togglefakefullscreen, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..dbea07a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int fakefullscreen; + Client *next; + Client *snext; + Monitor *mon; +@@ -178,6 +179,7 @@ static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); ++static void losefullscreen(Client *next); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +@@ -211,7 +213,9 @@ static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); ++static void togglefakefullscreen(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -520,9 +524,12 @@ clientmessage(XEvent *e) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] +- || cme->data.l[2] == netatom[NetWMFullscreen]) ++ || cme->data.l[2] == netatom[NetWMFullscreen]) { ++ if (c->fakefullscreen == 2 && c->isfullscreen) ++ c->fakefullscreen = 3; + 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); +@@ -566,7 +573,7 @@ configurenotify(XEvent *e) + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) +- if (c->isfullscreen) ++ if (c->isfullscreen && c->fakefullscreen != 1) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } +@@ -789,8 +796,10 @@ focus(Client *c) + { + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); +- if (selmon->sel && selmon->sel != c) ++ if (selmon->sel && selmon->sel != c) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + if (c) { + if (c->mon != selmon) + selmon = c->mon; +@@ -838,7 +847,7 @@ focusstack(const Arg *arg) + { + Client *c = NULL, *i; + +- if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) ++ if (!selmon->sel || (selmon->sel->isfullscreen && selmon->sel->fakefullscreen != 1)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); +@@ -1018,6 +1027,16 @@ killclient(const Arg *arg) + } + } + ++void ++losefullscreen(Client *next) ++{ ++ Client *sel = selmon->sel; ++ if (!sel || !next) ++ return; ++ if (sel->isfullscreen && sel->fakefullscreen != 1 && ISVISIBLE(sel) && sel->mon == next->mon && !next->isfloating) ++ setfullscreen(sel, 0); ++} ++ + void + manage(Window w, XWindowAttributes *wa) + { +@@ -1072,8 +1091,10 @@ manage(Window w, XWindowAttributes *wa) + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); +- if (c->mon == selmon) ++ if (c->mon == selmon) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); +@@ -1147,7 +1168,7 @@ movemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1302,7 +1323,7 @@ resizemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1476,29 +1497,79 @@ setfocus(Client *c) + void + setfullscreen(Client *c, int fullscreen) + { +- if (fullscreen && !c->isfullscreen) { +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ XEvent ev; ++ int savestate = 0, restorestate = 0, restorefakefullscreen = 0; ++ ++ if ((c->fakefullscreen == 0 && fullscreen && !c->isfullscreen) // normal fullscreen ++ || (c->fakefullscreen == 2 && fullscreen)) // fake fullscreen --> actual fullscreen ++ savestate = 1; // go actual fullscreen ++ else if ((c->fakefullscreen == 0 && !fullscreen && c->isfullscreen) // normal fullscreen exit ++ || (c->fakefullscreen >= 2 && !fullscreen)) // fullscreen exit --> fake fullscreen ++ restorestate = 1; // go back into tiled ++ ++ /* If leaving fullscreen and the window was previously fake fullscreen (2), then restore ++ * that while staying in fullscreen. The exception to this is if we are in said state, but ++ * the client itself disables fullscreen (3) then we let the client go out of fullscreen ++ * while keeping fake fullscreen enabled (as otherwise there will be a mismatch between the ++ * client and the window manager's perception of the client's fullscreen state). */ ++ if (c->fakefullscreen == 2 && !fullscreen && c->isfullscreen) { ++ restorefakefullscreen = 1; + c->isfullscreen = 1; +- c->oldstate = c->isfloating; ++ fullscreen = 1; ++ } ++ ++ if (fullscreen != c->isfullscreen) { // only send property change if necessary ++ if (fullscreen) ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ else ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)0, 0); ++ } ++ ++ c->isfullscreen = fullscreen; ++ ++ /* Some clients, e.g. firefox, will send a client message informing the window manager ++ * that it is going into fullscreen after receiving the above signal. This has the side ++ * effect of this function (setfullscreen) sometimes being called twice when toggling ++ * fullscreen on and off via the window manager as opposed to the application itself. ++ * To protect against obscure issues where the client settings are stored or restored ++ * when they are not supposed to we add an additional bit-lock on the old state so that ++ * settings can only be stored and restored in that precise order. */ ++ if (savestate && !(c->oldstate & (1 << 1))) { + c->oldbw = c->bw; ++ c->oldstate = c->isfloating | (1 << 1); + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); +- } else if (!fullscreen && c->isfullscreen){ +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)0, 0); +- c->isfullscreen = 0; +- c->isfloating = c->oldstate; ++ } else if (restorestate && (c->oldstate & (1 << 1))) { + c->bw = c->oldbw; +- c->x = c->oldx; +- c->y = c->oldy; +- c->w = c->oldw; +- c->h = c->oldh; ++ c->isfloating = c->oldstate = c->oldstate & 1; ++ if (restorefakefullscreen || c->fakefullscreen == 3) ++ c->fakefullscreen = 1; ++ /* The client may have been moved to another monitor whilst in fullscreen which if tiled ++ * we address by doing a full arrange of tiled clients. If the client is floating then the ++ * height and width may be larger than the monitor's window area, so we cap that by ++ * ensuring max / min values. */ ++ if (c->isfloating) { ++ c->x = MAX(c->mon->wx, c->oldx); ++ c->y = MAX(c->mon->wy, c->oldy); ++ c->w = MIN(c->mon->ww - c->x - 2*c->bw, c->oldw); ++ c->h = MIN(c->mon->wh - c->y - 2*c->bw, c->oldh); ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ restack(c->mon); ++ } else ++ arrange(c->mon); ++ } else + resizeclient(c, c->x, c->y, c->w, c->h); +- arrange(c->mon); +- } ++ ++ /* Exception: if the client was in actual fullscreen and we exit out to fake fullscreen ++ * mode, then the focus would sometimes drift to whichever window is under the mouse cursor ++ * at the time. To avoid this we ask X for all EnterNotify events and just ignore them. ++ */ ++ if (!c->isfullscreen) ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + + void +@@ -1669,9 +1740,19 @@ tag(const Arg *arg) + void + tagmon(const Arg *arg) + { +- if (!selmon->sel || !mons->next) ++ Client *c = selmon->sel; ++ if (!c || !mons->next) + return; +- sendmon(selmon->sel, dirtomon(arg->i)); ++ if (c->isfullscreen) { ++ c->isfullscreen = 0; ++ sendmon(c, dirtomon(arg->i)); ++ c->isfullscreen = 1; ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } else ++ sendmon(c, dirtomon(arg->i)); + } + + void +@@ -1711,18 +1792,51 @@ togglebar(const Arg *arg) + arrange(selmon); + } + ++void ++togglefakefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen != 1 && c->isfullscreen) { // exit fullscreen --> fake fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 0); ++ } else if (c->fakefullscreen == 1) { ++ setfullscreen(c, 0); ++ c->fakefullscreen = 0; ++ } else { ++ c->fakefullscreen = 1; ++ setfullscreen(c, 1); ++ } ++} ++ + void + togglefloating(const Arg *arg) + { +- if (!selmon->sel) ++ Client *c = selmon->sel; ++ if (!c) + return; +- if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support for fullscreen windows */ + return; +- selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; ++ c->isfloating = !c->isfloating || c->isfixed; + if (selmon->sel->isfloating) +- resize(selmon->sel, selmon->sel->x, selmon->sel->y, +- selmon->sel->w, selmon->sel->h, 0); +- arrange(selmon); ++ resize(c, c->x, c->y, c->w, c->h, 0); ++ arrange(c->mon); ++} ++ ++void ++togglefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen == 1) { // fake fullscreen --> fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 1); ++ } else ++ setfullscreen(c, !c->isfullscreen); + } + + void +-- +2.19.1 + + +From a3575ad7318c5da7f83ce08afc1e84acf63cdc1b Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:50:49 +0100 +Subject: [PATCH 2/2] Adding fake fullscreen client rule + +--- + config.def.h | 6 +++--- + dwm.c | 2 ++ + 2 files changed, 5 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 5f28f2c..dd98367 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 isfakefullscreen monitor */ ++ { "Gimp", NULL, NULL, 0, 1, 0, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1 }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index dbea07a..b0251d6 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -139,6 +139,7 @@ typedef struct { + const char *title; + unsigned int tags; + int isfloating; ++ int isfakefullscreen; + int monitor; + } Rule; + +@@ -304,6 +305,7 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ c->fakefullscreen = r->isfakefullscreen; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +-- +2.19.1 + diff --git a/dwm/dwm-fullscreen-compilation-tagallmon-6.3.diff b/dwm/dwm-fullscreen-compilation-tagallmon-6.3.diff new file mode 100644 index 0000000..2df7e53 --- /dev/null +++ b/dwm/dwm-fullscreen-compilation-tagallmon-6.3.diff @@ -0,0 +1,89 @@ +From de057561771f4e849c67be952c644374271a9603 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:52:05 +0100 +Subject: [PATCH 2/2] Adding fullscreen-compilation compatible tagallmon patch + +--- + config.def.h | 2 ++ + dwm.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 47 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 5f28f2c..0d5456a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -86,6 +86,8 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_comma, tagallmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_period, tagallmon, {.i = -1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/dwm.c b/dwm.c +index dbea07a..a703cad 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -210,6 +210,7 @@ static void showhide(Client *c); + static void sigchld(int unused); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); ++static void tagallmon(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); +@@ -1737,6 +1738,50 @@ tag(const Arg *arg) + } + } + ++void ++tagallmon(const Arg *arg) ++{ ++ Monitor *m; ++ Client *c, *last, *slast, *next; ++ ++ if (!mons->next) ++ return; ++ ++ m = dirtomon(arg->i); ++ for (last = m->clients; last && last->next; last = last->next); ++ for (slast = m->stack; slast && slast->snext; slast = slast->snext); ++ ++ for (c = selmon->clients; c; c = next) { ++ next = c->next; ++ if (!ISVISIBLE(c)) ++ continue; ++ unfocus(c, 1, NULL); ++ detach(c); ++ detachstack(c); ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ ++ c->next = NULL; ++ c->snext = NULL; ++ if (last) ++ last = last->next = c; ++ else ++ m->clients = last = c; ++ if (slast) ++ slast = slast->snext = c; ++ else ++ m->stack = slast = c; ++ if (c->isfullscreen) { ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ } ++ ++ focus(NULL); ++ arrange(NULL); ++} ++ + void + tagmon(const Arg *arg) + { +-- +2.19.1 + diff --git a/dwm/dwm-fullscreen-compilation-tagallmon-6.3_full.diff b/dwm/dwm-fullscreen-compilation-tagallmon-6.3_full.diff new file mode 100644 index 0000000..c2e21a4 --- /dev/null +++ b/dwm/dwm-fullscreen-compilation-tagallmon-6.3_full.diff @@ -0,0 +1,446 @@ +From 26143b40a079f2bded51a6e01dba8530bec8e486 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:47:56 +0100 +Subject: [PATCH 1/2] Compilation of fullscreen patches for dwm. + +This aims to provide a comprehensive fullscreen solution with the following features: + - toggle fullscreen for any window using a single keybinding rather than having + to rely on per-application keybindings + - the ability to have windows go fullscreen within the size and position the + window is currently in (fake fullscreen) + - allow a fullscreen window to be moved to an adjacent monitor while remaining + fullscreen + - make fullscreen windows lose fullscreen if another (e.g. new) window on the + same monitor receives focus, while still allowing dialog boxes to show while + in fullscreen + - allow seamless transition between the two fullscreen modes + +The default keybindings are: + - MOD+f to make a window fullscreen + - MOD+Shift+f to make a window fake fullscreen + +This incorporates, and expands on, the following patches: + - fakefullscreenclient + - togglefullscreen (a.k.a. actualfullscreen) + - tagmonfixfs + - losefullscreen +--- + config.def.h | 5 +- + dwm.c | 174 ++++++++++++++++++++++++++++++++++++++++++--------- + 2 files changed, 147 insertions(+), 32 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..5f28f2c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -35,7 +35,6 @@ static const Rule rules[] = { + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +-static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + + static const Layout layouts[] = { + /* symbol arrange function */ +@@ -75,10 +74,12 @@ static Key keys[] = { + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, ++ { MODKEY, XK_e, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_f, togglefullscreen, {0} }, ++ { MODKEY|ShiftMask, XK_f, togglefakefullscreen, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..dbea07a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int fakefullscreen; + Client *next; + Client *snext; + Monitor *mon; +@@ -178,6 +179,7 @@ static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); ++static void losefullscreen(Client *next); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +@@ -211,7 +213,9 @@ static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); ++static void togglefakefullscreen(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -520,9 +524,12 @@ clientmessage(XEvent *e) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] +- || cme->data.l[2] == netatom[NetWMFullscreen]) ++ || cme->data.l[2] == netatom[NetWMFullscreen]) { ++ if (c->fakefullscreen == 2 && c->isfullscreen) ++ c->fakefullscreen = 3; + 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); +@@ -566,7 +573,7 @@ configurenotify(XEvent *e) + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) +- if (c->isfullscreen) ++ if (c->isfullscreen && c->fakefullscreen != 1) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } +@@ -789,8 +796,10 @@ focus(Client *c) + { + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); +- if (selmon->sel && selmon->sel != c) ++ if (selmon->sel && selmon->sel != c) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + if (c) { + if (c->mon != selmon) + selmon = c->mon; +@@ -838,7 +847,7 @@ focusstack(const Arg *arg) + { + Client *c = NULL, *i; + +- if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) ++ if (!selmon->sel || (selmon->sel->isfullscreen && selmon->sel->fakefullscreen != 1)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); +@@ -1018,6 +1027,16 @@ killclient(const Arg *arg) + } + } + ++void ++losefullscreen(Client *next) ++{ ++ Client *sel = selmon->sel; ++ if (!sel || !next) ++ return; ++ if (sel->isfullscreen && sel->fakefullscreen != 1 && ISVISIBLE(sel) && sel->mon == next->mon && !next->isfloating) ++ setfullscreen(sel, 0); ++} ++ + void + manage(Window w, XWindowAttributes *wa) + { +@@ -1072,8 +1091,10 @@ manage(Window w, XWindowAttributes *wa) + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); +- if (c->mon == selmon) ++ if (c->mon == selmon) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); +@@ -1147,7 +1168,7 @@ movemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1302,7 +1323,7 @@ resizemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1476,29 +1497,79 @@ setfocus(Client *c) + void + setfullscreen(Client *c, int fullscreen) + { +- if (fullscreen && !c->isfullscreen) { +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ XEvent ev; ++ int savestate = 0, restorestate = 0, restorefakefullscreen = 0; ++ ++ if ((c->fakefullscreen == 0 && fullscreen && !c->isfullscreen) // normal fullscreen ++ || (c->fakefullscreen == 2 && fullscreen)) // fake fullscreen --> actual fullscreen ++ savestate = 1; // go actual fullscreen ++ else if ((c->fakefullscreen == 0 && !fullscreen && c->isfullscreen) // normal fullscreen exit ++ || (c->fakefullscreen >= 2 && !fullscreen)) // fullscreen exit --> fake fullscreen ++ restorestate = 1; // go back into tiled ++ ++ /* If leaving fullscreen and the window was previously fake fullscreen (2), then restore ++ * that while staying in fullscreen. The exception to this is if we are in said state, but ++ * the client itself disables fullscreen (3) then we let the client go out of fullscreen ++ * while keeping fake fullscreen enabled (as otherwise there will be a mismatch between the ++ * client and the window manager's perception of the client's fullscreen state). */ ++ if (c->fakefullscreen == 2 && !fullscreen && c->isfullscreen) { ++ restorefakefullscreen = 1; + c->isfullscreen = 1; +- c->oldstate = c->isfloating; ++ fullscreen = 1; ++ } ++ ++ if (fullscreen != c->isfullscreen) { // only send property change if necessary ++ if (fullscreen) ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ else ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)0, 0); ++ } ++ ++ c->isfullscreen = fullscreen; ++ ++ /* Some clients, e.g. firefox, will send a client message informing the window manager ++ * that it is going into fullscreen after receiving the above signal. This has the side ++ * effect of this function (setfullscreen) sometimes being called twice when toggling ++ * fullscreen on and off via the window manager as opposed to the application itself. ++ * To protect against obscure issues where the client settings are stored or restored ++ * when they are not supposed to we add an additional bit-lock on the old state so that ++ * settings can only be stored and restored in that precise order. */ ++ if (savestate && !(c->oldstate & (1 << 1))) { + c->oldbw = c->bw; ++ c->oldstate = c->isfloating | (1 << 1); + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); +- } else if (!fullscreen && c->isfullscreen){ +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)0, 0); +- c->isfullscreen = 0; +- c->isfloating = c->oldstate; ++ } else if (restorestate && (c->oldstate & (1 << 1))) { + c->bw = c->oldbw; +- c->x = c->oldx; +- c->y = c->oldy; +- c->w = c->oldw; +- c->h = c->oldh; ++ c->isfloating = c->oldstate = c->oldstate & 1; ++ if (restorefakefullscreen || c->fakefullscreen == 3) ++ c->fakefullscreen = 1; ++ /* The client may have been moved to another monitor whilst in fullscreen which if tiled ++ * we address by doing a full arrange of tiled clients. If the client is floating then the ++ * height and width may be larger than the monitor's window area, so we cap that by ++ * ensuring max / min values. */ ++ if (c->isfloating) { ++ c->x = MAX(c->mon->wx, c->oldx); ++ c->y = MAX(c->mon->wy, c->oldy); ++ c->w = MIN(c->mon->ww - c->x - 2*c->bw, c->oldw); ++ c->h = MIN(c->mon->wh - c->y - 2*c->bw, c->oldh); ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ restack(c->mon); ++ } else ++ arrange(c->mon); ++ } else + resizeclient(c, c->x, c->y, c->w, c->h); +- arrange(c->mon); +- } ++ ++ /* Exception: if the client was in actual fullscreen and we exit out to fake fullscreen ++ * mode, then the focus would sometimes drift to whichever window is under the mouse cursor ++ * at the time. To avoid this we ask X for all EnterNotify events and just ignore them. ++ */ ++ if (!c->isfullscreen) ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + + void +@@ -1669,9 +1740,19 @@ tag(const Arg *arg) + void + tagmon(const Arg *arg) + { +- if (!selmon->sel || !mons->next) ++ Client *c = selmon->sel; ++ if (!c || !mons->next) + return; +- sendmon(selmon->sel, dirtomon(arg->i)); ++ if (c->isfullscreen) { ++ c->isfullscreen = 0; ++ sendmon(c, dirtomon(arg->i)); ++ c->isfullscreen = 1; ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } else ++ sendmon(c, dirtomon(arg->i)); + } + + void +@@ -1711,18 +1792,51 @@ togglebar(const Arg *arg) + arrange(selmon); + } + ++void ++togglefakefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen != 1 && c->isfullscreen) { // exit fullscreen --> fake fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 0); ++ } else if (c->fakefullscreen == 1) { ++ setfullscreen(c, 0); ++ c->fakefullscreen = 0; ++ } else { ++ c->fakefullscreen = 1; ++ setfullscreen(c, 1); ++ } ++} ++ + void + togglefloating(const Arg *arg) + { +- if (!selmon->sel) ++ Client *c = selmon->sel; ++ if (!c) + return; +- if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support for fullscreen windows */ + return; +- selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; ++ c->isfloating = !c->isfloating || c->isfixed; + if (selmon->sel->isfloating) +- resize(selmon->sel, selmon->sel->x, selmon->sel->y, +- selmon->sel->w, selmon->sel->h, 0); +- arrange(selmon); ++ resize(c, c->x, c->y, c->w, c->h, 0); ++ arrange(c->mon); ++} ++ ++void ++togglefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen == 1) { // fake fullscreen --> fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 1); ++ } else ++ setfullscreen(c, !c->isfullscreen); + } + + void +-- +2.19.1 + + +From de057561771f4e849c67be952c644374271a9603 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:52:05 +0100 +Subject: [PATCH 2/2] Adding fullscreen-compilation compatible tagallmon patch + +--- + config.def.h | 2 ++ + dwm.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 47 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 5f28f2c..0d5456a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -86,6 +86,8 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_comma, tagallmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_period, tagallmon, {.i = -1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/dwm.c b/dwm.c +index dbea07a..a703cad 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -210,6 +210,7 @@ static void showhide(Client *c); + static void sigchld(int unused); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); ++static void tagallmon(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); +@@ -1737,6 +1738,50 @@ tag(const Arg *arg) + } + } + ++void ++tagallmon(const Arg *arg) ++{ ++ Monitor *m; ++ Client *c, *last, *slast, *next; ++ ++ if (!mons->next) ++ return; ++ ++ m = dirtomon(arg->i); ++ for (last = m->clients; last && last->next; last = last->next); ++ for (slast = m->stack; slast && slast->snext; slast = slast->snext); ++ ++ for (c = selmon->clients; c; c = next) { ++ next = c->next; ++ if (!ISVISIBLE(c)) ++ continue; ++ unfocus(c, 1, NULL); ++ detach(c); ++ detachstack(c); ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ ++ c->next = NULL; ++ c->snext = NULL; ++ if (last) ++ last = last->next = c; ++ else ++ m->clients = last = c; ++ if (slast) ++ slast = slast->snext = c; ++ else ++ m->stack = slast = c; ++ if (c->isfullscreen) { ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ } ++ ++ focus(NULL); ++ arrange(NULL); ++} ++ + void + tagmon(const Arg *arg) + { +-- +2.19.1 + diff --git a/dwm/dwm-fullscreen-compilation-tagswapmon-6.3.diff b/dwm/dwm-fullscreen-compilation-tagswapmon-6.3.diff new file mode 100644 index 0000000..ebab61a --- /dev/null +++ b/dwm/dwm-fullscreen-compilation-tagswapmon-6.3.diff @@ -0,0 +1,110 @@ +From 3008b8f55b7ebd78b8433f213559068f08145e48 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:52:50 +0100 +Subject: [PATCH 2/2] Adding fullscreen-compilation compatible tagswapmon patch + +--- + config.def.h | 2 ++ + dwm.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 68 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 5f28f2c..c47cddb 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -86,6 +86,8 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ControlMask, XK_comma, tagswapmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ControlMask, XK_period, tagswapmon, {.i = -1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/dwm.c b/dwm.c +index dbea07a..fc9c208 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -211,6 +211,7 @@ 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 tagswapmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefakefullscreen(const Arg *arg); +@@ -1755,6 +1756,71 @@ tagmon(const Arg *arg) + sendmon(c, dirtomon(arg->i)); + } + ++void ++tagswapmon(const Arg *arg) ++{ ++ Monitor *m; ++ Client *c, *sc = NULL, *mc = NULL, *next; ++ ++ if (!mons->next) ++ return; ++ ++ m = dirtomon(arg->i); ++ ++ for (c = selmon->clients; c; c = next) { ++ next = c->next; ++ if (!ISVISIBLE(c)) ++ continue; ++ unfocus(c, 1, NULL); ++ detach(c); ++ detachstack(c); ++ c->next = sc; ++ sc = c; ++ } ++ ++ for (c = m->clients; c; c = next) { ++ next = c->next; ++ if (!ISVISIBLE(c)) ++ continue; ++ unfocus(c, 1, NULL); ++ detach(c); ++ detachstack(c); ++ c->next = mc; ++ mc = c; ++ } ++ ++ for (c = sc; c; c = next) { ++ next = c->next; ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ ++ attach(c); ++ attachstack(c); ++ if (c->isfullscreen) { ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ } ++ ++ for (c = mc; c; c = next) { ++ next = c->next; ++ c->mon = selmon; ++ c->tags = selmon->tagset[selmon->seltags]; /* assign tags of target monitor */ ++ attach(c); ++ attachstack(c); ++ if (c->isfullscreen) { ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ } ++ ++ focus(NULL); ++ arrange(NULL); ++} ++ + void + tile(Monitor *m) + { +-- +2.19.1 + diff --git a/dwm/dwm-fullscreen-compilation-tagswapmon-6.3_full.diff b/dwm/dwm-fullscreen-compilation-tagswapmon-6.3_full.diff new file mode 100644 index 0000000..b158f19 --- /dev/null +++ b/dwm/dwm-fullscreen-compilation-tagswapmon-6.3_full.diff @@ -0,0 +1,467 @@ +From 26143b40a079f2bded51a6e01dba8530bec8e486 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:47:56 +0100 +Subject: [PATCH 1/2] Compilation of fullscreen patches for dwm. + +This aims to provide a comprehensive fullscreen solution with the following features: + - toggle fullscreen for any window using a single keybinding rather than having + to rely on per-application keybindings + - the ability to have windows go fullscreen within the size and position the + window is currently in (fake fullscreen) + - allow a fullscreen window to be moved to an adjacent monitor while remaining + fullscreen + - make fullscreen windows lose fullscreen if another (e.g. new) window on the + same monitor receives focus, while still allowing dialog boxes to show while + in fullscreen + - allow seamless transition between the two fullscreen modes + +The default keybindings are: + - MOD+f to make a window fullscreen + - MOD+Shift+f to make a window fake fullscreen + +This incorporates, and expands on, the following patches: + - fakefullscreenclient + - togglefullscreen (a.k.a. actualfullscreen) + - tagmonfixfs + - losefullscreen +--- + config.def.h | 5 +- + dwm.c | 174 ++++++++++++++++++++++++++++++++++++++++++--------- + 2 files changed, 147 insertions(+), 32 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..5f28f2c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -35,7 +35,6 @@ static const Rule rules[] = { + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +-static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + + static const Layout layouts[] = { + /* symbol arrange function */ +@@ -75,10 +74,12 @@ static Key keys[] = { + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, ++ { MODKEY, XK_e, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_f, togglefullscreen, {0} }, ++ { MODKEY|ShiftMask, XK_f, togglefakefullscreen, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..dbea07a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int fakefullscreen; + Client *next; + Client *snext; + Monitor *mon; +@@ -178,6 +179,7 @@ static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); ++static void losefullscreen(Client *next); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +@@ -211,7 +213,9 @@ static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); ++static void togglefakefullscreen(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -520,9 +524,12 @@ clientmessage(XEvent *e) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] +- || cme->data.l[2] == netatom[NetWMFullscreen]) ++ || cme->data.l[2] == netatom[NetWMFullscreen]) { ++ if (c->fakefullscreen == 2 && c->isfullscreen) ++ c->fakefullscreen = 3; + 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); +@@ -566,7 +573,7 @@ configurenotify(XEvent *e) + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) +- if (c->isfullscreen) ++ if (c->isfullscreen && c->fakefullscreen != 1) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } +@@ -789,8 +796,10 @@ focus(Client *c) + { + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); +- if (selmon->sel && selmon->sel != c) ++ if (selmon->sel && selmon->sel != c) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + if (c) { + if (c->mon != selmon) + selmon = c->mon; +@@ -838,7 +847,7 @@ focusstack(const Arg *arg) + { + Client *c = NULL, *i; + +- if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) ++ if (!selmon->sel || (selmon->sel->isfullscreen && selmon->sel->fakefullscreen != 1)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); +@@ -1018,6 +1027,16 @@ killclient(const Arg *arg) + } + } + ++void ++losefullscreen(Client *next) ++{ ++ Client *sel = selmon->sel; ++ if (!sel || !next) ++ return; ++ if (sel->isfullscreen && sel->fakefullscreen != 1 && ISVISIBLE(sel) && sel->mon == next->mon && !next->isfloating) ++ setfullscreen(sel, 0); ++} ++ + void + manage(Window w, XWindowAttributes *wa) + { +@@ -1072,8 +1091,10 @@ manage(Window w, XWindowAttributes *wa) + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); +- if (c->mon == selmon) ++ if (c->mon == selmon) { ++ losefullscreen(c); + unfocus(selmon->sel, 0); ++ } + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); +@@ -1147,7 +1168,7 @@ movemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1302,7 +1323,7 @@ resizemouse(const Arg *arg) + + if (!(c = selmon->sel)) + return; +- if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; +@@ -1476,29 +1497,79 @@ setfocus(Client *c) + void + setfullscreen(Client *c, int fullscreen) + { +- if (fullscreen && !c->isfullscreen) { +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ XEvent ev; ++ int savestate = 0, restorestate = 0, restorefakefullscreen = 0; ++ ++ if ((c->fakefullscreen == 0 && fullscreen && !c->isfullscreen) // normal fullscreen ++ || (c->fakefullscreen == 2 && fullscreen)) // fake fullscreen --> actual fullscreen ++ savestate = 1; // go actual fullscreen ++ else if ((c->fakefullscreen == 0 && !fullscreen && c->isfullscreen) // normal fullscreen exit ++ || (c->fakefullscreen >= 2 && !fullscreen)) // fullscreen exit --> fake fullscreen ++ restorestate = 1; // go back into tiled ++ ++ /* If leaving fullscreen and the window was previously fake fullscreen (2), then restore ++ * that while staying in fullscreen. The exception to this is if we are in said state, but ++ * the client itself disables fullscreen (3) then we let the client go out of fullscreen ++ * while keeping fake fullscreen enabled (as otherwise there will be a mismatch between the ++ * client and the window manager's perception of the client's fullscreen state). */ ++ if (c->fakefullscreen == 2 && !fullscreen && c->isfullscreen) { ++ restorefakefullscreen = 1; + c->isfullscreen = 1; +- c->oldstate = c->isfloating; ++ fullscreen = 1; ++ } ++ ++ if (fullscreen != c->isfullscreen) { // only send property change if necessary ++ if (fullscreen) ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ else ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)0, 0); ++ } ++ ++ c->isfullscreen = fullscreen; ++ ++ /* Some clients, e.g. firefox, will send a client message informing the window manager ++ * that it is going into fullscreen after receiving the above signal. This has the side ++ * effect of this function (setfullscreen) sometimes being called twice when toggling ++ * fullscreen on and off via the window manager as opposed to the application itself. ++ * To protect against obscure issues where the client settings are stored or restored ++ * when they are not supposed to we add an additional bit-lock on the old state so that ++ * settings can only be stored and restored in that precise order. */ ++ if (savestate && !(c->oldstate & (1 << 1))) { + c->oldbw = c->bw; ++ c->oldstate = c->isfloating | (1 << 1); + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); +- } else if (!fullscreen && c->isfullscreen){ +- XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, +- PropModeReplace, (unsigned char*)0, 0); +- c->isfullscreen = 0; +- c->isfloating = c->oldstate; ++ } else if (restorestate && (c->oldstate & (1 << 1))) { + c->bw = c->oldbw; +- c->x = c->oldx; +- c->y = c->oldy; +- c->w = c->oldw; +- c->h = c->oldh; ++ c->isfloating = c->oldstate = c->oldstate & 1; ++ if (restorefakefullscreen || c->fakefullscreen == 3) ++ c->fakefullscreen = 1; ++ /* The client may have been moved to another monitor whilst in fullscreen which if tiled ++ * we address by doing a full arrange of tiled clients. If the client is floating then the ++ * height and width may be larger than the monitor's window area, so we cap that by ++ * ensuring max / min values. */ ++ if (c->isfloating) { ++ c->x = MAX(c->mon->wx, c->oldx); ++ c->y = MAX(c->mon->wy, c->oldy); ++ c->w = MIN(c->mon->ww - c->x - 2*c->bw, c->oldw); ++ c->h = MIN(c->mon->wh - c->y - 2*c->bw, c->oldh); ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ restack(c->mon); ++ } else ++ arrange(c->mon); ++ } else + resizeclient(c, c->x, c->y, c->w, c->h); +- arrange(c->mon); +- } ++ ++ /* Exception: if the client was in actual fullscreen and we exit out to fake fullscreen ++ * mode, then the focus would sometimes drift to whichever window is under the mouse cursor ++ * at the time. To avoid this we ask X for all EnterNotify events and just ignore them. ++ */ ++ if (!c->isfullscreen) ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + + void +@@ -1669,9 +1740,19 @@ tag(const Arg *arg) + void + tagmon(const Arg *arg) + { +- if (!selmon->sel || !mons->next) ++ Client *c = selmon->sel; ++ if (!c || !mons->next) + return; +- sendmon(selmon->sel, dirtomon(arg->i)); ++ if (c->isfullscreen) { ++ c->isfullscreen = 0; ++ sendmon(c, dirtomon(arg->i)); ++ c->isfullscreen = 1; ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } else ++ sendmon(c, dirtomon(arg->i)); + } + + void +@@ -1711,18 +1792,51 @@ togglebar(const Arg *arg) + arrange(selmon); + } + ++void ++togglefakefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen != 1 && c->isfullscreen) { // exit fullscreen --> fake fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 0); ++ } else if (c->fakefullscreen == 1) { ++ setfullscreen(c, 0); ++ c->fakefullscreen = 0; ++ } else { ++ c->fakefullscreen = 1; ++ setfullscreen(c, 1); ++ } ++} ++ + void + togglefloating(const Arg *arg) + { +- if (!selmon->sel) ++ Client *c = selmon->sel; ++ if (!c) + return; +- if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ ++ if (c->isfullscreen && c->fakefullscreen != 1) /* no support for fullscreen windows */ + return; +- selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; ++ c->isfloating = !c->isfloating || c->isfixed; + if (selmon->sel->isfloating) +- resize(selmon->sel, selmon->sel->x, selmon->sel->y, +- selmon->sel->w, selmon->sel->h, 0); +- arrange(selmon); ++ resize(c, c->x, c->y, c->w, c->h, 0); ++ arrange(c->mon); ++} ++ ++void ++togglefullscreen(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ if (c->fakefullscreen == 1) { // fake fullscreen --> fullscreen ++ c->fakefullscreen = 2; ++ setfullscreen(c, 1); ++ } else ++ setfullscreen(c, !c->isfullscreen); + } + + void +-- +2.19.1 + + +From 3008b8f55b7ebd78b8433f213559068f08145e48 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:52:50 +0100 +Subject: [PATCH 2/2] Adding fullscreen-compilation compatible tagswapmon patch + +--- + config.def.h | 2 ++ + dwm.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 68 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 5f28f2c..c47cddb 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -86,6 +86,8 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ControlMask, XK_comma, tagswapmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ControlMask, XK_period, tagswapmon, {.i = -1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/dwm.c b/dwm.c +index dbea07a..fc9c208 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -211,6 +211,7 @@ 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 tagswapmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefakefullscreen(const Arg *arg); +@@ -1755,6 +1756,71 @@ tagmon(const Arg *arg) + sendmon(c, dirtomon(arg->i)); + } + ++void ++tagswapmon(const Arg *arg) ++{ ++ Monitor *m; ++ Client *c, *sc = NULL, *mc = NULL, *next; ++ ++ if (!mons->next) ++ return; ++ ++ m = dirtomon(arg->i); ++ ++ for (c = selmon->clients; c; c = next) { ++ next = c->next; ++ if (!ISVISIBLE(c)) ++ continue; ++ unfocus(c, 1, NULL); ++ detach(c); ++ detachstack(c); ++ c->next = sc; ++ sc = c; ++ } ++ ++ for (c = m->clients; c; c = next) { ++ next = c->next; ++ if (!ISVISIBLE(c)) ++ continue; ++ unfocus(c, 1, NULL); ++ detach(c); ++ detachstack(c); ++ c->next = mc; ++ mc = c; ++ } ++ ++ for (c = sc; c; c = next) { ++ next = c->next; ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ ++ attach(c); ++ attachstack(c); ++ if (c->isfullscreen) { ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ } ++ ++ for (c = mc; c; c = next) { ++ next = c->next; ++ c->mon = selmon; ++ c->tags = selmon->tagset[selmon->seltags]; /* assign tags of target monitor */ ++ attach(c); ++ attachstack(c); ++ if (c->isfullscreen) { ++ if (c->fakefullscreen != 1) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ } ++ ++ focus(NULL); ++ arrange(NULL); ++} ++ + void + tile(Monitor *m) + { +-- +2.19.1 + diff --git a/dwm/dwm-holdbar-modkey-6.3.diff b/dwm/dwm-holdbar-modkey-6.3.diff new file mode 100644 index 0000000..3b92e1e --- /dev/null +++ b/dwm/dwm-holdbar-modkey-6.3.diff @@ -0,0 +1,146 @@ +From 4faf7404d72587f940024a63568ae3d4f00e3028 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:53:22 +0100 +Subject: [PATCH] holdbar: variant of the patch where holdbar is only active + when the bar is toggled off + +Additionally this allows the use of the primary MOD key to be used as the holdbar key while +still allowing the bar to be toggled on and off using MOD+b. This gives a more intuitive and +flexible feel when using this functionality. + +Use xev to find the keysym for the key that you want to use and add/update the HOLDKEY +definition in config.h. + +E.g. using Alt_L as the HOLDKEY +--- + config.def.h | 2 ++ + dwm.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 52 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..94e02cf 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -51,6 +51,7 @@ 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 HOLDKEY 0 // replace 0 with the keysym to activate holdbar + + /* helper for spawning shell commands in the pre dwm-5.0 fashion */ + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } +@@ -95,6 +96,7 @@ static Key keys[] = { + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, ++ { 0, HOLDKEY, holdbar, {0} }, + }; + + /* button definitions */ +diff --git a/dwm.c b/dwm.c +index a96f33c..ec84ee8 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -177,6 +177,7 @@ static void grabbuttons(Client *c, int focused); + static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); ++static void keyrelease(XEvent *e); + static void killclient(const Arg *arg); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); +@@ -211,6 +212,7 @@ static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); ++static void holdbar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); +@@ -218,6 +220,7 @@ static void unfocus(Client *c, int setfocus); + static void unmanage(Client *c, int destroyed); + static void unmapnotify(XEvent *e); + static void updatebarpos(Monitor *m); ++static void updateholdbarpos(Monitor *m); + static void updatebars(void); + static void updateclientlist(void); + static int updategeom(void); +@@ -246,6 +249,7 @@ static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; + static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, ++ [ButtonRelease] = keyrelease, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, +@@ -253,6 +257,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, ++ [KeyRelease] = keyrelease, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, +@@ -276,6 +281,50 @@ static Window root, wmcheckwin; + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + + /* function implementations */ ++void ++holdbar(const Arg *arg) ++{ ++ if (selmon->showbar) ++ return; ++ selmon->showbar = 2; ++ updateholdbarpos(selmon); ++ XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++} ++ ++void ++keyrelease(XEvent *e) ++{ ++ if (XEventsQueued(dpy, QueuedAfterReading)) { ++ XEvent ne; ++ XPeekEvent(dpy, &ne); ++ ++ if (ne.type == KeyPress && ne.xkey.time == e->xkey.time && ++ ne.xkey.keycode == e->xkey.keycode) { ++ XNextEvent(dpy, &ne); ++ return; ++ } ++ } ++ if (e->xkey.keycode == XKeysymToKeycode(dpy, HOLDKEY) && selmon->showbar == 2) { ++ selmon->showbar = 0; ++ updateholdbarpos(selmon); ++ XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ arrange(selmon); ++ } ++} ++ ++void ++updateholdbarpos(Monitor *m) ++{ ++ m->wy = m->my; ++ m->wh = m->mh; ++ if (m->showbar) { ++ m->by = m->topbar ? m->wy : m->wy + m->wh - bh; ++ m->wy = m->topbar ? m->wy - bh + bh : m->wy; ++ } else { ++ m->by = -bh; ++ } ++} ++ + void + applyrules(Client *c) + { +@@ -1705,7 +1754,7 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = (selmon->showbar == 2 ? 1 : !selmon->showbar); + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +-- +2.19.1 + diff --git a/dwm/dwm-keypressrelease-6.3.diff b/dwm/dwm-keypressrelease-6.3.diff new file mode 100644 index 0000000..829f1c3 --- /dev/null +++ b/dwm/dwm-keypressrelease-6.3.diff @@ -0,0 +1,124 @@ +From 9a855a7a11ec8982bae1b8d5e052c4ff8b572733 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:54:40 +0100 +Subject: [PATCH] keypressrelease patch + +--- + config.def.h | 58 ++++++++++++++++++++++++++-------------------------- + dwm.c | 3 +++ + 2 files changed, 32 insertions(+), 29 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..fbced7e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -47,10 +47,10 @@ static const Layout layouts[] = { + /* key definitions */ + #define MODKEY Mod1Mask + #define TAGKEYS(KEY,TAG) \ +- { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ +- { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ +- { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ +- { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, ++ { KeyPress, MODKEY, KEY, view, {.ui = 1 << TAG} }, \ ++ { KeyPress, MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ ++ { KeyPress, MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ ++ { KeyPress, MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + + /* helper for spawning shell commands in the pre dwm-5.0 fashion */ + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } +@@ -61,30 +61,30 @@ static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, + static const char *termcmd[] = { "st", NULL }; + + static Key keys[] = { +- /* modifier key function argument */ +- { MODKEY, XK_p, spawn, {.v = dmenucmd } }, +- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, +- { MODKEY, XK_b, togglebar, {0} }, +- { MODKEY, XK_j, focusstack, {.i = +1 } }, +- { MODKEY, XK_k, focusstack, {.i = -1 } }, +- { MODKEY, XK_i, incnmaster, {.i = +1 } }, +- { MODKEY, XK_d, incnmaster, {.i = -1 } }, +- { MODKEY, XK_h, setmfact, {.f = -0.05} }, +- { MODKEY, XK_l, setmfact, {.f = +0.05} }, +- { MODKEY, XK_Return, zoom, {0} }, +- { MODKEY, XK_Tab, view, {0} }, +- { MODKEY|ShiftMask, XK_c, killclient, {0} }, +- { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, +- { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, +- { 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 } }, +- { MODKEY, XK_period, focusmon, {.i = +1 } }, +- { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, +- { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ /* type modifier key function argument */ ++ { KeyPress, MODKEY, XK_p, spawn, {.v = dmenucmd } }, ++ { KeyPress, MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { KeyPress, MODKEY, XK_b, togglebar, {0} }, ++ { KeyPress, MODKEY, XK_j, focusstack, {.i = +1 } }, ++ { KeyPress, MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { KeyPress, MODKEY, XK_i, incnmaster, {.i = +1 } }, ++ { KeyPress, MODKEY, XK_d, incnmaster, {.i = -1 } }, ++ { KeyPress, MODKEY, XK_h, setmfact, {.f = -0.05} }, ++ { KeyPress, MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { KeyPress, MODKEY, XK_Return, zoom, {0} }, ++ { KeyPress, MODKEY, XK_Tab, view, {0} }, ++ { KeyPress, MODKEY|ShiftMask, XK_c, killclient, {0} }, ++ { KeyPress, MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, ++ { KeyPress, MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, ++ { KeyPress, MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { KeyPress, MODKEY, XK_space, setlayout, {0} }, ++ { KeyPress, MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { KeyPress, MODKEY, XK_0, view, {.ui = ~0 } }, ++ { KeyPress, MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, ++ { KeyPress, MODKEY, XK_comma, focusmon, {.i = -1 } }, ++ { KeyPress, MODKEY, XK_period, focusmon, {.i = +1 } }, ++ { KeyPress, MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, ++ { KeyPress, MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -94,7 +94,7 @@ static Key keys[] = { + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) +- { MODKEY|ShiftMask, XK_q, quit, {0} }, ++ { KeyPress, MODKEY|ShiftMask, XK_q, quit, {0} }, + }; + + /* button definitions */ +diff --git a/dwm.c b/dwm.c +index a96f33c..5cf093a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -100,6 +100,7 @@ struct Client { + }; + + typedef struct { ++ int type; + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); +@@ -254,6 +255,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, ++ [KeyRelease] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, +@@ -997,6 +999,7 @@ keypress(XEvent *e) + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym ++ && ev->type == keys[i].type + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +-- +2.19.1 + diff --git a/dwm/dwm-keypressrelease-holdbar-6.3.diff b/dwm/dwm-keypressrelease-holdbar-6.3.diff new file mode 100644 index 0000000..6779c4c --- /dev/null +++ b/dwm/dwm-keypressrelease-holdbar-6.3.diff @@ -0,0 +1,113 @@ +From 0fd8c7aceab8886bf904f4a567e163040356c6aa Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:55:09 +0100 +Subject: [PATCH 2/2] Holdbar patch on top of keypressrelease + +--- + config.def.h | 5 +++-- + dwm.c | 24 ++++++++++++++++++++++-- + 2 files changed, 25 insertions(+), 4 deletions(-) + +diff --git a/config.def.h b/config.def.h +index fbced7e..267f8e9 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,7 +3,6 @@ + /* appearance */ + 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 */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; +@@ -45,7 +44,7 @@ static const Layout layouts[] = { + }; + + /* key definitions */ +-#define MODKEY Mod1Mask ++#define MODKEY Mod4Mask + #define TAGKEYS(KEY,TAG) \ + { KeyPress, MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { KeyPress, MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ +@@ -62,6 +61,8 @@ static const char *termcmd[] = { "st", NULL }; + + static Key keys[] = { + /* type modifier key function argument */ ++ { KeyPress, 0, XK_Super_L,showbar, {0} }, ++ { KeyRelease, MODKEY, XK_Super_L,hidebar, {0} }, + { KeyPress, MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { KeyPress, MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { KeyPress, MODKEY, XK_b, togglebar, {0} }, +diff --git a/dwm.c b/dwm.c +index 5cf093a..f7c2050 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -176,6 +176,7 @@ static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); ++static void hidebar(const Arg *arg); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); +@@ -207,6 +208,7 @@ static void setup(void); + static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void sigchld(int unused); ++static void showbar(const Arg *arg); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); +@@ -639,7 +641,7 @@ createmon(void) + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; +- m->showbar = showbar; ++ m->showbar = 0; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; +@@ -969,6 +971,15 @@ grabkeys(void) + } + } + ++void ++hidebar(const Arg *arg) ++{ ++ if (selmon->showbar == 2) { ++ selmon->showbar = 1; ++ togglebar(arg); ++ } ++} ++ + void + incnmaster(const Arg *arg) + { +@@ -1643,6 +1654,15 @@ sigchld(int unused) + while (0 < waitpid(-1, NULL, WNOHANG)); + } + ++void ++showbar(const Arg *arg) ++{ ++ if (!selmon->showbar) { ++ togglebar(arg); ++ selmon->showbar = 2; ++ } ++} ++ + void + spawn(const Arg *arg) + { +@@ -1708,7 +1728,7 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = (selmon->showbar == 2 ? 1 : !selmon->showbar); + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +-- +2.19.1 + diff --git a/dwm/dwm-keypressrelease-holdbar-6.3_full.diff b/dwm/dwm-keypressrelease-holdbar-6.3_full.diff new file mode 100644 index 0000000..c69335f --- /dev/null +++ b/dwm/dwm-keypressrelease-holdbar-6.3_full.diff @@ -0,0 +1,238 @@ +From 9a855a7a11ec8982bae1b8d5e052c4ff8b572733 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:54:40 +0100 +Subject: [PATCH 1/2] keypressrelease patch + +--- + config.def.h | 58 ++++++++++++++++++++++++++-------------------------- + dwm.c | 3 +++ + 2 files changed, 32 insertions(+), 29 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..fbced7e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -47,10 +47,10 @@ static const Layout layouts[] = { + /* key definitions */ + #define MODKEY Mod1Mask + #define TAGKEYS(KEY,TAG) \ +- { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ +- { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ +- { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ +- { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, ++ { KeyPress, MODKEY, KEY, view, {.ui = 1 << TAG} }, \ ++ { KeyPress, MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ ++ { KeyPress, MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ ++ { KeyPress, MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + + /* helper for spawning shell commands in the pre dwm-5.0 fashion */ + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } +@@ -61,30 +61,30 @@ static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, + static const char *termcmd[] = { "st", NULL }; + + static Key keys[] = { +- /* modifier key function argument */ +- { MODKEY, XK_p, spawn, {.v = dmenucmd } }, +- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, +- { MODKEY, XK_b, togglebar, {0} }, +- { MODKEY, XK_j, focusstack, {.i = +1 } }, +- { MODKEY, XK_k, focusstack, {.i = -1 } }, +- { MODKEY, XK_i, incnmaster, {.i = +1 } }, +- { MODKEY, XK_d, incnmaster, {.i = -1 } }, +- { MODKEY, XK_h, setmfact, {.f = -0.05} }, +- { MODKEY, XK_l, setmfact, {.f = +0.05} }, +- { MODKEY, XK_Return, zoom, {0} }, +- { MODKEY, XK_Tab, view, {0} }, +- { MODKEY|ShiftMask, XK_c, killclient, {0} }, +- { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, +- { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, +- { 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 } }, +- { MODKEY, XK_period, focusmon, {.i = +1 } }, +- { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, +- { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ /* type modifier key function argument */ ++ { KeyPress, MODKEY, XK_p, spawn, {.v = dmenucmd } }, ++ { KeyPress, MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { KeyPress, MODKEY, XK_b, togglebar, {0} }, ++ { KeyPress, MODKEY, XK_j, focusstack, {.i = +1 } }, ++ { KeyPress, MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { KeyPress, MODKEY, XK_i, incnmaster, {.i = +1 } }, ++ { KeyPress, MODKEY, XK_d, incnmaster, {.i = -1 } }, ++ { KeyPress, MODKEY, XK_h, setmfact, {.f = -0.05} }, ++ { KeyPress, MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { KeyPress, MODKEY, XK_Return, zoom, {0} }, ++ { KeyPress, MODKEY, XK_Tab, view, {0} }, ++ { KeyPress, MODKEY|ShiftMask, XK_c, killclient, {0} }, ++ { KeyPress, MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, ++ { KeyPress, MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, ++ { KeyPress, MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { KeyPress, MODKEY, XK_space, setlayout, {0} }, ++ { KeyPress, MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { KeyPress, MODKEY, XK_0, view, {.ui = ~0 } }, ++ { KeyPress, MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, ++ { KeyPress, MODKEY, XK_comma, focusmon, {.i = -1 } }, ++ { KeyPress, MODKEY, XK_period, focusmon, {.i = +1 } }, ++ { KeyPress, MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, ++ { KeyPress, MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -94,7 +94,7 @@ static Key keys[] = { + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) +- { MODKEY|ShiftMask, XK_q, quit, {0} }, ++ { KeyPress, MODKEY|ShiftMask, XK_q, quit, {0} }, + }; + + /* button definitions */ +diff --git a/dwm.c b/dwm.c +index a96f33c..5cf093a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -100,6 +100,7 @@ struct Client { + }; + + typedef struct { ++ int type; + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); +@@ -254,6 +255,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, ++ [KeyRelease] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, +@@ -997,6 +999,7 @@ keypress(XEvent *e) + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym ++ && ev->type == keys[i].type + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +-- +2.19.1 + + +From 0fd8c7aceab8886bf904f4a567e163040356c6aa Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 11:55:09 +0100 +Subject: [PATCH 2/2] Holdbar patch on top of keypressrelease + +--- + config.def.h | 5 +++-- + dwm.c | 24 ++++++++++++++++++++++-- + 2 files changed, 25 insertions(+), 4 deletions(-) + +diff --git a/config.def.h b/config.def.h +index fbced7e..267f8e9 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,7 +3,6 @@ + /* appearance */ + 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 */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; +@@ -45,7 +44,7 @@ static const Layout layouts[] = { + }; + + /* key definitions */ +-#define MODKEY Mod1Mask ++#define MODKEY Mod4Mask + #define TAGKEYS(KEY,TAG) \ + { KeyPress, MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { KeyPress, MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ +@@ -62,6 +61,8 @@ static const char *termcmd[] = { "st", NULL }; + + static Key keys[] = { + /* type modifier key function argument */ ++ { KeyPress, 0, XK_Super_L,showbar, {0} }, ++ { KeyRelease, MODKEY, XK_Super_L,hidebar, {0} }, + { KeyPress, MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { KeyPress, MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { KeyPress, MODKEY, XK_b, togglebar, {0} }, +diff --git a/dwm.c b/dwm.c +index 5cf093a..f7c2050 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -176,6 +176,7 @@ static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); ++static void hidebar(const Arg *arg); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); +@@ -207,6 +208,7 @@ static void setup(void); + static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void sigchld(int unused); ++static void showbar(const Arg *arg); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); +@@ -639,7 +641,7 @@ createmon(void) + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; +- m->showbar = showbar; ++ m->showbar = 0; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; +@@ -969,6 +971,15 @@ grabkeys(void) + } + } + ++void ++hidebar(const Arg *arg) ++{ ++ if (selmon->showbar == 2) { ++ selmon->showbar = 1; ++ togglebar(arg); ++ } ++} ++ + void + incnmaster(const Arg *arg) + { +@@ -1643,6 +1654,15 @@ sigchld(int unused) + while (0 < waitpid(-1, NULL, WNOHANG)); + } + ++void ++showbar(const Arg *arg) ++{ ++ if (!selmon->showbar) { ++ togglebar(arg); ++ selmon->showbar = 2; ++ } ++} ++ + void + spawn(const Arg *arg) + { +@@ -1708,7 +1728,7 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = (selmon->showbar == 2 ? 1 : !selmon->showbar); + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +-- +2.19.1 + diff --git a/dwm/dwm-losefullscreen-6.3.diff b/dwm/dwm-losefullscreen-6.3.diff new file mode 100644 index 0000000..147f379 --- /dev/null +++ b/dwm/dwm-losefullscreen-6.3.diff @@ -0,0 +1,61 @@ +From 0d1ef9f548011345e144b2b37565c2f9f964ea61 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 12:56:49 +0100 +Subject: [PATCH] Lose fullscreen on focus change + +This implements a separate losefullscreen function proposed by jzbor. +--- + dwm.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..2964adb 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -178,6 +178,7 @@ static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); ++static void losefullscreen(Client *sel, Client *next); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +@@ -854,6 +855,7 @@ focusstack(const Arg *arg) + c = i; + } + if (c) { ++ losefullscreen(selmon->sel, c); + focus(c); + restack(selmon); + } +@@ -1018,6 +1020,15 @@ killclient(const Arg *arg) + } + } + ++void ++losefullscreen(Client *sel, Client *next) ++{ ++ if (!sel || !next) ++ return; ++ if (sel->isfullscreen && ISVISIBLE(sel) && sel->mon == next->mon && !next->isfloating) ++ setfullscreen(sel, 0); ++} ++ + void + manage(Window w, XWindowAttributes *wa) + { +@@ -1072,8 +1083,10 @@ manage(Window w, XWindowAttributes *wa) + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); +- if (c->mon == selmon) ++ if (c->mon == selmon) { ++ losefullscreen(selmon->sel, c); + unfocus(selmon->sel, 0); ++ } + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); +-- +2.19.1 + diff --git a/dwm/dwm-monitorrules-6.3.diff b/dwm/dwm-monitorrules-6.3.diff new file mode 100644 index 0000000..25e6e83 --- /dev/null +++ b/dwm/dwm-monitorrules-6.3.diff @@ -0,0 +1,95 @@ +From f2e1a6e15ba38b29d20f1fa8fa979f8f380e93ae Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 12:57:26 +0100 +Subject: [PATCH] Monitor rules patch + +This patch allows the user to define layout, mfact, nmaster, showbar, +and topbar settings on a per monitor basis. An example use case could +be to have a layout with a horizontal split for a secondary vertical +monitor. +--- + config.def.h | 6 ++++++ + dwm.c | 36 ++++++++++++++++++++++++++++++++---- + 2 files changed, 38 insertions(+), 4 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..7718adc 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -31,6 +31,12 @@ static const Rule rules[] = { + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + }; + ++static const MonitorRule monrules[] = { ++ /* monitor layout mfact nmaster showbar topbar */ ++ { 1, 2, -1, -1, -1, -1 }, // use a different layout for the second monitor ++ { -1, 0, -1, -1, -1, -1 }, // default ++}; ++ + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ +diff --git a/dwm.c b/dwm.c +index a96f33c..31e5834 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -141,6 +141,15 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct { ++ int monitor; ++ int layout; ++ float mfact; ++ int nmaster; ++ int showbar; ++ int topbar; ++} MonitorRule; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -631,7 +640,9 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *mon; ++ unsigned int mi, j; ++ const MonitorRule *mr; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; +@@ -639,9 +650,26 @@ createmon(void) + 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); ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); ++ for (j = 0; j < LENGTH(monrules); j++) { ++ mr = &monrules[j]; ++ if ((mr->monitor == -1 || mr->monitor == mi)) { ++ m->lt[0] = &layouts[mr->layout]; ++ m->lt[1] = &layouts[1 % LENGTH(layouts)]; ++ strncpy(m->ltsymbol, layouts[mr->layout].symbol, sizeof m->ltsymbol); ++ ++ if (mr->mfact > -1) ++ m->mfact = mr->mfact; ++ if (mr->nmaster > -1) ++ m->nmaster = mr->nmaster; ++ if (mr->showbar > -1) ++ m->showbar = mr->showbar; ++ if (mr->topbar > -1) ++ m->topbar = mr->topbar; ++ break; ++ } ++ } + return m; + } + +-- +2.19.1 + diff --git a/dwm/dwm-monitortags-6.3.diff b/dwm/dwm-monitortags-6.3.diff new file mode 100644 index 0000000..5711031 --- /dev/null +++ b/dwm/dwm-monitortags-6.3.diff @@ -0,0 +1,84 @@ +From a6b1b5e62da24750c057fbd8fcbea4b05bb2c152 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 12:58:43 +0100 +Subject: [PATCH] Monitor tags patch + +This patch allows you to have different tags icons on a per-monitor +basis. This patch will conflict with a series of other patches, but +it should be more or less straightforward to sort out: + - LENGTH(tags) is replaced with TAGLENGTH + - tags[i] is replaced with tags[m->mon][i] +--- + config.def.h | 6 +++++- + dwm.c | 16 ++++++++-------- + 2 files changed, 13 insertions(+), 9 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..7845d7f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -19,7 +19,11 @@ static const char *colors[][3] = { + }; + + /* tagging */ +-static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; ++#define TAGLENGTH 9 ++static const char *tags[][TAGLENGTH] = { ++ { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, ++ { "A", "B", "C", "D", "E", "F", "G", "H", "I"}, ++}; + + static const Rule rules[] = { + /* xprop(1): +diff --git a/dwm.c b/dwm.c +index a96f33c..1ac16f4 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -54,7 +54,7 @@ + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +-#define TAGMASK ((1 << LENGTH(tags)) - 1) ++#define TAGMASK ((1 << TAGLENGTH) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +@@ -273,7 +273,7 @@ static Window root, wmcheckwin; + #include "config.h" + + /* compile-time check if all tags fit into an unsigned int bit array. */ +-struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; ++struct NumTags { char limitexceeded[TAGLENGTH > 31 ? -1 : 1]; }; + + /* function implementations */ + void +@@ -433,9 +433,9 @@ 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)) { ++ x += TEXTW(tags[selmon->num][i]); ++ while (ev->x >= x && ++i < TAGLENGTH); ++ if (i < TAGLENGTH) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + blw) +@@ -718,10 +718,10 @@ drawbar(Monitor *m) + urg |= c->tags; + } + x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); ++ for (i = 0; i < TAGLENGTH; i++) { ++ w = TEXTW(tags[m->num][i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[m->num][i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +-- +2.19.1 + diff --git a/dwm/dwm-moveresize-6.3.diff b/dwm/dwm-moveresize-6.3.diff new file mode 100644 index 0000000..b9a03c8 --- /dev/null +++ b/dwm/dwm-moveresize-6.3.diff @@ -0,0 +1,129 @@ +From 3e6552b77de5c70f04cf96f6191d84efebfbb448 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 12:59:49 +0100 +Subject: [PATCH] The moveresize patch allows floating windows to be resized + and moved using keyboard shortcuts. + +This example keybinding reduces the y position with 25 pixels. + + { MODKEY, XK_Up, moveresize, {.v = "0x -25y 0w 0h" } }, + +Use capital letters to specify absolute size and position should you need it. + + { MODKEY, XK_Up, moveresize, {.v = "0x 0y 500W 300H" } }, + +The above example would set the size of the client to 300x500 pixels, but leave the position as-is. + +Refer to: +https://dwm.suckless.org/patches/moveresize/ +--- + config.def.h | 8 +++++++ + dwm.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 74 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..94783ca 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -79,6 +79,14 @@ static Key keys[] = { + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_Down, moveresize, {.v = "0x 25y 0w 0h" } }, ++ { MODKEY, XK_Up, moveresize, {.v = "0x -25y 0w 0h" } }, ++ { MODKEY, XK_Right, moveresize, {.v = "25x 0y 0w 0h" } }, ++ { MODKEY, XK_Left, moveresize, {.v = "-25x 0y 0w 0h" } }, ++ { MODKEY|ShiftMask, XK_Down, moveresize, {.v = "0x 0y 0w 25h" } }, ++ { MODKEY|ShiftMask, XK_Up, moveresize, {.v = "0x 0y 0w -25h" } }, ++ { MODKEY|ShiftMask, XK_Right, moveresize, {.v = "0x 0y 25w 0h" } }, ++ { MODKEY|ShiftMask, XK_Left, moveresize, {.v = "0x 0y -25w 0h" } }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..a419c23 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -183,6 +183,7 @@ static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); ++static void moveresize(const Arg *arg); + static void movemouse(const Arg *arg); + static Client *nexttiled(Client *c); + static void pop(Client *); +@@ -1196,6 +1197,71 @@ movemouse(const Arg *arg) + } + } + ++void ++moveresize(const Arg *arg) { ++ /* only floating windows can be moved */ ++ Client *c; ++ c = selmon->sel; ++ int x, y, w, h, nx, ny, nw, nh, ox, oy, ow, oh; ++ char xAbs, yAbs, wAbs, hAbs; ++ int msx, msy, dx, dy, nmx, nmy; ++ unsigned int dui; ++ Window dummy; ++ ++ if (!c || !arg) ++ return; ++ if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) ++ return; ++ if (sscanf((char *)arg->v, "%d%c %d%c %d%c %d%c", &x, &xAbs, &y, &yAbs, &w, &wAbs, &h, &hAbs) != 8) ++ return; ++ ++ /* compute new window position; prevent window from be positioned outside the current monitor */ ++ nw = c->w + w; ++ if (wAbs == 'W') ++ nw = w < selmon->mw - 2 * c->bw ? w : selmon->mw - 2 * c->bw; ++ ++ nh = c->h + h; ++ if (hAbs == 'H') ++ nh = h < selmon->mh - 2 * c->bw ? h : selmon->mh - 2 * c->bw; ++ ++ nx = c->x + x; ++ if (xAbs == 'X') { ++ if (x < selmon->mx) ++ nx = selmon->mx; ++ else if (x > selmon->mx + selmon->mw) ++ nx = selmon->mx + selmon->mw - nw - 2 * c->bw; ++ else ++ nx = x; ++ } ++ ++ ny = c->y + y; ++ if (yAbs == 'Y') { ++ if (y < selmon->my) ++ ny = selmon->my; ++ else if (y > selmon->my + selmon->mh) ++ ny = selmon->my + selmon->mh - nh - 2 * c->bw; ++ else ++ ny = y; ++ } ++ ++ ox = c->x; ++ oy = c->y; ++ ow = c->w; ++ oh = c->h; ++ ++ XRaiseWindow(dpy, c->win); ++ Bool xqp = XQueryPointer(dpy, root, &dummy, &dummy, &msx, &msy, &dx, &dy, &dui); ++ resize(c, nx, ny, nw, nh, True); ++ ++ /* move cursor along with the window to avoid problems caused by the sloppy focus */ ++ if (xqp && ox <= msx && (ox + ow) >= msx && oy <= msy && (oy + oh) >= msy) ++ { ++ nmx = c->x - ox + c->w - ow; ++ nmy = c->y - oy + c->h - oh; ++ XWarpPointer(dpy, None, None, 0, 0, 0, 0, nmx, nmy); ++ } ++} ++ + Client * + nexttiled(Client *c) + { +-- +2.19.1 + diff --git a/dwm/dwm-netactiverules-6.3.diff b/dwm/dwm-netactiverules-6.3.diff new file mode 100644 index 0000000..c900390 --- /dev/null +++ b/dwm/dwm-netactiverules-6.3.diff @@ -0,0 +1,217 @@ +From bbc5059faa2372e6d01ae4c5e5de9ec230a6c020 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:00:24 +0100 +Subject: [PATCH] This is an example patch extending the focusonnetactive patch + to offer different behaviours on receipt of the net active event signal and + a per client rule for what action to take. + +An example use case is to ignore net active signals from Steam, +which triggers every time the client get focus. + +Being an example patch this has a lot of comments explaining why +code is being added in the places where they are and what the code +does. It is not the intention that the comments is kept. It is +intended as a learning exercise for making changes like this from +scratch. + +This patch was created in relation to this reddit post: +https://www.reddit.com/r/suckless/comments/gyrszt/dwm_and_steam_issue_with_steam_always_taking/ +--- + config.def.h | 16 ++++++-- + dwm.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 115 insertions(+), 5 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..33b0b42 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,16 @@ 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 */ ++/* Default action to take when we receive a net active signal. ++ * 0 - disable / does nothing ++ * 1 - focus the client (as per the focusonnetactive patch) ++ * 2 - focus the client tag in addition to the current tags ++ * 3 - set the urgency bit (as per dwm default) ++ * 4 - client is shown on current tag in addition to its existing tags ++ * 5 - client is moved to current tag ++ * 6 - client receives focus only if current tag is shown ++ */ ++static const int defnetactiverule = 1; + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -26,9 +36,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 netactiverule */ ++ { "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 a96f33c..b1bc3f7 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -66,6 +66,18 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++// Adding an enum here is not strictly necessary, but it ++// can help avoid "magic" numbers in your code base. An ++// example of this is when you read the code and you go ++// "wtf is 58?". Here we have that DoNothing is 0, Focus ++// is 1 and Urgent is 3. Other enums have a *Last value ++// at the end - this is only used when one need to loop ++// through all the values. Also note that you can use ++// these rather than plain numbers in your configuration ++// file, e.g. defnetactiverule = Focus; ++// I left the configuration with plain numbers just for ++// consistency. ++enum { DoNothing, Focus, FocusPlus, Urgent, ShowClient, MoveClient, FocusIfShown }; /* net active rule options */ + + typedef union { + int i; +@@ -91,6 +103,11 @@ struct Client { + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; ++ // We need to associate a specific behaviour on ++ // a per-client basis. As such we need to set a ++ // value for the client and therefore we also ++ // need a variable to store this in. ++ int onnetactive; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + Client *next; +@@ -139,6 +156,12 @@ typedef struct { + unsigned int tags; + int isfloating; + int monitor; ++ // Adding our new rule option, making it an int. ++ // Note that the order of the fields here are ++ // important and the "columns" for the rules ++ // array in your config needs to be in this ++ // exact order. ++ int netactiverule; + } Rule; + + /* function declarations */ +@@ -288,6 +311,11 @@ applyrules(Client *c) + /* rule matching */ + c->isfloating = 0; + c->tags = 0; ++ // In case we have no rule set up for our client, or ++ // the rule value is -1, then we'll want to set the ++ // default action to take when we receive a net active ++ // signal. ++ c->onnetactive = defnetactiverule; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; +@@ -303,6 +331,11 @@ applyrules(Client *c) + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; ++ // If our net active rule is not -1, then associate ++ // that action value with our client. ++ if (r->netactiverule > -1) ++ c->onnetactive = r->netactiverule; ++ + } + } + if (ch.res_class) +@@ -515,6 +548,7 @@ clientmessage(XEvent *e) + { + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); ++ unsigned int i; + + if (!c) + return; +@@ -524,8 +558,74 @@ 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); ++ // OK, so we have received the net active window signal, ++ // let's decide what to do about it. ++ switch(c->onnetactive) { ++ default: ++ break; ++ case DoNothing: ++ // Yes let's just not do anything. This is ++ // redudnant as we could have just left it ++ // for the default, but at last this is ++ // explicit and sort of readable. ++ break; ++ case Focus: ++ // This is lifted straight from the original ++ // focusonnetactive patch. ++ 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); ++ } ++ break; ++ case FocusPlus: ++ // Similar to the original focusonnetactive ++ // logic, but shows the client's tag in ++ // addition to your current tags. ++ for (i = 0; i < LENGTH(tags) && !((1 << i) & c->tags); i++); ++ if (i < LENGTH(tags)) { ++ selmon = c->mon; ++ view(&((Arg) {.ui = c->mon->seltags | (1 << i)})); ++ focus(c); ++ restack(selmon); ++ } ++ break; ++ case ShowClient: ++ // If client is not already on any of the currently ++ // viewed tags, then let the client be shown on the ++ // currently viewed tag(s) in addition to the client's ++ // existing tags. ++ if (!(c->mon->tagset[c->mon->seltags] & c->tags)) ++ c->tags |= c->mon->tagset[c->mon->seltags]; ++ focus(c); ++ arrange(c->mon); ++ break; ++ case MoveClient: ++ // If client is not already on any of the currently ++ // viewed tags, then move the client to the currently ++ // viewed tag(s). ++ if (!(c->mon->tagset[c->mon->seltags] & c->tags)) ++ c->tags = c->mon->tagset[c->mon->seltags]; ++ focus(c); ++ arrange(c->mon); ++ break; ++ case FocusIfShown: ++ // If client is already shown on the currently viewed ++ // tag then focus it, otherwise do nothing. ++ if ((c->mon->tagset[c->mon->seltags] & c->tags)) ++ focus(c); ++ break; ++ case Urgent: ++ // This is simply the original code. ++ if (c != selmon->sel && !c->isurgent) ++ seturgent(c, 1); ++ break; ++ // You could easily extend this to add other behaviours ++ // should you want it. ++ } + } + } + +-- +2.19.1 + diff --git a/dwm/dwm-netclientliststacking-6.3.diff b/dwm/dwm-netclientliststacking-6.3.diff new file mode 100644 index 0000000..c1873b6 --- /dev/null +++ b/dwm/dwm-netclientliststacking-6.3.diff @@ -0,0 +1,65 @@ +From 88398a44ab4dce31c48708d36d39d5ff2a21c8b2 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:01:24 +0100 +Subject: [PATCH] Adds the _NET_CLIENT_LIST_STACKING property which may be + needed by some applications, e.g. zoom for window sharing. + +--- + dwm.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..7e640dc 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -62,7 +62,7 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, +- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++ NetWMWindowTypeDialog, NetClientList, NetClientListStacking, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +@@ -1070,6 +1070,8 @@ manage(Window w, XWindowAttributes *wa) + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); ++ XChangeProperty(dpy, root, netatom[NetClientListStacking], XA_WINDOW, 32, PropModePrepend, ++ (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) +@@ -1566,6 +1568,7 @@ setup(void) + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ netatom[NetClientListStacking] = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); +@@ -1589,6 +1592,7 @@ setup(void) + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); ++ XDeleteProperty(dpy, root, netatom[NetClientListStacking]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask +@@ -1851,6 +1855,13 @@ updateclientlist() + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); ++ ++ XDeleteProperty(dpy, root, netatom[NetClientListStacking]); ++ for (m = mons; m; m = m->next) ++ for (c = m->stack; c; c = c->snext) ++ XChangeProperty(dpy, root, netatom[NetClientListStacking], ++ XA_WINDOW, 32, PropModeAppend, ++ (unsigned char *) &(c->win), 1); + } + + int +-- +2.19.1 + diff --git a/dwm/dwm-pertag-6.2.diff b/dwm/dwm-pertag-6.2.diff index ce88ed2..171facd 100644 --- a/dwm/dwm-pertag-6.2.diff +++ b/dwm/dwm-pertag-6.2.diff @@ -1,6 +1,6 @@ From 9ef25ecff0f049f7c3c5afe3a71da1961807ddb8 Mon Sep 17 00:00:00 2001 From: bakkeby -Date: Tue, 7 Apr 2020 12:34:15 +0200 +Date: Tue, 7 Apr 2020 12.19.15 +0200 Subject: [PATCH] pertag patch, keeps layout, mwfact, barpos and nmaster per tag diff --git a/dwm/dwm-pertag-6.3.diff b/dwm/dwm-pertag-6.3.diff new file mode 100644 index 0000000..1b23aab --- /dev/null +++ b/dwm/dwm-pertag-6.3.diff @@ -0,0 +1,207 @@ +From 8323f1e2e1e71223aa516f66415611fe4dd1f2ed Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:02:01 +0100 +Subject: [PATCH] pertag patch, keeps layout, mwfact, barpos and nmaster per + tag + +Refer to https://dwm.suckless.org/patches/pertag/ +--- + dwm.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 90 insertions(+), 7 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..b8ae4a5 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -111,6 +111,7 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++typedef struct Pertag Pertag; + struct Monitor { + char ltsymbol[16]; + float mfact; +@@ -130,6 +131,7 @@ struct Monitor { + Monitor *next; + Window barwin; + const Layout *lt[2]; ++ Pertag *pertag; + }; + + typedef struct { +@@ -272,6 +274,18 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++struct Pertag { ++ unsigned int curtag, prevtag; /* current and previous tag */ ++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ ++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ ++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ ++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ ++ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ ++ #if ZOOMSWAP_PATCH ++ Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */ ++ #endif // ZOOMSWAP_PATCH ++}; ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -507,6 +521,7 @@ cleanupmon(Monitor *mon) + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ free(mon->pertag); + free(mon); + } + +@@ -632,6 +647,7 @@ Monitor * + createmon(void) + { + Monitor *m; ++ int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; +@@ -642,6 +658,28 @@ createmon(void) + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); ++ m->pertag->curtag = m->pertag->prevtag = 1; ++ for (i = 0; i <= LENGTH(tags); i++) { ++ /* init nmaster */ ++ m->pertag->nmasters[i] = m->nmaster; ++ ++ /* init mfacts */ ++ m->pertag->mfacts[i] = m->mfact; ++ ++ /* init layouts */ ++ m->pertag->ltidxs[i][0] = m->lt[0]; ++ m->pertag->ltidxs[i][1] = m->lt[1]; ++ m->pertag->sellts[i] = m->sellt; ++ ++ /* init showbar */ ++ m->pertag->showbars[i] = m->showbar; ++ ++ #if ZOOMSWAP_PATCH ++ m->pertag->prevzooms[i] = NULL; ++ #endif // ZOOMSWAP_PATCH ++ } + return m; + } + +@@ -970,7 +1008,7 @@ grabkeys(void) + void + incnmaster(const Arg *arg) + { +- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); + } + +@@ -1504,10 +1542,13 @@ 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 != selmon->lt[selmon->sellt]) { ++ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ } + if (arg && arg->v) +- selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); +@@ -1526,7 +1567,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 = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); + } + +@@ -1705,7 +1746,7 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +@@ -1744,9 +1785,29 @@ void + toggleview(const Arg *arg) + { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); ++ int i; + + if (newtagset) { ++ if (newtagset == ~0) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = 0; ++ } ++ /* test if the user did not select the same tag */ ++ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ for (i=0; !(newtagset & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } + selmon->tagset[selmon->seltags] = newtagset; ++ ++ /* apply settings for this view */ ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); + focus(NULL); + arrange(selmon); + } +@@ -2041,11 +2102,33 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { ++ int i; ++ unsigned int tmptag; ++ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ +- if (arg->ui & TAGMASK) ++ if (arg->ui & TAGMASK) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ if (arg->ui == ~0) ++ selmon->pertag->curtag = 0; ++ else { ++ for (i=0; !(arg->ui & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ } else { ++ tmptag = selmon->pertag->prevtag; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = tmptag; ++ } ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); + focus(NULL); + arrange(selmon); + } +-- +2.19.1 + diff --git a/dwm/dwm-pertag-flextile_deluxe-6.3.diff b/dwm/dwm-pertag-flextile_deluxe-6.3.diff new file mode 100644 index 0000000..fd5f30c --- /dev/null +++ b/dwm/dwm-pertag-flextile_deluxe-6.3.diff @@ -0,0 +1,1310 @@ +From 6238981a179eca40688e3d8b1f790c3f66fbcb1f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:10:17 +0100 +Subject: [PATCH 2/2] flextile-deluxe layout - a re-envisoned and revamped + flextile + +Features: + - multiple split layouts + -- horizontal + -- vertical + -- centered + -- floating + -- fixed + - tile arrangement on a per split basis + -- rows + -- columns + -- various grids + -- fibonacci + - mimics hardcoded layouts such as + -- the default tile layout + -- monocle + -- deck + -- centered master + -- bstack + -- bstackhoriz + -- gapplessgrid + -- and many more + - supports rmaster like layout mirroring +--- + config.def.h | 33 +- + dwm.c | 105 +++--- + flextile-deluxe.c | 861 ++++++++++++++++++++++++++++++++++++++++++++++ + flextile-deluxe.h | 116 +++++++ + 4 files changed, 1061 insertions(+), 54 deletions(-) + create mode 100644 flextile-deluxe.c + create mode 100644 flextile-deluxe.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f6b4b0b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -34,14 +34,28 @@ static const Rule rules[] = { + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ ++static const int nstack = 0; /* number of clients in primary stack area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ + 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 }, ++ /* symbol arrange function, { nmaster, nstack, layout, master axis, stack axis, secondary stack axis } */ ++ { "[]=", flextile, { -1, -1, SPLIT_VERTICAL, TOP_TO_BOTTOM, TOP_TO_BOTTOM, 0, NULL } }, // default tile layout ++ { "><>", NULL, {0} }, /* no layout function means floating behavior */ ++ { "[M]", flextile, { -1, -1, NO_SPLIT, MONOCLE, MONOCLE, 0, NULL } }, // monocle ++ { "|||", flextile, { -1, -1, SPLIT_VERTICAL, LEFT_TO_RIGHT, TOP_TO_BOTTOM, 0, NULL } }, // columns (col) layout ++ { ">M>", flextile, { -1, -1, FLOATING_MASTER, LEFT_TO_RIGHT, LEFT_TO_RIGHT, 0, NULL } }, // floating master ++ { "[D]", flextile, { -1, -1, SPLIT_VERTICAL, TOP_TO_BOTTOM, MONOCLE, 0, NULL } }, // deck ++ { "TTT", flextile, { -1, -1, SPLIT_HORIZONTAL, LEFT_TO_RIGHT, LEFT_TO_RIGHT, 0, NULL } }, // bstack ++ { "===", flextile, { -1, -1, SPLIT_HORIZONTAL, LEFT_TO_RIGHT, TOP_TO_BOTTOM, 0, NULL } }, // bstackhoriz ++ { "|M|", flextile, { -1, -1, SPLIT_HORIZONTAL, LEFT_TO_RIGHT, TOP_TO_BOTTOM, 0, monoclesymbols } }, // centeredmaster ++ { ":::", flextile, { -1, -1, NO_SPLIT, GAPPLESSGRID, GAPPLESSGRID, 0, NULL } }, // gappless grid ++ { "[\\]", flextile, { -1, -1, NO_SPLIT, DWINDLE, DWINDLE, 0, NULL } }, // fibonacci dwindle ++ { "(@)", flextile, { -1, -1, NO_SPLIT, SPIRAL, SPIRAL, 0, NULL } }, // fibonacci spiral ++ { "[T]", flextile, { -1, -1, SPLIT_VERTICAL, LEFT_TO_RIGHT, TATAMI, 0, NULL } }, // tatami mats ++ #if CYCLELAYOUTS_PATCH ++ { NULL, NULL, {0} }, ++ #endif + }; + + /* key definitions */ +@@ -69,6 +83,8 @@ static Key keys[] = { + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, ++ { MODKEY|ControlMask, XK_i, incnstack, {.i = +1 } }, ++ { MODKEY|ControlMask, XK_u, incnstack, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, +@@ -77,6 +93,15 @@ static Key keys[] = { + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY|ControlMask, XK_w, rotatelayoutaxis, {.i = +1 } }, /* flextile, 1 = layout axis */ ++ { MODKEY|ControlMask, XK_e, rotatelayoutaxis, {.i = +2 } }, /* flextile, 2 = master axis */ ++ { MODKEY|ControlMask, XK_r, rotatelayoutaxis, {.i = +3 } }, /* flextile, 3 = stack axis */ ++ { MODKEY|ControlMask, XK_t, rotatelayoutaxis, {.i = +4 } }, /* flextile, 4 = secondary stack axis */ ++ { MODKEY|ControlMask|ShiftMask, XK_w, rotatelayoutaxis, {.i = -1 } }, /* flextile, 1 = layout axis */ ++ { MODKEY|ControlMask|ShiftMask, XK_e, rotatelayoutaxis, {.i = -2 } }, /* flextile, 2 = master axis */ ++ { MODKEY|ControlMask|ShiftMask, XK_r, rotatelayoutaxis, {.i = -3 } }, /* flextile, 3 = stack axis */ ++ { MODKEY|ControlMask|ShiftMask, XK_t, rotatelayoutaxis, {.i = -4 } }, /* flextile, 4 = secondary stack axis */ ++ { MODKEY|ControlMask, XK_Return, mirrorlayout, {0} }, /* flextile, flip master and stack areas */ + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, +diff --git a/dwm.c b/dwm.c +index b8ae4a5..e0b0313 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -106,15 +106,28 @@ typedef struct { + const Arg arg; + } Key; + ++typedef struct { ++ int nmaster; ++ int nstack; ++ int layout; ++ int masteraxis; // master stack area ++ int stack1axis; // primary stack area ++ int stack2axis; // secondary stack area, e.g. centered master ++ void (*symbolfunc)(Monitor *, unsigned int); ++} LayoutPreset; ++ + typedef struct { + const char *symbol; + void (*arrange)(Monitor *); ++ LayoutPreset preset; + } Layout; + + typedef struct Pertag Pertag; + struct Monitor { + char ltsymbol[16]; + float mfact; ++ int ltaxis[4]; ++ int nstack; + int nmaster; + int num; + int by; /* bar geometry */ +@@ -183,7 +196,6 @@ static void killclient(const Arg *arg); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +-static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); + static Client *nexttiled(Client *c); +@@ -211,7 +223,6 @@ 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 tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -237,6 +248,8 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "flextile-deluxe.h" ++ + /* variables */ + static const char broken[] = "broken"; + static char stext[256]; +@@ -279,13 +292,15 @@ struct Pertag { + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ +- const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ ++ int nstacks[LENGTH(tags) + 1]; /* number of windows in primary stack area */ ++ int ltaxis[LENGTH(tags) + 1][LTAXIS_LAST]; ++ const Layout *ltidxs[LENGTH(tags) + 1][3]; /* matrix of tags and layouts indexes */ + Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ +- #if ZOOMSWAP_PATCH + Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */ +- #endif // ZOOMSWAP_PATCH + }; + ++#include "flextile-deluxe.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -653,17 +668,25 @@ createmon(void) + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; ++ m->nstack = nstack; + 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->ltaxis[LAYOUT] = m->lt[0]->preset.layout; ++ m->ltaxis[MASTER] = m->lt[0]->preset.masteraxis; ++ m->ltaxis[STACK] = m->lt[0]->preset.stack1axis; ++ m->ltaxis[STACK2] = m->lt[0]->preset.stack2axis; ++ + if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + for (i = 0; i <= LENGTH(tags); i++) { + /* init nmaster */ + m->pertag->nmasters[i] = m->nmaster; ++ m->pertag->nstacks[i] = m->nstack; + + /* init mfacts */ + m->pertag->mfacts[i] = m->mfact; +@@ -673,12 +696,16 @@ createmon(void) + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + ++ m->pertag->ltaxis[i][LAYOUT] = m->ltaxis[LAYOUT]; ++ m->pertag->ltaxis[i][MASTER] = m->ltaxis[MASTER]; ++ m->pertag->ltaxis[i][STACK] = m->ltaxis[STACK]; ++ m->pertag->ltaxis[i][STACK2] = m->ltaxis[STACK2]; ++ + /* init showbar */ + m->pertag->showbars[i] = m->showbar; + +- #if ZOOMSWAP_PATCH ++ /* swap focus and zoomswap*/ + m->pertag->prevzooms[i] = NULL; +- #endif // ZOOMSWAP_PATCH + } + return m; + } +@@ -1142,21 +1169,6 @@ maprequest(XEvent *e) + manage(ev->window, &wa); + } + +-void +-monocle(Monitor *m) +-{ +- unsigned int n = 0; +- Client *c; +- +- for (c = m->clients; c; c = c->next) +- if (ISVISIBLE(c)) +- n++; +- if (n > 0) /* override layout symbol */ +- snprintf(m->ltsymbol, sizeof 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); +-} +- + void + motionnotify(XEvent *e) + { +@@ -1549,6 +1561,22 @@ setlayout(const Arg *arg) + if (arg && arg->v) + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ ++ if (selmon->lt[selmon->sellt]->preset.nmaster && selmon->lt[selmon->sellt]->preset.nmaster != -1) ++ selmon->nmaster = selmon->lt[selmon->sellt]->preset.nmaster; ++ if (selmon->lt[selmon->sellt]->preset.nstack && selmon->lt[selmon->sellt]->preset.nstack != -1) ++ selmon->nstack = selmon->lt[selmon->sellt]->preset.nstack; ++ ++ selmon->ltaxis[LAYOUT] = selmon->lt[selmon->sellt]->preset.layout; ++ selmon->ltaxis[MASTER] = selmon->lt[selmon->sellt]->preset.masteraxis; ++ selmon->ltaxis[STACK] = selmon->lt[selmon->sellt]->preset.stack1axis; ++ selmon->ltaxis[STACK2] = selmon->lt[selmon->sellt]->preset.stack2axis; ++ ++ selmon->pertag->ltaxis[selmon->pertag->curtag][LAYOUT] = selmon->ltaxis[LAYOUT]; ++ selmon->pertag->ltaxis[selmon->pertag->curtag][MASTER] = selmon->ltaxis[MASTER]; ++ selmon->pertag->ltaxis[selmon->pertag->curtag][STACK] = selmon->ltaxis[STACK]; ++ selmon->pertag->ltaxis[selmon->pertag->curtag][STACK2] = selmon->ltaxis[STACK2]; ++ + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); +@@ -1715,34 +1743,6 @@ tagmon(const Arg *arg) + sendmon(selmon->sel, dirtomon(arg->i)); + } + +-void +-tile(Monitor *m) +-{ +- unsigned int i, n, h, mw, my, ty; +- Client *c; +- +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); +- if (n == 0) +- return; +- +- if (n > m->nmaster) +- mw = m->nmaster ? m->ww * m->mfact : 0; +- else +- mw = m->ww; +- for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) +- if (i < m->nmaster) { +- h = (m->wh - my) / (MIN(n, m->nmaster) - i); +- resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); +- if (my + HEIGHT(c) < m->wh) +- my += HEIGHT(c); +- } else { +- h = (m->wh - ty) / (n - i); +- resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); +- if (ty + HEIGHT(c) < m->wh) +- ty += HEIGHT(c); +- } +-} +- + void + togglebar(const Arg *arg) + { +@@ -2123,10 +2123,15 @@ view(const Arg *arg) + selmon->pertag->curtag = tmptag; + } + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->nstack = selmon->pertag->nstacks[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ selmon->ltaxis[LAYOUT] = selmon->pertag->ltaxis[selmon->pertag->curtag][LAYOUT]; ++ selmon->ltaxis[MASTER] = selmon->pertag->ltaxis[selmon->pertag->curtag][MASTER]; ++ selmon->ltaxis[STACK] = selmon->pertag->ltaxis[selmon->pertag->curtag][STACK]; ++ selmon->ltaxis[STACK2] = selmon->pertag->ltaxis[selmon->pertag->curtag][STACK2]; + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + focus(NULL); +diff --git a/flextile-deluxe.c b/flextile-deluxe.c +new file mode 100644 +index 0000000..9c54302 +--- /dev/null ++++ b/flextile-deluxe.c +@@ -0,0 +1,861 @@ ++typedef struct { ++ void (*arrange)(Monitor *, int, int, int, int, int, int, int); ++} LayoutArranger; ++ ++typedef struct { ++ void (*arrange)(Monitor *, int, int, int, int, int, int, int, int, int); ++} TileArranger; ++ ++static const LayoutArranger flexlayouts[] = { ++ { layout_no_split }, ++ { layout_split_vertical }, ++ { layout_split_horizontal }, ++ { layout_split_centered_vertical }, ++ { layout_split_centered_horizontal }, ++ { layout_split_vertical_dual_stack }, ++ { layout_split_horizontal_dual_stack }, ++ { layout_floating_master }, ++ { layout_split_vertical_fixed }, ++ { layout_split_horizontal_fixed }, ++ { layout_split_centered_vertical_fixed }, ++ { layout_split_centered_horizontal_fixed }, ++ { layout_split_vertical_dual_stack_fixed }, ++ { layout_split_horizontal_dual_stack_fixed }, ++ { layout_floating_master_fixed }, ++}; ++ ++static const TileArranger flextiles[] = { ++ { arrange_top_to_bottom }, ++ { arrange_left_to_right }, ++ { arrange_monocle }, ++ { arrange_gapplessgrid }, ++ { arrange_gapplessgrid_alt1 }, ++ { arrange_gapplessgrid_alt2 }, ++ { arrange_gridmode }, ++ { arrange_horizgrid }, ++ { arrange_dwindle }, ++ { arrange_spiral }, ++ { arrange_tatami }, ++}; ++ ++static void ++getfactsforrange(Monitor *m, int an, int ai, int size, int *rest, float *fact) ++{ ++ int i; ++ float facts; ++ Client *c; ++ int total = 0; ++ ++ facts = 0; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i >= ai && i < (ai + an)) ++ #if CFACTS_PATCH ++ facts += c->cfact; ++ #else ++ facts += 1; ++ #endif // CFACTS_PATCH ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i >= ai && i < (ai + an)) ++ #if CFACTS_PATCH ++ total += size * (c->cfact / facts); ++ #else ++ total += size / facts; ++ #endif // CFACTS_PATCH ++ ++ *rest = size - total; ++ *fact = facts; ++} ++ ++static void ++layout_no_split(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, n, 0); ++} ++ ++static void ++layout_split_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (m->nmaster && n > m->nmaster) { ++ layout_split_vertical_fixed(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sw, sx; ++ ++ sw = (w - iv) * (1 - m->mfact); ++ w = (w - iv) * m->mfact; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sx = x; ++ x += sw + iv; ++ } else { ++ sx = x + w + iv; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, n - m->nmaster, m->nmaster); ++} ++ ++static void ++layout_split_vertical_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { ++ layout_split_vertical(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_split_vertical_dual_stack_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_vertical_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sh, sw, sx, oy, sc; ++ ++ if (m->nstack) ++ sc = m->nstack; ++ else ++ sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); ++ ++ sw = (w - iv) * (1 - m->mfact); ++ sh = (h - ih) / 2; ++ w = (w - iv) * m->mfact; ++ oy = y + sh + ih; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sx = x; ++ x += sw + iv; ++ } else { ++ sx = x + w + iv; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, sh, sw, ih, iv, n, sc, m->nmaster); ++ (&flextiles[m->ltaxis[STACK2]])->arrange(m, sx, oy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); ++} ++ ++static void ++layout_split_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (m->nmaster && n > m->nmaster) { ++ layout_split_horizontal_fixed(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sh, sy; ++ ++ sh = (h - ih) * (1 - m->mfact); ++ h = (h - ih) * m->mfact; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sy = y; ++ y += sh + ih; ++ } else { ++ sy = y + h + ih; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, n - m->nmaster, m->nmaster); ++} ++ ++static void ++layout_split_horizontal_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { ++ layout_split_horizontal(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_split_horizontal_dual_stack_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_horizontal_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sh, sy, ox, sc; ++ ++ if (m->nstack) ++ sc = m->nstack; ++ else ++ sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); ++ ++ sh = (h - ih) * (1 - m->mfact); ++ h = (h - ih) * m->mfact; ++ sw = (w - iv) / 2; ++ ox = x + sw + iv; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sy = y; ++ y += sh + ih; ++ } else { ++ sy = y + h + ih; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, sw, ih, iv, n, sc, m->nmaster); ++ (&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, sy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); ++} ++ ++static void ++layout_split_centered_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { ++ layout_split_vertical(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_split_centered_vertical_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_centered_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sw, sx, ox, sc; ++ ++ if (m->nstack) ++ sc = m->nstack; ++ else ++ sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); ++ ++ sw = (w - 2*iv) * (1 - m->mfact) / 2; ++ w = (w - 2*iv) * m->mfact; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sx = x; ++ x += sw + iv; ++ ox = x + w + iv; ++ } else { ++ ox = x; ++ x += sw + iv; ++ sx = x + w + iv; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, sc, m->nmaster); ++ (&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, y, h, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); ++} ++ ++static void ++layout_split_centered_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { ++ layout_split_horizontal(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_split_centered_horizontal_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_centered_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sh, sy, oy, sc; ++ ++ if (m->nstack) ++ sc = m->nstack; ++ else ++ sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); ++ ++ sh = (h - 2*ih) * (1 - m->mfact) / 2; ++ h = (h - 2*ih) * m->mfact; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sy = y; ++ y += sh + ih; ++ oy = y + h + ih; ++ } else { ++ oy = y; ++ y += sh + ih; ++ sy = y + h + ih; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, sc, m->nmaster); ++ (&flextiles[m->ltaxis[STACK2]])->arrange(m, x, oy, sh, w, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); ++} ++ ++static void ++layout_floating_master(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_floating_master_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_floating_master_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int mh, mw; ++ ++ /* Draw stack area first */ ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, x, y, h, w, ih, iv, n, n - m->nmaster, m->nmaster); ++ ++ if (w > h) { ++ mw = w * m->mfact; ++ mh = h * 0.9; ++ } else { ++ mw = w * 0.9; ++ mh = h * m->mfact; ++ } ++ x = x + (w - mw) / 2; ++ y = y + (h - mh) / 2; ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, mh, mw, ih, iv, n, m->nmaster, 0); ++} ++ ++static void ++arrange_left_to_right(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, rest; ++ float facts, fact = 1; ++ Client *c; ++ ++ if (ai + an > n) ++ an = n - ai; ++ ++ w -= iv * (an - 1); ++ getfactsforrange(m, an, ai, w, &rest, &facts); ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i >= ai && i < (ai + an)) { ++ #if CFACTS_PATCH ++ fact = c->cfact; ++ #endif // CFACTS_PATCH ++ resize(c, x, y, w * (fact / facts) + ((i - ai) < rest ? 1 : 0) - (2*c->bw), h - (2*c->bw), 0); ++ x += WIDTH(c) + iv; ++ } ++ } ++} ++ ++static void ++arrange_top_to_bottom(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, rest; ++ float facts, fact = 1; ++ Client *c; ++ ++ if (ai + an > n) ++ an = n - ai; ++ ++ h -= ih * (an - 1); ++ getfactsforrange(m, an, ai, h, &rest, &facts); ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i >= ai && i < (ai + an)) { ++ #if CFACTS_PATCH ++ fact = c->cfact; ++ #endif // CFACTS_PATCH ++ resize(c, x, y, w - (2*c->bw), h * (fact / facts) + ((i - ai) < rest ? 1 : 0) - (2*c->bw), 0); ++ y += HEIGHT(c) + ih; ++ } ++ } ++} ++ ++static void ++arrange_monocle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i; ++ Client *c; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i >= ai && i < (ai + an)) ++ resize(c, x, y, w - (2*c->bw), h - (2*c->bw), 0); ++} ++ ++static void ++arrange_gridmode(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, cols, rows, ch, cw, cx, cy, cc, cr, chrest, cwrest; // counters ++ Client *c; ++ ++ /* grid dimensions */ ++ for (rows = 0; rows <= an/2; rows++) ++ if (rows*rows >= an) ++ break; ++ cols = (rows && (rows - 1) * rows >= an) ? rows - 1 : rows; ++ ++ /* window geoms (cell height/width) */ ++ ch = (h - ih * (rows - 1)) / (rows ? rows : 1); ++ cw = (w - iv * (cols - 1)) / (cols ? cols : 1); ++ chrest = h - ih * (rows - 1) - ch * rows; ++ cwrest = w - iv * (cols - 1) - cw * cols; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i >= ai && i < (ai + an)) { ++ cc = ((i - ai) / rows); // client column number ++ cr = ((i - ai) % rows); // client row number ++ cx = x + cc * (cw + iv) + MIN(cc, cwrest); ++ cy = y + cr * (ch + ih) + MIN(cr, chrest); ++ resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); ++ } ++ } ++} ++ ++static void ++arrange_horizgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int ntop, nbottom, rh, rest; ++ ++ /* Exception when there is only one client; don't split into two rows */ ++ if (an == 1) { ++ arrange_monocle(m, x, y, h, w, ih, iv, n, an, ai); ++ return; ++ } ++ ++ ntop = an / 2; ++ nbottom = an - ntop; ++ rh = (h - ih) / 2; ++ rest = h - ih - rh * 2; ++ arrange_left_to_right(m, x, y, rh + rest, w, ih, iv, n, ntop, ai); ++ arrange_left_to_right(m, x, y + rh + ih + rest, rh, w, ih, iv, n, nbottom, ai + ntop); ++} ++ ++static void ++arrange_gapplessgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, cols, rows, ch, cw, cn, rn, cc, rrest, crest; // counters ++ Client *c; ++ ++ /* grid dimensions */ ++ for (cols = 1; cols <= an/2; cols++) ++ if (cols*cols >= an) ++ break; ++ if (an == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ ++ cols = 2; ++ rows = an/cols; ++ cn = rn = cc = 0; // reset column no, row no, client count ++ ++ ch = (h - ih * (rows - 1)) / rows; ++ rrest = (h - ih * (rows - 1)) - ch * rows; ++ cw = (w - iv * (cols - 1)) / cols; ++ crest = (w - iv * (cols - 1)) - cw * cols; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i >= ai && i < (ai + an)) { ++ if (cc/rows + 1 > cols - an%cols) { ++ rows = an/cols + 1; ++ ch = (h - ih * (rows - 1)) / rows; ++ rrest = (h - ih * (rows - 1)) - ch * rows; ++ } ++ resize(c, ++ x, ++ y + rn*(ch + ih) + MIN(rn, rrest), ++ cw + (cn < crest ? 1 : 0) - 2*c->bw, ++ ch + (rn < rrest ? 1 : 0) - 2*c->bw, ++ 0); ++ rn++; ++ cc++; ++ if (rn >= rows) { ++ rn = 0; ++ x += cw + ih + (cn < crest ? 1 : 0); ++ cn++; ++ } ++ } ++ } ++} ++ ++/* This version of gappless grid fills rows first */ ++static void ++arrange_gapplessgrid_alt1(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, cols, rows, rest, ch; ++ ++ /* grid dimensions */ ++ for (cols = 1; cols <= an/2; cols++) ++ if (cols*cols >= an) ++ break; ++ rows = (cols && (cols - 1) * cols >= an) ? cols - 1 : cols; ++ ch = (h - ih * (rows - 1)) / (rows ? rows : 1); ++ rest = (h - ih * (rows - 1)) - ch * rows; ++ ++ for (i = 0; i < rows; i++) { ++ arrange_left_to_right(m, x, y, ch + (i < rest ? 1 : 0), w, ih, iv, n, MIN(cols, an - i*cols), ai + i*cols); ++ y += ch + (i < rest ? 1 : 0) + ih; ++ } ++} ++ ++/* This version of gappless grid fills columns first */ ++static void ++arrange_gapplessgrid_alt2(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, cols, rows, rest, cw; ++ ++ /* grid dimensions */ ++ for (rows = 0; rows <= an/2; rows++) ++ if (rows*rows >= an) ++ break; ++ cols = (rows && (rows - 1) * rows >= an) ? rows - 1 : rows; ++ cw = (w - iv * (cols - 1)) / (cols ? cols : 1); ++ rest = (w - iv * (cols - 1)) - cw * cols; ++ ++ for (i = 0; i < cols; i++) { ++ arrange_top_to_bottom(m, x, y, h, cw + (i < rest ? 1 : 0), ih, iv, n, MIN(rows, an - i*rows), ai + i*rows); ++ x += cw + (i < rest ? 1 : 0) + iv; ++ } ++} ++ ++static void ++arrange_fibonacci(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai, int s) ++{ ++ int i, j, nv, hrest = 0, wrest = 0, nx = x, ny = y, nw = w, nh = h, r = 1; ++ Client *c; ++ ++ for (i = 0, j = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), j++) { ++ if (j >= ai && j < (ai + an)) { ++ if (r) { ++ if ((i % 2 && ((nh - ih) / 2) <= (bh + 2*c->bw)) || (!(i % 2) && ((nw - iv) / 2) <= (bh + 2*c->bw))) { ++ r = 0; ++ } ++ if (r && i < an - 1) { ++ if (i % 2) { ++ nv = (nh - ih) / 2; ++ hrest = nh - 2*nv - ih; ++ nh = nv; ++ } else { ++ nv = (nw - iv) / 2; ++ wrest = nw - 2*nv - iv; ++ nw = nv; ++ } ++ ++ if ((i % 4) == 2 && !s) ++ nx += nw + iv; ++ else if ((i % 4) == 3 && !s) ++ ny += nh + ih; ++ } ++ if ((i % 4) == 0) { ++ if (s) { ++ ny += nh + ih; ++ nh += hrest; ++ } else { ++ nh -= hrest; ++ ny -= nh + ih; ++ } ++ } else if ((i % 4) == 1) { ++ nx += nw + iv; ++ nw += wrest; ++ } else if ((i % 4) == 2) { ++ ny += nh + ih; ++ nh += hrest; ++ if (i < n - 1) ++ nw += wrest; ++ } else if ((i % 4) == 3) { ++ if (s) { ++ nx += nw + iv; ++ nw -= wrest; ++ } else { ++ nw -= wrest; ++ nx -= nw + iv; ++ nh += hrest; ++ } ++ } ++ if (i == 0) { ++ if (an != 1) { ++ nw = (w - iv) - (w - iv) * (1 - m->mfact); ++ wrest = 0; ++ } ++ ny = y; ++ } else if (i == 1) ++ nw = w - nw - iv; ++ i++; ++ } ++ ++ resize(c, nx, ny, nw - 2 * c->bw, nh - 2*c->bw, False); ++ } ++ } ++} ++ ++static void ++arrange_dwindle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 1); ++} ++ ++static void ++arrange_spiral(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 0); ++} ++ ++static void ++arrange_tatami(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ unsigned int i, j, nx, ny, nw, nh, tnx, tny, tnw, tnh, nhrest, hrest, wrest, areas, mats, cats; ++ Client *c; ++ ++ nx = x; ++ ny = y; ++ nw = w; ++ nh = h; ++ ++ mats = an / 5; ++ cats = an % 5; ++ hrest = 0; ++ wrest = 0; ++ ++ areas = mats + (cats > 0); ++ nh = (h - ih * (areas - 1)) / areas; ++ nhrest = (h - ih * (areas - 1)) % areas; ++ ++ for (i = 0, j = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), j++) { ++ if (j >= ai && j < (ai + an)) { ++ ++ tnw = nw; ++ tnx = nx; ++ tnh = nh; ++ tny = ny; ++ ++ if (j < ai + cats) { ++ /* Arrange cats (all excess clients that can't be tiled as mats). Cats sleep on mats. */ ++ ++ switch (cats) { ++ case 1: // fill ++ break; ++ case 2: // up and down ++ if ((i % 5) == 0) //up ++ tnh = (nh - ih) / 2 + (nh - ih) % 2; ++ else if ((i % 5) == 1) { //down ++ tny += (nh - ih) / 2 + (nh - ih) % 2 + ih; ++ tnh = (nh - ih) / 2; ++ } ++ break; ++ case 3: //bottom, up-left and up-right ++ if ((i % 5) == 0) { // up-left ++ tnw = (nw - iv) / 2 + (nw - iv) % 2; ++ tnh = (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3; ++ } else if ((i % 5) == 1) { // up-right ++ tnx += (nw - iv) / 2 + (nw - iv) % 2 + iv; ++ tnw = (nw - iv) / 2; ++ tnh = (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3; ++ } else if ((i % 5) == 2) { //bottom ++ tnh = (nh - ih) / 3; ++ tny += (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3 + ih; ++ } ++ break; ++ case 4: // bottom, left, right and top ++ if ((i % 5) == 0) { //top ++ hrest = (nh - 2 * ih) % 4; ++ tnh = (nh - 2 * ih) / 4 + (hrest ? 1 : 0); ++ } else if ((i % 5) == 1) { // left ++ tnw = (nw - iv) / 2 + (nw - iv) % 2; ++ tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + ih; ++ tnh = (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0); ++ } else if ((i % 5) == 2) { // right ++ tnx += (nw - iv) / 2 + (nw - iv) % 2 + iv; ++ tnw = (nw - iv) / 2; ++ tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + ih; ++ tnh = (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0); ++ } else if ((i % 5) == 3) { // bottom ++ tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0) + 2 * ih; ++ tnh = (nh - 2 * ih) / 4 + (hrest > 2 ? 1 : 0); ++ } ++ break; ++ } ++ ++ } else { ++ /* Arrange mats. One mat is a collection of five clients arranged tatami style */ ++ ++ if (((i - cats) % 5) == 0) { ++ if ((cats > 0) || ((i - cats) >= 5)) { ++ tny = ny = ny + nh + (nhrest > 0 ? 1 : 0) + ih; ++ --nhrest; ++ } ++ } ++ ++ switch ((i - cats) % 5) { ++ case 0: // top-left-vert ++ wrest = (nw - 2 * iv) % 3; ++ hrest = (nh - 2 * ih) % 3; ++ tnw = (nw - 2 * iv) / 3 + (wrest ? 1 : 0); ++ tnh = (nh - 2 * ih) * 2 / 3 + hrest + iv; ++ break; ++ case 1: // top-right-hor ++ tnx += (nw - 2 * iv) / 3 + (wrest ? 1 : 0) + iv; ++ tnw = (nw - 2 * iv) * 2 / 3 + (wrest > 1 ? 1 : 0) + iv; ++ tnh = (nh - 2 * ih) / 3 + (hrest ? 1 : 0); ++ break; ++ case 2: // center ++ tnx += (nw - 2 * iv) / 3 + (wrest ? 1 : 0) + iv; ++ tnw = (nw - 2 * iv) / 3 + (wrest > 1 ? 1 : 0); ++ tny += (nh - 2 * ih) / 3 + (hrest ? 1 : 0) + ih; ++ tnh = (nh - 2 * ih) / 3 + (hrest > 1 ? 1 : 0); ++ break; ++ case 3: // bottom-right-vert ++ tnx += (nw - 2 * iv) * 2 / 3 + wrest + 2 * iv; ++ tnw = (nw - 2 * iv) / 3; ++ tny += (nh - 2 * ih) / 3 + (hrest ? 1 : 0) + ih; ++ tnh = (nh - 2 * ih) * 2 / 3 + hrest + iv; ++ break; ++ case 4: // (oldest) bottom-left-hor ++ tnw = (nw - 2 * iv) * 2 / 3 + wrest + iv; ++ tny += (nh - 2 * ih) * 2 / 3 + hrest + 2 * iv; ++ tnh = (nh - 2 * ih) / 3; ++ break; ++ } ++ ++ } ++ ++ resize(c, tnx, tny, tnw - 2 * c->bw, tnh - 2 * c->bw, False); ++ ++i; ++ } ++ } ++} ++ ++static void ++flextile(Monitor *m) ++{ ++ unsigned int n; ++ int oh = 0, ov = 0, ih = 0, iv = 0; // gaps outer/inner horizontal/vertical ++ ++ #if VANITYGAPS_PATCH ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ #else ++ Client *c; ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ #endif // VANITYGAPS_PATCH ++ ++ if (m->lt[m->sellt]->preset.layout != m->ltaxis[LAYOUT] || ++ m->lt[m->sellt]->preset.masteraxis != m->ltaxis[MASTER] || ++ m->lt[m->sellt]->preset.stack1axis != m->ltaxis[STACK] || ++ m->lt[m->sellt]->preset.stack2axis != m->ltaxis[STACK2]) ++ setflexsymbols(m, n); ++ else if (m->lt[m->sellt]->preset.symbolfunc != NULL) ++ m->lt[m->sellt]->preset.symbolfunc(m, n); ++ ++ if (n == 0) ++ return; ++ ++ #if VANITYGAPS_PATCH && !VANITYGAPS_MONOCLE_PATCH ++ /* No outer gap if full screen monocle */ ++ if (abs(m->ltaxis[MASTER]) == MONOCLE && (abs(m->ltaxis[LAYOUT]) == NO_SPLIT || n <= m->nmaster)) { ++ oh = 0; ++ ov = 0; ++ } ++ #endif // VANITYGAPS_PATCH && !VANITYGAPS_MONOCLE_PATCH ++ ++ (&flexlayouts[abs(m->ltaxis[LAYOUT])])->arrange(m, m->wx + ov, m->wy + oh, m->wh - 2*oh, m->ww - 2*ov, ih, iv, n); ++ return; ++} ++ ++static void ++setflexsymbols(Monitor *m, unsigned int n) ++{ ++ int l; ++ char sym1, sym2, sym3; ++ Client *c; ++ ++ if (n == 0) ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ ++ l = abs(m->ltaxis[LAYOUT]); ++ if (m->ltaxis[MASTER] == MONOCLE && (l == NO_SPLIT || !m->nmaster || n <= m->nmaster)) { ++ monoclesymbols(m, n); ++ return; ++ } ++ ++ if (m->ltaxis[STACK] == MONOCLE && (l == SPLIT_VERTICAL || l == SPLIT_HORIZONTAL_FIXED)) { ++ decksymbols(m, n); ++ return; ++ } ++ ++ /* Layout symbols */ ++ if (l == NO_SPLIT || !m->nmaster) { ++ sym1 = sym2 = sym3 = (int)tilesymb[m->ltaxis[MASTER]]; ++ } else { ++ sym2 = layoutsymb[l]; ++ if (m->ltaxis[LAYOUT] < 0) { ++ sym1 = tilesymb[m->ltaxis[STACK]]; ++ sym3 = tilesymb[m->ltaxis[MASTER]]; ++ } else { ++ sym1 = tilesymb[m->ltaxis[MASTER]]; ++ sym3 = tilesymb[m->ltaxis[STACK]]; ++ } ++ } ++ ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%c", sym1, sym2, sym3); ++} ++ ++static void ++monoclesymbols(Monitor *m, unsigned int n) ++{ ++ if (n > 0) ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); ++ else ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[M]"); ++} ++ ++static void ++decksymbols(Monitor *m, unsigned int n) ++{ ++ if (n > m->nmaster) ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[]%d", n); ++ else ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[D]"); ++} ++ ++/* Mirror layout axis for flextile */ ++void ++mirrorlayout(const Arg *arg) ++{ ++ if (!selmon->lt[selmon->sellt]->arrange) ++ return; ++ selmon->ltaxis[LAYOUT] *= -1; ++ selmon->pertag->ltaxis[selmon->pertag->curtag][0] = selmon->ltaxis[LAYOUT]; ++ arrange(selmon); ++} ++ ++/* Rotate layout axis for flextile */ ++void ++rotatelayoutaxis(const Arg *arg) ++{ ++ int incr = (arg->i > 0 ? 1 : -1); ++ int axis = abs(arg->i) - 1; ++ ++ if (!selmon->lt[selmon->sellt]->arrange) ++ return; ++ if (axis == LAYOUT) { ++ if (selmon->ltaxis[LAYOUT] >= 0) { ++ selmon->ltaxis[LAYOUT] += incr; ++ if (selmon->ltaxis[LAYOUT] >= LAYOUT_LAST) ++ selmon->ltaxis[LAYOUT] = 0; ++ else if (selmon->ltaxis[LAYOUT] < 0) ++ selmon->ltaxis[LAYOUT] = LAYOUT_LAST - 1; ++ } else { ++ selmon->ltaxis[LAYOUT] -= incr; ++ if (selmon->ltaxis[LAYOUT] <= -LAYOUT_LAST) ++ selmon->ltaxis[LAYOUT] = 0; ++ else if (selmon->ltaxis[LAYOUT] > 0) ++ selmon->ltaxis[LAYOUT] = -LAYOUT_LAST + 1; ++ } ++ } else { ++ selmon->ltaxis[axis] += incr; ++ if (selmon->ltaxis[axis] >= AXIS_LAST) ++ selmon->ltaxis[axis] = 0; ++ else if (selmon->ltaxis[axis] < 0) ++ selmon->ltaxis[axis] = AXIS_LAST - 1; ++ } ++ selmon->pertag->ltaxis[selmon->pertag->curtag][axis] = selmon->ltaxis[axis]; ++ arrange(selmon); ++ setflexsymbols(selmon, 0); ++} ++ ++void ++incnstack(const Arg *arg) ++{ ++ selmon->nstack = selmon->pertag->nstacks[selmon->pertag->curtag] = MAX(selmon->nstack + arg->i, 0); ++ arrange(selmon); ++} +\ No newline at end of file +diff --git a/flextile-deluxe.h b/flextile-deluxe.h +new file mode 100644 +index 0000000..366be3f +--- /dev/null ++++ b/flextile-deluxe.h +@@ -0,0 +1,116 @@ ++static void flextile(Monitor *m); ++static void mirrorlayout(const Arg *arg); ++static void rotatelayoutaxis(const Arg *arg); ++static void incnstack(const Arg *arg); ++ ++/* Symbol handlers */ ++static void setflexsymbols(Monitor *m, unsigned int n); ++static void monoclesymbols(Monitor *m, unsigned int n); ++static void decksymbols(Monitor *m, unsigned int n); ++ ++/* Layout split */ ++static void layout_no_split(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_vertical_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_horizontal_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_centered_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_centered_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_floating_master(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_vertical_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_horizontal_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_centered_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_centered_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_floating_master_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++ ++/* Layout tile arrangements */ ++static void arrange_left_to_right(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_top_to_bottom(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_monocle(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_gapplessgrid(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_gapplessgrid_alt1(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_gapplessgrid_alt2(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_gridmode(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_horizgrid(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_dwindle(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_spiral(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_tatami(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++ ++/* Named flextile constants */ ++enum { ++ LAYOUT, // controls overall layout arrangement / split ++ MASTER, // indicates the tile arrangement for the master area ++ STACK, // indicates the tile arrangement for the stack area ++ STACK2, // indicates the tile arrangement for the secondary stack area ++ LTAXIS_LAST, ++}; ++ ++/* Layout arrangements */ ++enum { ++ NO_SPLIT, ++ SPLIT_VERTICAL, // master stack vertical split ++ SPLIT_HORIZONTAL, // master stack horizontal split ++ SPLIT_CENTERED_VERTICAL, // centered master vertical split ++ SPLIT_CENTERED_HORIZONTAL, // centered master horizontal split ++ SPLIT_VERTICAL_DUAL_STACK, // master stack vertical split with dual stack ++ SPLIT_HORIZONTAL_DUAL_STACK, // master stack vertical split with dual stack ++ FLOATING_MASTER, // (fake) floating master ++ SPLIT_VERTICAL_FIXED, // master stack vertical fixed split ++ SPLIT_HORIZONTAL_FIXED, // master stack horizontal fixed split ++ SPLIT_CENTERED_VERTICAL_FIXED, // centered master vertical fixed split ++ SPLIT_CENTERED_HORIZONTAL_FIXED, // centered master horizontal fixed split ++ SPLIT_VERTICAL_DUAL_STACK_FIXED, // master stack vertical split with fixed dual stack ++ SPLIT_HORIZONTAL_DUAL_STACK_FIXED, // master stack vertical split with fixed dual stack ++ FLOATING_MASTER_FIXED, // (fake) fixed floating master ++ LAYOUT_LAST, ++}; ++ ++static char layoutsymb[] = { ++ 32, // " ", ++ 124, // "|", ++ 61, // "=", ++ 94, // "^", ++ 126, // "~", ++ 58, // ":", ++ 59, // ";", ++ 43, // "+", ++ 124, // "¦", ++ 61, // "=", ++ 94, // "^", ++ 126, // "~", ++ 58, // ":", ++ 59, // ";", ++ 43, // "+", ++}; ++ ++/* Tile arrangements */ ++enum { ++ TOP_TO_BOTTOM, // clients are arranged vertically ++ LEFT_TO_RIGHT, // clients are arranged horizontally ++ MONOCLE, // clients are arranged in deck / monocle mode ++ GAPPLESSGRID, // clients are arranged in a gappless grid (original formula) ++ GAPPLESSGRID_ALT1, // clients are arranged in a gappless grid (alt. 1, fills rows first) ++ GAPPLESSGRID_ALT2, // clients are arranged in a gappless grid (alt. 2, fills columns first) ++ GRIDMODE, // clients are arranged in a grid ++ HORIZGRID, // clients are arranged in a horizontal grid ++ DWINDLE, // clients are arranged in fibonacci dwindle mode ++ SPIRAL, // clients are arranged in fibonacci spiral mode ++ TATAMI, // clients are arranged as tatami mats ++ AXIS_LAST, ++}; ++ ++static char tilesymb[] = { ++ 61, // "=", ++ 124, // "|", ++ 68, // "D", ++ 71, // "G", ++ 49, // "1", ++ 50, // "2" ++ 35, // "#", ++ 126, // "~", ++ 92, // "\\", ++ 64, // "@", ++ 84, // "T", ++}; +-- +2.19.1 + diff --git a/dwm/dwm-pertag-flextile_deluxe-6.3_full.diff b/dwm/dwm-pertag-flextile_deluxe-6.3_full.diff new file mode 100644 index 0000000..fa66686 --- /dev/null +++ b/dwm/dwm-pertag-flextile_deluxe-6.3_full.diff @@ -0,0 +1,1518 @@ +From 8323f1e2e1e71223aa516f66415611fe4dd1f2ed Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:02:01 +0100 +Subject: [PATCH 1/2] pertag patch, keeps layout, mwfact, barpos and nmaster + per tag + +Refer to https://dwm.suckless.org/patches/pertag/ +--- + dwm.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 90 insertions(+), 7 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..b8ae4a5 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -111,6 +111,7 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++typedef struct Pertag Pertag; + struct Monitor { + char ltsymbol[16]; + float mfact; +@@ -130,6 +131,7 @@ struct Monitor { + Monitor *next; + Window barwin; + const Layout *lt[2]; ++ Pertag *pertag; + }; + + typedef struct { +@@ -272,6 +274,18 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++struct Pertag { ++ unsigned int curtag, prevtag; /* current and previous tag */ ++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ ++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ ++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ ++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ ++ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ ++ #if ZOOMSWAP_PATCH ++ Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */ ++ #endif // ZOOMSWAP_PATCH ++}; ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -507,6 +521,7 @@ cleanupmon(Monitor *mon) + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ free(mon->pertag); + free(mon); + } + +@@ -632,6 +647,7 @@ Monitor * + createmon(void) + { + Monitor *m; ++ int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; +@@ -642,6 +658,28 @@ createmon(void) + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); ++ m->pertag->curtag = m->pertag->prevtag = 1; ++ for (i = 0; i <= LENGTH(tags); i++) { ++ /* init nmaster */ ++ m->pertag->nmasters[i] = m->nmaster; ++ ++ /* init mfacts */ ++ m->pertag->mfacts[i] = m->mfact; ++ ++ /* init layouts */ ++ m->pertag->ltidxs[i][0] = m->lt[0]; ++ m->pertag->ltidxs[i][1] = m->lt[1]; ++ m->pertag->sellts[i] = m->sellt; ++ ++ /* init showbar */ ++ m->pertag->showbars[i] = m->showbar; ++ ++ #if ZOOMSWAP_PATCH ++ m->pertag->prevzooms[i] = NULL; ++ #endif // ZOOMSWAP_PATCH ++ } + return m; + } + +@@ -970,7 +1008,7 @@ grabkeys(void) + void + incnmaster(const Arg *arg) + { +- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); + } + +@@ -1504,10 +1542,13 @@ 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 != selmon->lt[selmon->sellt]) { ++ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ } + if (arg && arg->v) +- selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); +@@ -1526,7 +1567,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 = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); + } + +@@ -1705,7 +1746,7 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +@@ -1744,9 +1785,29 @@ void + toggleview(const Arg *arg) + { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); ++ int i; + + if (newtagset) { ++ if (newtagset == ~0) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = 0; ++ } ++ /* test if the user did not select the same tag */ ++ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ for (i=0; !(newtagset & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } + selmon->tagset[selmon->seltags] = newtagset; ++ ++ /* apply settings for this view */ ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); + focus(NULL); + arrange(selmon); + } +@@ -2041,11 +2102,33 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { ++ int i; ++ unsigned int tmptag; ++ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ +- if (arg->ui & TAGMASK) ++ if (arg->ui & TAGMASK) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ if (arg->ui == ~0) ++ selmon->pertag->curtag = 0; ++ else { ++ for (i=0; !(arg->ui & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ } else { ++ tmptag = selmon->pertag->prevtag; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = tmptag; ++ } ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); + focus(NULL); + arrange(selmon); + } +-- +2.19.1 + + +From 6238981a179eca40688e3d8b1f790c3f66fbcb1f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:10:17 +0100 +Subject: [PATCH 2/2] flextile-deluxe layout - a re-envisoned and revamped + flextile + +Features: + - multiple split layouts + -- horizontal + -- vertical + -- centered + -- floating + -- fixed + - tile arrangement on a per split basis + -- rows + -- columns + -- various grids + -- fibonacci + - mimics hardcoded layouts such as + -- the default tile layout + -- monocle + -- deck + -- centered master + -- bstack + -- bstackhoriz + -- gapplessgrid + -- and many more + - supports rmaster like layout mirroring +--- + config.def.h | 33 +- + dwm.c | 105 +++--- + flextile-deluxe.c | 861 ++++++++++++++++++++++++++++++++++++++++++++++ + flextile-deluxe.h | 116 +++++++ + 4 files changed, 1061 insertions(+), 54 deletions(-) + create mode 100644 flextile-deluxe.c + create mode 100644 flextile-deluxe.h + +diff --git a/config.def.h b/config.def.h +index a2ac963..f6b4b0b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -34,14 +34,28 @@ static const Rule rules[] = { + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ ++static const int nstack = 0; /* number of clients in primary stack area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ + 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 }, ++ /* symbol arrange function, { nmaster, nstack, layout, master axis, stack axis, secondary stack axis } */ ++ { "[]=", flextile, { -1, -1, SPLIT_VERTICAL, TOP_TO_BOTTOM, TOP_TO_BOTTOM, 0, NULL } }, // default tile layout ++ { "><>", NULL, {0} }, /* no layout function means floating behavior */ ++ { "[M]", flextile, { -1, -1, NO_SPLIT, MONOCLE, MONOCLE, 0, NULL } }, // monocle ++ { "|||", flextile, { -1, -1, SPLIT_VERTICAL, LEFT_TO_RIGHT, TOP_TO_BOTTOM, 0, NULL } }, // columns (col) layout ++ { ">M>", flextile, { -1, -1, FLOATING_MASTER, LEFT_TO_RIGHT, LEFT_TO_RIGHT, 0, NULL } }, // floating master ++ { "[D]", flextile, { -1, -1, SPLIT_VERTICAL, TOP_TO_BOTTOM, MONOCLE, 0, NULL } }, // deck ++ { "TTT", flextile, { -1, -1, SPLIT_HORIZONTAL, LEFT_TO_RIGHT, LEFT_TO_RIGHT, 0, NULL } }, // bstack ++ { "===", flextile, { -1, -1, SPLIT_HORIZONTAL, LEFT_TO_RIGHT, TOP_TO_BOTTOM, 0, NULL } }, // bstackhoriz ++ { "|M|", flextile, { -1, -1, SPLIT_HORIZONTAL, LEFT_TO_RIGHT, TOP_TO_BOTTOM, 0, monoclesymbols } }, // centeredmaster ++ { ":::", flextile, { -1, -1, NO_SPLIT, GAPPLESSGRID, GAPPLESSGRID, 0, NULL } }, // gappless grid ++ { "[\\]", flextile, { -1, -1, NO_SPLIT, DWINDLE, DWINDLE, 0, NULL } }, // fibonacci dwindle ++ { "(@)", flextile, { -1, -1, NO_SPLIT, SPIRAL, SPIRAL, 0, NULL } }, // fibonacci spiral ++ { "[T]", flextile, { -1, -1, SPLIT_VERTICAL, LEFT_TO_RIGHT, TATAMI, 0, NULL } }, // tatami mats ++ #if CYCLELAYOUTS_PATCH ++ { NULL, NULL, {0} }, ++ #endif + }; + + /* key definitions */ +@@ -69,6 +83,8 @@ static Key keys[] = { + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, ++ { MODKEY|ControlMask, XK_i, incnstack, {.i = +1 } }, ++ { MODKEY|ControlMask, XK_u, incnstack, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, +@@ -77,6 +93,15 @@ static Key keys[] = { + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY|ControlMask, XK_w, rotatelayoutaxis, {.i = +1 } }, /* flextile, 1 = layout axis */ ++ { MODKEY|ControlMask, XK_e, rotatelayoutaxis, {.i = +2 } }, /* flextile, 2 = master axis */ ++ { MODKEY|ControlMask, XK_r, rotatelayoutaxis, {.i = +3 } }, /* flextile, 3 = stack axis */ ++ { MODKEY|ControlMask, XK_t, rotatelayoutaxis, {.i = +4 } }, /* flextile, 4 = secondary stack axis */ ++ { MODKEY|ControlMask|ShiftMask, XK_w, rotatelayoutaxis, {.i = -1 } }, /* flextile, 1 = layout axis */ ++ { MODKEY|ControlMask|ShiftMask, XK_e, rotatelayoutaxis, {.i = -2 } }, /* flextile, 2 = master axis */ ++ { MODKEY|ControlMask|ShiftMask, XK_r, rotatelayoutaxis, {.i = -3 } }, /* flextile, 3 = stack axis */ ++ { MODKEY|ControlMask|ShiftMask, XK_t, rotatelayoutaxis, {.i = -4 } }, /* flextile, 4 = secondary stack axis */ ++ { MODKEY|ControlMask, XK_Return, mirrorlayout, {0} }, /* flextile, flip master and stack areas */ + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, +diff --git a/dwm.c b/dwm.c +index b8ae4a5..e0b0313 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -106,15 +106,28 @@ typedef struct { + const Arg arg; + } Key; + ++typedef struct { ++ int nmaster; ++ int nstack; ++ int layout; ++ int masteraxis; // master stack area ++ int stack1axis; // primary stack area ++ int stack2axis; // secondary stack area, e.g. centered master ++ void (*symbolfunc)(Monitor *, unsigned int); ++} LayoutPreset; ++ + typedef struct { + const char *symbol; + void (*arrange)(Monitor *); ++ LayoutPreset preset; + } Layout; + + typedef struct Pertag Pertag; + struct Monitor { + char ltsymbol[16]; + float mfact; ++ int ltaxis[4]; ++ int nstack; + int nmaster; + int num; + int by; /* bar geometry */ +@@ -183,7 +196,6 @@ static void killclient(const Arg *arg); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +-static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); + static Client *nexttiled(Client *c); +@@ -211,7 +223,6 @@ 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 tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -237,6 +248,8 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++#include "flextile-deluxe.h" ++ + /* variables */ + static const char broken[] = "broken"; + static char stext[256]; +@@ -279,13 +292,15 @@ struct Pertag { + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ +- const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ ++ int nstacks[LENGTH(tags) + 1]; /* number of windows in primary stack area */ ++ int ltaxis[LENGTH(tags) + 1][LTAXIS_LAST]; ++ const Layout *ltidxs[LENGTH(tags) + 1][3]; /* matrix of tags and layouts indexes */ + Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ +- #if ZOOMSWAP_PATCH + Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */ +- #endif // ZOOMSWAP_PATCH + }; + ++#include "flextile-deluxe.c" ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -653,17 +668,25 @@ createmon(void) + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; ++ m->nstack = nstack; + 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->ltaxis[LAYOUT] = m->lt[0]->preset.layout; ++ m->ltaxis[MASTER] = m->lt[0]->preset.masteraxis; ++ m->ltaxis[STACK] = m->lt[0]->preset.stack1axis; ++ m->ltaxis[STACK2] = m->lt[0]->preset.stack2axis; ++ + if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + for (i = 0; i <= LENGTH(tags); i++) { + /* init nmaster */ + m->pertag->nmasters[i] = m->nmaster; ++ m->pertag->nstacks[i] = m->nstack; + + /* init mfacts */ + m->pertag->mfacts[i] = m->mfact; +@@ -673,12 +696,16 @@ createmon(void) + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + ++ m->pertag->ltaxis[i][LAYOUT] = m->ltaxis[LAYOUT]; ++ m->pertag->ltaxis[i][MASTER] = m->ltaxis[MASTER]; ++ m->pertag->ltaxis[i][STACK] = m->ltaxis[STACK]; ++ m->pertag->ltaxis[i][STACK2] = m->ltaxis[STACK2]; ++ + /* init showbar */ + m->pertag->showbars[i] = m->showbar; + +- #if ZOOMSWAP_PATCH ++ /* swap focus and zoomswap*/ + m->pertag->prevzooms[i] = NULL; +- #endif // ZOOMSWAP_PATCH + } + return m; + } +@@ -1142,21 +1169,6 @@ maprequest(XEvent *e) + manage(ev->window, &wa); + } + +-void +-monocle(Monitor *m) +-{ +- unsigned int n = 0; +- Client *c; +- +- for (c = m->clients; c; c = c->next) +- if (ISVISIBLE(c)) +- n++; +- if (n > 0) /* override layout symbol */ +- snprintf(m->ltsymbol, sizeof 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); +-} +- + void + motionnotify(XEvent *e) + { +@@ -1549,6 +1561,22 @@ setlayout(const Arg *arg) + if (arg && arg->v) + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ ++ if (selmon->lt[selmon->sellt]->preset.nmaster && selmon->lt[selmon->sellt]->preset.nmaster != -1) ++ selmon->nmaster = selmon->lt[selmon->sellt]->preset.nmaster; ++ if (selmon->lt[selmon->sellt]->preset.nstack && selmon->lt[selmon->sellt]->preset.nstack != -1) ++ selmon->nstack = selmon->lt[selmon->sellt]->preset.nstack; ++ ++ selmon->ltaxis[LAYOUT] = selmon->lt[selmon->sellt]->preset.layout; ++ selmon->ltaxis[MASTER] = selmon->lt[selmon->sellt]->preset.masteraxis; ++ selmon->ltaxis[STACK] = selmon->lt[selmon->sellt]->preset.stack1axis; ++ selmon->ltaxis[STACK2] = selmon->lt[selmon->sellt]->preset.stack2axis; ++ ++ selmon->pertag->ltaxis[selmon->pertag->curtag][LAYOUT] = selmon->ltaxis[LAYOUT]; ++ selmon->pertag->ltaxis[selmon->pertag->curtag][MASTER] = selmon->ltaxis[MASTER]; ++ selmon->pertag->ltaxis[selmon->pertag->curtag][STACK] = selmon->ltaxis[STACK]; ++ selmon->pertag->ltaxis[selmon->pertag->curtag][STACK2] = selmon->ltaxis[STACK2]; ++ + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); +@@ -1715,34 +1743,6 @@ tagmon(const Arg *arg) + sendmon(selmon->sel, dirtomon(arg->i)); + } + +-void +-tile(Monitor *m) +-{ +- unsigned int i, n, h, mw, my, ty; +- Client *c; +- +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); +- if (n == 0) +- return; +- +- if (n > m->nmaster) +- mw = m->nmaster ? m->ww * m->mfact : 0; +- else +- mw = m->ww; +- for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) +- if (i < m->nmaster) { +- h = (m->wh - my) / (MIN(n, m->nmaster) - i); +- resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); +- if (my + HEIGHT(c) < m->wh) +- my += HEIGHT(c); +- } else { +- h = (m->wh - ty) / (n - i); +- resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); +- if (ty + HEIGHT(c) < m->wh) +- ty += HEIGHT(c); +- } +-} +- + void + togglebar(const Arg *arg) + { +@@ -2123,10 +2123,15 @@ view(const Arg *arg) + selmon->pertag->curtag = tmptag; + } + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->nstack = selmon->pertag->nstacks[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ selmon->ltaxis[LAYOUT] = selmon->pertag->ltaxis[selmon->pertag->curtag][LAYOUT]; ++ selmon->ltaxis[MASTER] = selmon->pertag->ltaxis[selmon->pertag->curtag][MASTER]; ++ selmon->ltaxis[STACK] = selmon->pertag->ltaxis[selmon->pertag->curtag][STACK]; ++ selmon->ltaxis[STACK2] = selmon->pertag->ltaxis[selmon->pertag->curtag][STACK2]; + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + focus(NULL); +diff --git a/flextile-deluxe.c b/flextile-deluxe.c +new file mode 100644 +index 0000000..9c54302 +--- /dev/null ++++ b/flextile-deluxe.c +@@ -0,0 +1,861 @@ ++typedef struct { ++ void (*arrange)(Monitor *, int, int, int, int, int, int, int); ++} LayoutArranger; ++ ++typedef struct { ++ void (*arrange)(Monitor *, int, int, int, int, int, int, int, int, int); ++} TileArranger; ++ ++static const LayoutArranger flexlayouts[] = { ++ { layout_no_split }, ++ { layout_split_vertical }, ++ { layout_split_horizontal }, ++ { layout_split_centered_vertical }, ++ { layout_split_centered_horizontal }, ++ { layout_split_vertical_dual_stack }, ++ { layout_split_horizontal_dual_stack }, ++ { layout_floating_master }, ++ { layout_split_vertical_fixed }, ++ { layout_split_horizontal_fixed }, ++ { layout_split_centered_vertical_fixed }, ++ { layout_split_centered_horizontal_fixed }, ++ { layout_split_vertical_dual_stack_fixed }, ++ { layout_split_horizontal_dual_stack_fixed }, ++ { layout_floating_master_fixed }, ++}; ++ ++static const TileArranger flextiles[] = { ++ { arrange_top_to_bottom }, ++ { arrange_left_to_right }, ++ { arrange_monocle }, ++ { arrange_gapplessgrid }, ++ { arrange_gapplessgrid_alt1 }, ++ { arrange_gapplessgrid_alt2 }, ++ { arrange_gridmode }, ++ { arrange_horizgrid }, ++ { arrange_dwindle }, ++ { arrange_spiral }, ++ { arrange_tatami }, ++}; ++ ++static void ++getfactsforrange(Monitor *m, int an, int ai, int size, int *rest, float *fact) ++{ ++ int i; ++ float facts; ++ Client *c; ++ int total = 0; ++ ++ facts = 0; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i >= ai && i < (ai + an)) ++ #if CFACTS_PATCH ++ facts += c->cfact; ++ #else ++ facts += 1; ++ #endif // CFACTS_PATCH ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i >= ai && i < (ai + an)) ++ #if CFACTS_PATCH ++ total += size * (c->cfact / facts); ++ #else ++ total += size / facts; ++ #endif // CFACTS_PATCH ++ ++ *rest = size - total; ++ *fact = facts; ++} ++ ++static void ++layout_no_split(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, n, 0); ++} ++ ++static void ++layout_split_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (m->nmaster && n > m->nmaster) { ++ layout_split_vertical_fixed(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sw, sx; ++ ++ sw = (w - iv) * (1 - m->mfact); ++ w = (w - iv) * m->mfact; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sx = x; ++ x += sw + iv; ++ } else { ++ sx = x + w + iv; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, n - m->nmaster, m->nmaster); ++} ++ ++static void ++layout_split_vertical_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { ++ layout_split_vertical(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_split_vertical_dual_stack_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_vertical_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sh, sw, sx, oy, sc; ++ ++ if (m->nstack) ++ sc = m->nstack; ++ else ++ sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); ++ ++ sw = (w - iv) * (1 - m->mfact); ++ sh = (h - ih) / 2; ++ w = (w - iv) * m->mfact; ++ oy = y + sh + ih; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sx = x; ++ x += sw + iv; ++ } else { ++ sx = x + w + iv; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, sh, sw, ih, iv, n, sc, m->nmaster); ++ (&flextiles[m->ltaxis[STACK2]])->arrange(m, sx, oy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); ++} ++ ++static void ++layout_split_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (m->nmaster && n > m->nmaster) { ++ layout_split_horizontal_fixed(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sh, sy; ++ ++ sh = (h - ih) * (1 - m->mfact); ++ h = (h - ih) * m->mfact; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sy = y; ++ y += sh + ih; ++ } else { ++ sy = y + h + ih; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, n - m->nmaster, m->nmaster); ++} ++ ++static void ++layout_split_horizontal_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { ++ layout_split_horizontal(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_split_horizontal_dual_stack_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_horizontal_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sh, sy, ox, sc; ++ ++ if (m->nstack) ++ sc = m->nstack; ++ else ++ sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); ++ ++ sh = (h - ih) * (1 - m->mfact); ++ h = (h - ih) * m->mfact; ++ sw = (w - iv) / 2; ++ ox = x + sw + iv; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sy = y; ++ y += sh + ih; ++ } else { ++ sy = y + h + ih; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, sw, ih, iv, n, sc, m->nmaster); ++ (&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, sy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); ++} ++ ++static void ++layout_split_centered_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { ++ layout_split_vertical(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_split_centered_vertical_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_centered_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sw, sx, ox, sc; ++ ++ if (m->nstack) ++ sc = m->nstack; ++ else ++ sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); ++ ++ sw = (w - 2*iv) * (1 - m->mfact) / 2; ++ w = (w - 2*iv) * m->mfact; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sx = x; ++ x += sw + iv; ++ ox = x + w + iv; ++ } else { ++ ox = x; ++ x += sw + iv; ++ sx = x + w + iv; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, sc, m->nmaster); ++ (&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, y, h, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); ++} ++ ++static void ++layout_split_centered_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { ++ layout_split_horizontal(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_split_centered_horizontal_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_split_centered_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int sh, sy, oy, sc; ++ ++ if (m->nstack) ++ sc = m->nstack; ++ else ++ sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); ++ ++ sh = (h - 2*ih) * (1 - m->mfact) / 2; ++ h = (h - 2*ih) * m->mfact; ++ if (m->ltaxis[LAYOUT] < 0) { // mirror ++ sy = y; ++ y += sh + ih; ++ oy = y + h + ih; ++ } else { ++ oy = y; ++ y += sh + ih; ++ sy = y + h + ih; ++ } ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, sc, m->nmaster); ++ (&flextiles[m->ltaxis[STACK2]])->arrange(m, x, oy, sh, w, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); ++} ++ ++static void ++layout_floating_master(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ /* Split master into master + stack if we have enough clients */ ++ if (!m->nmaster || n <= m->nmaster) { ++ layout_no_split(m, x, y, h, w, ih, iv, n); ++ } else { ++ layout_floating_master_fixed(m, x, y, h, w, ih, iv, n); ++ } ++} ++ ++static void ++layout_floating_master_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) ++{ ++ int mh, mw; ++ ++ /* Draw stack area first */ ++ (&flextiles[m->ltaxis[STACK]])->arrange(m, x, y, h, w, ih, iv, n, n - m->nmaster, m->nmaster); ++ ++ if (w > h) { ++ mw = w * m->mfact; ++ mh = h * 0.9; ++ } else { ++ mw = w * 0.9; ++ mh = h * m->mfact; ++ } ++ x = x + (w - mw) / 2; ++ y = y + (h - mh) / 2; ++ ++ (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, mh, mw, ih, iv, n, m->nmaster, 0); ++} ++ ++static void ++arrange_left_to_right(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, rest; ++ float facts, fact = 1; ++ Client *c; ++ ++ if (ai + an > n) ++ an = n - ai; ++ ++ w -= iv * (an - 1); ++ getfactsforrange(m, an, ai, w, &rest, &facts); ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i >= ai && i < (ai + an)) { ++ #if CFACTS_PATCH ++ fact = c->cfact; ++ #endif // CFACTS_PATCH ++ resize(c, x, y, w * (fact / facts) + ((i - ai) < rest ? 1 : 0) - (2*c->bw), h - (2*c->bw), 0); ++ x += WIDTH(c) + iv; ++ } ++ } ++} ++ ++static void ++arrange_top_to_bottom(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, rest; ++ float facts, fact = 1; ++ Client *c; ++ ++ if (ai + an > n) ++ an = n - ai; ++ ++ h -= ih * (an - 1); ++ getfactsforrange(m, an, ai, h, &rest, &facts); ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i >= ai && i < (ai + an)) { ++ #if CFACTS_PATCH ++ fact = c->cfact; ++ #endif // CFACTS_PATCH ++ resize(c, x, y, w - (2*c->bw), h * (fact / facts) + ((i - ai) < rest ? 1 : 0) - (2*c->bw), 0); ++ y += HEIGHT(c) + ih; ++ } ++ } ++} ++ ++static void ++arrange_monocle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i; ++ Client *c; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i >= ai && i < (ai + an)) ++ resize(c, x, y, w - (2*c->bw), h - (2*c->bw), 0); ++} ++ ++static void ++arrange_gridmode(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, cols, rows, ch, cw, cx, cy, cc, cr, chrest, cwrest; // counters ++ Client *c; ++ ++ /* grid dimensions */ ++ for (rows = 0; rows <= an/2; rows++) ++ if (rows*rows >= an) ++ break; ++ cols = (rows && (rows - 1) * rows >= an) ? rows - 1 : rows; ++ ++ /* window geoms (cell height/width) */ ++ ch = (h - ih * (rows - 1)) / (rows ? rows : 1); ++ cw = (w - iv * (cols - 1)) / (cols ? cols : 1); ++ chrest = h - ih * (rows - 1) - ch * rows; ++ cwrest = w - iv * (cols - 1) - cw * cols; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i >= ai && i < (ai + an)) { ++ cc = ((i - ai) / rows); // client column number ++ cr = ((i - ai) % rows); // client row number ++ cx = x + cc * (cw + iv) + MIN(cc, cwrest); ++ cy = y + cr * (ch + ih) + MIN(cr, chrest); ++ resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); ++ } ++ } ++} ++ ++static void ++arrange_horizgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int ntop, nbottom, rh, rest; ++ ++ /* Exception when there is only one client; don't split into two rows */ ++ if (an == 1) { ++ arrange_monocle(m, x, y, h, w, ih, iv, n, an, ai); ++ return; ++ } ++ ++ ntop = an / 2; ++ nbottom = an - ntop; ++ rh = (h - ih) / 2; ++ rest = h - ih - rh * 2; ++ arrange_left_to_right(m, x, y, rh + rest, w, ih, iv, n, ntop, ai); ++ arrange_left_to_right(m, x, y + rh + ih + rest, rh, w, ih, iv, n, nbottom, ai + ntop); ++} ++ ++static void ++arrange_gapplessgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, cols, rows, ch, cw, cn, rn, cc, rrest, crest; // counters ++ Client *c; ++ ++ /* grid dimensions */ ++ for (cols = 1; cols <= an/2; cols++) ++ if (cols*cols >= an) ++ break; ++ if (an == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ ++ cols = 2; ++ rows = an/cols; ++ cn = rn = cc = 0; // reset column no, row no, client count ++ ++ ch = (h - ih * (rows - 1)) / rows; ++ rrest = (h - ih * (rows - 1)) - ch * rows; ++ cw = (w - iv * (cols - 1)) / cols; ++ crest = (w - iv * (cols - 1)) - cw * cols; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i >= ai && i < (ai + an)) { ++ if (cc/rows + 1 > cols - an%cols) { ++ rows = an/cols + 1; ++ ch = (h - ih * (rows - 1)) / rows; ++ rrest = (h - ih * (rows - 1)) - ch * rows; ++ } ++ resize(c, ++ x, ++ y + rn*(ch + ih) + MIN(rn, rrest), ++ cw + (cn < crest ? 1 : 0) - 2*c->bw, ++ ch + (rn < rrest ? 1 : 0) - 2*c->bw, ++ 0); ++ rn++; ++ cc++; ++ if (rn >= rows) { ++ rn = 0; ++ x += cw + ih + (cn < crest ? 1 : 0); ++ cn++; ++ } ++ } ++ } ++} ++ ++/* This version of gappless grid fills rows first */ ++static void ++arrange_gapplessgrid_alt1(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, cols, rows, rest, ch; ++ ++ /* grid dimensions */ ++ for (cols = 1; cols <= an/2; cols++) ++ if (cols*cols >= an) ++ break; ++ rows = (cols && (cols - 1) * cols >= an) ? cols - 1 : cols; ++ ch = (h - ih * (rows - 1)) / (rows ? rows : 1); ++ rest = (h - ih * (rows - 1)) - ch * rows; ++ ++ for (i = 0; i < rows; i++) { ++ arrange_left_to_right(m, x, y, ch + (i < rest ? 1 : 0), w, ih, iv, n, MIN(cols, an - i*cols), ai + i*cols); ++ y += ch + (i < rest ? 1 : 0) + ih; ++ } ++} ++ ++/* This version of gappless grid fills columns first */ ++static void ++arrange_gapplessgrid_alt2(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ int i, cols, rows, rest, cw; ++ ++ /* grid dimensions */ ++ for (rows = 0; rows <= an/2; rows++) ++ if (rows*rows >= an) ++ break; ++ cols = (rows && (rows - 1) * rows >= an) ? rows - 1 : rows; ++ cw = (w - iv * (cols - 1)) / (cols ? cols : 1); ++ rest = (w - iv * (cols - 1)) - cw * cols; ++ ++ for (i = 0; i < cols; i++) { ++ arrange_top_to_bottom(m, x, y, h, cw + (i < rest ? 1 : 0), ih, iv, n, MIN(rows, an - i*rows), ai + i*rows); ++ x += cw + (i < rest ? 1 : 0) + iv; ++ } ++} ++ ++static void ++arrange_fibonacci(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai, int s) ++{ ++ int i, j, nv, hrest = 0, wrest = 0, nx = x, ny = y, nw = w, nh = h, r = 1; ++ Client *c; ++ ++ for (i = 0, j = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), j++) { ++ if (j >= ai && j < (ai + an)) { ++ if (r) { ++ if ((i % 2 && ((nh - ih) / 2) <= (bh + 2*c->bw)) || (!(i % 2) && ((nw - iv) / 2) <= (bh + 2*c->bw))) { ++ r = 0; ++ } ++ if (r && i < an - 1) { ++ if (i % 2) { ++ nv = (nh - ih) / 2; ++ hrest = nh - 2*nv - ih; ++ nh = nv; ++ } else { ++ nv = (nw - iv) / 2; ++ wrest = nw - 2*nv - iv; ++ nw = nv; ++ } ++ ++ if ((i % 4) == 2 && !s) ++ nx += nw + iv; ++ else if ((i % 4) == 3 && !s) ++ ny += nh + ih; ++ } ++ if ((i % 4) == 0) { ++ if (s) { ++ ny += nh + ih; ++ nh += hrest; ++ } else { ++ nh -= hrest; ++ ny -= nh + ih; ++ } ++ } else if ((i % 4) == 1) { ++ nx += nw + iv; ++ nw += wrest; ++ } else if ((i % 4) == 2) { ++ ny += nh + ih; ++ nh += hrest; ++ if (i < n - 1) ++ nw += wrest; ++ } else if ((i % 4) == 3) { ++ if (s) { ++ nx += nw + iv; ++ nw -= wrest; ++ } else { ++ nw -= wrest; ++ nx -= nw + iv; ++ nh += hrest; ++ } ++ } ++ if (i == 0) { ++ if (an != 1) { ++ nw = (w - iv) - (w - iv) * (1 - m->mfact); ++ wrest = 0; ++ } ++ ny = y; ++ } else if (i == 1) ++ nw = w - nw - iv; ++ i++; ++ } ++ ++ resize(c, nx, ny, nw - 2 * c->bw, nh - 2*c->bw, False); ++ } ++ } ++} ++ ++static void ++arrange_dwindle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 1); ++} ++ ++static void ++arrange_spiral(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 0); ++} ++ ++static void ++arrange_tatami(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) ++{ ++ unsigned int i, j, nx, ny, nw, nh, tnx, tny, tnw, tnh, nhrest, hrest, wrest, areas, mats, cats; ++ Client *c; ++ ++ nx = x; ++ ny = y; ++ nw = w; ++ nh = h; ++ ++ mats = an / 5; ++ cats = an % 5; ++ hrest = 0; ++ wrest = 0; ++ ++ areas = mats + (cats > 0); ++ nh = (h - ih * (areas - 1)) / areas; ++ nhrest = (h - ih * (areas - 1)) % areas; ++ ++ for (i = 0, j = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), j++) { ++ if (j >= ai && j < (ai + an)) { ++ ++ tnw = nw; ++ tnx = nx; ++ tnh = nh; ++ tny = ny; ++ ++ if (j < ai + cats) { ++ /* Arrange cats (all excess clients that can't be tiled as mats). Cats sleep on mats. */ ++ ++ switch (cats) { ++ case 1: // fill ++ break; ++ case 2: // up and down ++ if ((i % 5) == 0) //up ++ tnh = (nh - ih) / 2 + (nh - ih) % 2; ++ else if ((i % 5) == 1) { //down ++ tny += (nh - ih) / 2 + (nh - ih) % 2 + ih; ++ tnh = (nh - ih) / 2; ++ } ++ break; ++ case 3: //bottom, up-left and up-right ++ if ((i % 5) == 0) { // up-left ++ tnw = (nw - iv) / 2 + (nw - iv) % 2; ++ tnh = (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3; ++ } else if ((i % 5) == 1) { // up-right ++ tnx += (nw - iv) / 2 + (nw - iv) % 2 + iv; ++ tnw = (nw - iv) / 2; ++ tnh = (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3; ++ } else if ((i % 5) == 2) { //bottom ++ tnh = (nh - ih) / 3; ++ tny += (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3 + ih; ++ } ++ break; ++ case 4: // bottom, left, right and top ++ if ((i % 5) == 0) { //top ++ hrest = (nh - 2 * ih) % 4; ++ tnh = (nh - 2 * ih) / 4 + (hrest ? 1 : 0); ++ } else if ((i % 5) == 1) { // left ++ tnw = (nw - iv) / 2 + (nw - iv) % 2; ++ tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + ih; ++ tnh = (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0); ++ } else if ((i % 5) == 2) { // right ++ tnx += (nw - iv) / 2 + (nw - iv) % 2 + iv; ++ tnw = (nw - iv) / 2; ++ tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + ih; ++ tnh = (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0); ++ } else if ((i % 5) == 3) { // bottom ++ tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0) + 2 * ih; ++ tnh = (nh - 2 * ih) / 4 + (hrest > 2 ? 1 : 0); ++ } ++ break; ++ } ++ ++ } else { ++ /* Arrange mats. One mat is a collection of five clients arranged tatami style */ ++ ++ if (((i - cats) % 5) == 0) { ++ if ((cats > 0) || ((i - cats) >= 5)) { ++ tny = ny = ny + nh + (nhrest > 0 ? 1 : 0) + ih; ++ --nhrest; ++ } ++ } ++ ++ switch ((i - cats) % 5) { ++ case 0: // top-left-vert ++ wrest = (nw - 2 * iv) % 3; ++ hrest = (nh - 2 * ih) % 3; ++ tnw = (nw - 2 * iv) / 3 + (wrest ? 1 : 0); ++ tnh = (nh - 2 * ih) * 2 / 3 + hrest + iv; ++ break; ++ case 1: // top-right-hor ++ tnx += (nw - 2 * iv) / 3 + (wrest ? 1 : 0) + iv; ++ tnw = (nw - 2 * iv) * 2 / 3 + (wrest > 1 ? 1 : 0) + iv; ++ tnh = (nh - 2 * ih) / 3 + (hrest ? 1 : 0); ++ break; ++ case 2: // center ++ tnx += (nw - 2 * iv) / 3 + (wrest ? 1 : 0) + iv; ++ tnw = (nw - 2 * iv) / 3 + (wrest > 1 ? 1 : 0); ++ tny += (nh - 2 * ih) / 3 + (hrest ? 1 : 0) + ih; ++ tnh = (nh - 2 * ih) / 3 + (hrest > 1 ? 1 : 0); ++ break; ++ case 3: // bottom-right-vert ++ tnx += (nw - 2 * iv) * 2 / 3 + wrest + 2 * iv; ++ tnw = (nw - 2 * iv) / 3; ++ tny += (nh - 2 * ih) / 3 + (hrest ? 1 : 0) + ih; ++ tnh = (nh - 2 * ih) * 2 / 3 + hrest + iv; ++ break; ++ case 4: // (oldest) bottom-left-hor ++ tnw = (nw - 2 * iv) * 2 / 3 + wrest + iv; ++ tny += (nh - 2 * ih) * 2 / 3 + hrest + 2 * iv; ++ tnh = (nh - 2 * ih) / 3; ++ break; ++ } ++ ++ } ++ ++ resize(c, tnx, tny, tnw - 2 * c->bw, tnh - 2 * c->bw, False); ++ ++i; ++ } ++ } ++} ++ ++static void ++flextile(Monitor *m) ++{ ++ unsigned int n; ++ int oh = 0, ov = 0, ih = 0, iv = 0; // gaps outer/inner horizontal/vertical ++ ++ #if VANITYGAPS_PATCH ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ #else ++ Client *c; ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ #endif // VANITYGAPS_PATCH ++ ++ if (m->lt[m->sellt]->preset.layout != m->ltaxis[LAYOUT] || ++ m->lt[m->sellt]->preset.masteraxis != m->ltaxis[MASTER] || ++ m->lt[m->sellt]->preset.stack1axis != m->ltaxis[STACK] || ++ m->lt[m->sellt]->preset.stack2axis != m->ltaxis[STACK2]) ++ setflexsymbols(m, n); ++ else if (m->lt[m->sellt]->preset.symbolfunc != NULL) ++ m->lt[m->sellt]->preset.symbolfunc(m, n); ++ ++ if (n == 0) ++ return; ++ ++ #if VANITYGAPS_PATCH && !VANITYGAPS_MONOCLE_PATCH ++ /* No outer gap if full screen monocle */ ++ if (abs(m->ltaxis[MASTER]) == MONOCLE && (abs(m->ltaxis[LAYOUT]) == NO_SPLIT || n <= m->nmaster)) { ++ oh = 0; ++ ov = 0; ++ } ++ #endif // VANITYGAPS_PATCH && !VANITYGAPS_MONOCLE_PATCH ++ ++ (&flexlayouts[abs(m->ltaxis[LAYOUT])])->arrange(m, m->wx + ov, m->wy + oh, m->wh - 2*oh, m->ww - 2*ov, ih, iv, n); ++ return; ++} ++ ++static void ++setflexsymbols(Monitor *m, unsigned int n) ++{ ++ int l; ++ char sym1, sym2, sym3; ++ Client *c; ++ ++ if (n == 0) ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ ++ l = abs(m->ltaxis[LAYOUT]); ++ if (m->ltaxis[MASTER] == MONOCLE && (l == NO_SPLIT || !m->nmaster || n <= m->nmaster)) { ++ monoclesymbols(m, n); ++ return; ++ } ++ ++ if (m->ltaxis[STACK] == MONOCLE && (l == SPLIT_VERTICAL || l == SPLIT_HORIZONTAL_FIXED)) { ++ decksymbols(m, n); ++ return; ++ } ++ ++ /* Layout symbols */ ++ if (l == NO_SPLIT || !m->nmaster) { ++ sym1 = sym2 = sym3 = (int)tilesymb[m->ltaxis[MASTER]]; ++ } else { ++ sym2 = layoutsymb[l]; ++ if (m->ltaxis[LAYOUT] < 0) { ++ sym1 = tilesymb[m->ltaxis[STACK]]; ++ sym3 = tilesymb[m->ltaxis[MASTER]]; ++ } else { ++ sym1 = tilesymb[m->ltaxis[MASTER]]; ++ sym3 = tilesymb[m->ltaxis[STACK]]; ++ } ++ } ++ ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%c", sym1, sym2, sym3); ++} ++ ++static void ++monoclesymbols(Monitor *m, unsigned int n) ++{ ++ if (n > 0) ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); ++ else ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[M]"); ++} ++ ++static void ++decksymbols(Monitor *m, unsigned int n) ++{ ++ if (n > m->nmaster) ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[]%d", n); ++ else ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[D]"); ++} ++ ++/* Mirror layout axis for flextile */ ++void ++mirrorlayout(const Arg *arg) ++{ ++ if (!selmon->lt[selmon->sellt]->arrange) ++ return; ++ selmon->ltaxis[LAYOUT] *= -1; ++ selmon->pertag->ltaxis[selmon->pertag->curtag][0] = selmon->ltaxis[LAYOUT]; ++ arrange(selmon); ++} ++ ++/* Rotate layout axis for flextile */ ++void ++rotatelayoutaxis(const Arg *arg) ++{ ++ int incr = (arg->i > 0 ? 1 : -1); ++ int axis = abs(arg->i) - 1; ++ ++ if (!selmon->lt[selmon->sellt]->arrange) ++ return; ++ if (axis == LAYOUT) { ++ if (selmon->ltaxis[LAYOUT] >= 0) { ++ selmon->ltaxis[LAYOUT] += incr; ++ if (selmon->ltaxis[LAYOUT] >= LAYOUT_LAST) ++ selmon->ltaxis[LAYOUT] = 0; ++ else if (selmon->ltaxis[LAYOUT] < 0) ++ selmon->ltaxis[LAYOUT] = LAYOUT_LAST - 1; ++ } else { ++ selmon->ltaxis[LAYOUT] -= incr; ++ if (selmon->ltaxis[LAYOUT] <= -LAYOUT_LAST) ++ selmon->ltaxis[LAYOUT] = 0; ++ else if (selmon->ltaxis[LAYOUT] > 0) ++ selmon->ltaxis[LAYOUT] = -LAYOUT_LAST + 1; ++ } ++ } else { ++ selmon->ltaxis[axis] += incr; ++ if (selmon->ltaxis[axis] >= AXIS_LAST) ++ selmon->ltaxis[axis] = 0; ++ else if (selmon->ltaxis[axis] < 0) ++ selmon->ltaxis[axis] = AXIS_LAST - 1; ++ } ++ selmon->pertag->ltaxis[selmon->pertag->curtag][axis] = selmon->ltaxis[axis]; ++ arrange(selmon); ++ setflexsymbols(selmon, 0); ++} ++ ++void ++incnstack(const Arg *arg) ++{ ++ selmon->nstack = selmon->pertag->nstacks[selmon->pertag->curtag] = MAX(selmon->nstack + arg->i, 0); ++ arrange(selmon); ++} +\ No newline at end of file +diff --git a/flextile-deluxe.h b/flextile-deluxe.h +new file mode 100644 +index 0000000..366be3f +--- /dev/null ++++ b/flextile-deluxe.h +@@ -0,0 +1,116 @@ ++static void flextile(Monitor *m); ++static void mirrorlayout(const Arg *arg); ++static void rotatelayoutaxis(const Arg *arg); ++static void incnstack(const Arg *arg); ++ ++/* Symbol handlers */ ++static void setflexsymbols(Monitor *m, unsigned int n); ++static void monoclesymbols(Monitor *m, unsigned int n); ++static void decksymbols(Monitor *m, unsigned int n); ++ ++/* Layout split */ ++static void layout_no_split(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_vertical_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_horizontal_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_centered_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_centered_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_floating_master(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_vertical_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_horizontal_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_centered_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_split_centered_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++static void layout_floating_master_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); ++ ++/* Layout tile arrangements */ ++static void arrange_left_to_right(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_top_to_bottom(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_monocle(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_gapplessgrid(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_gapplessgrid_alt1(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_gapplessgrid_alt2(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_gridmode(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_horizgrid(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_dwindle(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_spiral(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++static void arrange_tatami(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); ++ ++/* Named flextile constants */ ++enum { ++ LAYOUT, // controls overall layout arrangement / split ++ MASTER, // indicates the tile arrangement for the master area ++ STACK, // indicates the tile arrangement for the stack area ++ STACK2, // indicates the tile arrangement for the secondary stack area ++ LTAXIS_LAST, ++}; ++ ++/* Layout arrangements */ ++enum { ++ NO_SPLIT, ++ SPLIT_VERTICAL, // master stack vertical split ++ SPLIT_HORIZONTAL, // master stack horizontal split ++ SPLIT_CENTERED_VERTICAL, // centered master vertical split ++ SPLIT_CENTERED_HORIZONTAL, // centered master horizontal split ++ SPLIT_VERTICAL_DUAL_STACK, // master stack vertical split with dual stack ++ SPLIT_HORIZONTAL_DUAL_STACK, // master stack vertical split with dual stack ++ FLOATING_MASTER, // (fake) floating master ++ SPLIT_VERTICAL_FIXED, // master stack vertical fixed split ++ SPLIT_HORIZONTAL_FIXED, // master stack horizontal fixed split ++ SPLIT_CENTERED_VERTICAL_FIXED, // centered master vertical fixed split ++ SPLIT_CENTERED_HORIZONTAL_FIXED, // centered master horizontal fixed split ++ SPLIT_VERTICAL_DUAL_STACK_FIXED, // master stack vertical split with fixed dual stack ++ SPLIT_HORIZONTAL_DUAL_STACK_FIXED, // master stack vertical split with fixed dual stack ++ FLOATING_MASTER_FIXED, // (fake) fixed floating master ++ LAYOUT_LAST, ++}; ++ ++static char layoutsymb[] = { ++ 32, // " ", ++ 124, // "|", ++ 61, // "=", ++ 94, // "^", ++ 126, // "~", ++ 58, // ":", ++ 59, // ";", ++ 43, // "+", ++ 124, // "¦", ++ 61, // "=", ++ 94, // "^", ++ 126, // "~", ++ 58, // ":", ++ 59, // ";", ++ 43, // "+", ++}; ++ ++/* Tile arrangements */ ++enum { ++ TOP_TO_BOTTOM, // clients are arranged vertically ++ LEFT_TO_RIGHT, // clients are arranged horizontally ++ MONOCLE, // clients are arranged in deck / monocle mode ++ GAPPLESSGRID, // clients are arranged in a gappless grid (original formula) ++ GAPPLESSGRID_ALT1, // clients are arranged in a gappless grid (alt. 1, fills rows first) ++ GAPPLESSGRID_ALT2, // clients are arranged in a gappless grid (alt. 2, fills columns first) ++ GRIDMODE, // clients are arranged in a grid ++ HORIZGRID, // clients are arranged in a horizontal grid ++ DWINDLE, // clients are arranged in fibonacci dwindle mode ++ SPIRAL, // clients are arranged in fibonacci spiral mode ++ TATAMI, // clients are arranged as tatami mats ++ AXIS_LAST, ++}; ++ ++static char tilesymb[] = { ++ 61, // "=", ++ 124, // "|", ++ 68, // "D", ++ 71, // "G", ++ 49, // "1", ++ 50, // "2" ++ 35, // "#", ++ 126, // "~", ++ 92, // "\\", ++ 64, // "@", ++ 84, // "T", ++}; +-- +2.19.1 + diff --git a/dwm/dwm-pertag-monitorrules-6.3.diff b/dwm/dwm-pertag-monitorrules-6.3.diff new file mode 100644 index 0000000..d111e80 --- /dev/null +++ b/dwm/dwm-pertag-monitorrules-6.3.diff @@ -0,0 +1,139 @@ +From 2f39e89ed604854515f8c4be502af09b9a89d05f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:11:18 +0100 +Subject: [PATCH 2/2] Monitor rules patch on top of pertag + +This patch allows the user to define layout, mfact, nmaster, showbar, +and topbar settings on a per monitor basis. An example use case could +be to have a layout with a horizontal split for a secondary vertical +monitor. + +https://github.com/bakkeby/patches/wiki/monitorrules +--- + config.def.h | 6 +++++ + dwm.c | 67 +++++++++++++++++++++++++++++++++++++++++----------- + 2 files changed, 59 insertions(+), 14 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..cf8c7d1 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -44,6 +44,12 @@ static const Layout layouts[] = { + { "[M]", monocle }, + }; + ++static const MonitorRule monrules[] = { ++ /* monitor tag layout mfact nmaster showbar topbar */ ++ { 1, -1, 2, -1, -1, -1, -1 }, // use a different layout for the second monitor ++ { -1, -1, 0, -1, -1, -1, -1 }, // default ++}; ++ + /* key definitions */ + #define MODKEY Mod1Mask + #define TAGKEYS(KEY,TAG) \ +diff --git a/dwm.c b/dwm.c +index b8ae4a5..d167d42 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -143,6 +143,16 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct { ++ int monitor; ++ int tag; ++ int layout; ++ float mfact; ++ int nmaster; ++ int showbar; ++ int topbar; ++} MonitorRule; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -646,31 +656,46 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; +- int i; +- ++ Monitor *m, *mon; ++ int i, mi, j, layout; ++ const MonitorRule *mr; + 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); ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); ++ for (j = 0; j < LENGTH(monrules); j++) { ++ mr = &monrules[j]; ++ if ((mr->monitor == -1 || mr->monitor == mi) ++ && (mr->tag <= 0 || (m->tagset[0] & (1 << (mr->tag - 1)))) ++ ) { ++ layout = MAX(mr->layout, 0); ++ layout = MIN(layout, LENGTH(layouts) - 1); ++ m->lt[0] = &layouts[layout]; ++ m->lt[1] = &layouts[1 % LENGTH(layouts)]; ++ strncpy(m->ltsymbol, layouts[layout].symbol, sizeof m->ltsymbol); ++ ++ if (mr->mfact > -1) ++ m->mfact = mr->mfact; ++ if (mr->nmaster > -1) ++ m->nmaster = mr->nmaster; ++ if (mr->showbar > -1) ++ m->showbar = mr->showbar; ++ if (mr->topbar > -1) ++ m->topbar = mr->topbar; ++ break; ++ } ++ } ++ + if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; +- for (i = 0; i <= LENGTH(tags); i++) { +- /* init nmaster */ +- m->pertag->nmasters[i] = m->nmaster; +- +- /* init mfacts */ +- m->pertag->mfacts[i] = m->mfact; + ++ for (i = 0; i <= LENGTH(tags); i++) { + /* init layouts */ +- m->pertag->ltidxs[i][0] = m->lt[0]; +- m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + /* init showbar */ +@@ -679,6 +704,20 @@ createmon(void) + #if ZOOMSWAP_PATCH + m->pertag->prevzooms[i] = NULL; + #endif // ZOOMSWAP_PATCH ++ ++ for (j = 0; j < LENGTH(monrules); j++) { ++ mr = &monrules[j]; ++ if ((mr->monitor == -1 || mr->monitor == mi) && (mr->tag == -1 || mr->tag == i)) { ++ layout = MAX(mr->layout, 0); ++ layout = MIN(layout, LENGTH(layouts) - 1); ++ m->pertag->ltidxs[i][0] = &layouts[layout]; ++ m->pertag->ltidxs[i][1] = m->lt[0]; ++ m->pertag->nmasters[i] = (mr->nmaster > -1 ? mr->nmaster : m->nmaster); ++ m->pertag->mfacts[i] = (mr->mfact > -1 ? mr->mfact : m->mfact); ++ m->pertag->showbars[i] = (mr->showbar > -1 ? mr->showbar : m->showbar); ++ break; ++ } ++ } + } + return m; + } +-- +2.19.1 + diff --git a/dwm/dwm-pertag-monitorrules-6.3_full.diff b/dwm/dwm-pertag-monitorrules-6.3_full.diff new file mode 100644 index 0000000..19e82e7 --- /dev/null +++ b/dwm/dwm-pertag-monitorrules-6.3_full.diff @@ -0,0 +1,347 @@ +From 8323f1e2e1e71223aa516f66415611fe4dd1f2ed Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:02:01 +0100 +Subject: [PATCH 1/2] pertag patch, keeps layout, mwfact, barpos and nmaster + per tag + +Refer to https://dwm.suckless.org/patches/pertag/ +--- + dwm.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 90 insertions(+), 7 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..b8ae4a5 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -111,6 +111,7 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++typedef struct Pertag Pertag; + struct Monitor { + char ltsymbol[16]; + float mfact; +@@ -130,6 +131,7 @@ struct Monitor { + Monitor *next; + Window barwin; + const Layout *lt[2]; ++ Pertag *pertag; + }; + + typedef struct { +@@ -272,6 +274,18 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++struct Pertag { ++ unsigned int curtag, prevtag; /* current and previous tag */ ++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ ++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ ++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ ++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ ++ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ ++ #if ZOOMSWAP_PATCH ++ Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */ ++ #endif // ZOOMSWAP_PATCH ++}; ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -507,6 +521,7 @@ cleanupmon(Monitor *mon) + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ free(mon->pertag); + free(mon); + } + +@@ -632,6 +647,7 @@ Monitor * + createmon(void) + { + Monitor *m; ++ int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; +@@ -642,6 +658,28 @@ createmon(void) + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); ++ m->pertag->curtag = m->pertag->prevtag = 1; ++ for (i = 0; i <= LENGTH(tags); i++) { ++ /* init nmaster */ ++ m->pertag->nmasters[i] = m->nmaster; ++ ++ /* init mfacts */ ++ m->pertag->mfacts[i] = m->mfact; ++ ++ /* init layouts */ ++ m->pertag->ltidxs[i][0] = m->lt[0]; ++ m->pertag->ltidxs[i][1] = m->lt[1]; ++ m->pertag->sellts[i] = m->sellt; ++ ++ /* init showbar */ ++ m->pertag->showbars[i] = m->showbar; ++ ++ #if ZOOMSWAP_PATCH ++ m->pertag->prevzooms[i] = NULL; ++ #endif // ZOOMSWAP_PATCH ++ } + return m; + } + +@@ -970,7 +1008,7 @@ grabkeys(void) + void + incnmaster(const Arg *arg) + { +- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); + } + +@@ -1504,10 +1542,13 @@ 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 != selmon->lt[selmon->sellt]) { ++ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ } + if (arg && arg->v) +- selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); +@@ -1526,7 +1567,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 = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); + } + +@@ -1705,7 +1746,7 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +@@ -1744,9 +1785,29 @@ void + toggleview(const Arg *arg) + { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); ++ int i; + + if (newtagset) { ++ if (newtagset == ~0) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = 0; ++ } ++ /* test if the user did not select the same tag */ ++ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ for (i=0; !(newtagset & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } + selmon->tagset[selmon->seltags] = newtagset; ++ ++ /* apply settings for this view */ ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); + focus(NULL); + arrange(selmon); + } +@@ -2041,11 +2102,33 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { ++ int i; ++ unsigned int tmptag; ++ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ +- if (arg->ui & TAGMASK) ++ if (arg->ui & TAGMASK) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ if (arg->ui == ~0) ++ selmon->pertag->curtag = 0; ++ else { ++ for (i=0; !(arg->ui & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ } else { ++ tmptag = selmon->pertag->prevtag; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = tmptag; ++ } ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); + focus(NULL); + arrange(selmon); + } +-- +2.19.1 + + +From 2f39e89ed604854515f8c4be502af09b9a89d05f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:11:18 +0100 +Subject: [PATCH 2/2] Monitor rules patch on top of pertag + +This patch allows the user to define layout, mfact, nmaster, showbar, +and topbar settings on a per monitor basis. An example use case could +be to have a layout with a horizontal split for a secondary vertical +monitor. + +https://github.com/bakkeby/patches/wiki/monitorrules +--- + config.def.h | 6 +++++ + dwm.c | 67 +++++++++++++++++++++++++++++++++++++++++----------- + 2 files changed, 59 insertions(+), 14 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..cf8c7d1 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -44,6 +44,12 @@ static const Layout layouts[] = { + { "[M]", monocle }, + }; + ++static const MonitorRule monrules[] = { ++ /* monitor tag layout mfact nmaster showbar topbar */ ++ { 1, -1, 2, -1, -1, -1, -1 }, // use a different layout for the second monitor ++ { -1, -1, 0, -1, -1, -1, -1 }, // default ++}; ++ + /* key definitions */ + #define MODKEY Mod1Mask + #define TAGKEYS(KEY,TAG) \ +diff --git a/dwm.c b/dwm.c +index b8ae4a5..d167d42 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -143,6 +143,16 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct { ++ int monitor; ++ int tag; ++ int layout; ++ float mfact; ++ int nmaster; ++ int showbar; ++ int topbar; ++} MonitorRule; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -646,31 +656,46 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; +- int i; +- ++ Monitor *m, *mon; ++ int i, mi, j, layout; ++ const MonitorRule *mr; + 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); ++ ++ for (mi = 0, mon = mons; mon; mon = mon->next, mi++); ++ for (j = 0; j < LENGTH(monrules); j++) { ++ mr = &monrules[j]; ++ if ((mr->monitor == -1 || mr->monitor == mi) ++ && (mr->tag <= 0 || (m->tagset[0] & (1 << (mr->tag - 1)))) ++ ) { ++ layout = MAX(mr->layout, 0); ++ layout = MIN(layout, LENGTH(layouts) - 1); ++ m->lt[0] = &layouts[layout]; ++ m->lt[1] = &layouts[1 % LENGTH(layouts)]; ++ strncpy(m->ltsymbol, layouts[layout].symbol, sizeof m->ltsymbol); ++ ++ if (mr->mfact > -1) ++ m->mfact = mr->mfact; ++ if (mr->nmaster > -1) ++ m->nmaster = mr->nmaster; ++ if (mr->showbar > -1) ++ m->showbar = mr->showbar; ++ if (mr->topbar > -1) ++ m->topbar = mr->topbar; ++ break; ++ } ++ } ++ + if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; +- for (i = 0; i <= LENGTH(tags); i++) { +- /* init nmaster */ +- m->pertag->nmasters[i] = m->nmaster; +- +- /* init mfacts */ +- m->pertag->mfacts[i] = m->mfact; + ++ for (i = 0; i <= LENGTH(tags); i++) { + /* init layouts */ +- m->pertag->ltidxs[i][0] = m->lt[0]; +- m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + /* init showbar */ +@@ -679,6 +704,20 @@ createmon(void) + #if ZOOMSWAP_PATCH + m->pertag->prevzooms[i] = NULL; + #endif // ZOOMSWAP_PATCH ++ ++ for (j = 0; j < LENGTH(monrules); j++) { ++ mr = &monrules[j]; ++ if ((mr->monitor == -1 || mr->monitor == mi) && (mr->tag == -1 || mr->tag == i)) { ++ layout = MAX(mr->layout, 0); ++ layout = MIN(layout, LENGTH(layouts) - 1); ++ m->pertag->ltidxs[i][0] = &layouts[layout]; ++ m->pertag->ltidxs[i][1] = m->lt[0]; ++ m->pertag->nmasters[i] = (mr->nmaster > -1 ? mr->nmaster : m->nmaster); ++ m->pertag->mfacts[i] = (mr->mfact > -1 ? mr->mfact : m->mfact); ++ m->pertag->showbars[i] = (mr->showbar > -1 ? mr->showbar : m->showbar); ++ break; ++ } ++ } + } + return m; + } +-- +2.19.1 + diff --git a/dwm/dwm-pertag-togglelayout-6.3.diff b/dwm/dwm-pertag-togglelayout-6.3.diff new file mode 100644 index 0000000..ef56044 --- /dev/null +++ b/dwm/dwm-pertag-togglelayout-6.3.diff @@ -0,0 +1,32 @@ +From 31aef05d160eb20228919461a6e3a03670c09b0a Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:16:52 +0100 +Subject: [PATCH 2/2] togglelayout - keyboard shortcuts to set a given layout + will toggle to the previous layout if the given layout is already active + +--- + dwm.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/dwm.c b/dwm.c +index b8ae4a5..c4b3166 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1542,11 +1542,9 @@ setfullscreen(Client *c, int fullscreen) + void + setlayout(const Arg *arg) + { +- if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { +- selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; +- selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; +- } +- if (arg && arg->v) ++ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ if (arg && arg->v && arg->v != selmon->lt[selmon->sellt ^ 1]) + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); +-- +2.19.1 + diff --git a/dwm/dwm-pertag-togglelayout-6.3_full.diff b/dwm/dwm-pertag-togglelayout-6.3_full.diff new file mode 100644 index 0000000..83a37f6 --- /dev/null +++ b/dwm/dwm-pertag-togglelayout-6.3_full.diff @@ -0,0 +1,240 @@ +From 8323f1e2e1e71223aa516f66415611fe4dd1f2ed Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:02:01 +0100 +Subject: [PATCH 1/2] pertag patch, keeps layout, mwfact, barpos and nmaster + per tag + +Refer to https://dwm.suckless.org/patches/pertag/ +--- + dwm.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 90 insertions(+), 7 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..b8ae4a5 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -111,6 +111,7 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++typedef struct Pertag Pertag; + struct Monitor { + char ltsymbol[16]; + float mfact; +@@ -130,6 +131,7 @@ struct Monitor { + Monitor *next; + Window barwin; + const Layout *lt[2]; ++ Pertag *pertag; + }; + + typedef struct { +@@ -272,6 +274,18 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++struct Pertag { ++ unsigned int curtag, prevtag; /* current and previous tag */ ++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ ++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ ++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ ++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ ++ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ ++ #if ZOOMSWAP_PATCH ++ Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */ ++ #endif // ZOOMSWAP_PATCH ++}; ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -507,6 +521,7 @@ cleanupmon(Monitor *mon) + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ free(mon->pertag); + free(mon); + } + +@@ -632,6 +647,7 @@ Monitor * + createmon(void) + { + Monitor *m; ++ int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; +@@ -642,6 +658,28 @@ createmon(void) + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); ++ m->pertag->curtag = m->pertag->prevtag = 1; ++ for (i = 0; i <= LENGTH(tags); i++) { ++ /* init nmaster */ ++ m->pertag->nmasters[i] = m->nmaster; ++ ++ /* init mfacts */ ++ m->pertag->mfacts[i] = m->mfact; ++ ++ /* init layouts */ ++ m->pertag->ltidxs[i][0] = m->lt[0]; ++ m->pertag->ltidxs[i][1] = m->lt[1]; ++ m->pertag->sellts[i] = m->sellt; ++ ++ /* init showbar */ ++ m->pertag->showbars[i] = m->showbar; ++ ++ #if ZOOMSWAP_PATCH ++ m->pertag->prevzooms[i] = NULL; ++ #endif // ZOOMSWAP_PATCH ++ } + return m; + } + +@@ -970,7 +1008,7 @@ grabkeys(void) + void + incnmaster(const Arg *arg) + { +- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); + } + +@@ -1504,10 +1542,13 @@ 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 != selmon->lt[selmon->sellt]) { ++ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ } + if (arg && arg->v) +- selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); +@@ -1526,7 +1567,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 = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); + } + +@@ -1705,7 +1746,7 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +@@ -1744,9 +1785,29 @@ void + toggleview(const Arg *arg) + { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); ++ int i; + + if (newtagset) { ++ if (newtagset == ~0) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = 0; ++ } ++ /* test if the user did not select the same tag */ ++ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ for (i=0; !(newtagset & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } + selmon->tagset[selmon->seltags] = newtagset; ++ ++ /* apply settings for this view */ ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); + focus(NULL); + arrange(selmon); + } +@@ -2041,11 +2102,33 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { ++ int i; ++ unsigned int tmptag; ++ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ +- if (arg->ui & TAGMASK) ++ if (arg->ui & TAGMASK) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ if (arg->ui == ~0) ++ selmon->pertag->curtag = 0; ++ else { ++ for (i=0; !(arg->ui & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ } else { ++ tmptag = selmon->pertag->prevtag; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = tmptag; ++ } ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); + focus(NULL); + arrange(selmon); + } +-- +2.19.1 + + +From 31aef05d160eb20228919461a6e3a03670c09b0a Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:16:52 +0100 +Subject: [PATCH 2/2] togglelayout - keyboard shortcuts to set a given layout + will toggle to the previous layout if the given layout is already active + +--- + dwm.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/dwm.c b/dwm.c +index b8ae4a5..c4b3166 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1542,11 +1542,9 @@ setfullscreen(Client *c, int fullscreen) + void + setlayout(const Arg *arg) + { +- if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { +- selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; +- selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; +- } +- if (arg && arg->v) ++ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ if (arg && arg->v && arg->v != selmon->lt[selmon->sellt ^ 1]) + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); +-- +2.19.1 + diff --git a/dwm/dwm-placedir-6.3.diff b/dwm/dwm-placedir-6.3.diff new file mode 100644 index 0000000..380b129 --- /dev/null +++ b/dwm/dwm-placedir-6.3.diff @@ -0,0 +1,145 @@ +From 1d5bbf1fc8627507e62498a9335bd2bc9ed01896 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:17:56 +0100 +Subject: [PATCH] Adding placedir, moving clients around with behaviour similar + to that of focusdir + +--- + config.def.h | 4 +++ + dwm.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 102 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..e8719d3 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -67,6 +67,10 @@ static Key keys[] = { + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { MODKEY|ControlMask, XK_Left, placedir, {.i = 0 } }, // left ++ { MODKEY|ControlMask, XK_Right, placedir, {.i = 1 } }, // right ++ { MODKEY|ControlMask, XK_Up, placedir, {.i = 2 } }, // up ++ { MODKEY|ControlMask, XK_Down, placedir, {.i = 3 } }, // down + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..7a20672 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -185,6 +185,7 @@ static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); + static Client *nexttiled(Client *c); ++static void placedir(const Arg *arg); + static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); +@@ -1203,6 +1204,103 @@ nexttiled(Client *c) + return c; + } + ++void ++placedir(const Arg *arg) ++{ ++ Client *s = selmon->sel, *f = NULL, *c, *next, *fprior, *sprior; ++ ++ if (!s || s->isfloating) ++ return; ++ ++ unsigned int score = -1; ++ unsigned int client_score; ++ int dist; ++ int dirweight = 20; ++ ++ next = s->next; ++ if (!next) ++ next = s->mon->clients; ++ for (c = next; c != s; c = next) { ++ ++ next = c->next; ++ if (!next) ++ next = s->mon->clients; ++ ++ if (!ISVISIBLE(c)) // || HIDDEN(c) ++ continue; ++ ++ switch (arg->i) { ++ case 0: // left ++ dist = s->x - c->x - c->w; ++ client_score = ++ dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + ++ abs(s->y - c->y); ++ break; ++ case 1: // right ++ dist = c->x - s->x - s->w; ++ client_score = ++ dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + ++ abs(c->y - s->y); ++ break; ++ case 2: // up ++ dist = s->y - c->y - c->h; ++ client_score = ++ dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + ++ abs(s->x - c->x); ++ break; ++ default: ++ case 3: // down ++ dist = c->y - s->y - s->h; ++ client_score = ++ dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + ++ abs(c->x - s->x); ++ break; ++ } ++ ++ if (((arg->i == 0 || arg->i == 2) && client_score <= score) || client_score < score) { ++ score = client_score; ++ f = c; ++ } ++ } ++ ++ if (f && f != s) { ++ for (fprior = f->mon->clients; fprior && fprior->next != f; fprior = fprior->next); ++ for (sprior = s->mon->clients; sprior && sprior->next != s; sprior = sprior->next); ++ ++ if (s == fprior) { ++ next = f->next; ++ if (sprior) ++ sprior->next = f; ++ else ++ f->mon->clients = f; ++ f->next = s; ++ s->next = next; ++ } else if (f == sprior) { ++ next = s->next; ++ if (fprior) ++ fprior->next = s; ++ else ++ s->mon->clients = s; ++ s->next = f; ++ f->next = next; ++ } else { // clients are not adjacent to each other ++ next = f->next; ++ f->next = s->next; ++ s->next = next; ++ if (fprior) ++ fprior->next = s; ++ else ++ s->mon->clients = s; ++ if (sprior) ++ sprior->next = f; ++ else ++ f->mon->clients = f; ++ } ++ ++ arrange(f->mon); ++ } ++} ++ + void + pop(Client *c) + { +-- +2.19.1 + diff --git a/dwm/dwm-placemouse-6.3.diff b/dwm/dwm-placemouse-6.3.diff new file mode 100644 index 0000000..d6fd0de --- /dev/null +++ b/dwm/dwm-placemouse-6.3.diff @@ -0,0 +1,259 @@ +From 399cb51f86d259d0d15d2db767d31c9e030c0528 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:18:37 +0100 +Subject: [PATCH] Adding placemouse patch + +--- + config.def.h | 12 +++- + dwm.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 177 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..caaebf6 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -105,7 +105,17 @@ static Button buttons[] = { + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, +- { ClkClientWin, MODKEY, Button1, movemouse, {0} }, ++ /* placemouse options, choose which feels more natural: ++ * 0 - tiled position is relative to mouse cursor ++ * 1 - tiled postiion is relative to window center ++ * 2 - mouse pointer warps to window center ++ * ++ * The moveorplace uses movemouse or placemouse depending on the floating state ++ * of the selected client. Set up individual keybindings for the two if you want ++ * to control these separately (i.e. to retain the feature to move a tiled window ++ * into a floating position). ++ */ ++ { ClkClientWin, MODKEY, Button1, moveorplace, {.i = 1} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..5d57e18 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -49,6 +49,8 @@ + #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 INTERSECTC(x,y,w,h,z) (MAX(0, MIN((x)+(w),(z)->x+(z)->w) - MAX((x),(z)->x)) \ ++ * MAX(0, MIN((y)+(h),(z)->y+(z)->h) - MAX((y),(z)->y))) + #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) +@@ -93,6 +95,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int beingmoved; + Client *next; + Client *snext; + Monitor *mon; +@@ -184,10 +187,13 @@ static void maprequest(XEvent *e); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); ++static void moveorplace(const Arg *arg); + static Client *nexttiled(Client *c); ++static void placemouse(const Arg *arg); + static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); ++static Client *recttoclient(int x, int y, int w, int h); + static Monitor *recttomon(int x, int y, int w, int h); + static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); +@@ -1136,6 +1142,14 @@ motionnotify(XEvent *e) + mon = m; + } + ++void ++moveorplace(const Arg *arg) { ++ if ((!selmon->lt[selmon->sellt]->arrange || (selmon->sel && selmon->sel->isfloating))) ++ movemouse(arg); ++ else ++ placemouse(arg); ++} ++ + void + movemouse(const Arg *arg) + { +@@ -1203,6 +1217,139 @@ nexttiled(Client *c) + return c; + } + ++void ++placemouse(const Arg *arg) ++{ ++ int x, y, px, py, ocx, ocy, nx = -9999, ny = -9999, freemove = 0; ++ Client *c, *r = NULL, *at, *prevr; ++ Monitor *m; ++ XEvent ev; ++ XWindowAttributes wa; ++ Time lasttime = 0; ++ int attachmode, prevattachmode; ++ attachmode = prevattachmode = -1; ++ ++ if (!(c = selmon->sel) || !c->mon->lt[c->mon->sellt]->arrange) /* no support for placemouse when floating layout is used */ ++ return; ++ if (c->isfullscreen) /* no support placing fullscreen windows by mouse */ ++ return; ++ restack(selmon); ++ prevr = c; ++ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, ++ None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) ++ return; ++ ++ c->isfloating = 0; ++ c->beingmoved = 1; ++ ++ XGetWindowAttributes(dpy, c->win, &wa); ++ ocx = wa.x; ++ ocy = wa.y; ++ ++ if (arg->i == 2) // warp cursor to client center ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, WIDTH(c) / 2, HEIGHT(c) / 2); ++ ++ if (!getrootptr(&x, &y)) ++ return; ++ ++ do { ++ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); ++ switch (ev.type) { ++ case ConfigureRequest: ++ case Expose: ++ case MapRequest: ++ handler[ev.type](&ev); ++ break; ++ case MotionNotify: ++ if ((ev.xmotion.time - lasttime) <= (1000 / 60)) ++ continue; ++ lasttime = ev.xmotion.time; ++ ++ nx = ocx + (ev.xmotion.x - x); ++ ny = ocy + (ev.xmotion.y - y); ++ ++ if (!freemove && (abs(nx - ocx) > snap || abs(ny - ocy) > snap)) ++ freemove = 1; ++ ++ if (freemove) ++ XMoveWindow(dpy, c->win, nx, ny); ++ ++ if ((m = recttomon(ev.xmotion.x, ev.xmotion.y, 1, 1)) && m != selmon) ++ selmon = m; ++ ++ if (arg->i == 1) { // tiled position is relative to the client window center point ++ px = nx + wa.width / 2; ++ py = ny + wa.height / 2; ++ } else { // tiled position is relative to the mouse cursor ++ px = ev.xmotion.x; ++ py = ev.xmotion.y; ++ } ++ ++ r = recttoclient(px, py, 1, 1); ++ ++ if (!r || r == c) ++ break; ++ ++ attachmode = 0; // below ++ if (((float)(r->y + r->h - py) / r->h) > ((float)(r->x + r->w - px) / r->w)) { ++ if (abs(r->y - py) < r->h / 2) ++ attachmode = 1; // above ++ } else if (abs(r->x - px) < r->w / 2) ++ attachmode = 1; // above ++ ++ if ((r && r != prevr) || (attachmode != prevattachmode)) { ++ detachstack(c); ++ detach(c); ++ if (c->mon != r->mon) { ++ arrangemon(c->mon); ++ c->tags = r->mon->tagset[r->mon->seltags]; ++ } ++ ++ c->mon = r->mon; ++ r->mon->sel = r; ++ ++ if (attachmode) { ++ if (r == r->mon->clients) ++ attach(c); ++ else { ++ for (at = r->mon->clients; at->next != r; at = at->next); ++ c->next = at->next; ++ at->next = c; ++ } ++ } else { ++ c->next = r->next; ++ r->next = c; ++ } ++ ++ attachstack(c); ++ arrangemon(r->mon); ++ prevr = r; ++ prevattachmode = attachmode; ++ } ++ break; ++ } ++ } while (ev.type != ButtonRelease); ++ XUngrabPointer(dpy, CurrentTime); ++ ++ if ((m = recttomon(ev.xmotion.x, ev.xmotion.y, 1, 1)) && m != c->mon) { ++ detach(c); ++ detachstack(c); ++ arrangemon(c->mon); ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; ++ attach(c); ++ attachstack(c); ++ selmon = m; ++ } ++ ++ focus(c); ++ c->beingmoved = 0; ++ ++ if (nx != -9999) ++ resize(c, nx, ny, c->w, c->h, 0); ++ arrangemon(c->mon); ++} ++ + void + pop(Client *c) + { +@@ -1255,6 +1402,21 @@ quit(const Arg *arg) + running = 0; + } + ++Client * ++recttoclient(int x, int y, int w, int h) ++{ ++ Client *c, *r = NULL; ++ int a, area = 0; ++ ++ for (c = nexttiled(selmon->clients); c; c = nexttiled(c->next)) { ++ if ((a = INTERSECTC(x, y, w, h, c)) > area) { ++ area = a; ++ r = c; ++ } ++ } ++ return r; ++} ++ + Monitor * + recttomon(int x, int y, int w, int h) + { +@@ -1285,6 +1447,10 @@ resizeclient(Client *c, int x, int y, int w, int h) + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; ++ ++ if (c->beingmoved) ++ return; ++ + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); +-- +2.19.1 + diff --git a/dwm/dwm-powerline-6.3.diff b/dwm/dwm-powerline-6.3.diff new file mode 100644 index 0000000..7c67911 --- /dev/null +++ b/dwm/dwm-powerline-6.3.diff @@ -0,0 +1,377 @@ +From 2240bee664499ab165a662d0c98a6dbd594de6af Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:22:32 +0100 +Subject: [PATCH] Powerline patch - adds arrows for tags and status + +https://gitlab.com/udiboy1209-suckless/dwm/-/commit/071f5063e8ac4280666828179f92788d893eea40 +--- + config.def.h | 16 +++++-- + drw.c | 51 +++++++++++++++++++-- + drw.h | 2 + + dwm.c | 126 +++++++++++++++++++++++++++++++++++++++------------ + 4 files changed, 159 insertions(+), 36 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..d16d4b4 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -13,9 +13,19 @@ static const char col_gray3[] = "#bbbbbb"; + static const char col_gray4[] = "#eeeeee"; + static const char col_cyan[] = "#005577"; + static const char *colors[][3] = { +- /* fg bg border */ +- [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, +- [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ /* fg bg border */ ++ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, ++ [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeTitle] = { "#554433", "#FF4444", "#44FF44" }, ++ [SchemeTitleSel] = { "#4444FF", "#884400", "#440044" }, ++}; ++ ++static const char *statuscolors[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, ++ [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeTitle] = { "#554433", "#FF4444", "#44FF44" }, ++ [SchemeTitleSel] = { "#4444FF", "#884400", "#440044" }, + }; + + /* tagging */ +diff --git a/drw.c b/drw.c +index 4cdbcbe..6d676b1 100644 +--- a/drw.c ++++ b/drw.c +@@ -15,6 +15,7 @@ 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}; ++Clr transcheme[3]; + + static long + utf8decodebyte(const char c, size_t *i) +@@ -200,8 +201,8 @@ drw_clr_create(Drw *drw, Clr *dest, const char *clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), +- DefaultColormap(drw->dpy, drw->screen), +- clrname, dest)) ++ DefaultColormap(drw->dpy, drw->screen), ++ clrname, dest)) + die("error, cannot allocate color '%s'", clrname); + } + +@@ -236,6 +237,15 @@ drw_setscheme(Drw *drw, Clr *scm) + drw->scheme = scm; + } + ++void ++drw_settrans(Drw *drw, Clr *psc, Clr *nsc) ++{ ++ if (drw) { ++ transcheme[0] = psc[ColBg]; transcheme[1] = nsc[ColBg]; transcheme[2] = psc[ColBorder]; ++ drw->scheme = transcheme; ++ } ++} ++ + void + drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) + { +@@ -275,8 +285,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, +- DefaultVisual(drw->dpy, drw->screen), +- DefaultColormap(drw->dpy, drw->screen)); ++ DefaultVisual(drw->dpy, drw->screen), ++ DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; + } +@@ -323,7 +333,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], +- usedfont->xfont, x, ty, (XftChar8 *)buf, len); ++ usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; +@@ -379,6 +389,37 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp + return x + (render ? w : 0); + } + ++void ++drw_arrow(Drw *drw, int x, int y, unsigned int w, unsigned int h, int direction, int slash) ++{ ++ if (!drw || !drw->scheme) ++ return; ++ ++ /* direction=1 draws right arrow */ ++ x = direction ? x : x + w; ++ w = direction ? w : -w; ++ /* slash=1 draws slash instead of arrow */ ++ unsigned int hh = slash ? (direction ? 0 : h) : h/2; ++ ++ XPoint points[] = { ++ {x , y }, ++ {x + w, y + hh }, ++ {x , y + h }, ++ }; ++ ++ XPoint bg[] = { ++ {x , y }, ++ {x + w, y }, ++ {x + w, y + h}, ++ {x , y + h}, ++ }; ++ ++ XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel); ++ XFillPolygon(drw->dpy, drw->drawable, drw->gc, bg, 4, Convex, CoordModeOrigin); ++ XSetForeground(drw->dpy, drw->gc, drw->scheme[ColFg].pixel); ++ XFillPolygon(drw->dpy, drw->drawable, drw->gc, points, 3, Nonconvex, CoordModeOrigin); ++} ++ + void + drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) + { +diff --git a/drw.h b/drw.h +index 4bcd5ad..fc4f3bb 100644 +--- a/drw.h ++++ b/drw.h +@@ -48,10 +48,12 @@ void drw_cur_free(Drw *drw, Cur *cursor); + /* Drawing context manipulation */ + void drw_setfontset(Drw *drw, Fnt *set); + void drw_setscheme(Drw *drw, Clr *scm); ++void drw_settrans(Drw *drw, Clr *psc, Clr *nsc); + + /* Drawing functions */ + void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); + int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); ++void drw_arrow(Drw* drw, int x, int y, unsigned int w, unsigned int h, int direction, int slash); + + /* Map functions */ + void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); +diff --git a/dwm.c b/dwm.c +index a96f33c..ad9c174 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -48,7 +48,7 @@ + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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))) ++ * 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) +@@ -59,13 +59,13 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeTitle, SchemeTitleSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, +- NetWMFullscreen, NetActiveWindow, NetWMWindowType, +- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++ NetWMFullscreen, NetActiveWindow, NetWMWindowType, ++ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, +- ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + + typedef union { + int i; +@@ -162,6 +162,7 @@ static void detach(Client *c); + static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); ++static int drawstatus(Monitor *m); + static void drawbars(void); + static void enternotify(XEvent *e); + static void expose(XEvent *e); +@@ -240,7 +241,7 @@ static const char broken[] = "broken"; + 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 bh, plw, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -264,6 +265,7 @@ static Atom wmatom[WMLast], netatom[NetLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; ++static Clr **statusscheme; + static Display *dpy; + static Drw *drw; + static Monitor *mons, *selmon; +@@ -431,9 +433,9 @@ buttonpress(XEvent *e) + focus(NULL); + } + if (ev->window == selmon->barwin) { +- i = x = 0; ++ i = 0; x = plw; + do +- x += TEXTW(tags[i]); ++ x += TEXTW(tags[i]) + plw; + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; +@@ -696,11 +698,11 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; +- int boxs = drw->fonts->h / 9; +- int boxw = drw->fonts->h / 6 + 2; +- unsigned int i, occ = 0, urg = 0; ++ int x, w, wt, tw = 0; ++ unsigned int i, occ = 0, urg = 0, n = 0; ++ plw = drw->fonts->h / 2 + 1; + Client *c; ++ Clr *prevscheme, *nxtscheme; + + if (!m->showbar) + return; +@@ -708,44 +710,109 @@ drawbar(Monitor *m) + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ tw = drawstatus(m); + } + + for (c = m->clients; c; c = c->next) { ++ if (ISVISIBLE(c)) n++; + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; ++ ++ prevscheme = scheme[SchemeNorm]; + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); +- drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_settrans(drw, prevscheme, (nxtscheme = scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm])); ++ drw_arrow(drw, x, 0, plw, bh, 1, 0); ++ x += plw; ++ ++ drw_setscheme(drw, nxtscheme); + 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, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); + x += w; ++ ++ prevscheme = nxtscheme; ++ + } ++ nxtscheme = scheme[SchemeNorm]; ++ ++ drw_settrans(drw, prevscheme, nxtscheme); ++ drw_arrow(drw, x, 0, plw, bh, 1, 0); ++ x += plw; ++ + w = blw = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + +- if ((w = m->ww - tw - x) > bh) { +- if (m->sel) { +- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +- if (m->sel->isfloating) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); +- } else { +- drw_setscheme(drw, scheme[SchemeNorm]); +- drw_rect(drw, x, 0, w, bh, 1, 1); ++ if ((m->ww - tw - x) > bh && n > 0) { ++ wt = (m->ww - tw - x) / n - 2 * plw; ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c)) continue; /* only show titles of windows on current tag */ ++ drw_setscheme(drw, c == m->sel ? scheme[SchemeTitleSel] : scheme[SchemeTitle]); ++ drw_text(drw, x + plw, 0, wt, bh, lrpad / 2, c->name, 0); ++ ++ drw_settrans(drw, c == m->sel ? scheme[SchemeTitleSel] : scheme[SchemeTitle], scheme[SchemeNorm]); ++ drw_arrow(drw, x, 0, plw, bh, 0, 1); ++ drw_arrow(drw, x + wt + plw, 0, plw, bh, 1, 1); ++ ++ x += wt + 2 * plw; + } + } ++ ++ if (x < m->ww - sw) { /* when empty or not enough space to draw, clear out the title space */ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, m->ww - sw - x, bh, 1, 1); ++ } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); + } + ++int ++drawstatus(Monitor* m) ++{ ++ char status[256]; ++ int i, n = strlen(stext), cn = 0; ++ int x = m->ww, w = 0; ++ char *bs, bp = '|'; ++ Clr *prevscheme = statusscheme[0], *nxtscheme; ++ ++ strcpy(status, stext); ++ ++ for (i = n, bs = &status[n-1]; i >= 0; i--, bs--) { ++ if (*bs == '<' || *bs == '/' || *bs == '\\' || *bs == '|') { /* block start */ ++ cn = ((int) *(bs+1)) - 1; ++ ++ if (cn < LENGTH(statuscolors)) { ++ drw_settrans(drw, prevscheme, (nxtscheme = statusscheme[cn])); ++ } else { ++ drw_settrans(drw, prevscheme, (nxtscheme = statusscheme[0])); ++ } ++ ++ if (bp != '|') { ++ drw_arrow(drw, x - plw, 0, plw, bh, bp == '\\' ? 1 : 0, bp == '<' ? 0 : 1); ++ x -= plw; ++ } ++ ++ drw_setscheme(drw, nxtscheme); ++ w = TEXTW(bs+2); ++ drw_text(drw, x - w, 0, w, bh, lrpad / 2, bs+2, 0); ++ x -= w; ++ ++ bp = *bs; ++ *bs = 0; ++ prevscheme = nxtscheme; ++ } ++ } ++ if (bp != '|') { ++ drw_settrans(drw, prevscheme, scheme[SchemeNorm]); ++ drw_arrow(drw, x - plw, 0, plw, bh, bp == '\\' ? 1 : 0, bp == '<' ? 0 : 1); ++ drw_rect(drw, x - 2 * plw, 0, plw, bh, 1, 1); ++ x -= plw * 2; ++ } ++ ++ return m->ww - x; ++} ++ + void + drawbars(void) + { +@@ -1574,6 +1641,9 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); ++ statusscheme = ecalloc(LENGTH(statuscolors), sizeof(Clr *)); ++ for (i = 0; i < LENGTH(statuscolors); i++) ++ statusscheme[i] = drw_scm_create(drw, statuscolors[i], 3); + /* init bars */ + updatebars(); + updatestatus(); +-- +2.19.1 + diff --git a/dwm/dwm-renamedscratchpads-6.3.diff b/dwm/dwm-renamedscratchpads-6.3.diff new file mode 100644 index 0000000..b62680d --- /dev/null +++ b/dwm/dwm-renamedscratchpads-6.3.diff @@ -0,0 +1,357 @@ +From 07804818ec69a86887533f47f4393f040de5a447 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:23:00 +0100 +Subject: [PATCH] Named scratchpad variant + +--- + config.def.h | 17 ++++- + dwm.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 186 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..049b29f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -12,10 +12,14 @@ static const char col_gray2[] = "#444444"; + static const char col_gray3[] = "#bbbbbb"; + static const char col_gray4[] = "#eeeeee"; + static const char col_cyan[] = "#005577"; ++static const char col_red[] = "#FF0000"; ++static const char col_orange[] = "#FF8800"; + static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeScratchSel] = { col_gray4, col_cyan, col_red }, ++ [SchemeScratchNorm] = { col_gray4, col_cyan, col_orange }, + }; + + /* tagging */ +@@ -26,9 +30,10 @@ 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 scratch key */ ++ { "Gimp", NULL, NULL, 0, 1, -1, 0 }, ++ { "firefox", NULL, NULL, 1 << 8, 0, -1, 0 }, ++ { NULL, NULL, "scratchpad", 0, 1, -1, 's' }, + }; + + /* layout(s) */ +@@ -60,10 +65,16 @@ 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_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; + static const char *termcmd[] = { "st", NULL }; + ++/*First arg only serves to match against key in rules*/ ++static const char *scratchpadcmd[] = {"s", "st", "-t", "scratchpad", NULL}; ++ + static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { MODKEY, XK_g, togglescratch, {.v = scratchpadcmd } }, ++ { MODKEY|ShiftMask, XK_g, removescratch, {.v = scratchpadcmd } }, ++ { MODKEY|ControlMask, XK_g, setscratch, {.v = scratchpadcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..c58c6ed 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -59,7 +59,7 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeScratchNorm, SchemeScratchSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ char scratchkey; + Client *next; + Client *snext; + Monitor *mon; +@@ -139,6 +140,7 @@ typedef struct { + unsigned int tags; + int isfloating; + int monitor; ++ const char scratchkey; + } Rule; + + /* function declarations */ +@@ -189,6 +191,7 @@ static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void removescratch(const Arg *arg); + static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); +@@ -202,16 +205,19 @@ static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); ++static void setscratch(const Arg *arg); + static void setup(void); + 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 spawnscratch(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglescratch(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -288,6 +294,7 @@ applyrules(Client *c) + /* rule matching */ + c->isfloating = 0; + c->tags = 0; ++ c->scratchkey = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; +@@ -300,6 +307,7 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ c->scratchkey = r->scratchkey; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -309,6 +317,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]; + } + +@@ -799,7 +808,10 @@ focus(Client *c) + detachstack(c); + attachstack(c); + grabbuttons(c, 1); +- XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); ++ if (c->scratchkey != 0) ++ XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColBorder].pixel); ++ else ++ XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); +@@ -1269,6 +1281,15 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removescratch(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ c->scratchkey = 0; ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1530,6 +1551,16 @@ setmfact(const Arg *arg) + arrange(selmon); + } + ++void ++setscratch(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ c->scratchkey = ((char**)arg->v)[0][0]; ++} ++ + void + setup(void) + { +@@ -1626,6 +1657,9 @@ showhide(Client *c) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { ++ /* optional: auto-hide scratchpads when moving to other tags */ ++ if (c->scratchkey != 0 && !(c->tags & c->mon->tagset[c->mon->seltags])) ++ c->tags = 0; + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); +@@ -1656,6 +1690,19 @@ spawn(const Arg *arg) + } + } + ++void spawnscratch(const Arg *arg) ++{ ++ if (fork() == 0) { ++ if (dpy) ++ close(ConnectionNumber(dpy)); ++ setsid(); ++ execvp(((char **)arg->v)[1], ((char **)arg->v)+1); ++ fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[1]); ++ perror(" failed"); ++ exit(EXIT_SUCCESS); ++ } ++} ++ + void + tag(const Arg *arg) + { +@@ -1725,6 +1772,125 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglescratch(const Arg *arg) ++{ ++ Client *c, *next, *last = NULL, *found = NULL, *monclients = NULL; ++ Monitor *mon; ++ int scratchvisible = 0; // whether the scratchpads are currently visible or not ++ int multimonscratch = 0; // whether we have scratchpads that are placed on multiple monitors ++ int scratchmon = -1; // the monitor where the scratchpads exist ++ int numscratchpads = 0; // count of scratchpads ++ ++ /* Looping through monitors and client's twice, the first time to work out whether we need ++ to move clients across from one monitor to another or not */ ++ for (mon = mons; mon; mon = mon->next) ++ for (c = mon->clients; c; c = c->next) { ++ if (c->scratchkey != ((char**)arg->v)[0][0]) ++ continue; ++ if (scratchmon != -1 && scratchmon != mon->num) ++ multimonscratch = 1; ++ if (c->mon->tagset[c->mon->seltags] & c->tags) // && !HIDDEN(c) ++ ++scratchvisible; ++ scratchmon = mon->num; ++ ++numscratchpads; ++ } ++ ++ /* Now for the real deal. The logic should go like: ++ - hidden scratchpads will be shown ++ - shown scratchpads will be hidden, unless they are being moved to the current monitor ++ - the scratchpads will be moved to the current monitor if they all reside on the same monitor ++ - multiple scratchpads residing on separate monitors will be left in place ++ */ ++ for (mon = mons; mon; mon = mon->next) { ++ for (c = mon->stack; c; c = next) { ++ next = c->snext; ++ if (c->scratchkey != ((char**)arg->v)[0][0]) ++ continue; ++ ++ /* awesomebar / wintitleactions compatibility, unhide scratchpad if hidden ++ if (HIDDEN(c)) { ++ XMapWindow(dpy, c->win); ++ setclientstate(c, NormalState); ++ } ++ */ ++ ++ /* Record the first found scratchpad client for focus purposes, but prioritise the ++ scratchpad on the current monitor if one exists */ ++ if (!found || (mon == selmon && found->mon != selmon)) ++ found = c; ++ ++ /* If scratchpad clients reside on another monitor and we are moving them across then ++ as we are looping through monitors we could be moving a client to a monitor that has ++ not been processed yet, hence we could be processing a scratchpad twice. To avoid ++ this we detach them and add them to a temporary list (monclients) which is to be ++ processed later. */ ++ if (!multimonscratch && c->mon != selmon) { ++ detach(c); ++ detachstack(c); ++ c->next = NULL; ++ /* Note that we are adding clients at the end of the list, this is to preserve the ++ order of clients as they were on the adjacent monitor (relevant when tiled) */ ++ if (last) ++ last = last->next = c; ++ else ++ last = monclients = c; ++ } else if (scratchvisible == numscratchpads) { ++ c->tags = 0; ++ } else { ++ XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); ++ c->tags = c->mon->tagset[c->mon->seltags]; ++ if (c->isfloating) ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ } ++ ++ /* Attach moved scratchpad clients on the selected monitor */ ++ for (c = monclients; c; c = next) { ++ next = c->next; ++ mon = c->mon; ++ c->mon = selmon; ++ c->tags = selmon->tagset[selmon->seltags]; ++ /* Attach scratchpad clients from other monitors at the bottom of the stack */ ++ if (selmon->clients) { ++ for (last = selmon->clients; last && last->next; last = last->next); ++ last->next = c; ++ } else ++ selmon->clients = c; ++ c->next = NULL; ++ attachstack(c); ++ ++ /* Center floating scratchpad windows when moved from one monitor to another */ ++ if (c->isfloating) { ++ if (c->w > selmon->ww) ++ c->w = selmon->ww - c->bw * 2; ++ if (c->h > selmon->wh) ++ c->h = selmon->wh - c->bw * 2; ++ ++ if (numscratchpads > 1) { ++ c->x = c->mon->wx + (c->x - mon->wx) * ((double)(abs(c->mon->ww - WIDTH(c))) / MAX(abs(mon->ww - WIDTH(c)), 1)); ++ c->y = c->mon->wy + (c->y - mon->wy) * ((double)(abs(c->mon->wh - HEIGHT(c))) / MAX(abs(mon->wh - HEIGHT(c)), 1)); ++ } else if (c->x < c->mon->mx || c->x > c->mon->mx + c->mon->mw || ++ c->y < c->mon->my || c->y > c->mon->my + c->mon->mh) { ++ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); ++ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); ++ } ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ ++ if (found) { ++ focus(ISVISIBLE(found) ? found : NULL); ++ arrange(NULL); ++ if (found->isfloating) ++ XRaiseWindow(dpy, found->win); ++ } else { ++ spawnscratch(arg); ++ } ++} ++ + void + toggletag(const Arg *arg) + { +@@ -1758,7 +1924,10 @@ unfocus(Client *c, int setfocus) + if (!c) + return; + grabbuttons(c, 0); +- XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); ++ if (c->scratchkey != 0) ++ XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); ++ else ++ XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +-- +2.19.1 + diff --git a/dwm/dwm-renamedscratchpads-noscheme-6.3.diff b/dwm/dwm-renamedscratchpads-noscheme-6.3.diff new file mode 100644 index 0000000..1b255bd --- /dev/null +++ b/dwm/dwm-renamedscratchpads-noscheme-6.3.diff @@ -0,0 +1,33 @@ +From 5c5a096d1b07d1840d5f6f5e14af459705e195f1 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:26:37 +0100 +Subject: [PATCH 2/2] Named scratchpad variant (without scratch color scheme) + +--- + dwm.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dwm.c b/dwm.c +index c58c6ed..67e5079 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1820,6 +1820,8 @@ togglescratch(const Arg *arg) + if (!found || (mon == selmon && found->mon != selmon)) + found = c; + ++ unfocus(c, 0); // unfocus to avoid client border discrepancies ++ + /* If scratchpad clients reside on another monitor and we are moving them across then + as we are looping through monitors we could be moving a client to a monitor that has + not been processed yet, hence we could be processing a scratchpad twice. To avoid +@@ -1838,7 +1840,6 @@ togglescratch(const Arg *arg) + } else if (scratchvisible == numscratchpads) { + c->tags = 0; + } else { +- XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); + c->tags = c->mon->tagset[c->mon->seltags]; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +-- +2.19.1 + diff --git a/dwm/dwm-renamedscratchpads-noscheme-6.3_full.diff b/dwm/dwm-renamedscratchpads-noscheme-6.3_full.diff new file mode 100644 index 0000000..cba78ee --- /dev/null +++ b/dwm/dwm-renamedscratchpads-noscheme-6.3_full.diff @@ -0,0 +1,391 @@ +From 07804818ec69a86887533f47f4393f040de5a447 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:23:00 +0100 +Subject: [PATCH 1/2] Named scratchpad variant + +--- + config.def.h | 17 ++++- + dwm.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 186 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..049b29f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -12,10 +12,14 @@ static const char col_gray2[] = "#444444"; + static const char col_gray3[] = "#bbbbbb"; + static const char col_gray4[] = "#eeeeee"; + static const char col_cyan[] = "#005577"; ++static const char col_red[] = "#FF0000"; ++static const char col_orange[] = "#FF8800"; + static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeScratchSel] = { col_gray4, col_cyan, col_red }, ++ [SchemeScratchNorm] = { col_gray4, col_cyan, col_orange }, + }; + + /* tagging */ +@@ -26,9 +30,10 @@ 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 scratch key */ ++ { "Gimp", NULL, NULL, 0, 1, -1, 0 }, ++ { "firefox", NULL, NULL, 1 << 8, 0, -1, 0 }, ++ { NULL, NULL, "scratchpad", 0, 1, -1, 's' }, + }; + + /* layout(s) */ +@@ -60,10 +65,16 @@ 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_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; + static const char *termcmd[] = { "st", NULL }; + ++/*First arg only serves to match against key in rules*/ ++static const char *scratchpadcmd[] = {"s", "st", "-t", "scratchpad", NULL}; ++ + static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { MODKEY, XK_g, togglescratch, {.v = scratchpadcmd } }, ++ { MODKEY|ShiftMask, XK_g, removescratch, {.v = scratchpadcmd } }, ++ { MODKEY|ControlMask, XK_g, setscratch, {.v = scratchpadcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..c58c6ed 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -59,7 +59,7 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeScratchNorm, SchemeScratchSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ char scratchkey; + Client *next; + Client *snext; + Monitor *mon; +@@ -139,6 +140,7 @@ typedef struct { + unsigned int tags; + int isfloating; + int monitor; ++ const char scratchkey; + } Rule; + + /* function declarations */ +@@ -189,6 +191,7 @@ static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void removescratch(const Arg *arg); + static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); +@@ -202,16 +205,19 @@ static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); ++static void setscratch(const Arg *arg); + static void setup(void); + 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 spawnscratch(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglescratch(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -288,6 +294,7 @@ applyrules(Client *c) + /* rule matching */ + c->isfloating = 0; + c->tags = 0; ++ c->scratchkey = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; +@@ -300,6 +307,7 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ c->scratchkey = r->scratchkey; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -309,6 +317,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]; + } + +@@ -799,7 +808,10 @@ focus(Client *c) + detachstack(c); + attachstack(c); + grabbuttons(c, 1); +- XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); ++ if (c->scratchkey != 0) ++ XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColBorder].pixel); ++ else ++ XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); +@@ -1269,6 +1281,15 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removescratch(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ c->scratchkey = 0; ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1530,6 +1551,16 @@ setmfact(const Arg *arg) + arrange(selmon); + } + ++void ++setscratch(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ c->scratchkey = ((char**)arg->v)[0][0]; ++} ++ + void + setup(void) + { +@@ -1626,6 +1657,9 @@ showhide(Client *c) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { ++ /* optional: auto-hide scratchpads when moving to other tags */ ++ if (c->scratchkey != 0 && !(c->tags & c->mon->tagset[c->mon->seltags])) ++ c->tags = 0; + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); +@@ -1656,6 +1690,19 @@ spawn(const Arg *arg) + } + } + ++void spawnscratch(const Arg *arg) ++{ ++ if (fork() == 0) { ++ if (dpy) ++ close(ConnectionNumber(dpy)); ++ setsid(); ++ execvp(((char **)arg->v)[1], ((char **)arg->v)+1); ++ fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[1]); ++ perror(" failed"); ++ exit(EXIT_SUCCESS); ++ } ++} ++ + void + tag(const Arg *arg) + { +@@ -1725,6 +1772,125 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglescratch(const Arg *arg) ++{ ++ Client *c, *next, *last = NULL, *found = NULL, *monclients = NULL; ++ Monitor *mon; ++ int scratchvisible = 0; // whether the scratchpads are currently visible or not ++ int multimonscratch = 0; // whether we have scratchpads that are placed on multiple monitors ++ int scratchmon = -1; // the monitor where the scratchpads exist ++ int numscratchpads = 0; // count of scratchpads ++ ++ /* Looping through monitors and client's twice, the first time to work out whether we need ++ to move clients across from one monitor to another or not */ ++ for (mon = mons; mon; mon = mon->next) ++ for (c = mon->clients; c; c = c->next) { ++ if (c->scratchkey != ((char**)arg->v)[0][0]) ++ continue; ++ if (scratchmon != -1 && scratchmon != mon->num) ++ multimonscratch = 1; ++ if (c->mon->tagset[c->mon->seltags] & c->tags) // && !HIDDEN(c) ++ ++scratchvisible; ++ scratchmon = mon->num; ++ ++numscratchpads; ++ } ++ ++ /* Now for the real deal. The logic should go like: ++ - hidden scratchpads will be shown ++ - shown scratchpads will be hidden, unless they are being moved to the current monitor ++ - the scratchpads will be moved to the current monitor if they all reside on the same monitor ++ - multiple scratchpads residing on separate monitors will be left in place ++ */ ++ for (mon = mons; mon; mon = mon->next) { ++ for (c = mon->stack; c; c = next) { ++ next = c->snext; ++ if (c->scratchkey != ((char**)arg->v)[0][0]) ++ continue; ++ ++ /* awesomebar / wintitleactions compatibility, unhide scratchpad if hidden ++ if (HIDDEN(c)) { ++ XMapWindow(dpy, c->win); ++ setclientstate(c, NormalState); ++ } ++ */ ++ ++ /* Record the first found scratchpad client for focus purposes, but prioritise the ++ scratchpad on the current monitor if one exists */ ++ if (!found || (mon == selmon && found->mon != selmon)) ++ found = c; ++ ++ /* If scratchpad clients reside on another monitor and we are moving them across then ++ as we are looping through monitors we could be moving a client to a monitor that has ++ not been processed yet, hence we could be processing a scratchpad twice. To avoid ++ this we detach them and add them to a temporary list (monclients) which is to be ++ processed later. */ ++ if (!multimonscratch && c->mon != selmon) { ++ detach(c); ++ detachstack(c); ++ c->next = NULL; ++ /* Note that we are adding clients at the end of the list, this is to preserve the ++ order of clients as they were on the adjacent monitor (relevant when tiled) */ ++ if (last) ++ last = last->next = c; ++ else ++ last = monclients = c; ++ } else if (scratchvisible == numscratchpads) { ++ c->tags = 0; ++ } else { ++ XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); ++ c->tags = c->mon->tagset[c->mon->seltags]; ++ if (c->isfloating) ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ } ++ ++ /* Attach moved scratchpad clients on the selected monitor */ ++ for (c = monclients; c; c = next) { ++ next = c->next; ++ mon = c->mon; ++ c->mon = selmon; ++ c->tags = selmon->tagset[selmon->seltags]; ++ /* Attach scratchpad clients from other monitors at the bottom of the stack */ ++ if (selmon->clients) { ++ for (last = selmon->clients; last && last->next; last = last->next); ++ last->next = c; ++ } else ++ selmon->clients = c; ++ c->next = NULL; ++ attachstack(c); ++ ++ /* Center floating scratchpad windows when moved from one monitor to another */ ++ if (c->isfloating) { ++ if (c->w > selmon->ww) ++ c->w = selmon->ww - c->bw * 2; ++ if (c->h > selmon->wh) ++ c->h = selmon->wh - c->bw * 2; ++ ++ if (numscratchpads > 1) { ++ c->x = c->mon->wx + (c->x - mon->wx) * ((double)(abs(c->mon->ww - WIDTH(c))) / MAX(abs(mon->ww - WIDTH(c)), 1)); ++ c->y = c->mon->wy + (c->y - mon->wy) * ((double)(abs(c->mon->wh - HEIGHT(c))) / MAX(abs(mon->wh - HEIGHT(c)), 1)); ++ } else if (c->x < c->mon->mx || c->x > c->mon->mx + c->mon->mw || ++ c->y < c->mon->my || c->y > c->mon->my + c->mon->mh) { ++ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); ++ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); ++ } ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ ++ if (found) { ++ focus(ISVISIBLE(found) ? found : NULL); ++ arrange(NULL); ++ if (found->isfloating) ++ XRaiseWindow(dpy, found->win); ++ } else { ++ spawnscratch(arg); ++ } ++} ++ + void + toggletag(const Arg *arg) + { +@@ -1758,7 +1924,10 @@ unfocus(Client *c, int setfocus) + if (!c) + return; + grabbuttons(c, 0); +- XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); ++ if (c->scratchkey != 0) ++ XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); ++ else ++ XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +-- +2.19.1 + + +From 5c5a096d1b07d1840d5f6f5e14af459705e195f1 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:26:37 +0100 +Subject: [PATCH 2/2] Named scratchpad variant (without scratch color scheme) + +--- + dwm.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dwm.c b/dwm.c +index c58c6ed..67e5079 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1820,6 +1820,8 @@ togglescratch(const Arg *arg) + if (!found || (mon == selmon && found->mon != selmon)) + found = c; + ++ unfocus(c, 0); // unfocus to avoid client border discrepancies ++ + /* If scratchpad clients reside on another monitor and we are moving them across then + as we are looping through monitors we could be moving a client to a monitor that has + not been processed yet, hence we could be processing a scratchpad twice. To avoid +@@ -1838,7 +1840,6 @@ togglescratch(const Arg *arg) + } else if (scratchvisible == numscratchpads) { + c->tags = 0; + } else { +- XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); + c->tags = c->mon->tagset[c->mon->seltags]; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +-- +2.19.1 + diff --git a/dwm/dwm-renamedscratchpads-static-6.3.diff b/dwm/dwm-renamedscratchpads-static-6.3.diff new file mode 100644 index 0000000..ea00145 --- /dev/null +++ b/dwm/dwm-renamedscratchpads-static-6.3.diff @@ -0,0 +1,139 @@ +From fb46e7d8f708a5ba87ac36f3a614869a1f266cc7 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:30:39 +0100 +Subject: [PATCH 2/2] Another namedscratchpads variant where scratchpads remain + on the monitors where they reside + +--- + dwm.c | 101 ++++++++++------------------------------------------------ + 1 file changed, 16 insertions(+), 85 deletions(-) + +diff --git a/dwm.c b/dwm.c +index c58c6ed..6e49100 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1775,34 +1775,12 @@ togglefloating(const Arg *arg) + void + togglescratch(const Arg *arg) + { +- Client *c, *next, *last = NULL, *found = NULL, *monclients = NULL; ++ Client *c, *next, *found = NULL; + Monitor *mon; +- int scratchvisible = 0; // whether the scratchpads are currently visible or not +- int multimonscratch = 0; // whether we have scratchpads that are placed on multiple monitors +- int scratchmon = -1; // the monitor where the scratchpads exist +- int numscratchpads = 0; // count of scratchpads +- +- /* Looping through monitors and client's twice, the first time to work out whether we need +- to move clients across from one monitor to another or not */ +- for (mon = mons; mon; mon = mon->next) +- for (c = mon->clients; c; c = c->next) { +- if (c->scratchkey != ((char**)arg->v)[0][0]) +- continue; +- if (scratchmon != -1 && scratchmon != mon->num) +- multimonscratch = 1; +- if (c->mon->tagset[c->mon->seltags] & c->tags) // && !HIDDEN(c) +- ++scratchvisible; +- scratchmon = mon->num; +- ++numscratchpads; +- } ++ int tags = 0, monfound; + +- /* Now for the real deal. The logic should go like: +- - hidden scratchpads will be shown +- - shown scratchpads will be hidden, unless they are being moved to the current monitor +- - the scratchpads will be moved to the current monitor if they all reside on the same monitor +- - multiple scratchpads residing on separate monitors will be left in place +- */ + for (mon = mons; mon; mon = mon->next) { ++ monfound = 0; + for (c = mon->stack; c; c = next) { + next = c->snext; + if (c->scratchkey != ((char**)arg->v)[0][0]) +@@ -1815,70 +1793,23 @@ togglescratch(const Arg *arg) + } + */ + +- /* Record the first found scratchpad client for focus purposes, but prioritise the +- scratchpad on the current monitor if one exists */ +- if (!found || (mon == selmon && found->mon != selmon)) +- found = c; ++ unfocus(c, 0); ++ monfound = 1; + +- /* If scratchpad clients reside on another monitor and we are moving them across then +- as we are looping through monitors we could be moving a client to a monitor that has +- not been processed yet, hence we could be processing a scratchpad twice. To avoid +- this we detach them and add them to a temporary list (monclients) which is to be +- processed later. */ +- if (!multimonscratch && c->mon != selmon) { +- detach(c); +- detachstack(c); +- c->next = NULL; +- /* Note that we are adding clients at the end of the list, this is to preserve the +- order of clients as they were on the adjacent monitor (relevant when tiled) */ +- if (last) +- last = last->next = c; +- else +- last = monclients = c; +- } else if (scratchvisible == numscratchpads) { +- c->tags = 0; +- } else { +- XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); +- c->tags = c->mon->tagset[c->mon->seltags]; +- if (c->isfloating) +- XRaiseWindow(dpy, c->win); ++ if (!found || mon == selmon) { ++ found = c; ++ tags = (c->tags == 0 ? c->mon->tagset[c->mon->seltags] : 0); + } +- } +- } + +- /* Attach moved scratchpad clients on the selected monitor */ +- for (c = monclients; c; c = next) { +- next = c->next; +- mon = c->mon; +- c->mon = selmon; +- c->tags = selmon->tagset[selmon->seltags]; +- /* Attach scratchpad clients from other monitors at the bottom of the stack */ +- if (selmon->clients) { +- for (last = selmon->clients; last && last->next; last = last->next); +- last->next = c; +- } else +- selmon->clients = c; +- c->next = NULL; +- attachstack(c); +- +- /* Center floating scratchpad windows when moved from one monitor to another */ +- if (c->isfloating) { +- if (c->w > selmon->ww) +- c->w = selmon->ww - c->bw * 2; +- if (c->h > selmon->wh) +- c->h = selmon->wh - c->bw * 2; +- +- if (numscratchpads > 1) { +- c->x = c->mon->wx + (c->x - mon->wx) * ((double)(abs(c->mon->ww - WIDTH(c))) / MAX(abs(mon->ww - WIDTH(c)), 1)); +- c->y = c->mon->wy + (c->y - mon->wy) * ((double)(abs(c->mon->wh - HEIGHT(c))) / MAX(abs(mon->wh - HEIGHT(c)), 1)); +- } else if (c->x < c->mon->mx || c->x > c->mon->mx + c->mon->mw || +- c->y < c->mon->my || c->y > c->mon->my + c->mon->mh) { +- c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); +- c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); +- } +- resizeclient(c, c->x, c->y, c->w, c->h); +- XRaiseWindow(dpy, c->win); ++ detachstack(c); ++ attachstack(c); ++ c->tags = tags; ++ if (c->tags && c->isfloating) ++ XRaiseWindow(dpy, c->win); + } ++ ++ if (monfound) ++ arrange(mon); + } + + if (found) { +-- +2.19.1 + diff --git a/dwm/dwm-renamedscratchpads-static-6.3_full.diff b/dwm/dwm-renamedscratchpads-static-6.3_full.diff new file mode 100644 index 0000000..23cf220 --- /dev/null +++ b/dwm/dwm-renamedscratchpads-static-6.3_full.diff @@ -0,0 +1,497 @@ +From 07804818ec69a86887533f47f4393f040de5a447 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:23:00 +0100 +Subject: [PATCH 1/2] Named scratchpad variant + +--- + config.def.h | 17 ++++- + dwm.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 186 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..049b29f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -12,10 +12,14 @@ static const char col_gray2[] = "#444444"; + static const char col_gray3[] = "#bbbbbb"; + static const char col_gray4[] = "#eeeeee"; + static const char col_cyan[] = "#005577"; ++static const char col_red[] = "#FF0000"; ++static const char col_orange[] = "#FF8800"; + static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeScratchSel] = { col_gray4, col_cyan, col_red }, ++ [SchemeScratchNorm] = { col_gray4, col_cyan, col_orange }, + }; + + /* tagging */ +@@ -26,9 +30,10 @@ 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 scratch key */ ++ { "Gimp", NULL, NULL, 0, 1, -1, 0 }, ++ { "firefox", NULL, NULL, 1 << 8, 0, -1, 0 }, ++ { NULL, NULL, "scratchpad", 0, 1, -1, 's' }, + }; + + /* layout(s) */ +@@ -60,10 +65,16 @@ 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_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; + static const char *termcmd[] = { "st", NULL }; + ++/*First arg only serves to match against key in rules*/ ++static const char *scratchpadcmd[] = {"s", "st", "-t", "scratchpad", NULL}; ++ + static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { MODKEY, XK_g, togglescratch, {.v = scratchpadcmd } }, ++ { MODKEY|ShiftMask, XK_g, removescratch, {.v = scratchpadcmd } }, ++ { MODKEY|ControlMask, XK_g, setscratch, {.v = scratchpadcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..c58c6ed 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -59,7 +59,7 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeScratchNorm, SchemeScratchSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ char scratchkey; + Client *next; + Client *snext; + Monitor *mon; +@@ -139,6 +140,7 @@ typedef struct { + unsigned int tags; + int isfloating; + int monitor; ++ const char scratchkey; + } Rule; + + /* function declarations */ +@@ -189,6 +191,7 @@ static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void removescratch(const Arg *arg); + static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); +@@ -202,16 +205,19 @@ static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); ++static void setscratch(const Arg *arg); + static void setup(void); + 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 spawnscratch(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglescratch(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -288,6 +294,7 @@ applyrules(Client *c) + /* rule matching */ + c->isfloating = 0; + c->tags = 0; ++ c->scratchkey = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; +@@ -300,6 +307,7 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ c->scratchkey = r->scratchkey; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -309,6 +317,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]; + } + +@@ -799,7 +808,10 @@ focus(Client *c) + detachstack(c); + attachstack(c); + grabbuttons(c, 1); +- XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); ++ if (c->scratchkey != 0) ++ XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColBorder].pixel); ++ else ++ XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); +@@ -1269,6 +1281,15 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removescratch(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ c->scratchkey = 0; ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1530,6 +1551,16 @@ setmfact(const Arg *arg) + arrange(selmon); + } + ++void ++setscratch(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ if (!c) ++ return; ++ ++ c->scratchkey = ((char**)arg->v)[0][0]; ++} ++ + void + setup(void) + { +@@ -1626,6 +1657,9 @@ showhide(Client *c) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { ++ /* optional: auto-hide scratchpads when moving to other tags */ ++ if (c->scratchkey != 0 && !(c->tags & c->mon->tagset[c->mon->seltags])) ++ c->tags = 0; + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); +@@ -1656,6 +1690,19 @@ spawn(const Arg *arg) + } + } + ++void spawnscratch(const Arg *arg) ++{ ++ if (fork() == 0) { ++ if (dpy) ++ close(ConnectionNumber(dpy)); ++ setsid(); ++ execvp(((char **)arg->v)[1], ((char **)arg->v)+1); ++ fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[1]); ++ perror(" failed"); ++ exit(EXIT_SUCCESS); ++ } ++} ++ + void + tag(const Arg *arg) + { +@@ -1725,6 +1772,125 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglescratch(const Arg *arg) ++{ ++ Client *c, *next, *last = NULL, *found = NULL, *monclients = NULL; ++ Monitor *mon; ++ int scratchvisible = 0; // whether the scratchpads are currently visible or not ++ int multimonscratch = 0; // whether we have scratchpads that are placed on multiple monitors ++ int scratchmon = -1; // the monitor where the scratchpads exist ++ int numscratchpads = 0; // count of scratchpads ++ ++ /* Looping through monitors and client's twice, the first time to work out whether we need ++ to move clients across from one monitor to another or not */ ++ for (mon = mons; mon; mon = mon->next) ++ for (c = mon->clients; c; c = c->next) { ++ if (c->scratchkey != ((char**)arg->v)[0][0]) ++ continue; ++ if (scratchmon != -1 && scratchmon != mon->num) ++ multimonscratch = 1; ++ if (c->mon->tagset[c->mon->seltags] & c->tags) // && !HIDDEN(c) ++ ++scratchvisible; ++ scratchmon = mon->num; ++ ++numscratchpads; ++ } ++ ++ /* Now for the real deal. The logic should go like: ++ - hidden scratchpads will be shown ++ - shown scratchpads will be hidden, unless they are being moved to the current monitor ++ - the scratchpads will be moved to the current monitor if they all reside on the same monitor ++ - multiple scratchpads residing on separate monitors will be left in place ++ */ ++ for (mon = mons; mon; mon = mon->next) { ++ for (c = mon->stack; c; c = next) { ++ next = c->snext; ++ if (c->scratchkey != ((char**)arg->v)[0][0]) ++ continue; ++ ++ /* awesomebar / wintitleactions compatibility, unhide scratchpad if hidden ++ if (HIDDEN(c)) { ++ XMapWindow(dpy, c->win); ++ setclientstate(c, NormalState); ++ } ++ */ ++ ++ /* Record the first found scratchpad client for focus purposes, but prioritise the ++ scratchpad on the current monitor if one exists */ ++ if (!found || (mon == selmon && found->mon != selmon)) ++ found = c; ++ ++ /* If scratchpad clients reside on another monitor and we are moving them across then ++ as we are looping through monitors we could be moving a client to a monitor that has ++ not been processed yet, hence we could be processing a scratchpad twice. To avoid ++ this we detach them and add them to a temporary list (monclients) which is to be ++ processed later. */ ++ if (!multimonscratch && c->mon != selmon) { ++ detach(c); ++ detachstack(c); ++ c->next = NULL; ++ /* Note that we are adding clients at the end of the list, this is to preserve the ++ order of clients as they were on the adjacent monitor (relevant when tiled) */ ++ if (last) ++ last = last->next = c; ++ else ++ last = monclients = c; ++ } else if (scratchvisible == numscratchpads) { ++ c->tags = 0; ++ } else { ++ XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); ++ c->tags = c->mon->tagset[c->mon->seltags]; ++ if (c->isfloating) ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ } ++ ++ /* Attach moved scratchpad clients on the selected monitor */ ++ for (c = monclients; c; c = next) { ++ next = c->next; ++ mon = c->mon; ++ c->mon = selmon; ++ c->tags = selmon->tagset[selmon->seltags]; ++ /* Attach scratchpad clients from other monitors at the bottom of the stack */ ++ if (selmon->clients) { ++ for (last = selmon->clients; last && last->next; last = last->next); ++ last->next = c; ++ } else ++ selmon->clients = c; ++ c->next = NULL; ++ attachstack(c); ++ ++ /* Center floating scratchpad windows when moved from one monitor to another */ ++ if (c->isfloating) { ++ if (c->w > selmon->ww) ++ c->w = selmon->ww - c->bw * 2; ++ if (c->h > selmon->wh) ++ c->h = selmon->wh - c->bw * 2; ++ ++ if (numscratchpads > 1) { ++ c->x = c->mon->wx + (c->x - mon->wx) * ((double)(abs(c->mon->ww - WIDTH(c))) / MAX(abs(mon->ww - WIDTH(c)), 1)); ++ c->y = c->mon->wy + (c->y - mon->wy) * ((double)(abs(c->mon->wh - HEIGHT(c))) / MAX(abs(mon->wh - HEIGHT(c)), 1)); ++ } else if (c->x < c->mon->mx || c->x > c->mon->mx + c->mon->mw || ++ c->y < c->mon->my || c->y > c->mon->my + c->mon->mh) { ++ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); ++ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); ++ } ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ ++ if (found) { ++ focus(ISVISIBLE(found) ? found : NULL); ++ arrange(NULL); ++ if (found->isfloating) ++ XRaiseWindow(dpy, found->win); ++ } else { ++ spawnscratch(arg); ++ } ++} ++ + void + toggletag(const Arg *arg) + { +@@ -1758,7 +1924,10 @@ unfocus(Client *c, int setfocus) + if (!c) + return; + grabbuttons(c, 0); +- XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); ++ if (c->scratchkey != 0) ++ XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); ++ else ++ XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +-- +2.19.1 + + +From fb46e7d8f708a5ba87ac36f3a614869a1f266cc7 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:30:39 +0100 +Subject: [PATCH 2/2] Another namedscratchpads variant where scratchpads remain + on the monitors where they reside + +--- + dwm.c | 101 ++++++++++------------------------------------------------ + 1 file changed, 16 insertions(+), 85 deletions(-) + +diff --git a/dwm.c b/dwm.c +index c58c6ed..6e49100 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1775,34 +1775,12 @@ togglefloating(const Arg *arg) + void + togglescratch(const Arg *arg) + { +- Client *c, *next, *last = NULL, *found = NULL, *monclients = NULL; ++ Client *c, *next, *found = NULL; + Monitor *mon; +- int scratchvisible = 0; // whether the scratchpads are currently visible or not +- int multimonscratch = 0; // whether we have scratchpads that are placed on multiple monitors +- int scratchmon = -1; // the monitor where the scratchpads exist +- int numscratchpads = 0; // count of scratchpads +- +- /* Looping through monitors and client's twice, the first time to work out whether we need +- to move clients across from one monitor to another or not */ +- for (mon = mons; mon; mon = mon->next) +- for (c = mon->clients; c; c = c->next) { +- if (c->scratchkey != ((char**)arg->v)[0][0]) +- continue; +- if (scratchmon != -1 && scratchmon != mon->num) +- multimonscratch = 1; +- if (c->mon->tagset[c->mon->seltags] & c->tags) // && !HIDDEN(c) +- ++scratchvisible; +- scratchmon = mon->num; +- ++numscratchpads; +- } ++ int tags = 0, monfound; + +- /* Now for the real deal. The logic should go like: +- - hidden scratchpads will be shown +- - shown scratchpads will be hidden, unless they are being moved to the current monitor +- - the scratchpads will be moved to the current monitor if they all reside on the same monitor +- - multiple scratchpads residing on separate monitors will be left in place +- */ + for (mon = mons; mon; mon = mon->next) { ++ monfound = 0; + for (c = mon->stack; c; c = next) { + next = c->snext; + if (c->scratchkey != ((char**)arg->v)[0][0]) +@@ -1815,70 +1793,23 @@ togglescratch(const Arg *arg) + } + */ + +- /* Record the first found scratchpad client for focus purposes, but prioritise the +- scratchpad on the current monitor if one exists */ +- if (!found || (mon == selmon && found->mon != selmon)) +- found = c; ++ unfocus(c, 0); ++ monfound = 1; + +- /* If scratchpad clients reside on another monitor and we are moving them across then +- as we are looping through monitors we could be moving a client to a monitor that has +- not been processed yet, hence we could be processing a scratchpad twice. To avoid +- this we detach them and add them to a temporary list (monclients) which is to be +- processed later. */ +- if (!multimonscratch && c->mon != selmon) { +- detach(c); +- detachstack(c); +- c->next = NULL; +- /* Note that we are adding clients at the end of the list, this is to preserve the +- order of clients as they were on the adjacent monitor (relevant when tiled) */ +- if (last) +- last = last->next = c; +- else +- last = monclients = c; +- } else if (scratchvisible == numscratchpads) { +- c->tags = 0; +- } else { +- XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel); +- c->tags = c->mon->tagset[c->mon->seltags]; +- if (c->isfloating) +- XRaiseWindow(dpy, c->win); ++ if (!found || mon == selmon) { ++ found = c; ++ tags = (c->tags == 0 ? c->mon->tagset[c->mon->seltags] : 0); + } +- } +- } + +- /* Attach moved scratchpad clients on the selected monitor */ +- for (c = monclients; c; c = next) { +- next = c->next; +- mon = c->mon; +- c->mon = selmon; +- c->tags = selmon->tagset[selmon->seltags]; +- /* Attach scratchpad clients from other monitors at the bottom of the stack */ +- if (selmon->clients) { +- for (last = selmon->clients; last && last->next; last = last->next); +- last->next = c; +- } else +- selmon->clients = c; +- c->next = NULL; +- attachstack(c); +- +- /* Center floating scratchpad windows when moved from one monitor to another */ +- if (c->isfloating) { +- if (c->w > selmon->ww) +- c->w = selmon->ww - c->bw * 2; +- if (c->h > selmon->wh) +- c->h = selmon->wh - c->bw * 2; +- +- if (numscratchpads > 1) { +- c->x = c->mon->wx + (c->x - mon->wx) * ((double)(abs(c->mon->ww - WIDTH(c))) / MAX(abs(mon->ww - WIDTH(c)), 1)); +- c->y = c->mon->wy + (c->y - mon->wy) * ((double)(abs(c->mon->wh - HEIGHT(c))) / MAX(abs(mon->wh - HEIGHT(c)), 1)); +- } else if (c->x < c->mon->mx || c->x > c->mon->mx + c->mon->mw || +- c->y < c->mon->my || c->y > c->mon->my + c->mon->mh) { +- c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); +- c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); +- } +- resizeclient(c, c->x, c->y, c->w, c->h); +- XRaiseWindow(dpy, c->win); ++ detachstack(c); ++ attachstack(c); ++ c->tags = tags; ++ if (c->tags && c->isfloating) ++ XRaiseWindow(dpy, c->win); + } ++ ++ if (monfound) ++ arrange(mon); + } + + if (found) { +-- +2.19.1 + diff --git a/dwm/dwm-resizecorners-6.3.diff b/dwm/dwm-resizecorners-6.3.diff new file mode 100644 index 0000000..4c52cfc --- /dev/null +++ b/dwm/dwm-resizecorners-6.3.diff @@ -0,0 +1,89 @@ +From c605528712e408504cac41cbd5566e0e2cfdbc76 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:31:21 +0100 +Subject: [PATCH] Resize a floating window from any corner + +By default, windows only from the bottom right corner. +With this patch the mouse is warped to the nearest corner and you +resize from there. + +Refer to https://dwm.suckless.org/patches/resizecorners/ + +Authors: + dusty - dusty@teknik.io + Klemens Nanni kl3@posteo.org (6.1 version) +--- + dwm.c | 27 ++++++++++++++++++++++++--- + 1 file changed, 24 insertions(+), 3 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..67cfb1b 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1295,9 +1295,14 @@ void + resizemouse(const Arg *arg) + { + int ocx, ocy, nw, nh; ++ int ocx2, ocy2, nx, ny; + Client *c; + Monitor *m; + XEvent ev; ++ int horizcorner, vertcorner; ++ int di; ++ unsigned int dui; ++ Window dummy; + Time lasttime = 0; + + if (!(c = selmon->sel)) +@@ -1307,10 +1312,19 @@ resizemouse(const Arg *arg) + restack(selmon); + ocx = c->x; + ocy = c->y; ++ ocx2 = c->x + c->w; ++ ocy2 = c->y + c->h; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; +- XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); ++ if (!XQueryPointer (dpy, c->win, &dummy, &dummy, &di, &di, &nx, &ny, &dui)) ++ return; ++ horizcorner = nx < c->w / 2; ++ vertcorner = ny < c->h / 2; ++ XWarpPointer (dpy, None, c->win, 0, 0, 0, 0, ++ horizcorner ? (-c->bw) : (c->w + c->bw - 1), ++ vertcorner ? (-c->bw) : (c->h + c->bw - 1)); ++ + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { +@@ -1326,6 +1340,11 @@ resizemouse(const Arg *arg) + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); ++ nx = horizcorner ? ev.xmotion.x : c->x; ++ ny = vertcorner ? ev.xmotion.y : c->y; ++ nw = MAX(horizcorner ? (ocx2 - nx) : (ev.xmotion.x - ocx - 2 * c->bw + 1), 1); ++ nh = MAX(vertcorner ? (ocy2 - ny) : (ev.xmotion.y - ocy - 2 * c->bw + 1), 1); ++ + 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) + { +@@ -1334,11 +1353,13 @@ resizemouse(const Arg *arg) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) +- resize(c, c->x, c->y, nw, nh, 1); ++ resize(c, nx, ny, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); +- XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, ++ horizcorner ? (-c->bw) : (c->w + c->bw - 1), ++ vertcorner ? (-c->bw) : (c->h + c->bw - 1)); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { +-- +2.19.1 + diff --git a/dwm/dwm-resizepoint-6.3.diff b/dwm/dwm-resizepoint-6.3.diff new file mode 100644 index 0000000..be74ab4 --- /dev/null +++ b/dwm/dwm-resizepoint-6.3.diff @@ -0,0 +1,97 @@ +From 3ddd6032fb60b780cb628d0a1866f1f2d5febf0b Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:31:54 +0100 +Subject: [PATCH] resizepoint - Like resizecorners, but does not warp mouse + pointer + +--- + dwm.c | 30 +++++++++++++++++++++--------- + 1 file changed, 21 insertions(+), 9 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..6f5d833 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -58,7 +58,7 @@ + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +-enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ ++enum { CurResizeBR, CurResizeBL, CurResizeTR, CurResizeTL, CurNormal, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, +@@ -1294,10 +1294,13 @@ resizeclient(Client *c, int x, int y, int w, int h) + void + resizemouse(const Arg *arg) + { +- int ocx, ocy, nw, nh; ++ int opx, opy, ocx, ocy, och, ocw, nx, ny, nw, nh; + Client *c; + Monitor *m; + XEvent ev; ++ int horizcorner, vertcorner; ++ unsigned int dui; ++ Window dummy; + Time lasttime = 0; + + if (!(c = selmon->sel)) +@@ -1307,10 +1310,15 @@ resizemouse(const Arg *arg) + restack(selmon); + ocx = c->x; + ocy = c->y; ++ och = c->h; ++ ocw = c->w; ++ if (!XQueryPointer(dpy, c->win, &dummy, &dummy, &opx, &opy, &nx, &ny, &dui)) ++ return; ++ horizcorner = nx < c->w / 2; ++ vertcorner = ny < c->h / 2; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, +- None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) ++ None, cursor[horizcorner | (vertcorner << 1)]->cursor, CurrentTime) != GrabSuccess) + return; +- XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { +@@ -1324,8 +1332,10 @@ resizemouse(const Arg *arg) + continue; + lasttime = ev.xmotion.time; + +- nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); +- nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); ++ nx = horizcorner ? (ocx + ev.xmotion.x - opx) : c->x; ++ ny = vertcorner ? (ocy + ev.xmotion.y - opy) : c->y; ++ nw = MAX(horizcorner ? (ocx + ocw - nx) : (ocw + (ev.xmotion.x - opx)), 1); ++ nh = MAX(vertcorner ? (ocy + och - ny) : (och + (ev.xmotion.y - opy)), 1); + 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) + { +@@ -1334,11 +1344,10 @@ resizemouse(const Arg *arg) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) +- resize(c, c->x, c->y, nw, nh, 1); ++ resizeclient(c, nx, ny, nw, nh); + break; + } + } while (ev.type != ButtonRelease); +- XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { +@@ -1568,7 +1577,10 @@ setup(void) + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); +- cursor[CurResize] = drw_cur_create(drw, XC_sizing); ++ cursor[CurResizeBR] = drw_cur_create(drw, XC_bottom_right_corner); ++ cursor[CurResizeBL] = drw_cur_create(drw, XC_bottom_left_corner); ++ cursor[CurResizeTR] = drw_cur_create(drw, XC_top_right_corner); ++ cursor[CurResizeTL] = drw_cur_create(drw, XC_top_left_corner); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); +-- +2.19.1 + diff --git a/dwm/dwm-riodraw-6.3.diff b/dwm/dwm-riodraw-6.3.diff new file mode 100644 index 0000000..2921fff --- /dev/null +++ b/dwm/dwm-riodraw-6.3.diff @@ -0,0 +1,451 @@ +From bd42e1d726234a591e0f08f4de2360fb5c051d6d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:32:43 +0100 +Subject: [PATCH] Adding rio-like draw-to-resize windows. + +This was backported from instantWM and depends on an external tool +slop to be installed. + +Contributed by jzbor. +--- + config.def.h | 8 ++ + config.mk | 6 +- + dwm.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 248 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..bbbc6ca 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,12 @@ 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 */ ++static const char slopspawnstyle[] = "-t 0 -c 0.92,0.85,0.69,0.3 -o"; /* do NOT define -f (format) here */ ++static const char slopresizestyle[] = "-t 0 -c 0.92,0.85,0.69,0.3"; /* do NOT define -f (format) here */ ++static const int riodraw_borders = 0; /* 0 or 1, indicates whether the area drawn using slop includes the window borders */ ++static const int riodraw_matchpid = 1; /* 0 or 1, indicates whether to match the PID of the client that was spawned with riospawn */ ++static const int riodraw_spawnasync = 0; /* 0 means that the application is only spawned after a successful selection while ++ * 1 means that the application is being initialised in the background while the selection is made */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -64,6 +70,8 @@ static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { MODKEY|ControlMask, XK_Return, riospawn, {.v = termcmd } }, ++ { MODKEY, XK_s, rioresize, {0} }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, +diff --git a/config.mk b/config.mk +index b6eb7e0..ee5c46e 100644 +--- a/config.mk ++++ b/config.mk +@@ -19,10 +19,14 @@ FREETYPELIBS = -lfontconfig -lXft + FREETYPEINC = /usr/include/freetype2 + # OpenBSD (uncomment) + #FREETYPEINC = ${X11INC}/freetype2 ++#KVMLIB = -lkvm ++ ++# This is needed for the swallow patch ++XCBLIBS = -lX11-xcb -lxcb -lxcb-res + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XCBLIBS} ${KVMLIB} + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/dwm.c b/dwm.c +index a96f33c..95cfcb5 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -40,6 +40,12 @@ + #include + #endif /* XINERAMA */ + #include ++#include ++#include ++#ifdef __OpenBSD__ ++#include ++#include ++#endif /* __OpenBSD */ + + #include "drw.h" + #include "util.h" +@@ -48,7 +54,7 @@ + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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))) ++ * 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) +@@ -61,11 +67,11 @@ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, +- NetWMFullscreen, NetActiveWindow, NetWMWindowType, +- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++ NetWMFullscreen, NetActiveWindow, NetWMWindowType, ++ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, +- ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + + typedef union { + int i; +@@ -93,6 +99,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ pid_t pid; + Client *next; + Client *snext; + Monitor *mon; +@@ -170,12 +177,14 @@ static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); ++static pid_t getparentprocess(pid_t p); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); + static void incnmaster(const Arg *arg); ++static int isdescprocess(pid_t p, pid_t c); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); + static void manage(Window w, XWindowAttributes *wa); +@@ -193,6 +202,10 @@ static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); ++static int riodraw(Client *c, const char slopstyle[]); ++static void rioposition(Client *c, int x, int y, int w, int h); ++static void rioresize(const Arg *arg); ++static void riospawn(const Arg *arg); + static void run(void); + static void scan(void); + static int sendevent(Client *c, Atom proto); +@@ -207,6 +220,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 pid_t spawncmd(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +@@ -228,6 +242,7 @@ static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); ++static pid_t winpid(Window w); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); +@@ -244,6 +259,8 @@ static int bh, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; ++static int riodimensions[4] = { -1, -1, -1, -1 }; ++static pid_t riopid = 0; + static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, +@@ -268,6 +285,7 @@ static Display *dpy; + static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; ++static xcb_connection_t *xcon; + + /* configuration, allows nested code to access above variables */ + #include "config.h" +@@ -875,6 +893,39 @@ getatomprop(Client *c, Atom prop) + return atom; + } + ++pid_t ++getparentprocess(pid_t p) ++{ ++ unsigned int v = 0; ++ ++#ifdef __linux__ ++ FILE *f; ++ char buf[256]; ++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); ++ ++ if (!(f = fopen(buf, "r"))) ++ return 0; ++ ++ fscanf(f, "%*u %*s %*c %u", &v); ++ fclose(f); ++#endif /* __linux__*/ ++ ++#ifdef __OpenBSD__ ++ int n; ++ kvm_t *kd; ++ struct kinfo_proc *kp; ++ ++ kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); ++ if (!kd) ++ return 0; ++ ++ kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); ++ v = kp->p_ppid; ++#endif /* __OpenBSD__ */ ++ ++ return (pid_t)v; ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -974,6 +1025,15 @@ incnmaster(const Arg *arg) + arrange(selmon); + } + ++int ++isdescprocess(pid_t p, pid_t c) ++{ ++ while (p != c && c != 0) ++ c = getparentprocess(c); ++ ++ return (int)c; ++} ++ + #ifdef XINERAMA + static int + isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +@@ -1027,6 +1087,7 @@ manage(Window w, XWindowAttributes *wa) + + c = ecalloc(1, sizeof(Client)); + c->win = w; ++ c->pid = winpid(w); + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; +@@ -1075,6 +1136,15 @@ manage(Window w, XWindowAttributes *wa) + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; ++ ++ if (riopid && (!riodraw_matchpid || isdescprocess(riopid, c->pid))) { ++ if (riodimensions[3] != -1) ++ rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); ++ else { ++ killclient(&((Arg) { .v = c })); ++ return; ++ } ++ } + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); +@@ -1373,6 +1443,104 @@ restack(Monitor *m) + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + ++int ++riodraw(Client *c, const char slopstyle[]) ++{ ++ int i; ++ char str[100] = {0}; ++ char strout[100] = {0}; ++ char tmpstring[30] = {0}; ++ char slopcmd[100] = "slop -f x%xx%yx%wx%hx "; ++ int firstchar = 0; ++ int counter = 0; ++ ++ strcat(slopcmd, slopstyle); ++ FILE *fp = popen(slopcmd, "r"); ++ ++ while (fgets(str, 100, fp) != NULL) ++ strcat(strout, str); ++ ++ pclose(fp); ++ ++ if (strlen(strout) < 6) ++ return 0; ++ ++ for (i = 0; i < strlen(strout); i++){ ++ if (!firstchar) { ++ if (strout[i] == 'x') ++ firstchar = 1; ++ continue; ++ } ++ ++ if (strout[i] != 'x') ++ tmpstring[strlen(tmpstring)] = strout[i]; ++ else { ++ riodimensions[counter] = atoi(tmpstring); ++ counter++; ++ memset(tmpstring,0,strlen(tmpstring)); ++ } ++ } ++ ++ if (riodimensions[0] <= -40 || riodimensions[1] <= -40 || riodimensions[2] <= 50 || riodimensions[3] <= 50) { ++ riodimensions[3] = -1; ++ return 0; ++ } ++ ++ if (c) { ++ rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++void ++rioposition(Client *c, int x, int y, int w, int h) ++{ ++ Monitor *m; ++ if ((m = recttomon(x, y, w, h)) && m != c->mon) { ++ detach(c); ++ detachstack(c); ++ arrange(c->mon); ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; ++ attach(c); ++ attachstack(c); ++ selmon = m; ++ focus(c); ++ } ++ ++ c->isfloating = 1; ++ if (riodraw_borders) ++ resizeclient(c, x, y, w - (c->bw * 2), h - (c->bw * 2)); ++ else ++ resizeclient(c, x - c->bw, y - c->bw, w, h); ++ arrange(c->mon); ++ ++ riodimensions[3] = -1; ++ riopid = 0; ++} ++ ++/* drag out an area using slop and resize the selected window to it */ ++void ++rioresize(const Arg *arg) ++{ ++ Client *c = (arg && arg->v ? (Client*)arg->v : selmon->sel); ++ if (c) ++ riodraw(c, slopresizestyle); ++} ++ ++/* spawn a new window and drag out an area using slop to position it */ ++void ++riospawn(const Arg *arg) ++{ ++ if (riodraw_spawnasync) { ++ riopid = spawncmd(arg); ++ riodraw(NULL, slopspawnstyle); ++ } else if (riodraw(NULL, slopspawnstyle)) ++ riopid = spawncmd(arg); ++} ++ + void + run(void) + { +@@ -1643,9 +1811,16 @@ sigchld(int unused) + void + spawn(const Arg *arg) + { ++ spawncmd(arg); ++} ++ ++pid_t ++spawncmd(const Arg *arg) ++{ ++ pid_t pid; + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; +- if (fork() == 0) { ++ if ((pid = fork()) == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); +@@ -1654,6 +1829,7 @@ spawn(const Arg *arg) + perror(" failed"); + exit(EXIT_SUCCESS); + } ++ return pid; + } + + void +@@ -2050,6 +2226,58 @@ view(const Arg *arg) + arrange(selmon); + } + ++pid_t ++winpid(Window w) ++{ ++ pid_t result = 0; ++ ++#ifdef __linux__ ++ xcb_res_client_id_spec_t spec = {0}; ++ spec.client = w; ++ spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; ++ ++ xcb_generic_error_t *e = NULL; ++ xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); ++ xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); ++ ++ if (!r) ++ return (pid_t)0; ++ ++ xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); ++ for (; i.rem; xcb_res_client_id_value_next(&i)) { ++ spec = i.data->spec; ++ if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { ++ uint32_t *t = xcb_res_client_id_value_value(i.data); ++ result = *t; ++ break; ++ } ++ } ++ ++ free(r); ++ ++ if (result == (pid_t)-1) ++ result = 0; ++ ++#endif /* __linux__ */ ++ ++#ifdef __OpenBSD__ ++ Atom type; ++ int format; ++ unsigned long len, bytes; ++ unsigned char *prop; ++ pid_t ret; ++ ++ if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) ++ return 0; ++ ++ ret = *(pid_t*)prop; ++ XFree(prop); ++ result = ret; ++ ++#endif /* __OpenBSD__ */ ++ return result; ++} ++ + Client * + wintoclient(Window w) + { +@@ -2141,6 +2369,8 @@ main(int argc, char *argv[]) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); ++ if (!(xcon = XGetXCBConnection(dpy))) ++ die("dwm: cannot get xcb connection\n"); + checkotherwm(); + setup(); + #ifdef __OpenBSD__ +-- +2.19.1 + diff --git a/dwm/dwm-riodraw-nopidmatching-6.3.diff b/dwm/dwm-riodraw-nopidmatching-6.3.diff new file mode 100644 index 0000000..e814d30 --- /dev/null +++ b/dwm/dwm-riodraw-nopidmatching-6.3.diff @@ -0,0 +1,126 @@ +From b99f734407e5a60b7250f4e109e9f03f2de0b7f2 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:35:44 +0100 +Subject: [PATCH 2/2] Adding riodraw patch with no PID matching + +--- + config.def.h | 1 - + dwm.c | 55 +++++++--------------------------------------------- + 2 files changed, 7 insertions(+), 49 deletions(-) + +diff --git a/config.def.h b/config.def.h +index bbbc6ca..bb3d58f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -8,7 +8,6 @@ static const int topbar = 1; /* 0 means bottom bar */ + static const char slopspawnstyle[] = "-t 0 -c 0.92,0.85,0.69,0.3 -o"; /* do NOT define -f (format) here */ + static const char slopresizestyle[] = "-t 0 -c 0.92,0.85,0.69,0.3"; /* do NOT define -f (format) here */ + static const int riodraw_borders = 0; /* 0 or 1, indicates whether the area drawn using slop includes the window borders */ +-static const int riodraw_matchpid = 1; /* 0 or 1, indicates whether to match the PID of the client that was spawned with riospawn */ + static const int riodraw_spawnasync = 0; /* 0 means that the application is only spawned after a successful selection while + * 1 means that the application is being initialised in the background while the selection is made */ + static const char *fonts[] = { "monospace:size=10" }; +diff --git a/dwm.c b/dwm.c +index 95cfcb5..0a72d89 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -177,14 +177,12 @@ static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); +-static pid_t getparentprocess(pid_t p); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); + static void incnmaster(const Arg *arg); +-static int isdescprocess(pid_t p, pid_t c); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); + static void manage(Window w, XWindowAttributes *wa); +@@ -893,39 +891,6 @@ getatomprop(Client *c, Atom prop) + return atom; + } + +-pid_t +-getparentprocess(pid_t p) +-{ +- unsigned int v = 0; +- +-#ifdef __linux__ +- FILE *f; +- char buf[256]; +- snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); +- +- if (!(f = fopen(buf, "r"))) +- return 0; +- +- fscanf(f, "%*u %*s %*c %u", &v); +- fclose(f); +-#endif /* __linux__*/ +- +-#ifdef __OpenBSD__ +- int n; +- kvm_t *kd; +- struct kinfo_proc *kp; +- +- kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); +- if (!kd) +- return 0; +- +- kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); +- v = kp->p_ppid; +-#endif /* __OpenBSD__ */ +- +- return (pid_t)v; +-} +- + int + getrootptr(int *x, int *y) + { +@@ -1025,15 +990,6 @@ incnmaster(const Arg *arg) + arrange(selmon); + } + +-int +-isdescprocess(pid_t p, pid_t c) +-{ +- while (p != c && c != 0) +- c = getparentprocess(c); +- +- return (int)c; +-} +- + #ifdef XINERAMA + static int + isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +@@ -1137,7 +1093,7 @@ manage(Window w, XWindowAttributes *wa) + unfocus(selmon->sel, 0); + c->mon->sel = c; + +- if (riopid && (!riodraw_matchpid || isdescprocess(riopid, c->pid))) { ++ if (riopid) { + if (riodimensions[3] != -1) + rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); + else { +@@ -1535,10 +1491,13 @@ void + riospawn(const Arg *arg) + { + if (riodraw_spawnasync) { +- riopid = spawncmd(arg); ++ spawn(arg); ++ riopid = 1; + riodraw(NULL, slopspawnstyle); +- } else if (riodraw(NULL, slopspawnstyle)) +- riopid = spawncmd(arg); ++ } else if (riodraw(NULL, slopspawnstyle)) { ++ spawn(arg); ++ riopid = 1; ++ } + } + + void +-- +2.19.1 + diff --git a/dwm/dwm-riodraw-nopidmatching-6.3_full.diff b/dwm/dwm-riodraw-nopidmatching-6.3_full.diff new file mode 100644 index 0000000..02eb64c --- /dev/null +++ b/dwm/dwm-riodraw-nopidmatching-6.3_full.diff @@ -0,0 +1,578 @@ +From bd42e1d726234a591e0f08f4de2360fb5c051d6d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:32:43 +0100 +Subject: [PATCH 1/2] Adding rio-like draw-to-resize windows. + +This was backported from instantWM and depends on an external tool +slop to be installed. + +Contributed by jzbor. +--- + config.def.h | 8 ++ + config.mk | 6 +- + dwm.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 248 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..bbbc6ca 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,12 @@ 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 */ ++static const char slopspawnstyle[] = "-t 0 -c 0.92,0.85,0.69,0.3 -o"; /* do NOT define -f (format) here */ ++static const char slopresizestyle[] = "-t 0 -c 0.92,0.85,0.69,0.3"; /* do NOT define -f (format) here */ ++static const int riodraw_borders = 0; /* 0 or 1, indicates whether the area drawn using slop includes the window borders */ ++static const int riodraw_matchpid = 1; /* 0 or 1, indicates whether to match the PID of the client that was spawned with riospawn */ ++static const int riodraw_spawnasync = 0; /* 0 means that the application is only spawned after a successful selection while ++ * 1 means that the application is being initialised in the background while the selection is made */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -64,6 +70,8 @@ static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { MODKEY|ControlMask, XK_Return, riospawn, {.v = termcmd } }, ++ { MODKEY, XK_s, rioresize, {0} }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, +diff --git a/config.mk b/config.mk +index b6eb7e0..ee5c46e 100644 +--- a/config.mk ++++ b/config.mk +@@ -19,10 +19,14 @@ FREETYPELIBS = -lfontconfig -lXft + FREETYPEINC = /usr/include/freetype2 + # OpenBSD (uncomment) + #FREETYPEINC = ${X11INC}/freetype2 ++#KVMLIB = -lkvm ++ ++# This is needed for the swallow patch ++XCBLIBS = -lX11-xcb -lxcb -lxcb-res + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XCBLIBS} ${KVMLIB} + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/dwm.c b/dwm.c +index a96f33c..95cfcb5 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -40,6 +40,12 @@ + #include + #endif /* XINERAMA */ + #include ++#include ++#include ++#ifdef __OpenBSD__ ++#include ++#include ++#endif /* __OpenBSD */ + + #include "drw.h" + #include "util.h" +@@ -48,7 +54,7 @@ + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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))) ++ * 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) +@@ -61,11 +67,11 @@ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, +- NetWMFullscreen, NetActiveWindow, NetWMWindowType, +- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++ NetWMFullscreen, NetActiveWindow, NetWMWindowType, ++ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, +- ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + + typedef union { + int i; +@@ -93,6 +99,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ pid_t pid; + Client *next; + Client *snext; + Monitor *mon; +@@ -170,12 +177,14 @@ static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); ++static pid_t getparentprocess(pid_t p); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); + static void incnmaster(const Arg *arg); ++static int isdescprocess(pid_t p, pid_t c); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); + static void manage(Window w, XWindowAttributes *wa); +@@ -193,6 +202,10 @@ static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); ++static int riodraw(Client *c, const char slopstyle[]); ++static void rioposition(Client *c, int x, int y, int w, int h); ++static void rioresize(const Arg *arg); ++static void riospawn(const Arg *arg); + static void run(void); + static void scan(void); + static int sendevent(Client *c, Atom proto); +@@ -207,6 +220,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 pid_t spawncmd(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +@@ -228,6 +242,7 @@ static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); ++static pid_t winpid(Window w); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); +@@ -244,6 +259,8 @@ static int bh, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; ++static int riodimensions[4] = { -1, -1, -1, -1 }; ++static pid_t riopid = 0; + static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, +@@ -268,6 +285,7 @@ static Display *dpy; + static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; ++static xcb_connection_t *xcon; + + /* configuration, allows nested code to access above variables */ + #include "config.h" +@@ -875,6 +893,39 @@ getatomprop(Client *c, Atom prop) + return atom; + } + ++pid_t ++getparentprocess(pid_t p) ++{ ++ unsigned int v = 0; ++ ++#ifdef __linux__ ++ FILE *f; ++ char buf[256]; ++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); ++ ++ if (!(f = fopen(buf, "r"))) ++ return 0; ++ ++ fscanf(f, "%*u %*s %*c %u", &v); ++ fclose(f); ++#endif /* __linux__*/ ++ ++#ifdef __OpenBSD__ ++ int n; ++ kvm_t *kd; ++ struct kinfo_proc *kp; ++ ++ kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); ++ if (!kd) ++ return 0; ++ ++ kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); ++ v = kp->p_ppid; ++#endif /* __OpenBSD__ */ ++ ++ return (pid_t)v; ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -974,6 +1025,15 @@ incnmaster(const Arg *arg) + arrange(selmon); + } + ++int ++isdescprocess(pid_t p, pid_t c) ++{ ++ while (p != c && c != 0) ++ c = getparentprocess(c); ++ ++ return (int)c; ++} ++ + #ifdef XINERAMA + static int + isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +@@ -1027,6 +1087,7 @@ manage(Window w, XWindowAttributes *wa) + + c = ecalloc(1, sizeof(Client)); + c->win = w; ++ c->pid = winpid(w); + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; +@@ -1075,6 +1136,15 @@ manage(Window w, XWindowAttributes *wa) + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; ++ ++ if (riopid && (!riodraw_matchpid || isdescprocess(riopid, c->pid))) { ++ if (riodimensions[3] != -1) ++ rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); ++ else { ++ killclient(&((Arg) { .v = c })); ++ return; ++ } ++ } + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); +@@ -1373,6 +1443,104 @@ restack(Monitor *m) + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + ++int ++riodraw(Client *c, const char slopstyle[]) ++{ ++ int i; ++ char str[100] = {0}; ++ char strout[100] = {0}; ++ char tmpstring[30] = {0}; ++ char slopcmd[100] = "slop -f x%xx%yx%wx%hx "; ++ int firstchar = 0; ++ int counter = 0; ++ ++ strcat(slopcmd, slopstyle); ++ FILE *fp = popen(slopcmd, "r"); ++ ++ while (fgets(str, 100, fp) != NULL) ++ strcat(strout, str); ++ ++ pclose(fp); ++ ++ if (strlen(strout) < 6) ++ return 0; ++ ++ for (i = 0; i < strlen(strout); i++){ ++ if (!firstchar) { ++ if (strout[i] == 'x') ++ firstchar = 1; ++ continue; ++ } ++ ++ if (strout[i] != 'x') ++ tmpstring[strlen(tmpstring)] = strout[i]; ++ else { ++ riodimensions[counter] = atoi(tmpstring); ++ counter++; ++ memset(tmpstring,0,strlen(tmpstring)); ++ } ++ } ++ ++ if (riodimensions[0] <= -40 || riodimensions[1] <= -40 || riodimensions[2] <= 50 || riodimensions[3] <= 50) { ++ riodimensions[3] = -1; ++ return 0; ++ } ++ ++ if (c) { ++ rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++void ++rioposition(Client *c, int x, int y, int w, int h) ++{ ++ Monitor *m; ++ if ((m = recttomon(x, y, w, h)) && m != c->mon) { ++ detach(c); ++ detachstack(c); ++ arrange(c->mon); ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; ++ attach(c); ++ attachstack(c); ++ selmon = m; ++ focus(c); ++ } ++ ++ c->isfloating = 1; ++ if (riodraw_borders) ++ resizeclient(c, x, y, w - (c->bw * 2), h - (c->bw * 2)); ++ else ++ resizeclient(c, x - c->bw, y - c->bw, w, h); ++ arrange(c->mon); ++ ++ riodimensions[3] = -1; ++ riopid = 0; ++} ++ ++/* drag out an area using slop and resize the selected window to it */ ++void ++rioresize(const Arg *arg) ++{ ++ Client *c = (arg && arg->v ? (Client*)arg->v : selmon->sel); ++ if (c) ++ riodraw(c, slopresizestyle); ++} ++ ++/* spawn a new window and drag out an area using slop to position it */ ++void ++riospawn(const Arg *arg) ++{ ++ if (riodraw_spawnasync) { ++ riopid = spawncmd(arg); ++ riodraw(NULL, slopspawnstyle); ++ } else if (riodraw(NULL, slopspawnstyle)) ++ riopid = spawncmd(arg); ++} ++ + void + run(void) + { +@@ -1643,9 +1811,16 @@ sigchld(int unused) + void + spawn(const Arg *arg) + { ++ spawncmd(arg); ++} ++ ++pid_t ++spawncmd(const Arg *arg) ++{ ++ pid_t pid; + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; +- if (fork() == 0) { ++ if ((pid = fork()) == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); +@@ -1654,6 +1829,7 @@ spawn(const Arg *arg) + perror(" failed"); + exit(EXIT_SUCCESS); + } ++ return pid; + } + + void +@@ -2050,6 +2226,58 @@ view(const Arg *arg) + arrange(selmon); + } + ++pid_t ++winpid(Window w) ++{ ++ pid_t result = 0; ++ ++#ifdef __linux__ ++ xcb_res_client_id_spec_t spec = {0}; ++ spec.client = w; ++ spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; ++ ++ xcb_generic_error_t *e = NULL; ++ xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); ++ xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); ++ ++ if (!r) ++ return (pid_t)0; ++ ++ xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); ++ for (; i.rem; xcb_res_client_id_value_next(&i)) { ++ spec = i.data->spec; ++ if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { ++ uint32_t *t = xcb_res_client_id_value_value(i.data); ++ result = *t; ++ break; ++ } ++ } ++ ++ free(r); ++ ++ if (result == (pid_t)-1) ++ result = 0; ++ ++#endif /* __linux__ */ ++ ++#ifdef __OpenBSD__ ++ Atom type; ++ int format; ++ unsigned long len, bytes; ++ unsigned char *prop; ++ pid_t ret; ++ ++ if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) ++ return 0; ++ ++ ret = *(pid_t*)prop; ++ XFree(prop); ++ result = ret; ++ ++#endif /* __OpenBSD__ */ ++ return result; ++} ++ + Client * + wintoclient(Window w) + { +@@ -2141,6 +2369,8 @@ main(int argc, char *argv[]) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); ++ if (!(xcon = XGetXCBConnection(dpy))) ++ die("dwm: cannot get xcb connection\n"); + checkotherwm(); + setup(); + #ifdef __OpenBSD__ +-- +2.19.1 + + +From b99f734407e5a60b7250f4e109e9f03f2de0b7f2 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:35:44 +0100 +Subject: [PATCH 2/2] Adding riodraw patch with no PID matching + +--- + config.def.h | 1 - + dwm.c | 55 +++++++--------------------------------------------- + 2 files changed, 7 insertions(+), 49 deletions(-) + +diff --git a/config.def.h b/config.def.h +index bbbc6ca..bb3d58f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -8,7 +8,6 @@ static const int topbar = 1; /* 0 means bottom bar */ + static const char slopspawnstyle[] = "-t 0 -c 0.92,0.85,0.69,0.3 -o"; /* do NOT define -f (format) here */ + static const char slopresizestyle[] = "-t 0 -c 0.92,0.85,0.69,0.3"; /* do NOT define -f (format) here */ + static const int riodraw_borders = 0; /* 0 or 1, indicates whether the area drawn using slop includes the window borders */ +-static const int riodraw_matchpid = 1; /* 0 or 1, indicates whether to match the PID of the client that was spawned with riospawn */ + static const int riodraw_spawnasync = 0; /* 0 means that the application is only spawned after a successful selection while + * 1 means that the application is being initialised in the background while the selection is made */ + static const char *fonts[] = { "monospace:size=10" }; +diff --git a/dwm.c b/dwm.c +index 95cfcb5..0a72d89 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -177,14 +177,12 @@ static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); +-static pid_t getparentprocess(pid_t p); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); + static void incnmaster(const Arg *arg); +-static int isdescprocess(pid_t p, pid_t c); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); + static void manage(Window w, XWindowAttributes *wa); +@@ -893,39 +891,6 @@ getatomprop(Client *c, Atom prop) + return atom; + } + +-pid_t +-getparentprocess(pid_t p) +-{ +- unsigned int v = 0; +- +-#ifdef __linux__ +- FILE *f; +- char buf[256]; +- snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); +- +- if (!(f = fopen(buf, "r"))) +- return 0; +- +- fscanf(f, "%*u %*s %*c %u", &v); +- fclose(f); +-#endif /* __linux__*/ +- +-#ifdef __OpenBSD__ +- int n; +- kvm_t *kd; +- struct kinfo_proc *kp; +- +- kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); +- if (!kd) +- return 0; +- +- kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); +- v = kp->p_ppid; +-#endif /* __OpenBSD__ */ +- +- return (pid_t)v; +-} +- + int + getrootptr(int *x, int *y) + { +@@ -1025,15 +990,6 @@ incnmaster(const Arg *arg) + arrange(selmon); + } + +-int +-isdescprocess(pid_t p, pid_t c) +-{ +- while (p != c && c != 0) +- c = getparentprocess(c); +- +- return (int)c; +-} +- + #ifdef XINERAMA + static int + isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +@@ -1137,7 +1093,7 @@ manage(Window w, XWindowAttributes *wa) + unfocus(selmon->sel, 0); + c->mon->sel = c; + +- if (riopid && (!riodraw_matchpid || isdescprocess(riopid, c->pid))) { ++ if (riopid) { + if (riodimensions[3] != -1) + rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); + else { +@@ -1535,10 +1491,13 @@ void + riospawn(const Arg *arg) + { + if (riodraw_spawnasync) { +- riopid = spawncmd(arg); ++ spawn(arg); ++ riopid = 1; + riodraw(NULL, slopspawnstyle); +- } else if (riodraw(NULL, slopspawnstyle)) +- riopid = spawncmd(arg); ++ } else if (riodraw(NULL, slopspawnstyle)) { ++ spawn(arg); ++ riopid = 1; ++ } + } + + void +-- +2.19.1 + diff --git a/dwm/dwm-rotatestack-6.3.diff b/dwm/dwm-rotatestack-6.3.diff new file mode 100644 index 0000000..1da0545 --- /dev/null +++ b/dwm/dwm-rotatestack-6.3.diff @@ -0,0 +1,121 @@ +From b1026a04c1c7c49826a17d1aca2e44f59f4b5bf3 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:36:14 +0100 +Subject: [PATCH] rotatestack, allows you to scroll through the stack + +Stack rotation moves a client from the bottom to the top of the stack (or the other way round). This effectively rotates the clients by one position clockwise (or CCW, respectively). + +It should play well with arbitrary stack layouts and nmaster values. + +One may think of it as moving the zoom through the list of clients, very much in the same way as scrolling moves the view port around a pane. + +Refer to https://dwm.suckless.org/patches/rotatestack/ +--- + config.def.h | 2 ++ + dwm.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 58 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..546087f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -67,6 +67,8 @@ static Key keys[] = { + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { MODKEY|ShiftMask, XK_j, rotatestack, {.i = +1 } }, ++ { MODKEY|ShiftMask, XK_k, rotatestack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..091cc8a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -163,6 +163,8 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static void enqueue(Client *c); ++static void enqueuestack(Client *c); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -193,6 +195,7 @@ static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); ++static void rotatestack(const Arg *arg); + static void run(void); + static void scan(void); + static int sendevent(Client *c, Atom proto); +@@ -755,6 +758,28 @@ drawbars(void) + drawbar(m); + } + ++void ++enqueue(Client *c) ++{ ++ Client *l; ++ for (l = c->mon->clients; l && l->next; l = l->next); ++ if (l) { ++ l->next = c; ++ c->next = NULL; ++ } ++} ++ ++void ++enqueuestack(Client *c) ++{ ++ Client *l; ++ for (l = c->mon->stack; l && l->snext; l = l->snext); ++ if (l) { ++ l->snext = c; ++ c->snext = NULL; ++ } ++} ++ + void + enternotify(XEvent *e) + { +@@ -1373,6 +1398,37 @@ restack(Monitor *m) + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + ++void ++rotatestack(const Arg *arg) ++{ ++ Client *c = NULL, *f; ++ ++ if (!selmon->sel) ++ return; ++ f = selmon->sel; ++ if (arg->i > 0) { ++ for (c = nexttiled(selmon->clients); c && nexttiled(c->next); c = nexttiled(c->next)); ++ if (c){ ++ detach(c); ++ attach(c); ++ detachstack(c); ++ attachstack(c); ++ } ++ } else { ++ if ((c = nexttiled(selmon->clients))){ ++ detach(c); ++ enqueue(c); ++ detachstack(c); ++ enqueuestack(c); ++ } ++ } ++ if (c){ ++ arrange(selmon); ++ focus(f); ++ restack(selmon); ++ } ++} ++ + void + run(void) + { +-- +2.19.1 + diff --git a/dwm/dwm-savefloats-6.3.diff b/dwm/dwm-savefloats-6.3.diff new file mode 100644 index 0000000..27a558e --- /dev/null +++ b/dwm/dwm-savefloats-6.3.diff @@ -0,0 +1,55 @@ +From d8cd51f56a9089aa2d492ddbce88c2e563633c8d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:37:00 +0100 +Subject: [PATCH] Savefloats, saves size and position of floating windows + +Refer to https://dwm.suckless.org/patches/save_floats/ +--- + dwm.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..faa3c54 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -88,6 +88,7 @@ struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; ++ int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */ + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; +@@ -1060,6 +1061,10 @@ manage(Window w, XWindowAttributes *wa) + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); ++ c->sfx = c->x; ++ c->sfy = c->y; ++ c->sfw = c->w; ++ c->sfh = c->h; + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) +@@ -1720,8 +1725,16 @@ togglefloating(const Arg *arg) + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) +- resize(selmon->sel, selmon->sel->x, selmon->sel->y, +- selmon->sel->w, selmon->sel->h, 0); ++ /* restore last known float dimensions */ ++ resize(selmon->sel, selmon->sel->sfx, selmon->sel->sfy, ++ selmon->sel->sfw, selmon->sel->sfh, False); ++ else { ++ /* save last known float dimensions */ ++ selmon->sel->sfx = selmon->sel->x; ++ selmon->sel->sfy = selmon->sel->y; ++ selmon->sel->sfw = selmon->sel->w; ++ selmon->sel->sfh = selmon->sel->h; ++ } + arrange(selmon); + } + +-- +2.19.1 + diff --git a/dwm/dwm-sendmon_keepfocus-6.3.diff b/dwm/dwm-sendmon_keepfocus-6.3.diff new file mode 100644 index 0000000..76be770 --- /dev/null +++ b/dwm/dwm-sendmon_keepfocus-6.3.diff @@ -0,0 +1,40 @@ +From aa3796065846a03a81c10c9ea5cbcee85ab9e41e Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:37:27 +0100 +Subject: [PATCH] Adding sendmon_keepfocus patch + +--- + dwm.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..f46e77e 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1416,15 +1416,21 @@ sendmon(Client *c, Monitor *m) + { + if (c->mon == m) + return; ++ int hadfocus = (c == selmon->sel); + unfocus(c, 1); + detach(c); + detachstack(c); ++ arrange(c->mon); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); +- focus(NULL); +- arrange(NULL); ++ arrange(m); ++ if (hadfocus) { ++ focus(c); ++ restack(m); ++ } else ++ focus(NULL); + } + + void +-- +2.19.1 + diff --git a/dwm/dwm-shiftviewclients-6.3.diff b/dwm/dwm-shiftviewclients-6.3.diff new file mode 100644 index 0000000..cb1584b --- /dev/null +++ b/dwm/dwm-shiftviewclients-6.3.diff @@ -0,0 +1,86 @@ +From ad9512714214ea2cfb54babd8fc72de81a094c4b Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:38:05 +0100 +Subject: [PATCH] This variant of the shiftview patch adds left and right + circular shift through tags, but skips tags where there are no clients. + +--- + config.def.h | 2 ++ + dwm.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 43 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..ad7cabe 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -73,6 +73,8 @@ static Key keys[] = { + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, ++ { MODKEY|ShiftMask, XK_Tab, shiftviewclients, { .i = +1 } }, ++ { MODKEY|ShiftMask, XK_backslash, shiftviewclients, { .i = -1 } }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..0f8fd34 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -204,6 +204,7 @@ static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); + static void seturgent(Client *c, int urg); ++static void shiftviewclients(const Arg *arg); + static void showhide(Client *c); + static void sigchld(int unused); + static void spawn(const Arg *arg); +@@ -1614,6 +1615,46 @@ seturgent(Client *c, int urg) + XFree(wmh); + } + ++void ++shiftviewclients(const Arg *arg) ++{ ++ Arg shifted; ++ Client *c; ++ unsigned int tagmask = 0; ++ ++ for (c = selmon->clients; c; c = c->next) ++ #if SCRATCHPADS_PATCH ++ if (!(c->tags & SPTAGMASK)) ++ tagmask = tagmask | c->tags; ++ #else ++ tagmask = tagmask | c->tags; ++ #endif // SCRATCHPADS_PATCH ++ ++ #if SCRATCHPADS_PATCH ++ shifted.ui = selmon->tagset[selmon->seltags] & ~SPTAGMASK; ++ #else ++ shifted.ui = selmon->tagset[selmon->seltags]; ++ #endif // SCRATCHPADS_PATCH ++ if (arg->i > 0) // left circular shift ++ do { ++ shifted.ui = (shifted.ui << arg->i) ++ | (shifted.ui >> (LENGTH(tags) - arg->i)); ++ #if SCRATCHPADS_PATCH ++ shifted.ui &= ~SPTAGMASK; ++ #endif // SCRATCHPADS_PATCH ++ } while (tagmask && !(shifted.ui & tagmask)); ++ else // right circular shift ++ do { ++ shifted.ui = (shifted.ui >> (- arg->i) ++ | shifted.ui << (LENGTH(tags) + arg->i)); ++ #if SCRATCHPADS_PATCH ++ shifted.ui &= ~SPTAGMASK; ++ #endif // SCRATCHPADS_PATCH ++ } while (tagmask && !(shifted.ui & tagmask)); ++ ++ view(&shifted); ++} ++ + void + showhide(Client *c) + { +-- +2.19.1 + diff --git a/dwm/dwm-single_tagset-6.3.diff b/dwm/dwm-single_tagset-6.3.diff new file mode 100644 index 0000000..07aca80 --- /dev/null +++ b/dwm/dwm-single_tagset-6.3.diff @@ -0,0 +1,541 @@ +From 8591e832ea3c584841dfaf71c9e87aea8f01ce9e Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:40:01 +0100 +Subject: [PATCH] Adding single tagset + +Author: Jan Christoph Ebersbach +URL: http://dwm.suckless.org/patches/single_tagset +This patch addresses the multi-monitor setup. Instead of having separate tags +for every monitor there is just one list of tags for all monitors. Instead of +moving windows from one monitor to the other, the desired tag from the +other monitor can just be selected and all windows will be drawn on the +current monitor. + +Several deep changes needed to be made: +1. Macro ISVISIBLE expects a second parameter, the monitor +2. Monitor->clients and Monitor->stack were moved to the global variable + Clientlist cl. All monitors refer to this one list. +3. A new method attachclients was added. When changing between tags this + function ensures that all clients are pointing to the right monitor. + +Please be aware that this patch probably breaks any other patch! +--- + dwm.c | 214 +++++++++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 159 insertions(+), 55 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..81eec26 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -49,7 +49,7 @@ + #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 ISVISIBLE(C, M) ((C->tags & M->tagset[M->seltags])) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) +@@ -82,6 +82,7 @@ typedef struct { + const Arg arg; + } Button; + ++typedef struct Clientlist Clientlist; + typedef struct Monitor Monitor; + typedef struct Client Client; + struct Client { +@@ -124,14 +125,18 @@ struct Monitor { + unsigned int tagset[2]; + int showbar; + int topbar; +- Client *clients; ++ Clientlist *cl; + Client *sel; +- Client *stack; + Monitor *next; + Window barwin; + const Layout *lt[2]; + }; + ++struct Clientlist { ++ Client *clients; ++ Client *stack; ++}; ++ + typedef struct { + const char *class; + const char *instance; +@@ -147,6 +152,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachclients(Monitor *m); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -184,7 +190,7 @@ static void maprequest(XEvent *e); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); +-static Client *nexttiled(Client *c); ++static Client *nexttiled(Client *c, Monitor *m); + static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); +@@ -268,6 +274,7 @@ static Display *dpy; + static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; ++static Clientlist *cl; + + /* configuration, allows nested code to access above variables */ + #include "config.h" +@@ -300,7 +307,7 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; +- for (m = mons; m && m->num != r->monitor; m = m->next); ++ for (m = mons; m && (m->tagset[m->seltags] & c->tags) == 0; m = m->next) ; + if (m) + c->mon = m; + } +@@ -382,9 +389,9 @@ void + arrange(Monitor *m) + { + if (m) +- showhide(m->stack); ++ showhide(m->cl->stack); + else for (m = mons; m; m = m->next) +- showhide(m->stack); ++ showhide(m->cl->stack); + if (m) { + arrangemon(m); + restack(m); +@@ -403,15 +410,48 @@ arrangemon(Monitor *m) + void + attach(Client *c) + { +- c->next = c->mon->clients; +- c->mon->clients = c; ++ c->next = c->mon->cl->clients; ++ c->mon->cl->clients = c; ++} ++ ++void ++attachclients(Monitor *m) { ++ /* attach clients to the specified monitor */ ++ Monitor *tm; ++ Client *c; ++ unsigned int utags = 0; ++ Bool rmons = False; ++ if (!m) ++ return; ++ ++ /* collect information about the tags in use */ ++ for (tm = mons; tm; tm = tm->next) ++ if(tm != m) ++ utags |= tm->tagset[tm->seltags]; ++ ++ for (c = m->cl->clients; c; c = c->next) ++ if (ISVISIBLE(c, m)) { ++ /* if client is also visible on other tags that are displayed on ++ * other monitors, remove these tags */ ++ if (c->tags & utags) { ++ c->tags = c->tags & m->tagset[m->seltags]; ++ rmons = True; ++ } ++ unfocus(c, True); ++ c->mon = m; ++ } ++ ++ if (rmons) ++ for (tm = mons; tm; tm = tm->next) ++ if(tm != m) ++ arrange(tm); + } + + void + attachstack(Client *c) + { +- c->snext = c->mon->stack; +- c->mon->stack = c; ++ c->snext = c->mon->cl->stack; ++ c->mon->cl->stack = c; + } + + void +@@ -478,8 +518,8 @@ cleanup(void) + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) +- while (m->stack) +- unmanage(m->stack, 0); ++ while (m->cl->stack) ++ unmanage(m->cl->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); +@@ -565,7 +605,7 @@ configurenotify(XEvent *e) + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { +- for (c = m->clients; c; c = c->next) ++ for (c = m->cl->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); +@@ -611,7 +651,7 @@ configurerequest(XEvent *e) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); +- if (ISVISIBLE(c)) ++ if (ISVISIBLE(c, m)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); +@@ -631,10 +671,32 @@ configurerequest(XEvent *e) + Monitor * + createmon(void) + { +- Monitor *m; ++ Monitor *m, *tm; ++ int i; ++ ++ /* bail out if the number of monitors exceeds the number of tags */ ++ for (i=1, tm=mons; tm; i++, tm=tm->next); ++ if (i > LENGTH(tags)) { ++ fprintf(stderr, "dwm: failed to add monitor, number of tags exceeded\n"); ++ return NULL; ++ } ++ /* find the first tag that isn't in use */ ++ for (i=0; i < LENGTH(tags); i++) { ++ for (tm=mons; tm && !(tm->tagset[tm->seltags] & (1<next); ++ if (!tm) ++ break; ++ } ++ /* reassign all tags to monitors since there's currently no free tag for the ++ * new monitor */ ++ if (i >= LENGTH(tags)) ++ for (i=0, tm=mons; tm; tm=tm->next, i++) { ++ tm->seltags ^= 1; ++ tm->tagset[tm->seltags] = (1<tagset[0] = m->tagset[1] = 1; ++ m->cl = cl; ++ m->tagset[0] = m->tagset[1] = (1<mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; +@@ -660,7 +722,7 @@ detach(Client *c) + { + Client **tc; + +- for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); ++ for (tc = &c->mon->cl->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; + } + +@@ -669,11 +731,11 @@ detachstack(Client *c) + { + Client **tc, *t; + +- for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); ++ for (tc = &c->mon->cl->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { +- for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); ++ for (t = c->mon->cl->stack; t && !ISVISIBLE(t, c->mon); t = t->snext); + c->mon->sel = t; + } + } +@@ -712,7 +774,7 @@ drawbar(Monitor *m) + drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); + } + +- for (c = m->clients; c; c = c->next) { ++ for (c = m->cl->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; +@@ -787,8 +849,8 @@ expose(XEvent *e) + void + focus(Client *c) + { +- if (!c || !ISVISIBLE(c)) +- for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); ++ if (!c || !ISVISIBLE(c, selmon)) ++ for (c = selmon->cl->stack; c && !ISVISIBLE(c, selmon); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { +@@ -841,16 +903,16 @@ focusstack(const Arg *arg) + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { +- for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); ++ for (c = selmon->sel->next; c && !ISVISIBLE(c, selmon); c = c->next); + if (!c) +- for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); ++ for (c = selmon->cl->clients; c && !ISVISIBLE(c, selmon); c = c->next); + } else { +- for (i = selmon->clients; i != selmon->sel; i = i->next) +- if (ISVISIBLE(i)) ++ for (i = selmon->cl->clients; i != selmon->sel; i = i->next) ++ if (ISVISIBLE(i, selmon)) + c = i; + if (!c) + for (; i; i = i->next) +- if (ISVISIBLE(i)) ++ if (ISVISIBLE(i, selmon)) + c = i; + } + if (c) { +@@ -1110,12 +1172,12 @@ monocle(Monitor *m) + unsigned int n = 0; + Client *c; + +- for (c = m->clients; c; c = c->next) +- if (ISVISIBLE(c)) ++ for (c = m->cl->clients; c; c = c->next) ++ if (ISVISIBLE(c, m)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); +- for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) ++ for (c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); + } + +@@ -1197,9 +1259,9 @@ movemouse(const Arg *arg) + } + + Client * +-nexttiled(Client *c) ++nexttiled(Client *c, Monitor *m) + { +- for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); ++ for (; c && (c->isfloating || !ISVISIBLE(c, m)); c = c->next); + return c; + } + +@@ -1363,8 +1425,8 @@ restack(Monitor *m) + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; +- for (c = m->stack; c; c = c->snext) +- if (!c->isfloating && ISVISIBLE(c)) { ++ for (c = m->cl->stack; c; c = c->snext) ++ if (!c->isfloating && ISVISIBLE(c, m)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } +@@ -1417,11 +1479,9 @@ sendmon(Client *c, Monitor *m) + if (c->mon == m) + return; + unfocus(c, 1); +- detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1544,6 +1604,8 @@ setup(void) + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); ++ if (!(cl = (Clientlist *)calloc(1, sizeof(Clientlist)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Clientlist)); + root = RootWindow(dpy, screen); + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) +@@ -1619,7 +1681,7 @@ showhide(Client *c) + { + if (!c) + return; +- if (ISVISIBLE(c)) { ++ if (ISVISIBLE(c, c->mon)) { + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) +@@ -1659,7 +1721,22 @@ spawn(const Arg *arg) + void + tag(const Arg *arg) + { ++ Monitor *m; ++ unsigned int newtags; + if (selmon->sel && arg->ui & TAGMASK) { ++ newtags = arg->ui & TAGMASK; ++ for (m = mons; m; m = m->next) ++ /* if tag is visible on another monitor, move client to the new monitor */ ++ if (m != selmon && m->tagset[m->seltags] & newtags) { ++ /* prevent moving client to all tags (MODKEY-Shift-0) when multiple monitors are connected */ ++ if(newtags & selmon->tagset[selmon->seltags]) ++ return; ++ selmon->sel->tags = newtags; ++ selmon->sel->mon = m; ++ arrange(m); ++ break; ++ } ++ /* workaround in case just one monitor is connected */ + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); +@@ -1680,7 +1757,7 @@ tile(Monitor *m) + unsigned int i, n, h, mw, my, ty; + Client *c; + +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ for (n = 0, c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m), n++); + if (n == 0) + return; + +@@ -1688,7 +1765,7 @@ tile(Monitor *m) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; +- for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ for (i = my = ty = 0, c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); +@@ -1728,12 +1805,17 @@ togglefloating(const Arg *arg) + void + toggletag(const Arg *arg) + { ++ Monitor *m; + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { ++ /* prevent adding tags that are in use on other monitors */ ++ for (m = mons; m; m = m->next) ++ if (m != selmon && newtags & m->tagset[m->seltags]) ++ return; + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); +@@ -1743,12 +1825,17 @@ toggletag(const Arg *arg) + void + toggleview(const Arg *arg) + { ++ Monitor *m; + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { ++ /* prevent displaying the same tags on multiple monitors */ ++ for(m = mons; m; m = m->next) ++ if(m != selmon && newtagset & m->tagset[m->seltags]) ++ return; + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); +- arrange(selmon); ++ attachclients(selmon); + } + } + +@@ -1847,7 +1934,7 @@ updateclientlist() + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) +- for (c = m->clients; c; c = c->next) ++ for (c = m->cl->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1877,8 +1964,10 @@ updategeom(void) + if (n <= nn) { /* new monitors available */ + for (i = 0; i < (nn - n); i++) { + for (m = mons; m && m->next; m = m->next); +- if (m) ++ if (m) { + m->next = createmon(); ++ attachclients(m->next); ++ } + else + mons = createmon(); + } +@@ -1898,16 +1987,13 @@ updategeom(void) + } else { /* less monitors available nn < n */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); +- while ((c = m->clients)) { +- dirty = 1; +- m->clients = c->next; +- detachstack(c); +- c->mon = mons; +- attach(c); +- attachstack(c); +- } + if (m == selmon) + selmon = mons; ++ for (c = m->cl->clients; c; c = c->next) { ++ dirty = True; ++ if (c->mon == m) ++ c->mon = selmon; ++ } + cleanupmon(m); + } + } +@@ -2041,13 +2127,31 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { ++ Monitor *m; ++ unsigned int newtagset = selmon->tagset[selmon->seltags ^ 1]; + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; ++ /* swap tags when trying to display a tag from another monitor */ ++ if (arg->ui & TAGMASK) ++ newtagset = arg->ui & TAGMASK; ++ for (m = mons; m; m = m->next) ++ if (m != selmon && newtagset & m->tagset[m->seltags]) { ++ /* prevent displaying all tags (MODKEY-0) when multiple monitors ++ * are connected */ ++ if (newtagset & selmon->tagset[selmon->seltags]) ++ return; ++ m->sel = selmon->sel; ++ m->seltags ^= 1; ++ m->tagset[m->seltags] = selmon->tagset[selmon->seltags]; ++ attachclients(m); ++ arrange(m); ++ break; ++ } + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + focus(NULL); +- arrange(selmon); ++ attachclients(selmon); + } + + Client * +@@ -2057,7 +2161,7 @@ wintoclient(Window w) + Monitor *m; + + for (m = mons; m; m = m->next) +- for (c = m->clients; c; c = c->next) ++ for (c = m->cl->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +@@ -2124,8 +2228,8 @@ zoom(const Arg *arg) + if (!selmon->lt[selmon->sellt]->arrange + || (selmon->sel && selmon->sel->isfloating)) + return; +- if (c == nexttiled(selmon->clients)) +- if (!c || !(c = nexttiled(c->next))) ++ if (c == nexttiled(selmon->cl->clients, selmon)) ++ if (!c || !(c = nexttiled(c->next, selmon))) + return; + pop(c); + } +-- +2.19.1 + diff --git a/dwm/dwm-spotifyworkaround-6.3.diff b/dwm/dwm-spotifyworkaround-6.3.diff new file mode 100644 index 0000000..dbbaf15 --- /dev/null +++ b/dwm/dwm-spotifyworkaround-6.3.diff @@ -0,0 +1,44 @@ +From 3c1393381a78b7991e5702a3f2082cbe1352c841 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:40:38 +0100 +Subject: [PATCH] Workaround for programs like spotify which do not offer + instance or class hints when they initially map, resulting in no rules + applying + +--- + dwm.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..51f4900 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -63,7 +63,7 @@ enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +-enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ ++enum { WMClass, WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +@@ -1246,6 +1246,8 @@ propertynotify(XEvent *e) + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); ++ if (ev->atom == wmatom[WMClass]) ++ applyrules(c); + } + } + +@@ -1553,6 +1555,7 @@ setup(void) + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); ++ wmatom[WMClass] = XInternAtom(dpy, "WM_CLASS", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); +-- +2.19.1 + diff --git a/dwm/dwm-statuspadding-6.3.diff b/dwm/dwm-statuspadding-6.3.diff new file mode 100644 index 0000000..62763bc --- /dev/null +++ b/dwm/dwm-statuspadding-6.3.diff @@ -0,0 +1,54 @@ +From 3b58e394ee65fdbf425746922853b1dc7530daa1 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:42:17 +0100 +Subject: [PATCH] Adding statuspadding patch + +Refer to: +https://dwm.suckless.org/patches/statuspadding/ +--- + config.def.h | 2 ++ + dwm.c | 8 ++++---- + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..6cb845c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ 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 */ ++static const int horizpadbar = 2; /* horizontal padding for statusbar */ ++static const int vertpadbar = 0; /* vertical padding for statusbar */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/dwm.c b/dwm.c +index a96f33c..a1b8c95 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -708,8 +708,8 @@ drawbar(Monitor *m) + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ tw = TEXTW(stext); ++ drw_text(drw, m->ww - tw, 0, tw, bh, lrpad / 2, stext, 0); + } + + for (c = m->clients; c; c = c->next) { +@@ -1548,8 +1548,8 @@ setup(void) + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); +- lrpad = drw->fonts->h; +- bh = drw->fonts->h + 2; ++ lrpad = drw->fonts->h + horizpadbar; ++ bh = drw->fonts->h + vertpadbar; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); +-- +2.19.1 + diff --git a/dwm/dwm-steam-6.3.diff b/dwm/dwm-steam-6.3.diff new file mode 100644 index 0000000..525eb1d --- /dev/null +++ b/dwm/dwm-steam-6.3.diff @@ -0,0 +1,72 @@ +From 4cc3588c2601e6a2edb8d7117723d28d77f0fc61 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:42:45 +0100 +Subject: [PATCH] Steam patch + +Steam, and steam windows (games), trigger a ConfigureNotify request every time the window +gets focus. More so, the configure event passed along from Steam tends to have the wrong +x and y co-ordinates which can make the window, if floating, jump around the screen. + +This patch works around this age-old issue by ignoring the x and y co-ordinates for +ConfigureNotify requests relating to Steam windows. +--- + dwm.c | 22 +++++++++++++++------- + 1 file changed, 15 insertions(+), 7 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..318491b 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int issteam; + Client *next; + Client *snext; + Monitor *mon; +@@ -292,6 +293,9 @@ applyrules(Client *c) + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + ++ if (strstr(class, "Steam") || strstr(class, "steam_app_")) ++ c->issteam = 1; ++ + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) +@@ -589,13 +593,15 @@ configurerequest(XEvent *e) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; +- if (ev->value_mask & CWX) { +- c->oldx = c->x; +- c->x = m->mx + ev->x; +- } +- if (ev->value_mask & CWY) { +- c->oldy = c->y; +- c->y = m->my + ev->y; ++ if (!c->issteam) { ++ if (ev->value_mask & CWX) { ++ c->oldx = c->x; ++ c->x = m->mx + ev->x; ++ } ++ if (ev->value_mask & CWY) { ++ c->oldy = c->y; ++ c->y = m->my + ev->y; ++ } + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; +@@ -1470,6 +1476,8 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } ++ if (c->issteam) ++ setclientstate(c, NormalState); + sendevent(c, wmatom[WMTakeFocus]); + } + +-- +2.19.1 + diff --git a/dwm/dwm-swallow-6.3.diff b/dwm/dwm-swallow-6.3.diff new file mode 100644 index 0000000..a8e2f77 --- /dev/null +++ b/dwm/dwm-swallow-6.3.diff @@ -0,0 +1,506 @@ +From 66c7e6fef8a900a03e4ca861fdb11aff1f1af690 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:43:13 +0100 +Subject: [PATCH] Alternative swallow patch that replaces clients instead of + swapping windows offering better resize hints + +--- + config.def.h | 9 +- + config.mk | 6 +- + dwm.c | 300 +++++++++++++++++++++++++++++++++++++++++++++++---- + 3 files changed, 288 insertions(+), 27 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..dba4b83 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,7 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -26,9 +27,11 @@ 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 isterminal noswallow monitor */ ++ { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1, -1 }, ++ { "St", NULL, NULL, 0, 0, 1, 0, -1 }, ++ { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */ + }; + + /* layout(s) */ +diff --git a/config.mk b/config.mk +index b6eb7e0..ee5c46e 100644 +--- a/config.mk ++++ b/config.mk +@@ -19,10 +19,14 @@ FREETYPELIBS = -lfontconfig -lXft + FREETYPEINC = /usr/include/freetype2 + # OpenBSD (uncomment) + #FREETYPEINC = ${X11INC}/freetype2 ++#KVMLIB = -lkvm ++ ++# This is needed for the swallow patch ++XCBLIBS = -lX11-xcb -lxcb -lxcb-res + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XCBLIBS} ${KVMLIB} + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/dwm.c b/dwm.c +index a96f33c..8735093 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -40,6 +40,12 @@ + #include + #endif /* XINERAMA */ + #include ++#include ++#include ++#ifdef __OpenBSD__ ++#include ++#include ++#endif /* __OpenBSD */ + + #include "drw.h" + #include "util.h" +@@ -92,9 +98,12 @@ struct Client { + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; +- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow; ++ int ignorecfgreqpos, ignorecfgreqsize; ++ pid_t pid; + Client *next; + Client *snext; ++ Client *swallowing; + Monitor *mon; + Window win; + }; +@@ -138,6 +147,8 @@ typedef struct { + const char *title; + unsigned int tags; + int isfloating; ++ int isterminal; ++ int noswallow; + int monitor; + } Rule; + +@@ -189,6 +200,7 @@ static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void replaceclient(Client *old, Client *new); + static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); +@@ -235,6 +247,14 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++static int swallow(Client *p, Client *c); ++static void unswallow(Client *c); ++static pid_t getparentprocess(pid_t p); ++static int isdescprocess(pid_t p, pid_t c); ++static Client *swallowingclient(Window w); ++static Client *termforwin(const Client *c); ++static pid_t winpid(Window w); ++ + /* variables */ + static const char broken[] = "broken"; + static char stext[256]; +@@ -268,6 +288,7 @@ static Display *dpy; + static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; ++static xcb_connection_t *xcon; + + /* configuration, allows nested code to access above variables */ + #include "config.h" +@@ -298,6 +319,8 @@ applyrules(Client *c) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { ++ c->isterminal = r->isterminal; ++ c->noswallow = r->noswallow; + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); +@@ -588,22 +611,29 @@ configurerequest(XEvent *e) + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { ++ if (c->ignorecfgreqpos && c->ignorecfgreqsize) ++ return; ++ + m = c->mon; +- if (ev->value_mask & CWX) { +- c->oldx = c->x; +- c->x = m->mx + ev->x; +- } +- if (ev->value_mask & CWY) { +- c->oldy = c->y; +- c->y = m->my + ev->y; +- } +- if (ev->value_mask & CWWidth) { +- c->oldw = c->w; +- c->w = ev->width; ++ if (!c->ignorecfgreqpos) { ++ if (ev->value_mask & CWX) { ++ c->oldx = c->x; ++ c->x = m->mx + ev->x; ++ } ++ if (ev->value_mask & CWY) { ++ c->oldy = c->y; ++ c->y = m->my + ev->y; ++ } + } +- if (ev->value_mask & CWHeight) { +- c->oldh = c->h; +- c->h = ev->height; ++ if (!c->ignorecfgreqsize) { ++ if (ev->value_mask & CWWidth) { ++ c->oldw = c->w; ++ c->w = ev->width; ++ } ++ if (ev->value_mask & CWHeight) { ++ c->oldh = c->h; ++ c->h = ev->height; ++ } + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ +@@ -653,6 +683,8 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if ((c = swallowingclient(ev->window))) ++ unmanage(c->swallowing, 1); + } + + void +@@ -1021,12 +1053,14 @@ killclient(const Arg *arg) + void + manage(Window w, XWindowAttributes *wa) + { +- Client *c, *t = NULL; ++ Client *c, *t = NULL, *term = NULL; + Window trans = None; + XWindowChanges wc; ++ int focusclient = 1; + + c = ecalloc(1, sizeof(Client)); + c->win = w; ++ c->pid = winpid(w); + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; +@@ -1041,6 +1075,7 @@ manage(Window w, XWindowAttributes *wa) + } else { + c->mon = selmon; + applyrules(c); ++ term = termforwin(c); + } + + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) +@@ -1066,18 +1101,35 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); +- attachstack(c); ++ ++ /* Do not attach client if it is being swallowed */ ++ if (term && swallow(term, c)) { ++ /* Do not let swallowed client steal focus unless the terminal has focus */ ++ focusclient = (term == selmon->sel); ++ } else { ++ attach(c); ++ ++ if (focusclient || !c->mon->sel || !c->mon->stack) ++ attachstack(c); ++ else { ++ c->snext = c->mon->sel->snext; ++ c->mon->sel->snext = c; ++ } ++ } ++ + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); +- if (c->mon == selmon) +- unfocus(selmon->sel, 0); +- c->mon->sel = c; ++ if (focusclient) { ++ if (c->mon == selmon) ++ unfocus(selmon->sel, 0); ++ c->mon->sel = c; ++ } + arrange(c->mon); + XMapWindow(dpy, c->win); +- focus(NULL); ++ if (focusclient) ++ focus(NULL); + } + + void +@@ -1269,6 +1321,46 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++replaceclient(Client *old, Client *new) ++{ ++ Client *c = NULL; ++ Monitor *mon = old->mon; ++ ++ new->mon = mon; ++ new->tags = old->tags; ++ new->isfloating = old->isfloating; ++ ++ new->next = old->next; ++ new->snext = old->snext; ++ ++ if (old == mon->clients) ++ mon->clients = new; ++ else { ++ for (c = mon->clients; c && c->next != old; c = c->next); ++ c->next = new; ++ } ++ ++ if (old == mon->stack) ++ mon->stack = new; ++ else { ++ for (c = mon->stack; c && c->snext != old; c = c->snext); ++ c->snext = new; ++ } ++ ++ old->next = NULL; ++ old->snext = NULL; ++ ++ XMoveWindow(dpy, old->win, WIDTH(old) * -2, old->y); ++ ++ if (ISVISIBLE(new) && !new->isfullscreen) { ++ if (new->isfloating) ++ resize(new, old->x, old->y, new->w, new->h, 0); ++ else ++ resize(new, old->x, old->y, old->w, old->h, 0); ++ } ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1656,6 +1748,31 @@ spawn(const Arg *arg) + } + } + ++int ++swallow(Client *t, Client *c) ++{ ++ if (c->noswallow || c->isterminal) ++ return 0; ++ if (!swallowfloating && c->isfloating) ++ return 0; ++ ++ if (t->isfullscreen) ++ setfullscreen(c, 1); ++ ++ replaceclient(t, c); ++ c->ignorecfgreqpos = 1; ++ c->swallowing = t; ++ ++ return 1; ++} ++ ++void ++unswallow(Client *c) ++{ ++ replaceclient(c, c->swallowing); ++ c->swallowing = NULL; ++} ++ + void + tag(const Arg *arg) + { +@@ -1768,9 +1885,17 @@ unfocus(Client *c, int setfocus) + void + unmanage(Client *c, int destroyed) + { ++ Client *s; + Monitor *m = c->mon; + XWindowChanges wc; + ++ if (c->swallowing) ++ unswallow(c); ++ ++ s = swallowingclient(c->win); ++ if (s) ++ s->swallowing = NULL; ++ + detach(c); + detachstack(c); + if (!destroyed) { +@@ -2050,6 +2175,133 @@ view(const Arg *arg) + arrange(selmon); + } + ++pid_t ++winpid(Window w) ++{ ++ pid_t result = 0; ++ ++#ifdef __linux__ ++ xcb_res_client_id_spec_t spec = {0}; ++ spec.client = w; ++ spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; ++ ++ xcb_generic_error_t *e = NULL; ++ xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); ++ xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); ++ ++ if (!r) ++ return (pid_t)0; ++ ++ xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); ++ for (; i.rem; xcb_res_client_id_value_next(&i)) { ++ spec = i.data->spec; ++ if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { ++ uint32_t *t = xcb_res_client_id_value_value(i.data); ++ result = *t; ++ break; ++ } ++ } ++ ++ free(r); ++ ++ if (result == (pid_t)-1) ++ result = 0; ++#endif /* __linux__ */ ++#ifdef __OpenBSD__ ++ Atom type; ++ int format; ++ unsigned long len, bytes; ++ unsigned char *prop; ++ pid_t ret; ++ ++ if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) ++ return 0; ++ ++ ret = *(pid_t*)prop; ++ XFree(prop); ++ result = ret; ++#endif /* __OpenBSD__ */ ++ ++ return result; ++} ++ ++pid_t ++getparentprocess(pid_t p) ++{ ++ unsigned int v = 0; ++ ++#ifdef __linux__ ++ FILE *f; ++ char buf[256]; ++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); ++ ++ if (!(f = fopen(buf, "r"))) ++ return 0; ++ ++ fscanf(f, "%*u %*s %*c %u", &v); ++ fclose(f); ++#endif /* __linux__*/ ++ ++#ifdef __OpenBSD__ ++ int n; ++ kvm_t *kd; ++ struct kinfo_proc *kp; ++ ++ kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); ++ if (!kd) ++ return 0; ++ ++ kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); ++ v = kp->p_ppid; ++#endif /* __OpenBSD__ */ ++ ++ return (pid_t)v; ++} ++ ++int ++isdescprocess(pid_t p, pid_t c) ++{ ++ while (p != c && c != 0) ++ c = getparentprocess(c); ++ ++ return (int)c; ++} ++ ++Client * ++termforwin(const Client *w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ if (!w->pid || w->isterminal) ++ return NULL; ++ ++ for (m = mons; m; m = m->next) { ++ for (c = m->clients; c; c = c->next) { ++ if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) ++ return c; ++ } ++ } ++ ++ return NULL; ++} ++ ++Client * ++swallowingclient(Window w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ for (m = mons; m; m = m->next) { ++ for (c = m->clients; c; c = c->next) { ++ if (c->swallowing && c->swallowing->win == w) ++ return c; ++ } ++ } ++ ++ return NULL; ++} ++ + Client * + wintoclient(Window w) + { +@@ -2141,10 +2393,12 @@ main(int argc, char *argv[]) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); ++ if (!(xcon = XGetXCBConnection(dpy))) ++ die("dwm: cannot get xcb connection\n"); + checkotherwm(); + setup(); + #ifdef __OpenBSD__ +- if (pledge("stdio rpath proc exec", NULL) == -1) ++ if (pledge("stdio rpath proc exec ps", NULL) == -1) + die("pledge"); + #endif /* __OpenBSD__ */ + scan(); +-- +2.19.1 + diff --git a/dwm/dwm-swallow-riodraw-6.3.diff b/dwm/dwm-swallow-riodraw-6.3.diff new file mode 100644 index 0000000..b5a2ec0 --- /dev/null +++ b/dwm/dwm-swallow-riodraw-6.3.diff @@ -0,0 +1,222 @@ +From d27eb690b062552395732ff71c046f11e91bdaf2 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:44:42 +0100 +Subject: [PATCH 2/2] Adding riodraw on top of swallow + +--- + config.def.h | 8 ++++ + dwm.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 135 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index dba4b83..191190b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -6,6 +6,12 @@ static const unsigned int snap = 32; /* snap pixel */ + static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ ++static const char slopspawnstyle[] = "-t 0 -c 0.92,0.85,0.69,0.3 -o"; /* do NOT define -f (format) here */ ++static const char slopresizestyle[] = "-t 0 -c 0.92,0.85,0.69,0.3"; /* do NOT define -f (format) here */ ++static const int riodraw_borders = 0; /* 0 or 1, indicates whether the area drawn using slop includes the window borders */ ++static const int riodraw_matchpid = 1; /* 0 or 1, indicates whether to match the PID of the client that was spawned with riospawn */ ++static const int riodraw_spawnasync = 0; /* 0 means that the application is only spawned after a successful selection while ++ * 1 means that the application is being initialised in the background while the selection is made */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -67,6 +73,8 @@ static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { MODKEY|ControlMask, XK_Return, riospawn, {.v = termcmd } }, ++ { MODKEY, XK_s, rioresize, {0} }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index 8735093..8dc28d3 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -205,6 +205,10 @@ static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); ++static int riodraw(Client *c, const char slopstyle[]); ++static void rioposition(Client *c, int x, int y, int w, int h); ++static void rioresize(const Arg *arg); ++static void riospawn(const Arg *arg); + static void run(void); + static void scan(void); + static int sendevent(Client *c, Atom proto); +@@ -219,6 +223,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 pid_t spawncmd(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +@@ -264,6 +269,8 @@ static int bh, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; ++static int riodimensions[4] = { -1, -1, -1, -1 }; ++static pid_t riopid = 0; + static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, +@@ -1126,6 +1133,18 @@ manage(Window w, XWindowAttributes *wa) + unfocus(selmon->sel, 0); + c->mon->sel = c; + } ++ ++ if (!c->swallowing) { ++ if (riopid && (!riodraw_matchpid || isdescprocess(riopid, c->pid))) { ++ if (riodimensions[3] != -1) ++ rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); ++ else { ++ killclient(&((Arg) { .v = c })); ++ return; ++ } ++ } ++ } ++ + arrange(c->mon); + XMapWindow(dpy, c->win); + if (focusclient) +@@ -1465,6 +1484,105 @@ restack(Monitor *m) + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + ++int ++riodraw(Client *c, const char slopstyle[]) ++{ ++ int i; ++ char str[100]; ++ char strout[100]; ++ char tmpstring[30] = {0}; ++ char slopcmd[100] = "slop -f x%xx%yx%wx%hx "; ++ int firstchar = 0; ++ int counter = 0; ++ ++ strcat(slopcmd, slopstyle); ++ FILE *fp = popen(slopcmd, "r"); ++ ++ while (fgets(str, 100, fp) != NULL) ++ strcat(strout, str); ++ ++ pclose(fp); ++ ++ if (strlen(strout) < 6) ++ return 0; ++ ++ for (i = 0; i < strlen(strout); i++){ ++ if (!firstchar) { ++ if (strout[i] == 'x') ++ firstchar = 1; ++ continue; ++ } ++ ++ if (strout[i] != 'x') ++ tmpstring[strlen(tmpstring)] = strout[i]; ++ else { ++ riodimensions[counter] = atoi(tmpstring); ++ counter++; ++ memset(tmpstring,0,strlen(tmpstring)); ++ } ++ } ++ ++ if (riodimensions[0] <= -40 || riodimensions[1] <= -40 || riodimensions[2] <= 50 || riodimensions[3] <= 50) { ++ riodimensions[3] = -1; ++ return 0; ++ } ++ ++ if (c) { ++ rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++void ++rioposition(Client *c, int x, int y, int w, int h) ++{ ++ Monitor *m; ++ if ((m = recttomon(x, y, w, h)) && m != c->mon) { ++ detach(c); ++ detachstack(c); ++ arrange(c->mon); ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; ++ attach(c); ++ attachstack(c); ++ selmon = m; ++ focus(c); ++ } ++ ++ c->isfloating = 1; ++ if (riodraw_borders) ++ resizeclient(c, x, y, w - (c->bw * 2), h - (c->bw * 2)); ++ else ++ resizeclient(c, x - c->bw, y - c->bw, w, h); ++ drawbar(c->mon); ++ arrange(c->mon); ++ ++ riodimensions[3] = -1; ++ riopid = 0; ++} ++ ++/* drag out an area using slop and resize the selected window to it */ ++void ++rioresize(const Arg *arg) ++{ ++ Client *c = (arg && arg->v ? (Client*)arg->v : selmon->sel); ++ if (c) ++ riodraw(c, slopresizestyle); ++} ++ ++/* spawn a new window and drag out an area using slop to postiion it */ ++void ++riospawn(const Arg *arg) ++{ ++ if (riodraw_spawnasync) { ++ riopid = spawncmd(arg); ++ riodraw(NULL, slopspawnstyle); ++ } else if (riodraw(NULL, slopspawnstyle)) ++ riopid = spawncmd(arg); ++} ++ + void + run(void) + { +@@ -1735,9 +1853,16 @@ sigchld(int unused) + void + spawn(const Arg *arg) + { ++ spawncmd(arg); ++} ++ ++pid_t ++spawncmd(const Arg *arg) ++{ ++ pid_t pid; + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; +- if (fork() == 0) { ++ if ((pid = fork()) == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); +@@ -1746,6 +1871,7 @@ spawn(const Arg *arg) + perror(" failed"); + exit(EXIT_SUCCESS); + } ++ return pid; + } + + int +-- +2.19.1 + diff --git a/dwm/dwm-swallow-riodraw-6.3_full.diff b/dwm/dwm-swallow-riodraw-6.3_full.diff new file mode 100644 index 0000000..ae3816e --- /dev/null +++ b/dwm/dwm-swallow-riodraw-6.3_full.diff @@ -0,0 +1,729 @@ +From 66c7e6fef8a900a03e4ca861fdb11aff1f1af690 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:43:13 +0100 +Subject: [PATCH 1/2] Alternative swallow patch that replaces clients instead + of swapping windows offering better resize hints + +--- + config.def.h | 9 +- + config.mk | 6 +- + dwm.c | 300 +++++++++++++++++++++++++++++++++++++++++++++++---- + 3 files changed, 288 insertions(+), 27 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..dba4b83 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,7 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -26,9 +27,11 @@ 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 isterminal noswallow monitor */ ++ { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1, -1 }, ++ { "St", NULL, NULL, 0, 0, 1, 0, -1 }, ++ { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */ + }; + + /* layout(s) */ +diff --git a/config.mk b/config.mk +index b6eb7e0..ee5c46e 100644 +--- a/config.mk ++++ b/config.mk +@@ -19,10 +19,14 @@ FREETYPELIBS = -lfontconfig -lXft + FREETYPEINC = /usr/include/freetype2 + # OpenBSD (uncomment) + #FREETYPEINC = ${X11INC}/freetype2 ++#KVMLIB = -lkvm ++ ++# This is needed for the swallow patch ++XCBLIBS = -lX11-xcb -lxcb -lxcb-res + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XCBLIBS} ${KVMLIB} + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/dwm.c b/dwm.c +index a96f33c..8735093 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -40,6 +40,12 @@ + #include + #endif /* XINERAMA */ + #include ++#include ++#include ++#ifdef __OpenBSD__ ++#include ++#include ++#endif /* __OpenBSD */ + + #include "drw.h" + #include "util.h" +@@ -92,9 +98,12 @@ struct Client { + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; +- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow; ++ int ignorecfgreqpos, ignorecfgreqsize; ++ pid_t pid; + Client *next; + Client *snext; ++ Client *swallowing; + Monitor *mon; + Window win; + }; +@@ -138,6 +147,8 @@ typedef struct { + const char *title; + unsigned int tags; + int isfloating; ++ int isterminal; ++ int noswallow; + int monitor; + } Rule; + +@@ -189,6 +200,7 @@ static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void replaceclient(Client *old, Client *new); + static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); +@@ -235,6 +247,14 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++static int swallow(Client *p, Client *c); ++static void unswallow(Client *c); ++static pid_t getparentprocess(pid_t p); ++static int isdescprocess(pid_t p, pid_t c); ++static Client *swallowingclient(Window w); ++static Client *termforwin(const Client *c); ++static pid_t winpid(Window w); ++ + /* variables */ + static const char broken[] = "broken"; + static char stext[256]; +@@ -268,6 +288,7 @@ static Display *dpy; + static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; ++static xcb_connection_t *xcon; + + /* configuration, allows nested code to access above variables */ + #include "config.h" +@@ -298,6 +319,8 @@ applyrules(Client *c) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { ++ c->isterminal = r->isterminal; ++ c->noswallow = r->noswallow; + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); +@@ -588,22 +611,29 @@ configurerequest(XEvent *e) + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { ++ if (c->ignorecfgreqpos && c->ignorecfgreqsize) ++ return; ++ + m = c->mon; +- if (ev->value_mask & CWX) { +- c->oldx = c->x; +- c->x = m->mx + ev->x; +- } +- if (ev->value_mask & CWY) { +- c->oldy = c->y; +- c->y = m->my + ev->y; +- } +- if (ev->value_mask & CWWidth) { +- c->oldw = c->w; +- c->w = ev->width; ++ if (!c->ignorecfgreqpos) { ++ if (ev->value_mask & CWX) { ++ c->oldx = c->x; ++ c->x = m->mx + ev->x; ++ } ++ if (ev->value_mask & CWY) { ++ c->oldy = c->y; ++ c->y = m->my + ev->y; ++ } + } +- if (ev->value_mask & CWHeight) { +- c->oldh = c->h; +- c->h = ev->height; ++ if (!c->ignorecfgreqsize) { ++ if (ev->value_mask & CWWidth) { ++ c->oldw = c->w; ++ c->w = ev->width; ++ } ++ if (ev->value_mask & CWHeight) { ++ c->oldh = c->h; ++ c->h = ev->height; ++ } + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ +@@ -653,6 +683,8 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if ((c = swallowingclient(ev->window))) ++ unmanage(c->swallowing, 1); + } + + void +@@ -1021,12 +1053,14 @@ killclient(const Arg *arg) + void + manage(Window w, XWindowAttributes *wa) + { +- Client *c, *t = NULL; ++ Client *c, *t = NULL, *term = NULL; + Window trans = None; + XWindowChanges wc; ++ int focusclient = 1; + + c = ecalloc(1, sizeof(Client)); + c->win = w; ++ c->pid = winpid(w); + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; +@@ -1041,6 +1075,7 @@ manage(Window w, XWindowAttributes *wa) + } else { + c->mon = selmon; + applyrules(c); ++ term = termforwin(c); + } + + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) +@@ -1066,18 +1101,35 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); +- attachstack(c); ++ ++ /* Do not attach client if it is being swallowed */ ++ if (term && swallow(term, c)) { ++ /* Do not let swallowed client steal focus unless the terminal has focus */ ++ focusclient = (term == selmon->sel); ++ } else { ++ attach(c); ++ ++ if (focusclient || !c->mon->sel || !c->mon->stack) ++ attachstack(c); ++ else { ++ c->snext = c->mon->sel->snext; ++ c->mon->sel->snext = c; ++ } ++ } ++ + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); +- if (c->mon == selmon) +- unfocus(selmon->sel, 0); +- c->mon->sel = c; ++ if (focusclient) { ++ if (c->mon == selmon) ++ unfocus(selmon->sel, 0); ++ c->mon->sel = c; ++ } + arrange(c->mon); + XMapWindow(dpy, c->win); +- focus(NULL); ++ if (focusclient) ++ focus(NULL); + } + + void +@@ -1269,6 +1321,46 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++replaceclient(Client *old, Client *new) ++{ ++ Client *c = NULL; ++ Monitor *mon = old->mon; ++ ++ new->mon = mon; ++ new->tags = old->tags; ++ new->isfloating = old->isfloating; ++ ++ new->next = old->next; ++ new->snext = old->snext; ++ ++ if (old == mon->clients) ++ mon->clients = new; ++ else { ++ for (c = mon->clients; c && c->next != old; c = c->next); ++ c->next = new; ++ } ++ ++ if (old == mon->stack) ++ mon->stack = new; ++ else { ++ for (c = mon->stack; c && c->snext != old; c = c->snext); ++ c->snext = new; ++ } ++ ++ old->next = NULL; ++ old->snext = NULL; ++ ++ XMoveWindow(dpy, old->win, WIDTH(old) * -2, old->y); ++ ++ if (ISVISIBLE(new) && !new->isfullscreen) { ++ if (new->isfloating) ++ resize(new, old->x, old->y, new->w, new->h, 0); ++ else ++ resize(new, old->x, old->y, old->w, old->h, 0); ++ } ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1656,6 +1748,31 @@ spawn(const Arg *arg) + } + } + ++int ++swallow(Client *t, Client *c) ++{ ++ if (c->noswallow || c->isterminal) ++ return 0; ++ if (!swallowfloating && c->isfloating) ++ return 0; ++ ++ if (t->isfullscreen) ++ setfullscreen(c, 1); ++ ++ replaceclient(t, c); ++ c->ignorecfgreqpos = 1; ++ c->swallowing = t; ++ ++ return 1; ++} ++ ++void ++unswallow(Client *c) ++{ ++ replaceclient(c, c->swallowing); ++ c->swallowing = NULL; ++} ++ + void + tag(const Arg *arg) + { +@@ -1768,9 +1885,17 @@ unfocus(Client *c, int setfocus) + void + unmanage(Client *c, int destroyed) + { ++ Client *s; + Monitor *m = c->mon; + XWindowChanges wc; + ++ if (c->swallowing) ++ unswallow(c); ++ ++ s = swallowingclient(c->win); ++ if (s) ++ s->swallowing = NULL; ++ + detach(c); + detachstack(c); + if (!destroyed) { +@@ -2050,6 +2175,133 @@ view(const Arg *arg) + arrange(selmon); + } + ++pid_t ++winpid(Window w) ++{ ++ pid_t result = 0; ++ ++#ifdef __linux__ ++ xcb_res_client_id_spec_t spec = {0}; ++ spec.client = w; ++ spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; ++ ++ xcb_generic_error_t *e = NULL; ++ xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); ++ xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); ++ ++ if (!r) ++ return (pid_t)0; ++ ++ xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); ++ for (; i.rem; xcb_res_client_id_value_next(&i)) { ++ spec = i.data->spec; ++ if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { ++ uint32_t *t = xcb_res_client_id_value_value(i.data); ++ result = *t; ++ break; ++ } ++ } ++ ++ free(r); ++ ++ if (result == (pid_t)-1) ++ result = 0; ++#endif /* __linux__ */ ++#ifdef __OpenBSD__ ++ Atom type; ++ int format; ++ unsigned long len, bytes; ++ unsigned char *prop; ++ pid_t ret; ++ ++ if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) ++ return 0; ++ ++ ret = *(pid_t*)prop; ++ XFree(prop); ++ result = ret; ++#endif /* __OpenBSD__ */ ++ ++ return result; ++} ++ ++pid_t ++getparentprocess(pid_t p) ++{ ++ unsigned int v = 0; ++ ++#ifdef __linux__ ++ FILE *f; ++ char buf[256]; ++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); ++ ++ if (!(f = fopen(buf, "r"))) ++ return 0; ++ ++ fscanf(f, "%*u %*s %*c %u", &v); ++ fclose(f); ++#endif /* __linux__*/ ++ ++#ifdef __OpenBSD__ ++ int n; ++ kvm_t *kd; ++ struct kinfo_proc *kp; ++ ++ kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); ++ if (!kd) ++ return 0; ++ ++ kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); ++ v = kp->p_ppid; ++#endif /* __OpenBSD__ */ ++ ++ return (pid_t)v; ++} ++ ++int ++isdescprocess(pid_t p, pid_t c) ++{ ++ while (p != c && c != 0) ++ c = getparentprocess(c); ++ ++ return (int)c; ++} ++ ++Client * ++termforwin(const Client *w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ if (!w->pid || w->isterminal) ++ return NULL; ++ ++ for (m = mons; m; m = m->next) { ++ for (c = m->clients; c; c = c->next) { ++ if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) ++ return c; ++ } ++ } ++ ++ return NULL; ++} ++ ++Client * ++swallowingclient(Window w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ for (m = mons; m; m = m->next) { ++ for (c = m->clients; c; c = c->next) { ++ if (c->swallowing && c->swallowing->win == w) ++ return c; ++ } ++ } ++ ++ return NULL; ++} ++ + Client * + wintoclient(Window w) + { +@@ -2141,10 +2393,12 @@ main(int argc, char *argv[]) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); ++ if (!(xcon = XGetXCBConnection(dpy))) ++ die("dwm: cannot get xcb connection\n"); + checkotherwm(); + setup(); + #ifdef __OpenBSD__ +- if (pledge("stdio rpath proc exec", NULL) == -1) ++ if (pledge("stdio rpath proc exec ps", NULL) == -1) + die("pledge"); + #endif /* __OpenBSD__ */ + scan(); +-- +2.19.1 + + +From d27eb690b062552395732ff71c046f11e91bdaf2 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:44:42 +0100 +Subject: [PATCH 2/2] Adding riodraw on top of swallow + +--- + config.def.h | 8 ++++ + dwm.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 135 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index dba4b83..191190b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -6,6 +6,12 @@ static const unsigned int snap = 32; /* snap pixel */ + static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ ++static const char slopspawnstyle[] = "-t 0 -c 0.92,0.85,0.69,0.3 -o"; /* do NOT define -f (format) here */ ++static const char slopresizestyle[] = "-t 0 -c 0.92,0.85,0.69,0.3"; /* do NOT define -f (format) here */ ++static const int riodraw_borders = 0; /* 0 or 1, indicates whether the area drawn using slop includes the window borders */ ++static const int riodraw_matchpid = 1; /* 0 or 1, indicates whether to match the PID of the client that was spawned with riospawn */ ++static const int riodraw_spawnasync = 0; /* 0 means that the application is only spawned after a successful selection while ++ * 1 means that the application is being initialised in the background while the selection is made */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +@@ -67,6 +73,8 @@ static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { MODKEY|ControlMask, XK_Return, riospawn, {.v = termcmd } }, ++ { MODKEY, XK_s, rioresize, {0} }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index 8735093..8dc28d3 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -205,6 +205,10 @@ static void resize(Client *c, int x, int y, int w, int h, int interact); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); ++static int riodraw(Client *c, const char slopstyle[]); ++static void rioposition(Client *c, int x, int y, int w, int h); ++static void rioresize(const Arg *arg); ++static void riospawn(const Arg *arg); + static void run(void); + static void scan(void); + static int sendevent(Client *c, Atom proto); +@@ -219,6 +223,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 pid_t spawncmd(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +@@ -264,6 +269,8 @@ static int bh, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; ++static int riodimensions[4] = { -1, -1, -1, -1 }; ++static pid_t riopid = 0; + static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, +@@ -1126,6 +1133,18 @@ manage(Window w, XWindowAttributes *wa) + unfocus(selmon->sel, 0); + c->mon->sel = c; + } ++ ++ if (!c->swallowing) { ++ if (riopid && (!riodraw_matchpid || isdescprocess(riopid, c->pid))) { ++ if (riodimensions[3] != -1) ++ rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); ++ else { ++ killclient(&((Arg) { .v = c })); ++ return; ++ } ++ } ++ } ++ + arrange(c->mon); + XMapWindow(dpy, c->win); + if (focusclient) +@@ -1465,6 +1484,105 @@ restack(Monitor *m) + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } + ++int ++riodraw(Client *c, const char slopstyle[]) ++{ ++ int i; ++ char str[100]; ++ char strout[100]; ++ char tmpstring[30] = {0}; ++ char slopcmd[100] = "slop -f x%xx%yx%wx%hx "; ++ int firstchar = 0; ++ int counter = 0; ++ ++ strcat(slopcmd, slopstyle); ++ FILE *fp = popen(slopcmd, "r"); ++ ++ while (fgets(str, 100, fp) != NULL) ++ strcat(strout, str); ++ ++ pclose(fp); ++ ++ if (strlen(strout) < 6) ++ return 0; ++ ++ for (i = 0; i < strlen(strout); i++){ ++ if (!firstchar) { ++ if (strout[i] == 'x') ++ firstchar = 1; ++ continue; ++ } ++ ++ if (strout[i] != 'x') ++ tmpstring[strlen(tmpstring)] = strout[i]; ++ else { ++ riodimensions[counter] = atoi(tmpstring); ++ counter++; ++ memset(tmpstring,0,strlen(tmpstring)); ++ } ++ } ++ ++ if (riodimensions[0] <= -40 || riodimensions[1] <= -40 || riodimensions[2] <= 50 || riodimensions[3] <= 50) { ++ riodimensions[3] = -1; ++ return 0; ++ } ++ ++ if (c) { ++ rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++void ++rioposition(Client *c, int x, int y, int w, int h) ++{ ++ Monitor *m; ++ if ((m = recttomon(x, y, w, h)) && m != c->mon) { ++ detach(c); ++ detachstack(c); ++ arrange(c->mon); ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; ++ attach(c); ++ attachstack(c); ++ selmon = m; ++ focus(c); ++ } ++ ++ c->isfloating = 1; ++ if (riodraw_borders) ++ resizeclient(c, x, y, w - (c->bw * 2), h - (c->bw * 2)); ++ else ++ resizeclient(c, x - c->bw, y - c->bw, w, h); ++ drawbar(c->mon); ++ arrange(c->mon); ++ ++ riodimensions[3] = -1; ++ riopid = 0; ++} ++ ++/* drag out an area using slop and resize the selected window to it */ ++void ++rioresize(const Arg *arg) ++{ ++ Client *c = (arg && arg->v ? (Client*)arg->v : selmon->sel); ++ if (c) ++ riodraw(c, slopresizestyle); ++} ++ ++/* spawn a new window and drag out an area using slop to postiion it */ ++void ++riospawn(const Arg *arg) ++{ ++ if (riodraw_spawnasync) { ++ riopid = spawncmd(arg); ++ riodraw(NULL, slopspawnstyle); ++ } else if (riodraw(NULL, slopspawnstyle)) ++ riopid = spawncmd(arg); ++} ++ + void + run(void) + { +@@ -1735,9 +1853,16 @@ sigchld(int unused) + void + spawn(const Arg *arg) + { ++ spawncmd(arg); ++} ++ ++pid_t ++spawncmd(const Arg *arg) ++{ ++ pid_t pid; + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; +- if (fork() == 0) { ++ if ((pid = fork()) == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); +@@ -1746,6 +1871,7 @@ spawn(const Arg *arg) + perror(" failed"); + exit(EXIT_SUCCESS); + } ++ return pid; + } + + int +-- +2.19.1 + diff --git a/dwm/dwm-switchtag-6.3.diff b/dwm/dwm-switchtag-6.3.diff new file mode 100644 index 0000000..c3e3fc1 --- /dev/null +++ b/dwm/dwm-switchtag-6.3.diff @@ -0,0 +1,133 @@ +From 163533a11c3fed615ca4644ac9e279bc3a2ed5ed Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:45:08 +0100 +Subject: [PATCH] Adding switchtag option for rules allowing auto-moving + dedicated tags for specific applications + +dwm allow you to set application specific rules so that you can have your browser, for example, +start up on tag 9 optionally on a given monitor. +When you open your browser it is then automatically moved to the configured tag, but you have +to manually enable the tag to see the newly opened application. +This patch adds an extra configuration option for individual rules where: + - 0 is default behaviour + - 1 automatically moves you to the tag of the newly opened application and + - 2 enables the tag of the newly opened application in addition to your existing enabled tags + - 3 as 1, but closing that window reverts the view back to what it was previously (*) + - 4 as 2, but closing that window reverts the view back to what it was previously (*) + +(*) except if the client has been moved between tags or to another monitor +--- + config.def.h | 6 +++--- + dwm.c | 30 +++++++++++++++++++++++++++++- + 2 files changed, 32 insertions(+), 4 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..9287921 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 switchtag isfloating monitor */ ++ { "Gimp", NULL, NULL, 0, 1, 1, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 1, 0, -1 }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index a96f33c..216836f 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -92,6 +92,7 @@ struct Client { + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; ++ unsigned int switchtag; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + Client *next; + Client *snext; +@@ -137,6 +138,7 @@ typedef struct { + const char *instance; + const char *title; + unsigned int tags; ++ int switchtag; + int isfloating; + int monitor; + } Rule; +@@ -280,7 +282,7 @@ void + applyrules(Client *c) + { + const char *class, *instance; +- unsigned int i; ++ unsigned int i, newtagset; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; +@@ -303,6 +305,25 @@ applyrules(Client *c) + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; ++ ++ if (r->switchtag) { ++ selmon = c->mon; ++ if (r->switchtag == 2 || r->switchtag == 4) ++ newtagset = c->mon->tagset[c->mon->seltags] ^ c->tags; ++ else ++ newtagset = c->tags; ++ ++ if (newtagset && !(c->tags & c->mon->tagset[c->mon->seltags])) { ++ if (r->switchtag == 3 || r->switchtag == 4) ++ c->switchtag = c->mon->tagset[c->mon->seltags]; ++ if (r->switchtag == 1 || r->switchtag == 3) ++ view(&((Arg) { .ui = newtagset })); ++ else { ++ c->mon->tagset[c->mon->seltags] = newtagset; ++ arrange(c->mon); ++ } ++ } ++ } + } + } + if (ch.res_class) +@@ -1425,6 +1446,8 @@ sendmon(Client *c, Monitor *m) + attachstack(c); + focus(NULL); + arrange(NULL); ++ if (c->switchtag) ++ c->switchtag = 0; + } + + void +@@ -1661,6 +1684,8 @@ tag(const Arg *arg) + { + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; ++ if (selmon->sel->switchtag) ++ selmon->sel->switchtag = 0; + focus(NULL); + arrange(selmon); + } +@@ -1769,6 +1794,7 @@ void + unmanage(Client *c, int destroyed) + { + Monitor *m = c->mon; ++ unsigned int switchtag = c->switchtag; + XWindowChanges wc; + + detach(c); +@@ -1788,6 +1814,8 @@ unmanage(Client *c, int destroyed) + focus(NULL); + updateclientlist(); + arrange(m); ++ if (switchtag) ++ view(&((Arg) { .ui = switchtag })); + } + + void +-- +2.19.1 + diff --git a/dwm/dwm-systray-6.3.diff b/dwm/dwm-systray-6.3.diff new file mode 100644 index 0000000..9b3906c --- /dev/null +++ b/dwm/dwm-systray-6.3.diff @@ -0,0 +1,745 @@ +From 05fab06d1db4eead3ab0ea17595b3ea990589a5a Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:48:07 +0100 +Subject: [PATCH] Adding systray patch + +Refer to https://dwm.suckless.org/patches/systray/ +--- + config.def.h | 4 + + dwm.c | 407 +++++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 383 insertions(+), 28 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..22e699d 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,10 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, 0: display systray on the last monitor*/ ++static const int showsystray = 1; /* 0 means no systray */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +diff --git a/dwm.c b/dwm.c +index a96f33c..62ff5c4 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -57,12 +57,30 @@ + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++#define SYSTEM_TRAY_REQUEST_DOCK 0 ++#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 ++ ++/* XEMBED messages */ ++#define XEMBED_EMBEDDED_NOTIFY 0 ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_FOCUS_IN 4 ++#define XEMBED_MODALITY_ON 10 ++ ++#define XEMBED_MAPPED (1 << 0) ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_WINDOW_DEACTIVATE 2 ++ ++#define VERSION_MAJOR 0 ++#define VERSION_MINOR 0 ++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR ++ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +-enum { NetSupported, NetWMName, NetWMState, NetWMCheck, +- NetWMFullscreen, NetActiveWindow, NetWMWindowType, +- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++enum { NetSupported, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetWMCheck, ++ NetSystemTrayOrientationHorz, NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow, ++ NetWMWindowType, NetWMWindowTypeDialog, NetClientList, NetWMWindowTypeDock, NetLast }; /* EWMH atoms */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +@@ -141,6 +159,12 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++}; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -172,6 +196,7 @@ static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); ++static unsigned int getsystraywidth(); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); +@@ -189,13 +214,16 @@ static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void removesystrayicon(Client *i); + static void resize(Client *c, int x, int y, int w, int h, int interact); ++static void resizebarwin(Monitor *m); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); ++static void resizerequest(XEvent *e); + static void restack(Monitor *m); + static void run(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); +@@ -207,6 +235,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 Monitor *systraytomon(Monitor *m); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +@@ -224,18 +253,24 @@ static int updategeom(void); + static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); ++static void updatesystray(void); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); + static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); ++static Client *wintosystrayicon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + + /* variables */ ++static Systray *systray = NULL; ++static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; + static const char broken[] = "broken"; + static char stext[256]; + static int screen; +@@ -258,9 +293,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -440,7 +476,7 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth()) + click = ClkStatusText; + else + click = ClkWinTitle; +@@ -483,6 +519,13 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ if (showsystray) { ++ while (systray->icons) ++ removesystrayicon(systray->icons); ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ free(systray); ++ } + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -513,9 +556,50 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ c->win = cme->data.l[2]; ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ XGetWindowAttributes(dpy, c->win, &wa); ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = 0; //scheme[SchemeNorm].bg->pix; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ /* FIXME not sure if I have to send these events, too */ ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ resizebarwin(selmon); ++ updatesystray(); ++ setclientstate(c, NormalState); ++ } ++ return; ++ } ++ + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -568,7 +652,7 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ resizebarwin(m); + } + focus(NULL); + arrange(NULL); +@@ -653,6 +737,11 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if ((c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + } + + void +@@ -696,7 +785,7 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int x, w, tw = 0, stw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; +@@ -705,13 +794,17 @@ drawbar(Monitor *m) + if (!m->showbar) + return; + ++ if(showsystray && m == systraytomon(m)) ++ stw = getsystraywidth(); ++ + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ drw_text(drw, m->ww - tw - stw, 0, tw, bh, 0, stext, 0); + } + ++ resizebarwin(m); + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) +@@ -732,7 +825,7 @@ drawbar(Monitor *m) + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + +- if ((w = m->ww - tw - x) > bh) { ++ if ((w = m->ww - tw - stw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +@@ -743,7 +836,7 @@ drawbar(Monitor *m) + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); + } + + void +@@ -780,8 +873,11 @@ 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 (m == selmon) ++ updatesystray(); ++ } + } + + void +@@ -866,10 +962,17 @@ getatomprop(Client *c, Atom prop) + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; ++ /* FIXME getatomprop should return the number of items and a pointer to ++ * the stored data instead of this workaround */ ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; +@@ -903,6 +1006,16 @@ getstate(Window w) + return result; + } + ++unsigned int ++getsystraywidth() ++{ ++ unsigned int w = 0; ++ Client *i; ++ if(showsystray) ++ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; ++ return w ? w + systrayspacing : 1; ++} ++ + int + gettextprop(Window w, Atom atom, char *text, unsigned int size) + { +@@ -1007,7 +1120,7 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1095,6 +1208,12 @@ maprequest(XEvent *e) + { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; ++ Client *i; ++ if ((i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; +@@ -1219,6 +1338,16 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + ++ if ((c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } ++ else ++ updatesystrayiconstate(c, ev); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) +@@ -1269,6 +1398,19 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removesystrayicon(Client *i) ++{ ++ Client **ii; ++ ++ if (!showsystray || !i) ++ return; ++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); ++ if (ii) ++ *ii = i->next; ++ free(i); ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1276,6 +1418,14 @@ resize(Client *c, int x, int y, int w, int h, int interact) + resizeclient(c, x, y, w, h); + } + ++void ++resizebarwin(Monitor *m) { ++ unsigned int w = m->ww; ++ if (showsystray && m == systraytomon(m)) ++ w -= getsystraywidth(); ++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); ++} ++ + void + resizeclient(Client *c, int x, int y, int w, int h) + { +@@ -1348,6 +1498,19 @@ resizemouse(const Arg *arg) + } + } + ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++} ++ + void + restack(Monitor *m) + { +@@ -1437,26 +1600,36 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; +- Atom *protocols; ++ Atom *protocols, mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ } ++ else { ++ exists = True; ++ mt = proto; + } + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1470,7 +1643,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1559,13 +1732,21 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); ++ netatom[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); +@@ -1574,6 +1755,8 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); ++ /* init system tray */ ++ updatesystray(); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1707,7 +1890,18 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ resizebarwin(selmon); ++ if (showsystray) { ++ XWindowChanges wc; ++ if (!selmon->showbar) ++ wc.y = -bh; ++ else if (selmon->showbar) { ++ wc.y = 0; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh; ++ } ++ XConfigureWindow(dpy, systray->win, CWY, &wc); ++ } + arrange(selmon); + } + +@@ -1802,11 +1996,18 @@ unmapnotify(XEvent *e) + else + unmanage(c, 0); + } ++ else if ((c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ updatesystray(); ++ } + } + + void + updatebars(void) + { ++ unsigned int w; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, +@@ -1817,10 +2018,15 @@ updatebars(void) + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), ++ w = m->ww; ++ if (showsystray && m == systraytomon(m)) ++ w -= getsystraywidth(); ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); ++ if (showsystray && m == systraytomon(m)) ++ XMapRaised(dpy, systray->win); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +@@ -1996,6 +2202,125 @@ updatestatus(void) + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); ++ updatesystray(); ++} ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((float)w / (float)h)); ++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); ++ /* force icons into the systray dimenons if they don't want to */ ++ if (i->h > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ if (i->w > 2*bh) ++ i->w = bh; ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !i || ev->atom != xatom[XembedInfo] || ++ !(flags = getatomprop(i, xatom[XembedInfo]))) ++ return; ++ ++ if (flags & XEMBED_MAPPED && !i->tags) { ++ i->tags = 1; ++ code = XEMBED_WINDOW_ACTIVATE; ++ XMapRaised(dpy, i->win); ++ setclientstate(i, NormalState); ++ } ++ else if (!(flags & XEMBED_MAPPED) && i->tags) { ++ i->tags = 0; ++ code = XEMBED_WINDOW_DEACTIVATE; ++ XUnmapWindow(dpy, i->win); ++ setclientstate(i, WithdrawnState); ++ } ++ else ++ return; ++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, ++ systray->win, XEMBED_EMBEDDED_VERSION); ++} ++ ++void ++updatesystray(void) ++{ ++ XSetWindowAttributes wa; ++ XWindowChanges wc; ++ Client *i; ++ Monitor *m = systraytomon(NULL); ++ unsigned int x = m->mx + m->mw; ++ unsigned int w = 1; ++ ++ if (!showsystray) ++ return; ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); ++ wa.event_mask = ButtonPressMask | ExposureMask; ++ wa.override_redirect = True; ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&systrayorientation, 1); ++ XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1); ++ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); ++ XMapRaised(dpy, systray->win); ++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); ++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { ++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); ++ XSync(dpy, False); ++ } ++ else { ++ fprintf(stderr, "dwm: unable to obtain system tray.\n"); ++ free(systray); ++ systray = NULL; ++ return; ++ } ++ } ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ /* make sure the background color stays the same */ ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ w += systrayspacing; ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->mon != m) ++ i->mon = m; ++ } ++ w = w ? w + systrayspacing : 1; ++ x -= w; ++ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); ++ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; ++ wc.stack_mode = Above; wc.sibling = m->barwin; ++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); ++ XMapWindow(dpy, systray->win); ++ XMapSubwindows(dpy, systray->win); ++ /* redraw background */ ++ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); ++ XSync(dpy, False); + } + + void +@@ -2063,6 +2388,16 @@ wintoclient(Window w) + return NULL; + } + ++Client * ++wintosystrayicon(Window w) { ++ Client *i = NULL; ++ ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next) ; ++ return i; ++} ++ + Monitor * + wintomon(Window w) + { +@@ -2116,6 +2451,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++Monitor * ++systraytomon(Monitor *m) { ++ Monitor *t; ++ int i, n; ++ if(!systraypinning) { ++ if(!m) ++ return selmon; ++ return m == selmon ? m : NULL; ++ } ++ for(n = 1, t = mons; t && t->next; n++, t = t->next) ; ++ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; ++ if(systraypinningfailfirst && n < systraypinning) ++ return mons; ++ return t; ++} ++ + void + zoom(const Arg *arg) + { +-- +2.19.1 + diff --git a/dwm/dwm-tagallmon-6.3.diff b/dwm/dwm-tagallmon-6.3.diff new file mode 100644 index 0000000..a0e627b --- /dev/null +++ b/dwm/dwm-tagallmon-6.3.diff @@ -0,0 +1,87 @@ +From c31415e692201cc85976a4147ad7d8d8d1bda8b1 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:48:33 +0100 +Subject: [PATCH] tagallmon, move all visible windows to an adjacent monitor + +--- + config.def.h | 2 ++ + dwm.c | 43 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 45 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..0061425 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -85,6 +85,8 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_comma, tagallmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_period, tagallmon, {.i = -1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/dwm.c b/dwm.c +index a96f33c..75dae54 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -209,6 +209,7 @@ 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 tagallmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); +@@ -1674,6 +1675,48 @@ tagmon(const Arg *arg) + sendmon(selmon->sel, dirtomon(arg->i)); + } + ++void ++tagallmon(const Arg *arg) ++{ ++ Monitor *m; ++ Client *c, *last, *slast, *next; ++ ++ if (!mons->next) ++ return; ++ ++ m = dirtomon(arg->i); ++ for (last = m->clients; last && last->next; last = last->next); ++ for (slast = m->stack; slast && slast->snext; slast = slast->snext); ++ ++ for (c = selmon->clients; c; c = next) { ++ next = c->next; ++ if (!ISVISIBLE(c)) ++ continue; ++ unfocus(c, 1); ++ detach(c); ++ detachstack(c); ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ ++ c->next = NULL; ++ c->snext = NULL; ++ if (last) ++ last = last->next = c; ++ else ++ m->clients = last = c; ++ if (slast) ++ slast = slast->snext = c; ++ else ++ m->stack = slast = c; ++ if (c->isfullscreen) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ ++ focus(NULL); ++ arrange(NULL); ++} ++ + void + tile(Monitor *m) + { +-- +2.19.1 + diff --git a/dwm/dwm-tagicons-6.3.diff b/dwm/dwm-tagicons-6.3.diff new file mode 100644 index 0000000..e075842 --- /dev/null +++ b/dwm/dwm-tagicons-6.3.diff @@ -0,0 +1,274 @@ +From 9aa9efbbcfcae50ce7d61f46c72f5e582afe3ad2 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:49:42 +0100 +Subject: [PATCH] tagicons patch + +Refer to: +https://github.com/bakkeby/patches/wiki/tagicons +--- + config.def.h | 13 +++++-- + dwm.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++------ + 2 files changed, 99 insertions(+), 12 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..7e63669 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -18,8 +18,13 @@ static const char *colors[][3] = { + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; + +-/* tagging */ +-static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; ++/* tagging: refer to https://github.com/bakkeby/patches/wiki/tagicons */ ++static const char *tags[NUMTAGS] = { NULL }; /* left for compatibility reasons, i.e. code that checks LENGTH(tags) */ ++static char *tagicons[][NUMTAGS] = { ++ [IconsDefault] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, ++ [IconsVacant] = { NULL }, ++ [IconsOccupied] = { NULL }, ++}; + + static const Rule rules[] = { + /* xprop(1): +@@ -85,6 +90,8 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++// { MODKEY|ShiftMask, XK_a, seticonset, {.i = 0 } }, ++// { MODKEY|ShiftMask, XK_b, seticonset, {.i = 1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -112,5 +119,7 @@ static Button buttons[] = { + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, ++ { ClkTagBar, 0, Button4, cycleiconset, {.i = +1 } }, ++ { ClkTagBar, 0, Button5, cycleiconset, {.i = -1 } }, + }; + +diff --git a/dwm.c b/dwm.c +index a96f33c..63b800f 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define NUMTAGS 9 /* the number of tags per monitor */ + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -54,7 +55,7 @@ + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +-#define TAGMASK ((1 << LENGTH(tags)) - 1) ++#define TAGMASK ((1 << NUMTAGS) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +@@ -66,6 +67,12 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ IconsDefault, ++ IconsVacant, ++ IconsOccupied, ++ IconsLast ++}; /* icon sets */ + + typedef union { + int i; +@@ -124,6 +131,7 @@ struct Monitor { + unsigned int tagset[2]; + int showbar; + int topbar; ++ int iconset; + Client *clients; + Client *sel; + Client *stack; +@@ -157,6 +165,7 @@ static void configure(Client *c); + static void configurenotify(XEvent *e); + static void configurerequest(XEvent *e); + static Monitor *createmon(void); ++static void cycleiconset(const Arg *arg); + static void destroynotify(XEvent *e); + static void detach(Client *c); + static void detachstack(Client *c); +@@ -170,6 +179,7 @@ static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); ++static char * geticon(Monitor *m, int tag, int iconset); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +@@ -200,6 +210,7 @@ static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); ++static void seticonset(const Arg *arg); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); +@@ -208,6 +219,7 @@ static void showhide(Client *c); + static void sigchld(int unused); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); ++static char * tagicon(Monitor *m, int tag); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); +@@ -273,7 +285,7 @@ static Window root, wmcheckwin; + #include "config.h" + + /* compile-time check if all tags fit into an unsigned int bit array. */ +-struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; ++struct NumTags { char limitexceeded[NUMTAGS > 31 ? -1 : 1]; }; + + /* function implementations */ + void +@@ -417,7 +429,7 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ unsigned int i, x, tw, click; + Arg arg = {0}; + Client *c; + Monitor *m; +@@ -432,10 +444,13 @@ 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)) { ++ do { ++ tw = TEXTW(tagicon(selmon, i)); ++ if (tw <= lrpad) ++ continue; ++ x += tw; ++ } while (ev->x >= x && ++i < NUMTAGS); ++ if (i < NUMTAGS) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + blw) +@@ -645,6 +660,24 @@ createmon(void) + return m; + } + ++void ++cycleiconset(const Arg *arg) ++{ ++ Monitor *m = selmon; ++ if (arg->i == 0) ++ return; ++ if (arg->i > 0) { ++ for (++m->iconset; m->iconset < IconsLast && tagicons[m->iconset][0] == NULL; ++m->iconset); ++ if (m->iconset >= IconsLast) ++ m->iconset = 0; ++ } else if (arg->i < 0) { ++ for (--m->iconset; m->iconset > 0 && tagicons[m->iconset][0] == NULL; --m->iconset); ++ if (m->iconset < 0) ++ for (m->iconset = IconsLast - 1; m->iconset > 0 && tagicons[m->iconset][0] == NULL; --m->iconset); ++ } ++ drawbar(m); ++} ++ + void + destroynotify(XEvent *e) + { +@@ -700,6 +733,7 @@ drawbar(Monitor *m) + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; ++ char *icon; + Client *c; + + if (!m->showbar) +@@ -718,10 +752,13 @@ drawbar(Monitor *m) + urg |= c->tags; + } + x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); ++ for (i = 0; i < NUMTAGS; i++) { ++ icon = tagicon(m, i); ++ w = TEXTW(icon); ++ if (w <= lrpad) ++ continue; + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, icon, urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +@@ -875,6 +912,20 @@ getatomprop(Client *c, Atom prop) + return atom; + } + ++char * ++geticon(Monitor *m, int tag, int iconset) ++{ ++ int i; ++ int tagindex = tag + NUMTAGS * m->num; ++ for (i = 0; i < LENGTH(tagicons[iconset]) && tagicons[iconset][i] != NULL; ++i); ++ if (i == 0) ++ tagindex = 0; ++ else if (tagindex >= i) ++ tagindex = tagindex % i; ++ ++ return tagicons[iconset][tagindex]; ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -1501,6 +1552,15 @@ setfullscreen(Client *c, int fullscreen) + } + } + ++void ++seticonset(const Arg *arg) ++{ ++ if (arg->i >= 0 && arg->i < IconsLast) { ++ selmon->iconset = arg->i; ++ drawbar(selmon); ++ } ++} ++ + void + setlayout(const Arg *arg) + { +@@ -1666,6 +1726,24 @@ tag(const Arg *arg) + } + } + ++char * ++tagicon(Monitor *m, int tag) ++{ ++ Client *c; ++ char *icon; ++ for (c = m->clients; c && (!(c->tags & 1 << tag)); c = c->next); ++ // for (c = m->clients; c && (!(c->tags & 1 << tag) || HIDDEN(c)); c = c->next); // awesomebar / wintitleactions compatibility ++ if (c && tagicons[IconsOccupied][0] != NULL) ++ icon = geticon(m, tag, IconsOccupied); ++ else { ++ icon = geticon(m, tag, m->iconset); ++ if (TEXTW(icon) <= lrpad && m->tagset[m->seltags] & 1 << tag) ++ icon = geticon(m, tag, IconsVacant); ++ } ++ ++ return icon; ++} ++ + void + tagmon(const Arg *arg) + { +-- +2.19.1 + diff --git a/dwm/dwm-tagicons-selected-6.3.diff b/dwm/dwm-tagicons-selected-6.3.diff new file mode 100644 index 0000000..695e1bc --- /dev/null +++ b/dwm/dwm-tagicons-selected-6.3.diff @@ -0,0 +1,55 @@ +From 133fb2668556041439a03c61f29cd23d56ed928d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:50:15 +0100 +Subject: [PATCH 2/2] tagicons with separate icon for selected tags + +Example patch ref. +https://www.reddit.com/r/suckless/comments/ln0t41/use_icon_instead_of_rectangle_as/ +--- + config.def.h | 5 +++-- + dwm.c | 5 ++++- + 2 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 7e63669..84d80c4 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -21,9 +21,10 @@ static const char *colors[][3] = { + /* tagging: refer to https://github.com/bakkeby/patches/wiki/tagicons */ + static const char *tags[NUMTAGS] = { NULL }; /* left for compatibility reasons, i.e. code that checks LENGTH(tags) */ + static char *tagicons[][NUMTAGS] = { +- [IconsDefault] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, ++ [IconsDefault] = { "○" }, + [IconsVacant] = { NULL }, +- [IconsOccupied] = { NULL }, ++ [IconsOccupied] = { "☉" }, ++ [IconsSelected] = { "◉" }, + }; + + static const Rule rules[] = { +diff --git a/dwm.c b/dwm.c +index 63b800f..f6fb72a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -71,6 +71,7 @@ enum { + IconsDefault, + IconsVacant, + IconsOccupied, ++ IconsSelected, + IconsLast + }; /* icon sets */ + +@@ -1733,7 +1734,9 @@ tagicon(Monitor *m, int tag) + char *icon; + for (c = m->clients; c && (!(c->tags & 1 << tag)); c = c->next); + // for (c = m->clients; c && (!(c->tags & 1 << tag) || HIDDEN(c)); c = c->next); // awesomebar / wintitleactions compatibility +- if (c && tagicons[IconsOccupied][0] != NULL) ++ if (m->tagset[m->seltags] & 1 << tag && tagicons[IconsSelected][0] != NULL) ++ icon = geticon(m, tag, IconsSelected); ++ else if (c && tagicons[IconsOccupied][0] != NULL) + icon = geticon(m, tag, IconsOccupied); + else { + icon = geticon(m, tag, m->iconset); +-- +2.19.1 + diff --git a/dwm/dwm-tagicons-selected-6.3_full.diff b/dwm/dwm-tagicons-selected-6.3_full.diff new file mode 100644 index 0000000..d2db3d3 --- /dev/null +++ b/dwm/dwm-tagicons-selected-6.3_full.diff @@ -0,0 +1,330 @@ +From 9aa9efbbcfcae50ce7d61f46c72f5e582afe3ad2 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:49:42 +0100 +Subject: [PATCH 1/2] tagicons patch + +Refer to: +https://github.com/bakkeby/patches/wiki/tagicons +--- + config.def.h | 13 +++++-- + dwm.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++------ + 2 files changed, 99 insertions(+), 12 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..7e63669 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -18,8 +18,13 @@ static const char *colors[][3] = { + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; + +-/* tagging */ +-static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; ++/* tagging: refer to https://github.com/bakkeby/patches/wiki/tagicons */ ++static const char *tags[NUMTAGS] = { NULL }; /* left for compatibility reasons, i.e. code that checks LENGTH(tags) */ ++static char *tagicons[][NUMTAGS] = { ++ [IconsDefault] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, ++ [IconsVacant] = { NULL }, ++ [IconsOccupied] = { NULL }, ++}; + + static const Rule rules[] = { + /* xprop(1): +@@ -85,6 +90,8 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++// { MODKEY|ShiftMask, XK_a, seticonset, {.i = 0 } }, ++// { MODKEY|ShiftMask, XK_b, seticonset, {.i = 1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -112,5 +119,7 @@ static Button buttons[] = { + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, ++ { ClkTagBar, 0, Button4, cycleiconset, {.i = +1 } }, ++ { ClkTagBar, 0, Button5, cycleiconset, {.i = -1 } }, + }; + +diff --git a/dwm.c b/dwm.c +index a96f33c..63b800f 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -45,6 +45,7 @@ + #include "util.h" + + /* macros */ ++#define NUMTAGS 9 /* the number of tags per monitor */ + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #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)) \ +@@ -54,7 +55,7 @@ + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +-#define TAGMASK ((1 << LENGTH(tags)) - 1) ++#define TAGMASK ((1 << NUMTAGS) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +@@ -66,6 +67,12 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++enum { ++ IconsDefault, ++ IconsVacant, ++ IconsOccupied, ++ IconsLast ++}; /* icon sets */ + + typedef union { + int i; +@@ -124,6 +131,7 @@ struct Monitor { + unsigned int tagset[2]; + int showbar; + int topbar; ++ int iconset; + Client *clients; + Client *sel; + Client *stack; +@@ -157,6 +165,7 @@ static void configure(Client *c); + static void configurenotify(XEvent *e); + static void configurerequest(XEvent *e); + static Monitor *createmon(void); ++static void cycleiconset(const Arg *arg); + static void destroynotify(XEvent *e); + static void detach(Client *c); + static void detachstack(Client *c); +@@ -170,6 +179,7 @@ static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); ++static char * geticon(Monitor *m, int tag, int iconset); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +@@ -200,6 +210,7 @@ static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); ++static void seticonset(const Arg *arg); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); +@@ -208,6 +219,7 @@ static void showhide(Client *c); + static void sigchld(int unused); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); ++static char * tagicon(Monitor *m, int tag); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); +@@ -273,7 +285,7 @@ static Window root, wmcheckwin; + #include "config.h" + + /* compile-time check if all tags fit into an unsigned int bit array. */ +-struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; ++struct NumTags { char limitexceeded[NUMTAGS > 31 ? -1 : 1]; }; + + /* function implementations */ + void +@@ -417,7 +429,7 @@ attachstack(Client *c) + void + buttonpress(XEvent *e) + { +- unsigned int i, x, click; ++ unsigned int i, x, tw, click; + Arg arg = {0}; + Client *c; + Monitor *m; +@@ -432,10 +444,13 @@ 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)) { ++ do { ++ tw = TEXTW(tagicon(selmon, i)); ++ if (tw <= lrpad) ++ continue; ++ x += tw; ++ } while (ev->x >= x && ++i < NUMTAGS); ++ if (i < NUMTAGS) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + blw) +@@ -645,6 +660,24 @@ createmon(void) + return m; + } + ++void ++cycleiconset(const Arg *arg) ++{ ++ Monitor *m = selmon; ++ if (arg->i == 0) ++ return; ++ if (arg->i > 0) { ++ for (++m->iconset; m->iconset < IconsLast && tagicons[m->iconset][0] == NULL; ++m->iconset); ++ if (m->iconset >= IconsLast) ++ m->iconset = 0; ++ } else if (arg->i < 0) { ++ for (--m->iconset; m->iconset > 0 && tagicons[m->iconset][0] == NULL; --m->iconset); ++ if (m->iconset < 0) ++ for (m->iconset = IconsLast - 1; m->iconset > 0 && tagicons[m->iconset][0] == NULL; --m->iconset); ++ } ++ drawbar(m); ++} ++ + void + destroynotify(XEvent *e) + { +@@ -700,6 +733,7 @@ drawbar(Monitor *m) + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; ++ char *icon; + Client *c; + + if (!m->showbar) +@@ -718,10 +752,13 @@ drawbar(Monitor *m) + urg |= c->tags; + } + x = 0; +- for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); ++ for (i = 0; i < NUMTAGS; i++) { ++ icon = tagicon(m, i); ++ w = TEXTW(icon); ++ if (w <= lrpad) ++ continue; + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, icon, urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +@@ -875,6 +912,20 @@ getatomprop(Client *c, Atom prop) + return atom; + } + ++char * ++geticon(Monitor *m, int tag, int iconset) ++{ ++ int i; ++ int tagindex = tag + NUMTAGS * m->num; ++ for (i = 0; i < LENGTH(tagicons[iconset]) && tagicons[iconset][i] != NULL; ++i); ++ if (i == 0) ++ tagindex = 0; ++ else if (tagindex >= i) ++ tagindex = tagindex % i; ++ ++ return tagicons[iconset][tagindex]; ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -1501,6 +1552,15 @@ setfullscreen(Client *c, int fullscreen) + } + } + ++void ++seticonset(const Arg *arg) ++{ ++ if (arg->i >= 0 && arg->i < IconsLast) { ++ selmon->iconset = arg->i; ++ drawbar(selmon); ++ } ++} ++ + void + setlayout(const Arg *arg) + { +@@ -1666,6 +1726,24 @@ tag(const Arg *arg) + } + } + ++char * ++tagicon(Monitor *m, int tag) ++{ ++ Client *c; ++ char *icon; ++ for (c = m->clients; c && (!(c->tags & 1 << tag)); c = c->next); ++ // for (c = m->clients; c && (!(c->tags & 1 << tag) || HIDDEN(c)); c = c->next); // awesomebar / wintitleactions compatibility ++ if (c && tagicons[IconsOccupied][0] != NULL) ++ icon = geticon(m, tag, IconsOccupied); ++ else { ++ icon = geticon(m, tag, m->iconset); ++ if (TEXTW(icon) <= lrpad && m->tagset[m->seltags] & 1 << tag) ++ icon = geticon(m, tag, IconsVacant); ++ } ++ ++ return icon; ++} ++ + void + tagmon(const Arg *arg) + { +-- +2.19.1 + + +From 133fb2668556041439a03c61f29cd23d56ed928d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:50:15 +0100 +Subject: [PATCH 2/2] tagicons with separate icon for selected tags + +Example patch ref. +https://www.reddit.com/r/suckless/comments/ln0t41/use_icon_instead_of_rectangle_as/ +--- + config.def.h | 5 +++-- + dwm.c | 5 ++++- + 2 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 7e63669..84d80c4 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -21,9 +21,10 @@ static const char *colors[][3] = { + /* tagging: refer to https://github.com/bakkeby/patches/wiki/tagicons */ + static const char *tags[NUMTAGS] = { NULL }; /* left for compatibility reasons, i.e. code that checks LENGTH(tags) */ + static char *tagicons[][NUMTAGS] = { +- [IconsDefault] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, ++ [IconsDefault] = { "○" }, + [IconsVacant] = { NULL }, +- [IconsOccupied] = { NULL }, ++ [IconsOccupied] = { "☉" }, ++ [IconsSelected] = { "◉" }, + }; + + static const Rule rules[] = { +diff --git a/dwm.c b/dwm.c +index 63b800f..f6fb72a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -71,6 +71,7 @@ enum { + IconsDefault, + IconsVacant, + IconsOccupied, ++ IconsSelected, + IconsLast + }; /* icon sets */ + +@@ -1733,7 +1734,9 @@ tagicon(Monitor *m, int tag) + char *icon; + for (c = m->clients; c && (!(c->tags & 1 << tag)); c = c->next); + // for (c = m->clients; c && (!(c->tags & 1 << tag) || HIDDEN(c)); c = c->next); // awesomebar / wintitleactions compatibility +- if (c && tagicons[IconsOccupied][0] != NULL) ++ if (m->tagset[m->seltags] & 1 << tag && tagicons[IconsSelected][0] != NULL) ++ icon = geticon(m, tag, IconsSelected); ++ else if (c && tagicons[IconsOccupied][0] != NULL) + icon = geticon(m, tag, IconsOccupied); + else { + icon = geticon(m, tag, m->iconset); +-- +2.19.1 + diff --git a/dwm/dwm-tagmonfixfs-6.3.diff b/dwm/dwm-tagmonfixfs-6.3.diff new file mode 100644 index 0000000..7a9ab45 --- /dev/null +++ b/dwm/dwm-tagmonfixfs-6.3.diff @@ -0,0 +1,37 @@ +From 7467e0d7dfc55e63dd3415ffd798297a91eb8e06 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:51:13 +0100 +Subject: [PATCH] tagmonfixfs: Allow moving a fullscreen window to another + monitor + +--- + dwm.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..d340599 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1669,9 +1669,17 @@ tag(const Arg *arg) + void + tagmon(const Arg *arg) + { +- if (!selmon->sel || !mons->next) ++ Client *c = selmon->sel; ++ if (!c || !mons->next) + return; +- sendmon(selmon->sel, dirtomon(arg->i)); ++ if (c->isfullscreen) { ++ c->isfullscreen = 0; ++ sendmon(c, dirtomon(arg->i)); ++ c->isfullscreen = 1; ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } else ++ sendmon(c, dirtomon(arg->i)); + } + + void +-- +2.19.1 + diff --git a/dwm/dwm-tagswapmon-6.3.diff b/dwm/dwm-tagswapmon-6.3.diff new file mode 100644 index 0000000..18fac2d --- /dev/null +++ b/dwm/dwm-tagswapmon-6.3.diff @@ -0,0 +1,107 @@ +From 0b662a1515f6d270c6fc6ac5102411a54a663f2d Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:52:25 +0100 +Subject: [PATCH] tagswapmon, swap all visible windows with those of one of the + adjacent monitors + +--- + config.def.h | 2 ++ + dwm.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 64 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..5e48109 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -85,6 +85,8 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ControlMask, XK_comma, tagswapmon, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ControlMask, XK_period, tagswapmon, {.i = -1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/dwm.c b/dwm.c +index a96f33c..5b9c480 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -210,6 +210,7 @@ static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); ++static void tagswapmon(const Arg *arg); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -1674,6 +1675,67 @@ tagmon(const Arg *arg) + sendmon(selmon->sel, dirtomon(arg->i)); + } + ++void ++tagswapmon(const Arg *arg) ++{ ++ Monitor *m; ++ Client *c, *sc = NULL, *mc = NULL, *next; ++ ++ if (!mons->next) ++ return; ++ ++ m = dirtomon(arg->i); ++ ++ for (c = selmon->clients; c; c = next) { ++ next = c->next; ++ if (!ISVISIBLE(c)) ++ continue; ++ unfocus(c, 1); ++ detach(c); ++ detachstack(c); ++ c->next = sc; ++ sc = c; ++ } ++ ++ for (c = m->clients; c; c = next) { ++ next = c->next; ++ if (!ISVISIBLE(c)) ++ continue; ++ unfocus(c, 1); ++ detach(c); ++ detachstack(c); ++ c->next = mc; ++ mc = c; ++ } ++ ++ for (c = sc; c; c = next) { ++ next = c->next; ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ ++ attach(c); ++ attachstack(c); ++ if (c->isfullscreen) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ ++ for (c = mc; c; c = next) { ++ next = c->next; ++ c->mon = selmon; ++ c->tags = selmon->tagset[selmon->seltags]; /* assign tags of target monitor */ ++ attach(c); ++ attachstack(c); ++ if (c->isfullscreen) { ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } ++ } ++ ++ focus(NULL); ++ arrange(NULL); ++} ++ + void + tile(Monitor *m) + { +-- +2.19.1 + diff --git a/dwm/dwm-togglefullscreen-6.3.diff b/dwm/dwm-togglefullscreen-6.3.diff new file mode 100644 index 0000000..fd0de6e --- /dev/null +++ b/dwm/dwm-togglefullscreen-6.3.diff @@ -0,0 +1,53 @@ +From 2a49bcf36a7bb8350dbd2650e6ba7d10e60a18c4 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:52:51 +0100 +Subject: [PATCH] togglefullscreen, allows you to toggle fullscreen on and off + using the same shortcut key + +--- + config.def.h | 1 + + dwm.c | 9 +++++++++ + 2 files changed, 10 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a2ac963..bebb0fb 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -79,6 +79,7 @@ static Key keys[] = { + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_y, togglefullscreen, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index a96f33c..897797f 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -212,6 +212,7 @@ static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -1725,6 +1726,14 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglefullscreen(const Arg *arg) { ++ if (!selmon->sel) ++ return; ++ ++ setfullscreen(selmon->sel, !selmon->sel->isfullscreen); ++} ++ + void + toggletag(const Arg *arg) + { +-- +2.19.1 + diff --git a/dwm/dwm-togglelayout-6.3.diff b/dwm/dwm-togglelayout-6.3.diff new file mode 100644 index 0000000..6febf15 --- /dev/null +++ b/dwm/dwm-togglelayout-6.3.diff @@ -0,0 +1,29 @@ +From d1dd47c496e469f4c826ee884793d153f704a7ed Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:53:40 +0100 +Subject: [PATCH] togglelayout - keyboard shortcuts to set a given layout will + toggle to the previous layout if the given layout is already active + +--- + dwm.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..9006fef 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1504,9 +1504,8 @@ 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) ++ selmon->sellt ^= 1; ++ if (arg && arg->v && arg->v != selmon->lt[selmon->sellt ^ 1]) + selmon->lt[selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) +-- +2.19.1 + diff --git a/dwm/dwm-toggletag-6.3.diff b/dwm/dwm-toggletag-6.3.diff new file mode 100644 index 0000000..39622e3 --- /dev/null +++ b/dwm/dwm-toggletag-6.3.diff @@ -0,0 +1,29 @@ +From d3db7d14cc9d1bb1af02dc299327df0a67eb4a65 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:54:13 +0100 +Subject: [PATCH] toggletag - keyboard shortcuts to view a given tag will + toggle to the previous tag if the given tag is already active + +--- + dwm.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..68ceea2 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -2041,8 +2041,10 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { +- if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) ++ if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) { ++ view(&((Arg) { .ui = 0 })); + return; ++ } + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; +-- +2.19.1 + diff --git a/dwm/dwm-toggletag-anytag-6.3.diff b/dwm/dwm-toggletag-anytag-6.3.diff new file mode 100644 index 0000000..499bd1e --- /dev/null +++ b/dwm/dwm-toggletag-anytag-6.3.diff @@ -0,0 +1,31 @@ +From 0bcea267991d4809f1585bf5c7462d27709c47a5 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 13:59:08 +0100 +Subject: [PATCH] toggletag - keyboard shortcuts to view a given tag will + toggle to the previous tag if the given tag is already active or if the tag + was previously selected + +--- + dwm.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/dwm.c b/dwm.c +index a96f33c..c7dcc98 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -2041,8 +2041,11 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { +- if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) ++ if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags] ++ || ((arg->ui & TAGMASK) & selmon->tagset[selmon->seltags^1])) { ++ view(&((Arg) { .ui = 0 })); + return; ++ } + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; +-- +2.19.1 + diff --git a/dwm/dwm-unmanaged-6.3.diff b/dwm/dwm-unmanaged-6.3.diff new file mode 100644 index 0000000..1ec58a9 --- /dev/null +++ b/dwm/dwm-unmanaged-6.3.diff @@ -0,0 +1,106 @@ +From f99462eb1afd038023d3f25a25616a0560fb895b Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:00:06 +0100 +Subject: [PATCH] Unmanaged patch for dwm + +This patch adds a new rule option allowing the user to specify windows that +are not supposed to be managed by the window manager. + +This may include external bars, conky, widgets or desktop icons. + +If a window is unmanaged then its size and position is controlled by the +window application itself, or you can control these using external tools. + +As such you will not be able to resize or move a window within dwm like +normal managed windows are. + +The patch adds a new rule option "unmanaged" that is intentionally placed +at the end of the rules options. This is because as long as the field is +at the very end you do not actually need to specify a value (it will +default to 0) which means that existing rules can be left as-is. + + /* class instance title tags mask isfloating monitor unmanaged */ + { "Gimp", NULL, NULL, 0, 1, -1, 0 }, + { "Firefox", NULL, NULL, 1 << 8, 0, -1, 0 }, + +Whether unmanaged windows should stay on top of other windows or not depends +very much on the program. It may be desirable to place a bar on top of other +windows whereas an application that provides desktop icons is naturally best +placed below all other windows. + +As such the value of the unmanged rule plays a part: + * 0 - the window is managed by the window manager + * 1 - the window will be placed above all other windows + * 2 - the window will be placed below all other windows + * 3 - the window is left as-is (neither lowered nor raised) +--- + config.def.h | 6 +++--- + dwm.c | 14 ++++++++++++++ + 2 files changed, 17 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..2eb65f8 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 unmanaged */ ++ { "Gimp", NULL, NULL, 0, 1, -1, 0 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, -1, 0 }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index a96f33c..c99a81c 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -139,6 +139,7 @@ typedef struct { + unsigned int tags; + int isfloating; + int monitor; ++ int unmanaged; + } Rule; + + /* function declarations */ +@@ -241,6 +242,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 unmanaged = 0; /* whether the window manager should manage the new window or not */ + static int lrpad; /* sum of left and right padding for text */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; +@@ -300,6 +302,7 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ unmanaged = r->unmanaged; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -1043,6 +1046,17 @@ manage(Window w, XWindowAttributes *wa) + applyrules(c); + } + ++ if (unmanaged) { ++ XMapWindow(dpy, c->win); ++ if (unmanaged == 1) ++ XRaiseWindow(dpy, c->win); ++ else if (unmanaged == 2) ++ XLowerWindow(dpy, c->win); ++ free(c); ++ unmanaged = 0; ++ return; ++ } ++ + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) + c->x = c->mon->mx + c->mon->mw - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) +-- +2.19.1 + diff --git a/dwm/dwm-use_a_different_font_for_the_status-6.3.diff b/dwm/dwm-use_a_different_font_for_the_status-6.3.diff new file mode 100644 index 0000000..42cddc9 --- /dev/null +++ b/dwm/dwm-use_a_different_font_for_the_status-6.3.diff @@ -0,0 +1,39 @@ +From bda52d9a5bc430489dde92c7584896aee8751338 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:01:03 +0100 +Subject: [PATCH] This is meant as an example patch (horrible hack) to use a + different font for the status compared to the rest of the bar. + +The gist of it is that we forcibly use the second, or third, font to +draw the bar, leaving the primary (first) font to draw the rest. +--- + dwm.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/dwm.c b/dwm.c +index a96f33c..87497af 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -701,15 +701,19 @@ drawbar(Monitor *m) + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; ++ Fnt *cur; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ ++ cur = drw->fonts; // remember which was the first font ++ drw->fonts = drw->fonts->next; // skip to the second font, add more of these to get to third, fourth etc. + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ drw->fonts = cur; // set the normal font back to the first font + } + + for (c = m->clients; c; c = c->next) { +-- +2.19.1 + diff --git a/dwm/dwm-vanitygaps-6.3.diff b/dwm/dwm-vanitygaps-6.3.diff new file mode 100644 index 0000000..601ad99 --- /dev/null +++ b/dwm/dwm-vanitygaps-6.3.diff @@ -0,0 +1,973 @@ +From 17ec7643b997f8a7e77aecc36e93185c2b815544 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:02:07 +0100 +Subject: [PATCH] vanitygaps - adds gaps to layouts + +This patch differentiates between inner and outer gaps as well as +horizontal and vertical gaps. + +The logic of these layouts also aims to be pixel perfect by ensuring +an even split of the available space and re-distributing the remainder +among the available clients. +--- + config.def.h | 38 ++- + dwm.c | 37 +-- + vanitygaps.c | 809 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 854 insertions(+), 30 deletions(-) + create mode 100644 vanitygaps.c + +diff --git a/config.def.h b/config.def.h +index a2ac963..9165001 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,11 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const unsigned int gappih = 20; /* horiz inner gap between windows */ ++static const unsigned int gappiv = 10; /* vert inner gap between windows */ ++static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ ++static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ ++static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -37,11 +42,26 @@ static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ + static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + ++#define FORCE_VSPLIT 1 /* nrowgrid layout: force two clients to always split vertically */ ++#include "vanitygaps.c" ++ + static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ +- { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "[@]", spiral }, ++ { "[\\]", dwindle }, ++ { "D[]", deck }, ++ { "TTT", bstack }, ++ { "===", bstackhoriz }, ++ { "HHH", grid }, ++ { "###", nrowgrid }, ++ { "---", horizgrid }, ++ { ":::", gaplessgrid }, ++ { "|M|", centeredmaster }, ++ { ">M>", centeredfloatingmaster }, ++ { "><>", NULL }, /* no layout function means floating behavior */ ++ { NULL, NULL }, + }; + + /* key definitions */ +@@ -72,6 +92,22 @@ static Key keys[] = { + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, ++ { MODKEY|Mod4Mask, XK_u, incrgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_u, incrgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_i, incrigaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_i, incrigaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_o, incrogaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_o, incrogaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_6, incrihgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_6, incrihgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_7, incrivgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_7, incrivgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_8, incrohgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_8, incrohgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_9, incrovgaps, {.i = +1 } }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_9, incrovgaps, {.i = -1 } }, ++ { MODKEY|Mod4Mask, XK_0, togglegaps, {0} }, ++ { MODKEY|Mod4Mask|ShiftMask, XK_0, defaultgaps, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +diff --git a/dwm.c b/dwm.c +index a96f33c..7a0119d 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -119,6 +119,10 @@ struct Monitor { + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ ++ int gappih; /* horizontal gap between windows */ ++ int gappiv; /* vertical gap between windows */ ++ int gappoh; /* horizontal outer gaps */ ++ int gappov; /* vertical outer gaps */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; +@@ -209,7 +213,6 @@ 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 tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -639,6 +642,10 @@ createmon(void) + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; ++ m->gappih = gappih; ++ m->gappiv = gappiv; ++ m->gappoh = gappoh; ++ m->gappov = gappov; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); +@@ -1674,34 +1681,6 @@ tagmon(const Arg *arg) + sendmon(selmon->sel, dirtomon(arg->i)); + } + +-void +-tile(Monitor *m) +-{ +- unsigned int i, n, h, mw, my, ty; +- Client *c; +- +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); +- if (n == 0) +- return; +- +- if (n > m->nmaster) +- mw = m->nmaster ? m->ww * m->mfact : 0; +- else +- mw = m->ww; +- for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) +- if (i < m->nmaster) { +- h = (m->wh - my) / (MIN(n, m->nmaster) - i); +- resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); +- if (my + HEIGHT(c) < m->wh) +- my += HEIGHT(c); +- } else { +- h = (m->wh - ty) / (n - i); +- resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); +- if (ty + HEIGHT(c) < m->wh) +- ty += HEIGHT(c); +- } +-} +- + void + togglebar(const Arg *arg) + { +diff --git a/vanitygaps.c b/vanitygaps.c +new file mode 100644 +index 0000000..3f31593 +--- /dev/null ++++ b/vanitygaps.c +@@ -0,0 +1,809 @@ ++/* Key binding functions */ ++static void defaultgaps(const Arg *arg); ++static void incrgaps(const Arg *arg); ++static void incrigaps(const Arg *arg); ++static void incrogaps(const Arg *arg); ++static void incrohgaps(const Arg *arg); ++static void incrovgaps(const Arg *arg); ++static void incrihgaps(const Arg *arg); ++static void incrivgaps(const Arg *arg); ++static void togglegaps(const Arg *arg); ++/* Layouts (delete the ones you do not need) */ ++static void bstack(Monitor *m); ++static void bstackhoriz(Monitor *m); ++static void centeredmaster(Monitor *m); ++static void centeredfloatingmaster(Monitor *m); ++static void deck(Monitor *m); ++static void dwindle(Monitor *m); ++static void fibonacci(Monitor *m, int s); ++static void grid(Monitor *m); ++static void nrowgrid(Monitor *m); ++static void spiral(Monitor *m); ++static void tile(Monitor *m); ++/* Internals */ ++static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); ++static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr); ++static void setgaps(int oh, int ov, int ih, int iv); ++ ++/* Settings */ ++#if !PERTAG_PATCH ++static int enablegaps = 1; ++#endif // PERTAG_PATCH ++ ++void ++setgaps(int oh, int ov, int ih, int iv) ++{ ++ if (oh < 0) oh = 0; ++ if (ov < 0) ov = 0; ++ if (ih < 0) ih = 0; ++ if (iv < 0) iv = 0; ++ ++ selmon->gappoh = oh; ++ selmon->gappov = ov; ++ selmon->gappih = ih; ++ selmon->gappiv = iv; ++ arrange(selmon); ++} ++ ++void ++togglegaps(const Arg *arg) ++{ ++ #if PERTAG_PATCH ++ selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag]; ++ #else ++ enablegaps = !enablegaps; ++ #endif // PERTAG_PATCH ++ arrange(NULL); ++} ++ ++void ++defaultgaps(const Arg *arg) ++{ ++ setgaps(gappoh, gappov, gappih, gappiv); ++} ++ ++void ++incrgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh + arg->i, ++ selmon->gappov + arg->i, ++ selmon->gappih + arg->i, ++ selmon->gappiv + arg->i ++ ); ++} ++ ++void ++incrigaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov, ++ selmon->gappih + arg->i, ++ selmon->gappiv + arg->i ++ ); ++} ++ ++void ++incrogaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh + arg->i, ++ selmon->gappov + arg->i, ++ selmon->gappih, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrohgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh + arg->i, ++ selmon->gappov, ++ selmon->gappih, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrovgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov + arg->i, ++ selmon->gappih, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrihgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov, ++ selmon->gappih + arg->i, ++ selmon->gappiv ++ ); ++} ++ ++void ++incrivgaps(const Arg *arg) ++{ ++ setgaps( ++ selmon->gappoh, ++ selmon->gappov, ++ selmon->gappih, ++ selmon->gappiv + arg->i ++ ); ++} ++ ++void ++getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) ++{ ++ unsigned int n, oe, ie; ++ #if PERTAG_PATCH ++ oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag]; ++ #else ++ oe = ie = enablegaps; ++ #endif // PERTAG_PATCH ++ Client *c; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (smartgaps && n == 1) { ++ oe = 0; // outer gaps disabled when only one client ++ } ++ ++ *oh = m->gappoh*oe; // outer horizontal gap ++ *ov = m->gappov*oe; // outer vertical gap ++ *ih = m->gappih*ie; // inner horizontal gap ++ *iv = m->gappiv*ie; // inner vertical gap ++ *nc = n; // number of clients ++} ++ ++void ++getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) ++{ ++ unsigned int n; ++ float mfacts, sfacts; ++ int mtotal = 0, stotal = 0; ++ Client *c; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ mfacts = MIN(n, m->nmaster); ++ sfacts = n - m->nmaster; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ++ if (n < m->nmaster) ++ mtotal += msize / mfacts; ++ else ++ stotal += ssize / sfacts; ++ ++ *mf = mfacts; // total factor of master area ++ *sf = sfacts; // total factor of stack area ++ *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split ++ *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split ++} ++ ++/*** ++ * Layouts ++ */ ++ ++/* ++ * Bottomstack layout + gaps ++ * https://dwm.suckless.org/patches/bottomstack/ ++ */ ++static void ++bstack(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh; ++ mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); ++ sw = m->ww - 2*ov - iv * (n - m->nmaster - 1); ++ ++ if (m->nmaster && n > m->nmaster) { ++ sh = (mh - ih) * (1 - m->mfact); ++ mh = mh - ih - sh; ++ sx = mx; ++ sy = my + mh + ih; ++ } ++ ++ getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i < m->nmaster) { ++ resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv; ++ } else { ++ resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); ++ sx += WIDTH(c) + iv; ++ } ++ } ++} ++ ++static void ++bstackhoriz(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ mh = m->wh - 2*oh; ++ sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); ++ mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); ++ sw = m->ww - 2*ov; ++ ++ if (m->nmaster && n > m->nmaster) { ++ sh = (mh - ih) * (1 - m->mfact); ++ mh = mh - ih - sh; ++ sy = my + mh + ih; ++ sh = m->wh - mh - 2*oh - ih * (n - m->nmaster); ++ } ++ ++ getfacts(m, mw, sh, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (i < m->nmaster) { ++ resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv; ++ } else { ++ resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); ++ sy += HEIGHT(c) + ih; ++ } ++ } ++} ++ ++/* ++ * Centred master layout + gaps ++ * https://dwm.suckless.org/patches/centeredmaster/ ++ */ ++void ++centeredmaster(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int lx = 0, ly = 0, lw = 0, lh = 0; ++ int rx = 0, ry = 0, rw = 0, rh = 0; ++ float mfacts = 0, lfacts = 0, rfacts = 0; ++ int mtotal = 0, ltotal = 0, rtotal = 0; ++ int mrest = 0, lrest = 0, rrest = 0; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ /* initialize areas */ ++ mx = m->wx + ov; ++ my = m->wy + oh; ++ mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1); ++ mw = m->ww - 2*ov; ++ lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1); ++ rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1)); ++ ++ if (m->nmaster && n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ if (n - m->nmaster > 1) { ++ /* ||<-S->|<---M--->|<-S->|| */ ++ mw = (m->ww - 2*ov - 2*iv) * m->mfact; ++ lw = (m->ww - mw - 2*ov - 2*iv) / 2; ++ rw = (m->ww - mw - 2*ov - 2*iv) - lw; ++ mx += lw + iv; ++ } else { ++ /* ||<---M--->|<-S->|| */ ++ mw = (mw - iv) * m->mfact; ++ lw = 0; ++ rw = m->ww - mw - iv - 2*ov; ++ } ++ lx = m->wx + ov; ++ ly = m->wy + oh; ++ rx = mx + mw + iv; ++ ry = m->wy + oh; ++ } ++ ++ /* calculate facts */ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ if (!m->nmaster || n < m->nmaster) ++ mfacts += 1; ++ else if ((n - m->nmaster) % 2) ++ lfacts += 1; // total factor of left hand stack area ++ else ++ rfacts += 1; // total factor of right hand stack area ++ } ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ++ if (!m->nmaster || n < m->nmaster) ++ mtotal += mh / mfacts; ++ else if ((n - m->nmaster) % 2) ++ ltotal += lh / lfacts; ++ else ++ rtotal += rh / rfacts; ++ ++ mrest = mh - mtotal; ++ lrest = lh - ltotal; ++ rrest = rh - rtotal; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ if (!m->nmaster || i < m->nmaster) { ++ /* nmaster clients are stacked vertically, in the center of the screen */ ++ resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); ++ my += HEIGHT(c) + ih; ++ } else { ++ /* stack clients are stacked vertically */ ++ if ((i - m->nmaster) % 2 ) { ++ resize(c, lx, ly, lw - (2*c->bw), (lh / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); ++ ly += HEIGHT(c) + ih; ++ } else { ++ resize(c, rx, ry, rw - (2*c->bw), (rh / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); ++ ry += HEIGHT(c) + ih; ++ } ++ } ++ } ++} ++ ++void ++centeredfloatingmaster(Monitor *m) ++{ ++ unsigned int i, n; ++ float mfacts, sfacts; ++ float mivf = 1.0; // master inner vertical gap factor ++ int oh, ov, ih, iv, mrest, srest; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh; ++ mw = m->ww - 2*ov - iv*(n - 1); ++ sw = m->ww - 2*ov - iv*(n - m->nmaster - 1); ++ ++ if (m->nmaster && n > m->nmaster) { ++ mivf = 0.8; ++ /* go mfact box in the center if more than nmaster clients */ ++ if (m->ww > m->wh) { ++ mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1); ++ mh = m->wh * 0.9; ++ } else { ++ mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1); ++ mh = m->wh * m->mfact; ++ } ++ mx = m->wx + (m->ww - mw) / 2; ++ my = m->wy + (m->wh - mh - 2*oh) / 2; ++ ++ sx = m->wx + ov; ++ sy = m->wy + oh; ++ sh = m->wh - 2*oh; ++ } ++ ++ getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ /* nmaster clients are stacked horizontally, in the center of the screen */ ++ resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv*mivf; ++ } else { ++ /* stack clients are stacked horizontally */ ++ resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); ++ sx += WIDTH(c) + iv; ++ } ++} ++ ++/* ++ * Deck layout + gaps ++ * https://dwm.suckless.org/patches/deck/ ++ */ ++void ++deck(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); ++ sw = mw = m->ww - 2*ov; ++ ++ if (m->nmaster && n > m->nmaster) { ++ sw = (mw - iv) * (1 - m->mfact); ++ mw = mw - iv - sw; ++ sx = mx + mw + iv; ++ sh = m->wh - 2*oh; ++ } ++ ++ getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); ++ ++ if (n - m->nmaster > 0) /* override layout symbol */ ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); ++ my += HEIGHT(c) + ih; ++ } else { ++ resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0); ++ } ++} ++ ++/* ++ * Fibonacci layout + gaps ++ * https://dwm.suckless.org/patches/fibonacci/ ++ */ ++void ++fibonacci(Monitor *m, int s) ++{ ++ unsigned int i, n; ++ int nx, ny, nw, nh; ++ int oh, ov, ih, iv; ++ int nv, hrest = 0, wrest = 0, r = 1; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ nx = m->wx + ov; ++ ny = m->wy + oh; ++ nw = m->ww - 2*ov; ++ nh = m->wh - 2*oh; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++ if (r) { ++ if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw)) ++ || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) { ++ r = 0; ++ } ++ if (r && i < n - 1) { ++ if (i % 2) { ++ nv = (nh - ih) / 2; ++ hrest = nh - 2*nv - ih; ++ nh = nv; ++ } else { ++ nv = (nw - iv) / 2; ++ wrest = nw - 2*nv - iv; ++ nw = nv; ++ } ++ ++ if ((i % 4) == 2 && !s) ++ nx += nw + iv; ++ else if ((i % 4) == 3 && !s) ++ ny += nh + ih; ++ } ++ ++ if ((i % 4) == 0) { ++ if (s) { ++ ny += nh + ih; ++ nh += hrest; ++ } ++ else { ++ nh -= hrest; ++ ny -= nh + ih; ++ } ++ } ++ else if ((i % 4) == 1) { ++ nx += nw + iv; ++ nw += wrest; ++ } ++ else if ((i % 4) == 2) { ++ ny += nh + ih; ++ nh += hrest; ++ if (i < n - 1) ++ nw += wrest; ++ } ++ else if ((i % 4) == 3) { ++ if (s) { ++ nx += nw + iv; ++ nw -= wrest; ++ } else { ++ nw -= wrest; ++ nx -= nw + iv; ++ nh += hrest; ++ } ++ } ++ if (i == 0) { ++ if (n != 1) { ++ nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact); ++ wrest = 0; ++ } ++ ny = m->wy + oh; ++ } ++ else if (i == 1) ++ nw = m->ww - nw - iv - 2*ov; ++ i++; ++ } ++ ++ resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); ++ } ++} ++ ++void ++dwindle(Monitor *m) ++{ ++ fibonacci(m, 1); ++} ++ ++void ++spiral(Monitor *m) ++{ ++ fibonacci(m, 0); ++} ++ ++/* ++ * Gappless grid layout + gaps (ironically) ++ * https://dwm.suckless.org/patches/gaplessgrid/ ++ */ ++void ++gaplessgrid(Monitor *m) ++{ ++ unsigned int i, n; ++ int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters ++ int oh, ov, ih, iv; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ /* grid dimensions */ ++ for (cols = 0; cols <= n/2; cols++) ++ if (cols*cols >= n) ++ break; ++ if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ ++ cols = 2; ++ rows = n/cols; ++ cn = rn = 0; // reset column no, row no, client count ++ ++ ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; ++ cw = (m->ww - 2*ov - iv * (cols - 1)) / cols; ++ rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; ++ crest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; ++ x = m->wx + ov; ++ y = m->wy + oh; ++ ++ for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { ++ if (i/rows + 1 > cols - n%cols) { ++ rows = n/cols + 1; ++ ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; ++ rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; ++ } ++ resize(c, ++ x, ++ y + rn*(ch + ih) + MIN(rn, rrest), ++ cw + (cn < crest ? 1 : 0) - 2*c->bw, ++ ch + (rn < rrest ? 1 : 0) - 2*c->bw, ++ 0); ++ rn++; ++ if (rn >= rows) { ++ rn = 0; ++ x += cw + ih + (cn < crest ? 1 : 0); ++ cn++; ++ } ++ } ++} ++ ++/* ++ * Gridmode layout + gaps ++ * https://dwm.suckless.org/patches/gridmode/ ++ */ ++void ++grid(Monitor *m) ++{ ++ unsigned int i, n; ++ int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows; ++ int oh, ov, ih, iv; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ ++ /* grid dimensions */ ++ for (rows = 0; rows <= n/2; rows++) ++ if (rows*rows >= n) ++ break; ++ cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; ++ ++ /* window geoms (cell height/width) */ ++ ch = (m->wh - 2*oh - ih * (rows - 1)) / (rows ? rows : 1); ++ cw = (m->ww - 2*ov - iv * (cols - 1)) / (cols ? cols : 1); ++ chrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; ++ cwrest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { ++ cc = i / rows; ++ cr = i % rows; ++ cx = m->wx + ov + cc * (cw + iv) + MIN(cc, cwrest); ++ cy = m->wy + oh + cr * (ch + ih) + MIN(cr, chrest); ++ resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); ++ } ++} ++ ++/* ++ * Horizontal grid layout + gaps ++ * https://dwm.suckless.org/patches/horizgrid/ ++ */ ++void ++horizgrid(Monitor *m) { ++ Client *c; ++ unsigned int n, i; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ int ntop, nbottom = 1; ++ float mfacts, sfacts; ++ int mrest, srest; ++ ++ /* Count windows */ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ if (n <= 2) ++ ntop = n; ++ else { ++ ntop = n / 2; ++ nbottom = n - ntop; ++ } ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ sh = mh = m->wh - 2*oh; ++ sw = mw = m->ww - 2*ov; ++ ++ if (n > ntop) { ++ sh = (mh - ih) / 2; ++ mh = mh - ih - sh; ++ sy = my + mh + ih; ++ mw = m->ww - 2*ov - iv * (ntop - 1); ++ sw = m->ww - 2*ov - iv * (nbottom - 1); ++ } ++ ++ mfacts = ntop; ++ sfacts = nbottom; ++ mrest = mw - (mw / ntop) * ntop; ++ srest = sw - (sw / nbottom) * nbottom; ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < ntop) { ++ resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); ++ mx += WIDTH(c) + iv; ++ } else { ++ resize(c, sx, sy, (sw / sfacts) + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); ++ sx += WIDTH(c) + iv; ++ } ++} ++ ++/* ++ * nrowgrid layout + gaps ++ * https://dwm.suckless.org/patches/nrowgrid/ ++ */ ++void ++nrowgrid(Monitor *m) ++{ ++ unsigned int n; ++ int ri = 0, ci = 0; /* counters */ ++ int oh, ov, ih, iv; /* vanitygap settings */ ++ unsigned int cx, cy, cw, ch; /* client geometry */ ++ unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */ ++ unsigned int cols, rows = m->nmaster + 1; ++ Client *c; ++ ++ /* count clients */ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ ++ /* nothing to do here */ ++ if (n == 0) ++ return; ++ ++ /* force 2 clients to always split vertically */ ++ if (FORCE_VSPLIT && n == 2) ++ rows = 1; ++ ++ /* never allow empty rows */ ++ if (n < rows) ++ rows = n; ++ ++ /* define first row */ ++ cols = n / rows; ++ uc = cols; ++ cy = m->wy + oh; ++ ch = (m->wh - 2*oh - ih*(rows - 1)) / rows; ++ uh = ch; ++ ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next), ci++) { ++ if (ci == cols) { ++ uw = 0; ++ ci = 0; ++ ri++; ++ ++ /* next row */ ++ cols = (n - uc) / (rows - ri); ++ uc += cols; ++ cy = m->wy + oh + uh + ih; ++ uh += ch + ih; ++ } ++ ++ cx = m->wx + ov + uw; ++ cw = (m->ww - 2*ov - uw) / (cols - ci); ++ uw += cw + iv; ++ ++ resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0); ++ } ++} ++ ++/* ++ * Default tile layout + gaps ++ */ ++static void ++tile(Monitor *m) ++{ ++ unsigned int i, n; ++ int oh, ov, ih, iv; ++ int mx = 0, my = 0, mh = 0, mw = 0; ++ int sx = 0, sy = 0, sh = 0, sw = 0; ++ float mfacts, sfacts; ++ int mrest, srest; ++ Client *c; ++ ++ getgaps(m, &oh, &ov, &ih, &iv, &n); ++ if (n == 0) ++ return; ++ ++ sx = mx = m->wx + ov; ++ sy = my = m->wy + oh; ++ mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); ++ sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); ++ sw = mw = m->ww - 2*ov; ++ ++ if (m->nmaster && n > m->nmaster) { ++ sw = (mw - iv) * (1 - m->mfact); ++ mw = mw - iv - sw; ++ sx = mx + mw + iv; ++ } ++ ++ getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); ++ ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); ++ my += HEIGHT(c) + ih; ++ } else { ++ resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); ++ sy += HEIGHT(c) + ih; ++ } ++} +\ No newline at end of file +-- +2.19.1 + diff --git a/dwm/dwm-warp-6.3.diff b/dwm/dwm-warp-6.3.diff new file mode 100644 index 0000000..6fc3bad --- /dev/null +++ b/dwm/dwm-warp-6.3.diff @@ -0,0 +1,74 @@ +From 56373b668df6166b3a15b97257d8d449164c6446 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:02:41 +0100 +Subject: [PATCH] Warp: warps the mouse cursor to the center of the currently + focused window or screen when the mouse cursor is (a) on a different screen + or (b) on top of a different window. + +If you are using barmodules then you may want to have a look at the warp function here: +https://github.com/bakkeby/dwm-flexipatch/blob/master/patch/warp.c +--- + dwm.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/dwm.c b/dwm.c +index a96f33c..7febf7e 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -228,6 +228,7 @@ static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); ++static void warp(const Client *c); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); +@@ -831,6 +832,7 @@ focusmon(const Arg *arg) + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); ++ warp(selmon->sel); + } + + void +@@ -1371,6 +1373,8 @@ restack(Monitor *m) + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); ++ if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle) ++ warp(m->sel); + } + + void +@@ -2050,6 +2054,28 @@ view(const Arg *arg) + arrange(selmon); + } + ++void ++warp(const Client *c) ++{ ++ int x, y; ++ ++ if (!c) { ++ XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww / 2, selmon->wy + selmon->wh / 2); ++ return; ++ } ++ ++ if (!getrootptr(&x, &y) || ++ (x > c->x - c->bw && ++ y > c->y - c->bw && ++ x < c->x + c->w + c->bw*2 && ++ y < c->y + c->h + c->bw*2) || ++ (y > c->mon->by && y < c->mon->by + bh) || ++ (c->mon->topbar && !y)) ++ return; ++ ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2); ++} ++ + Client * + wintoclient(Window w) + { +-- +2.19.1 + diff --git a/dwm/dwm-windowrolerule-6.3.diff b/dwm/dwm-windowrolerule-6.3.diff new file mode 100644 index 0000000..a61af88 --- /dev/null +++ b/dwm/dwm-windowrolerule-6.3.diff @@ -0,0 +1,85 @@ +From eac1c79a73ae2cee4870db8282a163bb64d5ea88 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:03:26 +0100 +Subject: [PATCH] Adding window role rule + +This patch adds a new rule property based on WM_WINDOW_ROLE(STRING) so that one can +differentiate between window roles, e.g. Firefox "browser" vs "Preferences". +--- + config.def.h | 7 ++++--- + dwm.c | 7 ++++++- + 2 files changed, 10 insertions(+), 4 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..bbb7c85 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -25,10 +25,11 @@ static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title ++ * WM_WINDOW_ROLE(STRING) = role + */ +- /* class instance title tags mask isfloating monitor */ +- { "Gimp", NULL, NULL, 0, 1, -1 }, +- { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ /* class role instance title tags mask isfloating monitor */ ++ { "Gimp", NULL, NULL, NULL, 0, 1, -1 }, ++ { "Firefox", NULL, NULL, NULL, 1 << 8, 0, -1 }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index a96f33c..ae66867 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -63,7 +63,7 @@ enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +-enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ ++enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMWindowRole, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +@@ -134,6 +134,7 @@ struct Monitor { + + typedef struct { + const char *class; ++ const char *role; + const char *instance; + const char *title; + unsigned int tags; +@@ -280,6 +281,7 @@ void + applyrules(Client *c) + { + const char *class, *instance; ++ char role[64]; + unsigned int i; + const Rule *r; + Monitor *m; +@@ -291,11 +293,13 @@ applyrules(Client *c) + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; ++ gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role)); + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) ++ && (!r->role || strstr(role, r->role)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; +@@ -1557,6 +1561,7 @@ setup(void) + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); ++ wmatom[WMWindowRole] = XInternAtom(dpy, "WM_WINDOW_ROLE", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); +-- +2.19.1 + diff --git a/dwm/dwm-zero_depth_safeguard-6.3.diff b/dwm/dwm-zero_depth_safeguard-6.3.diff new file mode 100644 index 0000000..8abe993 --- /dev/null +++ b/dwm/dwm-zero_depth_safeguard-6.3.diff @@ -0,0 +1,86 @@ +From 0ea022a0d39fcbfdaa421b34fedf486a00d34571 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 10 Jan 2022 14:04:15 +0100 +Subject: [PATCH] Zero depth safeguard for dwm + +The colour depth attribute of an X window represents the number of bits +that are used to represent the colour. + +A window that has a depth of 0 suggests that it does not support colours +and is therefore not intended to be displayed. + +Mapping a window that has a depth of 0 will result in dwm crashing and +this safeguard will avoid that. + +An example of this would be one of the hidden windows that the firefox +browser manages. + +$ xdotool search firefox +Defaulting to search window name, class, classname, and role +77594627 +77594625 +77594681 +77594684 + +Two of the windows listed above should have a depth of 0, this can be +checked using xwininfo like this: + +$ xwininfo -id 77594625 + +xwininfo: Window id: 0x4a00001 "Firefox" + + Absolute upper-left X: 10 + Absolute upper-left Y: 10 + Relative upper-left X: 10 + Relative upper-left Y: 10 + Width: 10 + Height: 10 + Depth: 0 + Visual: 0x506 + Visual Class: TrueColor + Border width: 0 + Class: InputOnly + Colormap: 0x0 (not installed) + Bit Gravity State: ForgetGravity + Window Gravity State: NorthWestGravity + Backing Store State: NotUseful + Save Under State: no + Map State: IsUnMapped + Override Redirect State: no + Corners: +10+10 -6180+10 -6180-1900 +10-1900 + -geometry 10x10+10+10 + +The window can then be explicitly mapped using xdotool. + +$ xdotool windowmap 77594625 + +Without the safeguard dwm should crash following the above command. + +It should be noted that these kind of windows are not intended to +be explicitly mapped like this and they won't be under normal +circumstances. + +If you are trying to write a script that uses tools like mapw or +xdotool to map and unmap windows then you may want to add some +checks to avoid that you don't include windows that have a depth +of 0. +--- + dwm.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dwm.c b/dwm.c +index a96f33c..807fcb9 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1098,6 +1098,8 @@ maprequest(XEvent *e) + + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; ++ if (!wa.depth) ++ return; + if (wa.override_redirect) + return; + if (!wintoclient(ev->window)) +-- +2.19.1 + diff --git a/st/st-default_cursor-0.8.4-20210328-4536f46.diff b/st/st-default_cursor-0.8.4-20210328-4536f46.diff index 7bb1688..02165f9 100644 --- a/st/st-default_cursor-0.8.4-20210328-4536f46.diff +++ b/st/st-default_cursor-0.8.4-20210328-4536f46.diff @@ -44,5 +44,5 @@ index 7186040..0e4b4ee 100644 } -- -2.33.0 +2.34.1 diff --git a/tabbed/tabbed-bottomtabs-0.6-20200512-dabf6a2.diff b/tabbed/tabbed-bottomtabs-0.6-20200512-dabf6a2.diff index 18e1eaf..cb29ce7 100644 --- a/tabbed/tabbed-bottomtabs-0.6-20200512-dabf6a2.diff +++ b/tabbed/tabbed-bottomtabs-0.6-20200512-dabf6a2.diff @@ -48,5 +48,5 @@ index eafe28a..7fcd53c 100644 ce.height = wc.height = h; ce.type = ConfigureNotify; -- -2.33.0 +2.34.1 diff --git a/tabbed/tabbed-center-0.6-20200512-dabf6a2.diff b/tabbed/tabbed-center-0.6-20200512-dabf6a2.diff index 01d1ce0..d958d60 100644 --- a/tabbed/tabbed-center-0.6-20200512-dabf6a2.diff +++ b/tabbed/tabbed-center-0.6-20200512-dabf6a2.diff @@ -23,5 +23,5 @@ index eafe28a..52039eb 100644 d = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen)); XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8 *) buf, len); -- -2.33.0 +2.34.1