commit fd287578b0f927111ffc78d23497bd8295f496e3
parent 72b689a7e89692fbaf67bd994459ba87d10fe09b
Author: grouse <bdmfegys@duck.com>
Date: Wed, 6 Dec 2023 21:13:01 -0500
remove deprecated birdwm.patch
Diffstat:
| D | extras/birdwm.patch | | | 6313 | ------------------------------------------------------------------------------- |
1 file changed, 0 insertions(+), 6313 deletions(-)
diff --git a/extras/birdwm.patch b/extras/birdwm.patch
@@ -1,6313 +0,0 @@
-commit 6f06d6d3c1d694fbae0f5587fff5ca761843429b
-Author: grouse <bdmfegys@duck.com>
-Date: Sat Dec 2 23:40:19 2023 -0500
-
- birdwm patch adds multiple patches to vanilla 6.4 dwm
-
-diff --git a/Makefile b/Makefile
-index ffa69b4..4e8e249 100644
---- a/Makefile
-+++ b/Makefile
-@@ -6,7 +6,13 @@ include config.mk
- SRC = drw.c dwm.c util.c
- OBJ = ${SRC:.c=.o}
-
--all: dwm
-+all: options dwm
-+
-+options:
-+ @echo dwm build options:
-+ @echo "CFLAGS = ${CFLAGS}"
-+ @echo "LDFLAGS = ${LDFLAGS}"
-+ @echo "CC = ${CC}"
-
- .c.o:
- ${CC} -c ${CFLAGS} $<
-@@ -20,7 +26,7 @@ dwm: ${OBJ}
- ${CC} -o $@ ${OBJ} ${LDFLAGS}
-
- clean:
-- rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
-+ rm -f config.h ./*.rej ./*.orig dwm ${OBJ} dwm-${VERSION}.tar.gz
-
- dist: clean
- mkdir -p dwm-${VERSION}
-@@ -37,9 +43,11 @@ install: all
- mkdir -p ${DESTDIR}${MANPREFIX}/man1
- sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
- chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
-+ mkdir -p /usr/share/xsessions
-+ cp -f dwm.desktop /usr/share/xsessions/dwm.desktop
-
- uninstall:
- rm -f ${DESTDIR}${PREFIX}/bin/dwm\
- ${DESTDIR}${MANPREFIX}/man1/dwm.1
-
--.PHONY: all clean dist install uninstall
-+.PHONY: all options clean dist install uninstall
-diff --git a/README b/README
-index 95d4fd0..cc076c4 100644
---- a/README
-+++ b/README
-@@ -1,36 +1,46 @@
--dwm - dynamic window manager
-+birdwm - dynamic window manager for birds
- ============================
--dwm is an extremely fast, small, and dynamic window manager for X.
-+ _ _ _
-+| |__ (_)_ __ __| |_ ___ __ ___
-+| '_ \| | '__/ _` \ \ /\ / / '_ ` _ \
-+| |_) | | | | (_| |\ V V /| | | | | |
-+|_.__/|_|_| \__,_| \_/\_/ |_| |_| |_|
-+
-+This is my dwm fork.
-+It includes my slstatus configs and it depends on some of my scripts included in my dotfiles.
-+For more info see https://git.tetraonini.online/grouse/dotfiles
-+
-+birdwm is an extremely fast, small, and dynamic window manager for birds.
-
-
- Requirements
- ------------
--In order to build dwm you need the Xlib header files.
-+In order to build birdwm you need the Xlib header files.
-
-
- Installation
- ------------
--Edit config.mk to match your local setup (dwm is installed into
-+Edit config.mk to match your local setup (birdwm is installed into
- the /usr/local namespace by default).
-
--Afterwards enter the following command to build and install dwm (if
-+Afterwards enter the following command to build and install birdwm (if
- necessary as root):
-
- make clean install
-
-
--Running dwm
-+Running birdwm
- -----------
--Add the following line to your .xinitrc to start dwm using startx:
-+Add the following line to your .xinitrc to start birdwm using startx:
-
- exec dwm
-
--In order to connect dwm to a specific display, make sure that
-+In order to connect birdwm to a specific display, make sure that
- the DISPLAY environment variable is set correctly, e.g.:
-
- DISPLAY=foo.bar:1 exec dwm
-
--(This will start dwm on display :1 of the host foo.bar.)
-+(This will start birdwm on display :1 of the host foo.bar.)
-
- In order to display status info in the bar, you can do something
- like this in your .xinitrc:
-@@ -44,5 +54,5 @@ like this in your .xinitrc:
-
- Configuration
- -------------
--The configuration of dwm is done by creating a custom config.h
-+The configuration of birdwm is done by creating a custom config.h
- and (re)compiling the source code.
-diff --git a/config.def.h b/config.def.h
-index 9efa774..7377bf7 100644
---- a/config.def.h
-+++ b/config.def.h
-@@ -1,40 +1,84 @@
- /* See LICENSE file for copyright and license details. */
--
-+#include <X11/XF86keysym.h>
- /* appearance */
- static const unsigned int borderpx = 1; /* border pixel of windows */
-+static const unsigned int gappx = 7; /* gaps between 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";
--static const char col_gray1[] = "#222222";
--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 *colors[][3] = {
-- /* fg bg border */
-- [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
-- [SchemeSel] = { col_gray4, col_cyan, col_cyan },
-+#define ICONSIZE 16 /* icon size */
-+#define ICONSPACING 5 /* space between icon and title */
-+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[] = { "Inconsolata-ExpandedBold:pixelsize=22" };
-+static const char dmenufont[] = "Inconsolata-ExpandedBold:pixelsize=22";
-+static const unsigned int baralpha = 0x70;
-+static const unsigned int borderalpha = OPAQUE;
-+static char normbgcolor[] = "#222222";
-+static char normbordercolor[] = "#444444";
-+static char normfgcolor[] = "#bbbbbb";
-+static char selfgcolor[] = "#eeeeee";
-+static char selbordercolor[] = "#005577";
-+static char selbgcolor[] = "#005577";
-+static char *colors[][3] = {
-+ /* fg bg border */
-+ [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor },
-+ [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor },
-+};
-+typedef struct {
-+ const char *name;
-+ const void *cmd;
-+} Sp;
-+const char *spcmd1[] = {"st", "-n", "spterm", "-g", "144x41", NULL };
-+const char *spcmd2[] = {"st", "-n", "spfm", "-g", "144x41", "-e", "ranger", NULL };
-+const char *spcmd3[] = {"st", "-n", "upgrader", "-g", "144x41", "-e", "upgrader", NULL };
-+static Sp scratchpads[] = {
-+ /* name cmd */
-+ {"spterm", spcmd1},
-+ {"spranger", spcmd2},
-+ {"upgrader", spcmd3},
- };
-
--/* tagging */
--static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
-+static const unsigned int alphas[][3] = {
-+ /* fg bg border*/
-+ [SchemeNorm] = { OPAQUE, baralpha, borderalpha },
-+ [SchemeSel] = { OPAQUE, baralpha, borderalpha },
-+};
-
-+/* tagging */
-+static const char *tags[] = { "", "", "", "", "", "", };
- static const Rule rules[] = {
- /* xprop(1):
- * 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 },
-+ { "Gimp", NULL, NULL, 0, 1, -1 }, // gimp menu floating rule
-+/* scratchpad floating rules */
-+ { NULL, "spterm", NULL, SPTAG(0), 1, -1 }, // scratchpad floating rule
-+ { NULL, "spfm", NULL, SPTAG(1), 1, -1 }, // suckless file manager command
-+ { NULL, "upgrader", NULL, SPTAG(2), 1, -1 }, // upgrade script command floating
-+// { "Alacritty","Alacritty",NULL, 0, 1, -1 }, // alacritty
-+/* end of scratchpad floating rules */
-+ { "widget", NULL, NULL, 0, 1, -1 }, // used sometimes, for using sww widgets
-+ { "python3", NULL, NULL, 0, 1, -1 }, // i cant remember what this is tbh
-+ { NULL, "Toolkit", NULL, 0, 1, -1 }, // firefox floating media player floating rule
-+ { "steamwebhelper","steamwebhelper",NULL,0, 1, -1 }, // just to make the steam menus float
-+/* all rules below this start on the second monitor */
-+ { "VencordDesktop", "vencorddesktop", NULL, 1 << 4, 0, 0 }, // starts discord on the discord icon tag
-+ { "Cider", "cider", NULL, 1 << 3, 0, 0 }, // starts cider on the music tag
-+ { "VSCodium","vscodium", NULL, 1 << 1, 0, 0 }, // starts vscodium by default on the cli/code tag
-+ { "tutanota-desktop","tutanota-desktop",NULL,1 << 5,0, 0 }, // starts tutanota-desktop on the mail tag
-+ { "Element", "element", NULL, 1 << 5, 0, 0 }, // starts element on the chat tag
-+ { "Signal", "signal", NULL, 1 << 2, 0, 0 }, // starts element on the chat tag
- };
-
- /* 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 resizehints = 1; /* 1 means respect size hints in tiled resizals */
-+static const int resizehints = 0; /* 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[] = {
-@@ -57,13 +101,31 @@ static const Layout layouts[] = {
-
- /* commands */
- 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 };
-+static const char *dmenucmd[] = { "dmenu_run", "-i", NULL };
-+static const char *termcmd[] = { "kitty", NULL };
-+//static const char *wallpaper[] = { "nsxiv", "-t", "/home/tetraonini/Pictures/wallpapers/*.png", NULL };
-+//static const char *wallpapergif[] = { "nsxiv", "-t", "/home/tetraonini/Pictures/wallpapers/gif/*.gif", NULL };
-+static const char *wallpaper[] = { "xwallpaper" };
-+static const char *wallpapergif[] = { "xwallpapergif" };
-+static const char *restarter[] = { "restarter", NULL};
-+static const char *volup[] = { "volume.sh", "up", NULL};
-+static const char *voldown[] = { "volume.sh", "down", NULL};
-+static const char *volmute[] = { "volume.sh", "mute", NULL};
-+static const char *firefox[] = { "librewolf", NULL};
-+
-
-+#include "exitdwm.c"
- static const Key keys[] = {
- /* modifier key function argument */
- { MODKEY, XK_p, spawn, {.v = dmenucmd } },
- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
-+ { MODKEY, XK_w, spawn, {.v = wallpaper } },
-+ { MODKEY, XK_g, spawn, {.v = wallpapergif } },
-+ { MODKEY|ShiftMask, XK_j, spawn, {.v = restarter } },
-+ { MODKEY, XK_f, spawn, {.v = firefox } },
-+ { 0, XF86XK_AudioRaiseVolume, spawn, {.v = volup} },
-+ { 0, XF86XK_AudioLowerVolume, spawn, {.v = voldown} },
-+ { 0, XF86XK_AudioMute, spawn, {.v = volmute} },
- { MODKEY, XK_b, togglebar, {0} },
- { MODKEY, XK_j, focusstack, {.i = +1 } },
- { MODKEY, XK_k, focusstack, {.i = -1 } },
-@@ -75,7 +137,6 @@ static const 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_m, setlayout, {.v = &layouts[2]} },
- { MODKEY, XK_space, setlayout, {0} },
- { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
-@@ -85,6 +146,10 @@ static const Key keys[] = {
- { MODKEY, XK_period, focusmon, {.i = +1 } },
- { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
- { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
-+ { MODKEY, XK_F5, xrdb, {.v = NULL } },
-+ { MODKEY, XK_y, togglescratch, {.ui = 0 } },
-+ { MODKEY, XK_u, togglescratch, {.ui = 1 } },
-+ { MODKEY|ShiftMask, XK_u, togglescratch, {.ui = 2 } },
- TAGKEYS( XK_1, 0)
- TAGKEYS( XK_2, 1)
- TAGKEYS( XK_3, 2)
-@@ -94,7 +159,10 @@ static const Key keys[] = {
- TAGKEYS( XK_7, 6)
- TAGKEYS( XK_8, 7)
- TAGKEYS( XK_9, 8)
-- { MODKEY|ShiftMask, XK_q, quit, {0} },
-+// following line is commented out due to exitmenu patch
-+// { MODKEY|ShiftMask, XK_q, quit, {0} },
-+ { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} },
-+ { MODKEY|ShiftMask, XK_e, exitdwm, {0} },
- };
-
- /* button definitions */
-@@ -107,7 +175,7 @@ static const Button buttons[] = {
- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
- { ClkClientWin, MODKEY, Button1, movemouse, {0} },
- { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
-- { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
-+ { ClkClientWin, MODKEY, Button1, resizemouse, {0} },
- { ClkTagBar, 0, Button1, view, {0} },
- { ClkTagBar, 0, Button3, toggleview, {0} },
- { ClkTagBar, MODKEY, Button1, tag, {0} },
-diff --git a/config.mk b/config.mk
-index ba64d3d..886f060 100644
---- a/config.mk
-+++ b/config.mk
-@@ -23,7 +23,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 -lImlib2
-
- # flags
- CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
-diff --git a/drw.c b/drw.c
-index a58a2b4..effb137 100644
---- a/drw.c
-+++ b/drw.c
-@@ -4,6 +4,7 @@
- #include <string.h>
- #include <X11/Xlib.h>
- #include <X11/Xft/Xft.h>
-+#include <Imlib2.h>
-
- #include "drw.h"
- #include "util.h"
-@@ -61,7 +62,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 +71,12 @@ 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->picture = XRenderCreatePicture(dpy, drw->drawable, XRenderFindVisualFormat(dpy, visual), 0, NULL);
-+ drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
- XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
-
- return drw;
-@@ -85,14 +90,18 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
-
- drw->w = w;
- drw->h = h;
-+ if (drw->picture)
-+ XRenderFreePicture(drw->dpy, drw->picture);
- 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);
-+ drw->picture = XRenderCreatePicture(drw->dpy, drw->drawable, XRenderFindVisualFormat(drw->dpy, drw->visual), 0, NULL);
- }
-
- void
- drw_free(Drw *drw)
- {
-+ XRenderFreePicture(drw->dpy, drw->picture);
- XFreePixmap(drw->dpy, drw->drawable);
- XFreeGC(drw->dpy, drw->gc);
- drw_fontset_free(drw->fonts);
-@@ -181,21 +190,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, char *clrnames[], const unsigned int alphas[], size_t clrcount)
- {
- size_t i;
- Clr *ret;
-@@ -205,7 +215,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;
- }
-
-@@ -223,6 +233,67 @@ drw_setscheme(Drw *drw, Clr *scm)
- drw->scheme = scm;
- }
-
-+Picture
-+drw_picture_create_resized(Drw *drw, char *src, unsigned int srcw, unsigned int srch, unsigned int dstw, unsigned int dsth) {
-+ Pixmap pm;
-+ Picture pic;
-+ GC gc;
-+
-+ if (srcw <= (dstw << 1u) && srch <= (dsth << 1u)) {
-+ XImage img = {
-+ srcw, srch, 0, ZPixmap, src,
-+ ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32,
-+ 32, 0, 32,
-+ 0, 0, 0
-+ };
-+ XInitImage(&img);
-+
-+ pm = XCreatePixmap(drw->dpy, drw->root, srcw, srch, 32);
-+ gc = XCreateGC(drw->dpy, pm, 0, NULL);
-+ XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, srcw, srch);
-+ XFreeGC(drw->dpy, gc);
-+
-+ pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL);
-+ XFreePixmap(drw->dpy, pm);
-+
-+ XRenderSetPictureFilter(drw->dpy, pic, FilterBilinear, NULL, 0);
-+ XTransform xf;
-+ xf.matrix[0][0] = (srcw << 16u) / dstw; xf.matrix[0][1] = 0; xf.matrix[0][2] = 0;
-+ xf.matrix[1][0] = 0; xf.matrix[1][1] = (srch << 16u) / dsth; xf.matrix[1][2] = 0;
-+ xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = 65536;
-+ XRenderSetPictureTransform(drw->dpy, pic, &xf);
-+ } else {
-+ Imlib_Image origin = imlib_create_image_using_data(srcw, srch, (DATA32 *)src);
-+ if (!origin) return None;
-+ imlib_context_set_image(origin);
-+ imlib_image_set_has_alpha(1);
-+ Imlib_Image scaled = imlib_create_cropped_scaled_image(0, 0, srcw, srch, dstw, dsth);
-+ imlib_free_image_and_decache();
-+ if (!scaled) return None;
-+ imlib_context_set_image(scaled);
-+ imlib_image_set_has_alpha(1);
-+
-+ XImage img = {
-+ dstw, dsth, 0, ZPixmap, (char *)imlib_image_get_data_for_reading_only(),
-+ ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32,
-+ 32, 0, 32,
-+ 0, 0, 0
-+ };
-+ XInitImage(&img);
-+
-+ pm = XCreatePixmap(drw->dpy, drw->root, dstw, dsth, 32);
-+ gc = XCreateGC(drw->dpy, pm, 0, NULL);
-+ XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, dstw, dsth);
-+ imlib_free_image_and_decache();
-+ XFreeGC(drw->dpy, gc);
-+
-+ pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL);
-+ XFreePixmap(drw->dpy, pm);
-+ }
-+
-+ return pic;
-+}
-+
- void
- drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
- {
-@@ -263,9 +334,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;
- }
-@@ -384,6 +453,14 @@ no_match:
- return x + (render ? w : 0);
- }
-
-+void
-+drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic)
-+{
-+ if (!drw)
-+ return;
-+ XRenderComposite(drw->dpy, PictOpOver, pic, None, drw->picture, 0, 0, 0, 0, x, y, w, h);
-+}
-+
- 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 6471431..f32a93b 100644
---- a/drw.h
-+++ b/drw.h
-@@ -20,14 +20,18 @@ typedef struct {
- Display *dpy;
- int screen;
- Window root;
-+ Visual *visual;
-+ unsigned int depth;
-+ Colormap cmap;
- Drawable drawable;
-+ Picture picture;
- GC gc;
- Clr *scheme;
- Fnt *fonts;
- } 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);
-
-@@ -39,8 +43,8 @@ unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int
- 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, char *clrnames[], const unsigned int alphas[], size_t clrcount);
-
- /* Cursor abstraction */
- Cur *drw_cur_create(Drw *drw, int shape);
-@@ -50,9 +54,12 @@ void drw_cur_free(Drw *drw, Cur *cursor);
- void drw_setfontset(Drw *drw, Fnt *set);
- void drw_setscheme(Drw *drw, Clr *scm);
-
-+Picture drw_picture_create_resized(Drw *drw, char *src, unsigned int src_w, unsigned int src_h, unsigned int dst_w, unsigned int dst_h);
-+
- /* 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_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic);
-
- /* Map functions */
- void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
-diff --git a/dwm.1 b/dwm.1
-index ddc8321..02a415f 100644
---- a/dwm.1
-+++ b/dwm.1
-@@ -1,11 +1,11 @@
--.TH DWM 1 dwm\-VERSION
-+.TH DWM 1 birdwm\-VERSION
- .SH NAME
--dwm \- dynamic window manager
-+birdwm \- dynamic window manager for birds
- .SH SYNOPSIS
- .B dwm
- .RB [ \-v ]
- .SH DESCRIPTION
--dwm is a dynamic window manager for X. It manages windows in tiled, monocle
-+birdwm is a dynamic window manager for birds. It manages windows in tiled, monocle
- and floating layouts. Either layout can be applied dynamically, optimising the
- environment for the application in use and the task performed.
- .P
-@@ -29,7 +29,15 @@ color. The tags of the focused window are indicated with a filled square in the
- top left corner. The tags which are applied to one or more windows are
- indicated with an empty square in the top left corner.
- .P
--dwm draws a small border around windows to indicate the focus state.
-+birdwm draws a small border around windows to indicate the focus state.
-+.P
-+On start, birdwm can start additional programs that may be specified in two special
-+shell scripts (see the FILES section below), autostart_blocking.sh and
-+autostart.sh. The former is executed first and birdwm will wait for its
-+termination before starting. The latter is executed in the background before
-+birdwm enters its handler loop.
-+.P
-+Either of these files may be omitted.
- .SH OPTIONS
- .TP
- .B \-v
-@@ -141,7 +149,10 @@ View all windows with any tag.
- Add/remove all windows with nth tag to/from the view.
- .TP
- .B Mod1\-Shift\-q
--Quit dwm.
-+Quit birdwm.
-+.TP
-+.B Mod1\-Control\-Shift\-q
-+Restart birdwm.
- .SS Mouse commands
- .TP
- .B Mod1\-Button1
-@@ -152,9 +163,31 @@ Toggles focused window between floating and tiled state.
- .TP
- .B Mod1\-Button3
- Resize focused window while dragging. Tiled windows will be toggled to the floating state.
-+.SH FILES
-+The files containing programs to be started along with birdwm are searched for in
-+the following directories:
-+.IP "1. $XDG_DATA_HOME/birdwm"
-+.IP "2. $HOME/.local/share/birdwm"
-+.IP "3. $HOME/.birdwm"
-+.P
-+The first existing directory is scanned for any of the autostart files below.
-+.TP 15
-+autostart.sh
-+This file is started as a shell background process before birdwm enters its handler
-+loop.
-+.TP 15
-+autostart_blocking.sh
-+This file is started before any autostart.sh; birdwm waits for its termination.
- .SH CUSTOMIZATION
--dwm is customized by creating a custom config.h and (re)compiling the source
-+birdwm is customized by creating a custom config.h and (re)compiling the source
- code. This keeps it fast, secure and simple.
-+.SH SIGNALS
-+.TP
-+.B SIGHUP - 1
-+Restart the dwm process.
-+.TP
-+.B SIGTERM - 15
-+Cleanly terminate the dwm process.
- .SH SEE ALSO
- .BR dmenu (1),
- .BR st (1)
-diff --git a/dwm.c b/dwm.c
-index f1d86b2..9ca208a 100644
---- a/dwm.c
-+++ b/dwm.c
-@@ -17,9 +17,9 @@
- * client.
- *
- * Keys and tagging rules are organized as arrays and defined in config.h.
-- *
- * To understand everything else, start reading main().
- */
-+#include <X11/XF86keysym.h>
- #include <errno.h>
- #include <locale.h>
- #include <signal.h>
-@@ -28,13 +28,17 @@
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
-+#include <limits.h>
-+#include <stdint.h>
- #include <sys/types.h>
-+#include <sys/stat.h>
- #include <sys/wait.h>
- #include <X11/cursorfont.h>
- #include <X11/keysym.h>
- #include <X11/Xatom.h>
- #include <X11/Xlib.h>
- #include <X11/Xproto.h>
-+#include <X11/Xresource.h>
- #include <X11/Xutil.h>
- #ifdef XINERAMA
- #include <X11/extensions/Xinerama.h>
-@@ -52,17 +56,55 @@
- #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
- #define LENGTH(X) (sizeof X / sizeof X[0])
- #define MOUSEMASK (BUTTONMASK|PointerMotionMask)
--#define WIDTH(X) ((X)->w + 2 * (X)->bw)
--#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
--#define TAGMASK ((1 << LENGTH(tags)) - 1)
-+#define WIDTH(X) ((X)->w + 2 * (X)->bw + gappx)
-+#define HEIGHT(X) ((X)->h + 2 * (X)->bw + gappx)
-+#define NUMTAGS (LENGTH(tags) + LENGTH(scratchpads))
-+#define TAGMASK ((1 << NUMTAGS) - 1)
-+#define SPTAG(i) ((1 << LENGTH(tags)) << (i))
-+#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << LENGTH(tags))
- #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
-
-+#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \
-+ if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \
-+ int i = 1; \
-+ for (; i <= 6; i++) { \
-+ if (value.addr[i] < 48) break; \
-+ if (value.addr[i] > 57 && value.addr[i] < 65) break; \
-+ if (value.addr[i] > 70 && value.addr[i] < 97) break; \
-+ if (value.addr[i] > 102) break; \
-+ } \
-+ if (i == 7) { \
-+ strncpy(V, value.addr, 7); \
-+ V[7] = '\0'; \
-+ } \
-+ } \
-+ }
-+#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, NetWMIcon, 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 */
-@@ -93,6 +135,7 @@ struct Client {
- int bw, oldbw;
- unsigned int tags;
- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
-+ unsigned int icw, ich; Picture icon;
- Client *next;
- Client *snext;
- Monitor *mon;
-@@ -141,6 +184,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);
-@@ -170,14 +219,17 @@ 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 Picture geticonprop(Window w, unsigned int *icw, unsigned int *ich);
- 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);
- static void keypress(XEvent *e);
- static void killclient(const Arg *arg);
-+static void loadxrdb(void);
- static void manage(Window w, XWindowAttributes *wa);
- static void mappingnotify(XEvent *e);
- static void maprequest(XEvent *e);
-@@ -192,10 +244,13 @@ 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 runautostart(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);
-@@ -205,14 +260,19 @@ static void setmfact(const Arg *arg);
- static void setup(void);
- static void seturgent(Client *c, int urg);
- static void showhide(Client *c);
-+static void sighup(int unused);
-+static void sigterm(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 *m);
- 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 freeicon(Client *c);
- static void unfocus(Client *c, int setfocus);
- static void unmanage(Client *c, int destroyed);
- static void unmapnotify(XEvent *e);
-@@ -223,19 +283,30 @@ 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 updateicon(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 xinitvisual();
-+static void xrdb(const Arg *arg);
- static void zoom(const Arg *arg);
-
- /* variables */
-+static const char autostartblocksh[] = "autostart_blocking.sh";
-+static const char autostartsh[] = "autostart.sh";
- static const char broken[] = "broken";
-+static const char dwmdir[] = "dwm";
-+static const char localshare[] = ".local/share";
- static char stext[256];
- static int screen;
- static int sw, sh; /* X display screen geometry width, height */
-@@ -257,9 +328,11 @@ 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 restart = 0;
- static int running = 1;
- static Cur *cursor[CurLast];
- static Clr **scheme;
-@@ -268,6 +341,14 @@ 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;
-+static Colormap cmap;
-+
- /* configuration, allows nested code to access above variables */
- #include "config.h"
-
-@@ -299,6 +380,11 @@ applyrules(Client *c)
- {
- c->isfloating = r->isfloating;
- c->tags |= r->tags;
-+ if ((r->tags & SPTAGMASK) && r->isfloating) {
-+ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2);
-+ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2);
-+ }
-+
- for (m = mons; m && m->num != r->monitor; m = m->next);
- if (m)
- c->mon = m;
-@@ -308,7 +394,7 @@ applyrules(Client *c)
- XFree(ch.res_class);
- if (ch.res_name)
- XFree(ch.res_name);
-- c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
-+ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK);
- }
-
- int
-@@ -441,7 +527,7 @@ buttonpress(XEvent *e)
- arg.ui = 1 << i;
- } else if (ev->x < x + TEXTW(selmon->ltsymbol))
- click = ClkLtSymbol;
-- else if (ev->x > selmon->ww - (int)TEXTW(stext))
-+ else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth())
- click = ClkStatusText;
- else
- click = ClkWinTitle;
-@@ -484,6 +570,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++)
-@@ -515,9 +608,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]) {
-@@ -655,6 +788,10 @@ destroynotify(XEvent *e)
-
- if ((c = wintoclient(ev->window)))
- unmanage(c, 1);
-+ else if (showsystray && (c = wintosystrayicon(ev->window))) {
-+ removesystrayicon(c);
-+ updatesystray(1);
-+ }
- }
-
- void
-@@ -698,12 +835,18 @@ 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;
- Client *c;
-
-+ if (showsystray && m == systraytomon(m)) {
-+ stw = getsystraywidth();
-+ drw_setscheme(drw, scheme[SchemeNorm]);
-+ drw_rect(drw, m->ww - stw, 0, stw, bh, 1, 1);
-+ }
-+
- if (!m->showbar)
- return;
-
-@@ -711,7 +854,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 - stw, 0, tw, bh, 0, stext, 0);
- }
-
- for (c = m->clients; c; c = c->next) {
-@@ -734,10 +877,11 @@ 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);
-+ drw_text(drw, x, 0, w, bh, lrpad / 2 + (m->sel->icon ? m->sel->icw + ICONSPACING : 0), m->sel->name, 0);
-+ if (m->sel->icon) drw_pic(drw, x + lrpad / 2, (bh - m->sel->ich) / 2, m->sel->icw, m->sel->ich, m->sel->icon);
- if (m->sel->isfloating)
- drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
- } else {
-@@ -755,6 +899,9 @@ drawbars(void)
-
- for (m = mons; m; m = m->next)
- drawbar(m);
-+
-+ if (showsystray && !systraypinning)
-+ updatesystray(0);
- }
-
- void
-@@ -782,8 +929,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
-@@ -833,6 +984,8 @@ focusmon(const Arg *arg)
- unfocus(selmon->sel, 0);
- selmon = m;
- focus(NULL);
-+ if (selmon->sel)
-+ XWarpPointer(dpy, None, selmon->sel->win, 0, 0, 0, 0, selmon->sel->w/2, selmon->sel->h/2);
- }
-
- void
-@@ -858,6 +1011,7 @@ focusstack(const Arg *arg)
- if (c) {
- focus(c);
- restack(selmon);
-+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2);
- }
- }
-
-@@ -869,14 +1023,83 @@ 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;
- }
-
-+static uint32_t prealpha(uint32_t p) {
-+ uint8_t a = p >> 24u;
-+ uint32_t rb = (a * (p & 0xFF00FFu)) >> 8u;
-+ uint32_t g = (a * (p & 0x00FF00u)) >> 8u;
-+ return (rb & 0xFF00FFu) | (g & 0x00FF00u) | (a << 24u);
-+}
-+
-+Picture
-+geticonprop(Window win, unsigned int *picw, unsigned int *pich)
-+{
-+ int format;
-+ unsigned long n, extra, *p = NULL;
-+ Atom real;
-+
-+ if (XGetWindowProperty(dpy, win, netatom[NetWMIcon], 0L, LONG_MAX, False, AnyPropertyType,
-+ &real, &format, &n, &extra, (unsigned char **)&p) != Success)
-+ return None;
-+ if (n == 0 || format != 32) { XFree(p); return None; }
-+
-+ unsigned long *bstp = NULL;
-+ uint32_t w, h, sz;
-+ {
-+ unsigned long *i; const unsigned long *end = p + n;
-+ uint32_t bstd = UINT32_MAX, d, m;
-+ for (i = p; i < end - 1; i += sz) {
-+ if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; }
-+ if ((sz = w * h) > end - i) break;
-+ if ((m = w > h ? w : h) >= ICONSIZE && (d = m - ICONSIZE) < bstd) { bstd = d; bstp = i; }
-+ }
-+ if (!bstp) {
-+ for (i = p; i < end - 1; i += sz) {
-+ if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; }
-+ if ((sz = w * h) > end - i) break;
-+ if ((d = ICONSIZE - (w > h ? w : h)) < bstd) { bstd = d; bstp = i; }
-+ }
-+ }
-+ if (!bstp) { XFree(p); return None; }
-+ }
-+
-+ if ((w = *(bstp - 2)) == 0 || (h = *(bstp - 1)) == 0) { XFree(p); return None; }
-+
-+ uint32_t icw, ich;
-+ if (w <= h) {
-+ ich = ICONSIZE; icw = w * ICONSIZE / h;
-+ if (icw == 0) icw = 1;
-+ }
-+ else {
-+ icw = ICONSIZE; ich = h * ICONSIZE / w;
-+ if (ich == 0) ich = 1;
-+ }
-+ *picw = icw; *pich = ich;
-+
-+ uint32_t i, *bstp32 = (uint32_t *)bstp;
-+ for (sz = w * h, i = 0; i < sz; ++i) bstp32[i] = prealpha(bstp[i]);
-+
-+ Picture ret = drw_picture_create_resized(drw, (char *)bstp, w, h, icw, ich);
-+ XFree(p);
-+
-+ return ret;
-+}
-+
- int
- getrootptr(int *x, int *y)
- {
-@@ -905,6 +1128,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)
- {
-@@ -1017,7 +1250,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);
-@@ -1028,6 +1261,37 @@ killclient(const Arg *arg)
- }
- }
-
-+void
-+loadxrdb()
-+{
-+ Display *display;
-+ char * resm;
-+ XrmDatabase xrdb;
-+ char *type;
-+ XrmValue value;
-+
-+ display = XOpenDisplay(NULL);
-+
-+ if (display != NULL) {
-+ resm = XResourceManagerString(display);
-+
-+ if (resm != NULL) {
-+ xrdb = XrmGetStringDatabase(resm);
-+
-+ if (xrdb != NULL) {
-+ XRDB_LOAD_COLOR("dwm.normbordercolor", normbordercolor);
-+ XRDB_LOAD_COLOR("dwm.normbgcolor", normbgcolor);
-+ XRDB_LOAD_COLOR("dwm.normfgcolor", normfgcolor);
-+ XRDB_LOAD_COLOR("dwm.selbordercolor", selbordercolor);
-+ XRDB_LOAD_COLOR("dwm.selbgcolor", selbgcolor);
-+ XRDB_LOAD_COLOR("dwm.selfgcolor", selfgcolor);
-+ }
-+ }
-+ }
-+
-+ XCloseDisplay(display);
-+}
-+
- void
- manage(Window w, XWindowAttributes *wa)
- {
-@@ -1044,6 +1308,7 @@ manage(Window w, XWindowAttributes *wa)
- c->h = c->oldh = wa->height;
- c->oldbw = wa->border_width;
-
-+ updateicon(c);
- updatetitle(c);
- if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
- c->mon = t->mon;
-@@ -1104,6 +1369,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) || wa.override_redirect)
- return;
- if (!wintoclient(ev->window))
-@@ -1225,6 +1496,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)
-@@ -1250,6 +1531,11 @@ propertynotify(XEvent *e)
- if (c == c->mon->sel)
- drawbar(c->mon);
- }
-+ else if (ev->atom == netatom[NetWMIcon]) {
-+ updateicon(c);
-+ if (c == c->mon->sel)
-+ drawbar(c->mon);
-+ }
- if (ev->atom == netatom[NetWMWindowType])
- updatewindowtype(c);
- }
-@@ -1258,6 +1544,7 @@ propertynotify(XEvent *e)
- void
- quit(const Arg *arg)
- {
-+ if(arg->i) restart = 1;
- running = 0;
- }
-
-@@ -1275,6 +1562,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)
- {
-@@ -1286,12 +1586,36 @@ void
- resizeclient(Client *c, int x, int y, int w, int h)
- {
- XWindowChanges wc;
-+ unsigned int n;
-+ unsigned int gapoffset;
-+ unsigned int gapincr;
-+ Client *nbc;
-
-- 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;
- wc.border_width = c->bw;
-+
-+ /* Get number of clients for the client's monitor */
-+ for (n = 0, nbc = nexttiled(c->mon->clients); nbc; nbc = nexttiled(nbc->next), n++);
-+
-+ /* Do nothing if layout is floating */
-+ if (c->isfloating || c->mon->lt[c->mon->sellt]->arrange == NULL) {
-+ gapincr = gapoffset = 0;
-+ } else {
-+ /* Remove border and gap if layout is monocle or only one client */
-+ if (c->mon->lt[c->mon->sellt]->arrange == monocle || n == 1) {
-+ gapoffset = 0;
-+ gapincr = -2 * borderpx;
-+ wc.border_width = 0;
-+ } else {
-+ gapoffset = gappx;
-+ gapincr = 2 * gappx;
-+ }
-+ }
-+
-+ c->oldx = c->x; c->x = wc.x = x + gapoffset;
-+ c->oldy = c->y; c->y = wc.y = y + gapoffset;
-+ c->oldw = c->w; c->w = wc.width = w - gapincr;
-+ c->oldh = c->h; c->h = wc.height = h - gapincr;
-+
- XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
- configure(c);
- XSync(dpy, False);
-@@ -1354,6 +1678,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)
- {
-@@ -1390,6 +1726,83 @@ run(void)
- handler[ev.type](&ev); /* call handler */
- }
-
-+void
-+runautostart(void)
-+{
-+ char *pathpfx;
-+ char *path;
-+ char *xdgdatahome;
-+ char *home;
-+ struct stat sb;
-+
-+ if ((home = getenv("HOME")) == NULL)
-+ /* this is almost impossible */
-+ return;
-+
-+ /* if $XDG_DATA_HOME is set and not empty, use $XDG_DATA_HOME/dwm,
-+ * otherwise use ~/.local/share/dwm as autostart script directory
-+ */
-+ xdgdatahome = getenv("XDG_DATA_HOME");
-+ if (xdgdatahome != NULL && *xdgdatahome != '\0') {
-+ /* space for path segments, separators and nul */
-+ pathpfx = ecalloc(1, strlen(xdgdatahome) + strlen(dwmdir) + 2);
-+
-+ if (sprintf(pathpfx, "%s/%s", xdgdatahome, dwmdir) <= 0) {
-+ free(pathpfx);
-+ return;
-+ }
-+ } else {
-+ /* space for path segments, separators and nul */
-+ pathpfx = ecalloc(1, strlen(home) + strlen(localshare)
-+ + strlen(dwmdir) + 3);
-+
-+ if (sprintf(pathpfx, "%s/%s/%s", home, localshare, dwmdir) < 0) {
-+ free(pathpfx);
-+ return;
-+ }
-+ }
-+
-+ /* check if the autostart script directory exists */
-+ if (! (stat(pathpfx, &sb) == 0 && S_ISDIR(sb.st_mode))) {
-+ /* the XDG conformant path does not exist or is no directory
-+ * so we try ~/.dwm instead
-+ */
-+ char *pathpfx_new = realloc(pathpfx, strlen(home) + strlen(dwmdir) + 3);
-+ if(pathpfx_new == NULL) {
-+ free(pathpfx);
-+ return;
-+ }
-+ pathpfx = pathpfx_new;
-+
-+ if (sprintf(pathpfx, "%s/.%s", home, dwmdir) <= 0) {
-+ free(pathpfx);
-+ return;
-+ }
-+ }
-+
-+ /* try the blocking script first */
-+ path = ecalloc(1, strlen(pathpfx) + strlen(autostartblocksh) + 2);
-+ if (sprintf(path, "%s/%s", pathpfx, autostartblocksh) <= 0) {
-+ free(path);
-+ free(pathpfx);
-+ }
-+
-+ if (access(path, X_OK) == 0)
-+ system(path);
-+
-+ /* now the non-blocking script */
-+ if (sprintf(path, "%s/%s", pathpfx, autostartsh) <= 0) {
-+ free(path);
-+ free(pathpfx);
-+ }
-+
-+ if (access(path, X_OK) == 0)
-+ system(strcat(path, " &"));
-+
-+ free(pathpfx);
-+ free(path);
-+}
-+
- void
- scan(void)
- {
-@@ -1443,26 +1856,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;
- }
-@@ -1476,7 +1898,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
-@@ -1553,12 +1975,16 @@ setup(void)
- /* clean up any zombies (inherited from .xinitrc etc) immediately */
- while (waitpid(-1, NULL, WNOHANG) > 0);
-
-+ signal(SIGHUP, sighup);
-+ signal(SIGTERM, sigterm);
-+
- /* init screen */
- screen = DefaultScreen(dpy);
- 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;
-@@ -1572,13 +1998,23 @@ 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[NetWMIcon] = XInternAtom(dpy, "_NET_WM_ICON", 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);
-@@ -1586,7 +2022,10 @@ 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 system tray */
-+ if (showsystray)
-+ updatesystray(0);
- /* init bars */
- updatebars();
- updatestatus();
-@@ -1632,6 +2071,10 @@ showhide(Client *c)
- if (!c)
- return;
- if (ISVISIBLE(c)) {
-+ if ((c->tags & SPTAGMASK) && c->isfloating) {
-+ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2);
-+ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2);
-+ }
- /* show clients top down */
- XMoveWindow(dpy, c->win, c->x, c->y);
- if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
-@@ -1644,6 +2087,20 @@ showhide(Client *c)
- }
- }
-
-+void
-+sighup(int unused)
-+{
-+ Arg a = {.i = 1};
-+ quit(&a);
-+}
-+
-+void
-+sigterm(int unused)
-+{
-+ Arg a = {.i = 0};
-+ quit(&a);
-+}
-+
- void
- spawn(const Arg *arg)
- {
-@@ -1666,6 +2123,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)
- {
-@@ -1701,7 +2175,7 @@ tile(Monitor *m)
- 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);
-+ resize(c, m->wx, m->wy + my, mw - (2*c->bw) + (n > 1 ? gappx : 0), h - (2*c->bw), 0);
- if (my + HEIGHT(c) < m->wh)
- my += HEIGHT(c);
- } else {
-@@ -1718,6 +2192,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);
- }
-
-@@ -1735,6 +2226,32 @@ togglefloating(const Arg *arg)
- arrange(selmon);
- }
-
-+void
-+togglescratch(const Arg *arg)
-+{
-+ Client *c;
-+ unsigned int found = 0;
-+ unsigned int scratchtag = SPTAG(arg->ui);
-+ Arg sparg = {.v = scratchpads[arg->ui].cmd};
-+
-+ for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next);
-+ if (found) {
-+ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag;
-+ if (newtagset) {
-+ selmon->tagset[selmon->seltags] = newtagset;
-+ focus(NULL);
-+ arrange(selmon);
-+ }
-+ if (ISVISIBLE(c)) {
-+ focus(c);
-+ restack(selmon);
-+ }
-+ } else {
-+ selmon->tagset[selmon->seltags] |= scratchtag;
-+ spawn(&sparg);
-+ }
-+}
-+
- void
- toggletag(const Arg *arg)
- {
-@@ -1762,6 +2279,15 @@ toggleview(const Arg *arg)
- }
- }
-
-+void
-+freeicon(Client *c)
-+{
-+ if (c->icon) {
-+ XRenderFreePicture(dpy, c->icon);
-+ c->icon = None;
-+ }
-+}
-+
- void
- unfocus(Client *c, int setfocus)
- {
-@@ -1783,6 +2309,7 @@ unmanage(Client *c, int destroyed)
-
- detach(c);
- detachstack(c);
-+ freeicon(c);
- if (!destroyed) {
- wc.border_width = c->oldbw;
- XGrabServer(dpy); /* avoid race conditions */
-@@ -1812,6 +2339,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);
- }
- }
-
-@@ -1821,17 +2353,21 @@ 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);
-+ if (showsystray && m == systraytomon(m))
-+ XMapRaised(dpy, systray->win);
- XMapRaised(dpy, m->barwin);
- XSetClassHint(dpy, m->barwin, &ch);
- }
-@@ -2010,6 +2546,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)
- {
-@@ -2019,6 +2686,13 @@ updatetitle(Client *c)
- strcpy(c->name, broken);
- }
-
-+void
-+updateicon(Client *c)
-+{
-+ freeicon(c);
-+ c->icon = geticonprop(c->win, &c->icw, &c->ich);
-+}
-+
- void
- updatewindowtype(Client *c)
- {
-@@ -2092,6 +2766,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. */
-@@ -2128,6 +2812,55 @@ 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
-+xrdb(const Arg *arg)
-+{
-+ loadxrdb();
-+ int i;
-+ 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);
-+ focus(NULL);
-+ arrange(NULL);
-+}
-+
- void
- zoom(const Arg *arg)
- {
-@@ -2152,13 +2885,17 @@ main(int argc, char *argv[])
- if (!(dpy = XOpenDisplay(NULL)))
- die("dwm: cannot open display");
- checkotherwm();
-+ XrmInitialize();
-+ loadxrdb();
- setup();
- #ifdef __OpenBSD__
- if (pledge("stdio rpath proc exec", NULL) == -1)
- die("pledge");
- #endif /* __OpenBSD__ */
- scan();
-+ runautostart();
- run();
-+ if(restart) execvp(argv[0], argv);
- cleanup();
- XCloseDisplay(dpy);
- return EXIT_SUCCESS;
-diff --git a/dwm.desktop b/dwm.desktop
-new file mode 100644
-index 0000000..be5db48
---- /dev/null
-+++ b/dwm.desktop
-@@ -0,0 +1,7 @@
-+[Desktop Entry]
-+Name=DWM
-+Comment=Dynamic Window Manager
-+TryExec=dwm
-+Exec=dwm
-+Type=Application
-+Icon=dwm
-diff --git a/exitdwm.c b/exitdwm.c
-new file mode 100644
-index 0000000..b8b95bd
---- /dev/null
-+++ b/exitdwm.c
-@@ -0,0 +1,87 @@
-+# include <stdio.h>
-+# include <string.h>
-+
-+void exitdwm ()
-+{
-+# if \
-+ defined S_LOCK || \
-+ defined S_RESTART_DWM || \
-+ defined S_OFFSCREEN || \
-+ defined S_EXIT || \
-+ defined S_REBOOT || \
-+ defined S_SHUTDOWN || \
-+ defined S_LOCK_ICON || \
-+ defined S_RESTART_DWM_ICON || \
-+ defined S_OFFSCREEN_ICON || \
-+ defined S_EXIT_ICON || \
-+ defined S_REBOOT_ICON || \
-+ defined S_SHUTDOWN_ICON || \
-+ defined S_FORMAT || \
-+ defined S_FORMAT_CLEAR
-+# error (conflicting macro names)
-+# endif
-+
-+# define S_LOCK "Lock"
-+# define S_RESTART_DWM "restart birdwm"
-+# define S_OFFSCREEN "Off-screen"
-+# define S_EXIT "Exit"
-+# define S_REBOOT "Reboot"
-+# define S_SHUTDOWN "Shutdown"
-+# define S_LOCK_ICON "\uf023" // <= FontAwesome icons
-+# define S_RESTART_DWM_ICON "\uf01e"
-+# define S_OFFSCREEN_ICON "\uf108"
-+# define S_EXIT_ICON "\uf2f5"
-+# define S_REBOOT_ICON "\uf021"
-+# define S_SHUTDOWN_ICON "\uf011"
-+
-+# define S_FORMAT(ACTION) S_##ACTION##_ICON " " S_##ACTION
-+# define S_FORMAT_CLEAR "sed 's/^..//'"
-+
-+ FILE * exit_menu = popen (
-+ "echo \""
-+ S_FORMAT (LOCK) "\n"
-+ S_FORMAT (RESTART_DWM) "\n"
-+ S_FORMAT (OFFSCREEN) "\n"
-+ S_FORMAT (EXIT) "\n"
-+ S_FORMAT (REBOOT) "\n"
-+ S_FORMAT (SHUTDOWN)
-+ "\" | dmenu -i -p exit: | " S_FORMAT_CLEAR
-+ ,
-+ "r"
-+ );
-+
-+ char exit_action [16];
-+
-+ if (
-+ exit_menu == NULL ||
-+ fscanf (exit_menu, "%15[a-zA-Z -]", exit_action) == EOF
-+ ) {
-+ fputs ("Error. Failure in exit_dwm.", stderr);
-+ goto close_streams;
-+ }
-+
-+ if (strcmp (exit_action, S_LOCK) == 0) system ("slock & sleep 10; xset dpms force off");
-+ else if (strcmp (exit_action, S_RESTART_DWM) == 0) quit (& (const Arg) {1});
-+ else if (strcmp (exit_action, S_OFFSCREEN) == 0) system ("sleep .5; xset dpms force off");
-+ else if (strcmp (exit_action, S_EXIT) == 0) quit (& (const Arg) {0});
-+ else if (strcmp (exit_action, S_REBOOT) == 0) system ("systemctl reboot");
-+ else if (strcmp (exit_action, S_SHUTDOWN) == 0) system ("systemctl poweroff -i");
-+
-+close_streams:
-+ pclose (exit_menu);
-+
-+# undef S_LOCK
-+# undef S_RESTART_DWM
-+# undef S_OFFSCREEN
-+# undef S_EXIT
-+# undef S_REBOOT
-+# undef S_SHUTDOWN
-+# undef S_LOCK_ICON
-+# undef S_RESTART_DWM_ICON
-+# undef S_OFFSCREEN_ICON
-+# undef S_EXIT_ICON
-+# undef S_REBOOT_ICON
-+# undef S_SHUTDOWN_ICON
-+# undef S_FORMAT
-+# undef S_FORMAT_CLEAR
-+}
-diff --git a/patches/dwm-exitmenu-6.3.diff b/patches/dwm-exitmenu-6.3.diff
-new file mode 100644
-index 0000000..c31d921
---- /dev/null
-+++ b/patches/dwm-exitmenu-6.3.diff
-@@ -0,0 +1,114 @@
-+diff --git a/config.def.h b/config.def.h
-+index a2ac963..92a6a81 100644
-+--- a/config.def.h
-++++ b/config.def.h
-+@@ -60,6 +60,7 @@ 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 };
-+
-++#include "exitdwm.c"
-+ static Key keys[] = {
-+ /* modifier key function argument */
-+ { MODKEY, XK_p, spawn, {.v = dmenucmd } },
-+@@ -94,7 +95,7 @@ static Key keys[] = {
-+ TAGKEYS( XK_7, 6)
-+ TAGKEYS( XK_8, 7)
-+ TAGKEYS( XK_9, 8)
-+- { MODKEY|ShiftMask, XK_q, quit, {0} },
-++ { MODKEY|ShiftMask, XK_e, exitdwm, {0} },
-+ };
-+
-+ /* button definitions */
-+diff --git a/exitdwm.c b/exitdwm.c
-+new file mode 100644
-+index 0000000..74c514f
-+--- /dev/null
-++++ b/exitdwm.c
-+@@ -0,0 +1,87 @@
-++# include <stdio.h>
-++# include <string.h>
-++
-++void exitdwm ()
-++{
-++# if \
-++ defined S_LOCK || \
-++ defined S_RESTART_DWM || \
-++ defined S_OFFSCREEN || \
-++ defined S_EXIT || \
-++ defined S_REBOOT || \
-++ defined S_SHUTDOWN || \
-++ defined S_LOCK_ICON || \
-++ defined S_RESTART_DWM_ICON || \
-++ defined S_OFFSCREEN_ICON || \
-++ defined S_EXIT_ICON || \
-++ defined S_REBOOT_ICON || \
-++ defined S_SHUTDOWN_ICON || \
-++ defined S_FORMAT || \
-++ defined S_FORMAT_CLEAR
-++# error (conflicting macro names)
-++# endif
-++
-++# define S_LOCK "Lock"
-++# define S_RESTART_DWM "restart Dwm"
-++# define S_OFFSCREEN "Off-screen"
-++# define S_EXIT "Exit"
-++# define S_REBOOT "Reboot"
-++# define S_SHUTDOWN "Shutdown"
-++# define S_LOCK_ICON "\uf023" // <= FontAwesome icons
-++# define S_RESTART_DWM_ICON "\uf01e"
-++# define S_OFFSCREEN_ICON "\uf108"
-++# define S_EXIT_ICON "\uf2f5"
-++# define S_REBOOT_ICON "\uf021"
-++# define S_SHUTDOWN_ICON "\uf011"
-++
-++# define S_FORMAT(ACTION) S_##ACTION##_ICON " " S_##ACTION
-++# define S_FORMAT_CLEAR "sed 's/^..//'"
-++
-++ FILE * exit_menu = popen (
-++ "echo \""
-++ S_FORMAT (LOCK) "\n"
-++ S_FORMAT (RESTART_DWM) "\n"
-++ S_FORMAT (OFFSCREEN) "\n"
-++ S_FORMAT (EXIT) "\n"
-++ S_FORMAT (REBOOT) "\n"
-++ S_FORMAT (SHUTDOWN)
-++ "\" | dmenu -p exit: | " S_FORMAT_CLEAR
-++ ,
-++ "r"
-++ );
-++
-++ char exit_action [16];
-++
-++ if (
-++ exit_menu == NULL ||
-++ fscanf (exit_menu, "%15[a-zA-Z -]", exit_action) == EOF
-++ ) {
-++ fputs ("Error. Failure in exit_dwm.", stderr);
-++ goto close_streams;
-++ }
-++
-++ if (strcmp (exit_action, S_LOCK) == 0) system ("slock & sleep .5; xset dpms force off");
-++ else if (strcmp (exit_action, S_RESTART_DWM) == 0) quit (& (const Arg) {1});
-++ else if (strcmp (exit_action, S_OFFSCREEN) == 0) system ("sleep .5; xset dpms force off");
-++ else if (strcmp (exit_action, S_EXIT) == 0) quit (& (const Arg) {0});
-++ else if (strcmp (exit_action, S_REBOOT) == 0) system ("systemctl reboot");
-++ else if (strcmp (exit_action, S_SHUTDOWN) == 0) system ("systemctl poweroff -i");
-++
-++close_streams:
-++ pclose (exit_menu);
-++
-++# undef S_LOCK
-++# undef S_RESTART_DWM
-++# undef S_OFFSCREEN
-++# undef S_EXIT
-++# undef S_REBOOT
-++# undef S_SHUTDOWN
-++# undef S_LOCK_ICON
-++# undef S_RESTART_DWM_ICON
-++# undef S_OFFSCREEN_ICON
-++# undef S_EXIT_ICON
-++# undef S_REBOOT_ICON
-++# undef S_SHUTDOWN_ICON
-++# undef S_FORMAT
-++# undef S_FORMAT_CLEAR
-++}
-diff --git a/patches/used/dwm-restartsig-20180523-6.2.diff b/patches/used/dwm-restartsig-20180523-6.2.diff
-new file mode 100644
-index 0000000..f1f8680
---- /dev/null
-+++ b/patches/used/dwm-restartsig-20180523-6.2.diff
-@@ -0,0 +1,139 @@
-+From 2991f37f0aaf44b9f9b11e7893ff0af8eb88f649 Mon Sep 17 00:00:00 2001
-+From: Christopher Drelich <cd@cdrakka.com>
-+Date: Wed, 23 May 2018 22:50:38 -0400
-+Subject: [PATCH] Modifies quit to handle restarts and adds SIGHUP and SIGTERM
-+ handlers.
-+
-+Modified quit() to restart if it receives arg .i = 1
-+MOD+CTRL+SHIFT+Q was added to confid.def.h to do just that.
-+
-+Signal handlers were handled for SIGHUP and SIGTERM.
-+If dwm receives these signals it calls quit() with
-+arg .i = to 1 or 0, respectively.
-+
-+To restart dwm:
-+MOD+CTRL+SHIFT+Q
-+or
-+kill -HUP dwmpid
-+
-+To quit dwm cleanly:
-+MOD+SHIFT+Q
-+or
-+kill -TERM dwmpid
-+---
-+ config.def.h | 1 +
-+ dwm.1 | 10 ++++++++++
-+ dwm.c | 22 ++++++++++++++++++++++
-+ 3 files changed, 33 insertions(+)
-+
-+diff --git a/config.def.h b/config.def.h
-+index a9ac303..e559429 100644
-+--- a/config.def.h
-++++ b/config.def.h
-+@@ -94,6 +94,7 @@ static Key keys[] = {
-+ TAGKEYS( XK_8, 7)
-+ TAGKEYS( XK_9, 8)
-+ { MODKEY|ShiftMask, XK_q, quit, {0} },
-++ { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} },
-+ };
-+
-+ /* button definitions */
-+diff --git a/dwm.1 b/dwm.1
-+index 13b3729..36a331c 100644
-+--- a/dwm.1
-++++ b/dwm.1
-+@@ -142,6 +142,9 @@ Add/remove all windows with nth tag to/from the view.
-+ .TP
-+ .B Mod1\-Shift\-q
-+ Quit dwm.
-++.TP
-++.B Mod1\-Control\-Shift\-q
-++Restart dwm.
-+ .SS Mouse commands
-+ .TP
-+ .B Mod1\-Button1
-+@@ -155,6 +158,13 @@ Resize focused window while dragging. Tiled windows will be toggled to the float
-+ .SH CUSTOMIZATION
-+ dwm is customized by creating a custom config.h and (re)compiling the source
-+ code. This keeps it fast, secure and simple.
-++.SH SIGNALS
-++.TP
-++.B SIGHUP - 1
-++Restart the dwm process.
-++.TP
-++.B SIGTERM - 15
-++Cleanly terminate the dwm process.
-+ .SH SEE ALSO
-+ .BR dmenu (1),
-+ .BR st (1)
-+diff --git a/dwm.c b/dwm.c
-+index bb95e26..286eecd 100644
-+--- a/dwm.c
-++++ b/dwm.c
-+@@ -205,6 +205,8 @@ static void setup(void);
-+ static void seturgent(Client *c, int urg);
-+ static void showhide(Client *c);
-+ static void sigchld(int unused);
-++static void sighup(int unused);
-++static void sigterm(int unused);
-+ static void spawn(const Arg *arg);
-+ static void tag(const Arg *arg);
-+ static void tagmon(const Arg *arg);
-+@@ -260,6 +262,7 @@ static void (*handler[LASTEvent]) (XEvent *) = {
-+ [UnmapNotify] = unmapnotify
-+ };
-+ static Atom wmatom[WMLast], netatom[NetLast];
-++static int restart = 0;
-+ static int running = 1;
-+ static Cur *cursor[CurLast];
-+ static Clr **scheme;
-+@@ -1248,6 +1251,7 @@ propertynotify(XEvent *e)
-+ void
-+ quit(const Arg *arg)
-+ {
-++ if(arg->i) restart = 1;
-+ running = 0;
-+ }
-+
-+@@ -1536,6 +1540,9 @@ setup(void)
-+ /* clean up any zombies immediately */
-+ sigchld(0);
-+
-++ signal(SIGHUP, sighup);
-++ signal(SIGTERM, sigterm);
-++
-+ /* init screen */
-+ screen = DefaultScreen(dpy);
-+ sw = DisplayWidth(dpy, screen);
-+@@ -1637,6 +1644,20 @@ sigchld(int unused)
-+ }
-+
-+ void
-++sighup(int unused)
-++{
-++ Arg a = {.i = 1};
-++ quit(&a);
-++}
-++
-++void
-++sigterm(int unused)
-++{
-++ Arg a = {.i = 0};
-++ quit(&a);
-++}
-++
-++void
-+ spawn(const Arg *arg)
-+ {
-+ if (arg->v == dmenucmd)
-+@@ -2139,6 +2160,7 @@ main(int argc, char *argv[])
-+ setup();
-+ scan();
-+ run();
-++ if(restart) execvp(argv[0], argv);
-+ cleanup();
-+ XCloseDisplay(dpy);
-+ return EXIT_SUCCESS;
-+--
-+2.7.4
-+
-diff --git a/slock/LICENSE b/slock/LICENSE
-new file mode 100644
-index 0000000..2e4419b
---- /dev/null
-+++ b/slock/LICENSE
-@@ -0,0 +1,24 @@
-+MIT/X Consortium License
-+
-+© 2015-2016 Markus Teich <markus.teich@stusta.mhn.de>
-+© 2014 Dimitris Papastamos <sin@2f30.org>
-+© 2006-2014 Anselm R Garbe <anselm@garbe.us>
-+© 2014-2016 Laslo Hunhold <dev@frign.de>
-+
-+Permission is hereby granted, free of charge, to any person obtaining a
-+copy of this software and associated documentation files (the "Software"),
-+to deal in the Software without restriction, including without limitation
-+the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+and/or sell copies of the Software, and to permit persons to whom the
-+Software is furnished to do so, subject to the following conditions:
-+
-+The above copyright notice and this permission notice shall be included in
-+all copies or substantial portions of the Software.
-+
-+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+DEALINGS IN THE SOFTWARE.
-diff --git a/slock/Makefile b/slock/Makefile
-new file mode 100644
-index 0000000..e730eb6
---- /dev/null
-+++ b/slock/Makefile
-@@ -0,0 +1,61 @@
-+# slock - simple screen locker
-+# See LICENSE file for copyright and license details.
-+
-+include config.mk
-+
-+SRC = slock.c ${COMPATSRC}
-+OBJ = ${SRC:.c=.o}
-+
-+all: options slock
-+
-+options:
-+ @echo slock build options:
-+ @echo "CFLAGS = ${CFLAGS}"
-+ @echo "LDFLAGS = ${LDFLAGS}"
-+ @echo "CC = ${CC}"
-+
-+.c.o:
-+ @echo CC $<
-+ @${CC} -c ${CFLAGS} $<
-+
-+${OBJ}: config.h config.mk arg.h util.h
-+
-+config.h:
-+ @echo creating $@ from config.def.h
-+ @cp config.def.h $@
-+
-+slock: ${OBJ}
-+ @echo CC -o $@
-+ @${CC} -o $@ ${OBJ} ${LDFLAGS}
-+
-+clean:
-+ @echo cleaning
-+ @rm -f config.h slock ${OBJ} slock-${VERSION}.tar.gz
-+
-+dist: clean
-+ @echo creating dist tarball
-+ @mkdir -p slock-${VERSION}
-+ @cp -R LICENSE Makefile README slock.1 config.mk \
-+ ${SRC} explicit_bzero.c config.def.h arg.h util.h slock-${VERSION}
-+ @tar -cf slock-${VERSION}.tar slock-${VERSION}
-+ @gzip slock-${VERSION}.tar
-+ @rm -rf slock-${VERSION}
-+
-+install: all
-+ @echo installing executable file to ${DESTDIR}${PREFIX}/bin
-+ @mkdir -p ${DESTDIR}${PREFIX}/bin
-+ @cp -f slock ${DESTDIR}${PREFIX}/bin
-+ @chmod 755 ${DESTDIR}${PREFIX}/bin/slock
-+ @chmod u+s ${DESTDIR}${PREFIX}/bin/slock
-+ @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
-+ @mkdir -p ${DESTDIR}${MANPREFIX}/man1
-+ @sed "s/VERSION/${VERSION}/g" <slock.1 >${DESTDIR}${MANPREFIX}/man1/slock.1
-+ @chmod 644 ${DESTDIR}${MANPREFIX}/man1/slock.1
-+
-+uninstall:
-+ @echo removing executable file from ${DESTDIR}${PREFIX}/bin
-+ @rm -f ${DESTDIR}${PREFIX}/bin/slock
-+ @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
-+ @rm -f ${DESTDIR}${MANPREFIX}/man1/slock.1
-+
-+.PHONY: all options clean dist install uninstall
-diff --git a/slock/README b/slock/README
-new file mode 100644
-index 0000000..dcacd01
---- /dev/null
-+++ b/slock/README
-@@ -0,0 +1,24 @@
-+slock - simple screen locker
-+============================
-+simple screen locker utility for X.
-+
-+
-+Requirements
-+------------
-+In order to build slock you need the Xlib header files.
-+
-+
-+Installation
-+------------
-+Edit config.mk to match your local setup (slock is installed into
-+the /usr/local namespace by default).
-+
-+Afterwards enter the following command to build and install slock
-+(if necessary as root):
-+
-+ make clean install
-+
-+
-+Running slock
-+-------------
-+Simply invoke the 'slock' command. To get out of it, enter your password.
-diff --git a/slock/arg.h b/slock/arg.h
-new file mode 100644
-index 0000000..0b23c53
---- /dev/null
-+++ b/slock/arg.h
-@@ -0,0 +1,65 @@
-+/*
-+ * Copy me if you can.
-+ * by 20h
-+ */
-+
-+#ifndef ARG_H__
-+#define ARG_H__
-+
-+extern char *argv0;
-+
-+/* use main(int argc, char *argv[]) */
-+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
-+ argv[0] && argv[0][0] == '-'\
-+ && argv[0][1];\
-+ argc--, argv++) {\
-+ char argc_;\
-+ char **argv_;\
-+ int brk_;\
-+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
-+ argv++;\
-+ argc--;\
-+ break;\
-+ }\
-+ for (brk_ = 0, argv[0]++, argv_ = argv;\
-+ argv[0][0] && !brk_;\
-+ argv[0]++) {\
-+ if (argv_ != argv)\
-+ break;\
-+ argc_ = argv[0][0];\
-+ switch (argc_)
-+
-+/* Handles obsolete -NUM syntax */
-+#define ARGNUM case '0':\
-+ case '1':\
-+ case '2':\
-+ case '3':\
-+ case '4':\
-+ case '5':\
-+ case '6':\
-+ case '7':\
-+ case '8':\
-+ case '9'
-+
-+#define ARGEND }\
-+ }
-+
-+#define ARGC() argc_
-+
-+#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX))
-+
-+#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
-+ ((x), abort(), (char *)0) :\
-+ (brk_ = 1, (argv[0][1] != '\0')?\
-+ (&argv[0][1]) :\
-+ (argc--, argv++, argv[0])))
-+
-+#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
-+ (char *)0 :\
-+ (brk_ = 1, (argv[0][1] != '\0')?\
-+ (&argv[0][1]) :\
-+ (argc--, argv++, argv[0])))
-+
-+#define LNGARG() &argv[0][0]
-+
-+#endif
-diff --git a/slock/config.def.h b/slock/config.def.h
-new file mode 100644
-index 0000000..730b31a
---- /dev/null
-+++ b/slock/config.def.h
-@@ -0,0 +1,33 @@
-+/* user and group to drop privileges to */
-+static const char *user = "nobody";
-+static const char *group = "nobody";
-+
-+static const char *colorname[NUMCOLS] = {
-+ [BACKGROUND] = "black", /* after initialization */
-+ [INIT] = "#2d2d2d", /* after initialization */
-+ [INPUT] = "#f4cdd4", /* during input */
-+ [FAILED] = "#861a22", /* wrong password */
-+ [CAPS] = "#ff00ff", /* CapsLock on */
-+};
-+
-+/* treat a cleared input like a wrong password (color) */
-+static const int failonclear = 1;
-+
-+/* insert grid pattern with scale 1:1, the size can be changed with logosize */
-+static const int logosize = 75;
-+static const int logow = 12; /* grid width and height for right center alignment*/
-+static const int logoh = 6;
-+
-+static XRectangle rectangles[9] = {
-+ /* x y w h */
-+ { 0, 3, 1, 3 },
-+ { 1, 3, 2, 1 },
-+ { 0, 5, 8, 1 },
-+ { 3, 0, 1, 5 },
-+ { 5, 3, 1, 2 },
-+ { 7, 3, 1, 2 },
-+ { 8, 3, 4, 1 },
-+ { 9, 4, 1, 2 },
-+ { 11, 4, 1, 2 },
-+
-+};
-diff --git a/slock/config.mk b/slock/config.mk
-new file mode 100644
-index 0000000..08356e8
---- /dev/null
-+++ b/slock/config.mk
-@@ -0,0 +1,40 @@
-+# slock version
-+VERSION = 1.4
-+
-+# Customize below to fit your system
-+
-+# paths
-+PREFIX = /usr/local
-+MANPREFIX = ${PREFIX}/share/man
-+
-+X11INC = /usr/X11R6/include
-+X11LIB = /usr/X11R6/lib
-+
-+# Xinerama, comment if you don't want it
-+XINERAMALIBS = -lXinerama
-+XINERAMAFLAGS = -DXINERAMA
-+
-+# freetype
-+FREETYPELIBS = -lXft
-+FREETYPEINC = /usr/include/freetype2
-+
-+# includes and libs
-+INCS = -I. -I/usr/include -I${X11INC} -I${FREETYPEINC}
-+LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXext -lXrandr
-+
-+# flags
-+CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -DHAVE_SHADOW_H ${XINERAMAFLAGS}
-+CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
-+LDFLAGS = -s ${LIBS}
-+COMPATSRC = explicit_bzero.c
-+
-+# On OpenBSD and Darwin remove -lcrypt from LIBS
-+#LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXext -lXrandr
-+# On *BSD remove -DHAVE_SHADOW_H from CPPFLAGS
-+# On NetBSD add -D_NETBSD_SOURCE to CPPFLAGS
-+#CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE -D_NETBSD_SOURCE
-+# On OpenBSD set COMPATSRC to empty
-+#COMPATSRC =
-+
-+# compiler and linker
-+CC = cc
-diff --git a/slock/explicit_bzero.c b/slock/explicit_bzero.c
-new file mode 100644
-index 0000000..3e33ca8
---- /dev/null
-+++ b/slock/explicit_bzero.c
-@@ -0,0 +1,19 @@
-+/* $OpenBSD: explicit_bzero.c,v 1.3 2014/06/21 02:34:26 matthew Exp $ */
-+/*
-+ * Public domain.
-+ * Written by Matthew Dempsky.
-+ */
-+
-+#include <string.h>
-+
-+__attribute__((weak)) void
-+__explicit_bzero_hook(void *buf, size_t len)
-+{
-+}
-+
-+void
-+explicit_bzero(void *buf, size_t len)
-+{
-+ memset(buf, 0, len);
-+ __explicit_bzero_hook(buf, len);
-+}
-diff --git a/slock/slock.1 b/slock/slock.1
-new file mode 100644
-index 0000000..82cdcd6
---- /dev/null
-+++ b/slock/slock.1
-@@ -0,0 +1,39 @@
-+.Dd 2016-08-23
-+.Dt SLOCK 1
-+.Sh NAME
-+.Nm slock
-+.Nd simple X screen locker
-+.Sh SYNOPSIS
-+.Nm
-+.Op Fl v
-+.Op Ar cmd Op Ar arg ...
-+.Sh DESCRIPTION
-+.Nm
-+is a simple X screen locker. If provided,
-+.Ar cmd Op Ar arg ...
-+is executed after the screen has been locked.
-+.Sh OPTIONS
-+.Bl -tag -width Ds
-+.It Fl v
-+Print version information to stdout and exit.
-+.El
-+.Sh SECURITY CONSIDERATIONS
-+To make sure a locked screen can not be bypassed by switching VTs
-+or killing the X server with Ctrl+Alt+Backspace, it is recommended
-+to disable both in
-+.Xr xorg.conf 5
-+for maximum security:
-+.Bd -literal -offset left
-+Section "ServerFlags"
-+ Option "DontVTSwitch" "True"
-+ Option "DontZap" "True"
-+EndSection
-+.Ed
-+.Sh EXAMPLES
-+$
-+.Nm
-+/usr/sbin/s2ram
-+.Sh CUSTOMIZATION
-+.Nm
-+can be customized by creating a custom config.h from config.def.h and
-+(re)compiling the source code. This keeps it fast, secure and simple.
-diff --git a/slock/slock.c b/slock/slock.c
-new file mode 100644
-index 0000000..94b4a47
---- /dev/null
-+++ b/slock/slock.c
-@@ -0,0 +1,482 @@
-+/* See LICENSE file for license details. */
-+#define _XOPEN_SOURCE 500
-+#define LENGTH(X) (sizeof X / sizeof X[0])
-+#if HAVE_SHADOW_H
-+#include <shadow.h>
-+#endif
-+
-+#include <ctype.h>
-+#include <errno.h>
-+#include <grp.h>
-+#include <pwd.h>
-+#include <stdarg.h>
-+#include <stdlib.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <unistd.h>
-+#include <sys/types.h>
-+#include <X11/extensions/Xrandr.h>
-+#ifdef XINERAMA
-+#include <X11/extensions/Xinerama.h>
-+#endif
-+#include <X11/keysym.h>
-+#include <X11/XF86keysym.h>
-+#include <X11/Xlib.h>
-+#include <X11/Xutil.h>
-+#include <X11/Xft/Xft.h>
-+#include <X11/XKBlib.h>
-+
-+#include "arg.h"
-+#include "util.h"
-+
-+char *argv0;
-+
-+enum {
-+ BACKGROUND,
-+ INIT,
-+ INPUT,
-+ FAILED,
-+ CAPS,
-+ NUMCOLS
-+};
-+
-+#include "config.h"
-+
-+struct lock {
-+ int screen;
-+ Window root, win;
-+ Pixmap pmap;
-+ unsigned long colors[NUMCOLS];
-+ unsigned int x, y;
-+ unsigned int xoff, yoff, mw, mh;
-+ Drawable drawable;
-+ GC gc;
-+ XRectangle rectangles[LENGTH(rectangles)]
-+};
-+
-+struct xrandr {
-+ int active;
-+ int evbase;
-+ int errbase;
-+};
-+
-+static void
-+die(const char *errstr, ...)
-+{
-+ va_list ap;
-+
-+ va_start(ap, errstr);
-+ vfprintf(stderr, errstr, ap);
-+ va_end(ap);
-+ exit(1);
-+}
-+
-+#ifdef __linux__
-+#include <fcntl.h>
-+#include <linux/oom.h>
-+
-+static void
-+dontkillme(void)
-+{
-+ FILE *f;
-+ const char oomfile[] = "/proc/self/oom_score_adj";
-+
-+ if (!(f = fopen(oomfile, "w"))) {
-+ if (errno == ENOENT)
-+ return;
-+ die("slock: fopen %s: %s\n", oomfile, strerror(errno));
-+ }
-+ fprintf(f, "%d", OOM_SCORE_ADJ_MIN);
-+ if (fclose(f)) {
-+ if (errno == EACCES)
-+ die("slock: unable to disable OOM killer. "
-+ "Make sure to suid or sgid slock.\n");
-+ else
-+ die("slock: fclose %s: %s\n", oomfile, strerror(errno));
-+ }
-+}
-+#endif
-+
-+static const char *
-+gethash(void)
-+{
-+ const char *hash;
-+ struct passwd *pw;
-+
-+ /* Check if the current user has a password entry */
-+ errno = 0;
-+ if (!(pw = getpwuid(getuid()))) {
-+ if (errno)
-+ die("slock: getpwuid: %s\n", strerror(errno));
-+ else
-+ die("slock: cannot retrieve password entry\n");
-+ }
-+ hash = pw->pw_passwd;
-+
-+#if HAVE_SHADOW_H
-+ if (!strcmp(hash, "x")) {
-+ struct spwd *sp;
-+ if (!(sp = getspnam(pw->pw_name)))
-+ die("slock: getspnam: cannot retrieve shadow entry. "
-+ "Make sure to suid or sgid slock.\n");
-+ hash = sp->sp_pwdp;
-+ }
-+#else
-+ if (!strcmp(hash, "*")) {
-+#ifdef __OpenBSD__
-+ if (!(pw = getpwuid_shadow(getuid())))
-+ die("slock: getpwnam_shadow: cannot retrieve shadow entry. "
-+ "Make sure to suid or sgid slock.\n");
-+ hash = pw->pw_passwd;
-+#else
-+ die("slock: getpwuid: cannot retrieve shadow entry. "
-+ "Make sure to suid or sgid slock.\n");
-+#endif /* __OpenBSD__ */
-+ }
-+#endif /* HAVE_SHADOW_H */
-+
-+ return hash;
-+}
-+
-+static void
-+resizerectangles(struct lock *lock)
-+{
-+ int i;
-+
-+ for (i = 0; i < LENGTH(rectangles); i++){
-+ lock->rectangles[i].x = (rectangles[i].x * logosize)
-+ + lock->xoff + ((lock->mw) / 2) - (logow / 2 * logosize);
-+ lock->rectangles[i].y = (rectangles[i].y * logosize)
-+ + lock->yoff + ((lock->mh) / 2) - (logoh / 2 * logosize);
-+ lock->rectangles[i].width = rectangles[i].width * logosize;
-+ lock->rectangles[i].height = rectangles[i].height * logosize;
-+ }
-+}
-+
-+static void
-+drawlogo(Display *dpy, struct lock *lock, int color)
-+{
-+ XSetForeground(dpy, lock->gc, lock->colors[BACKGROUND]);
-+ XFillRectangle(dpy, lock->drawable, lock->gc, 0, 0, lock->x, lock->y);
-+ XSetForeground(dpy, lock->gc, lock->colors[color]);
-+ XFillRectangles(dpy, lock->drawable, lock->gc, lock->rectangles, LENGTH(rectangles));
-+ XCopyArea(dpy, lock->drawable, lock->win, lock->gc, 0, 0, lock->x, lock->y, 0, 0);
-+ XSync(dpy, False);
-+}
-+
-+static void
-+readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens,
-+ const char *hash)
-+{
-+ XRRScreenChangeNotifyEvent *rre;
-+ char buf[32], passwd[256], *inputhash;
-+ int caps, num, screen, running, failure, oldc;
-+ unsigned int len, color, indicators;
-+ KeySym ksym;
-+ XEvent ev;
-+
-+ len = 0;
-+ caps = 0;
-+ running = 1;
-+ failure = 0;
-+ oldc = INIT;
-+
-+ if (!XkbGetIndicatorState(dpy, XkbUseCoreKbd, &indicators))
-+ caps = indicators & 1;
-+
-+ while (running && !XNextEvent(dpy, &ev)) {
-+ if (ev.type == KeyPress) {
-+ explicit_bzero(&buf, sizeof(buf));
-+ num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0);
-+ if (IsKeypadKey(ksym)) {
-+ if (ksym == XK_KP_Enter)
-+ ksym = XK_Return;
-+ else if (ksym >= XK_KP_0 && ksym <= XK_KP_9)
-+ ksym = (ksym - XK_KP_0) + XK_0;
-+ }
-+ if (IsFunctionKey(ksym) ||
-+ IsKeypadKey(ksym) ||
-+ IsMiscFunctionKey(ksym) ||
-+ IsPFKey(ksym) ||
-+ IsPrivateKeypadKey(ksym))
-+ continue;
-+ switch (ksym) {
-+ case XK_Return:
-+ passwd[len] = '\0';
-+ errno = 0;
-+ if (!(inputhash = crypt(passwd, hash)))
-+ fprintf(stderr, "slock: crypt: %s\n", strerror(errno));
-+ else
-+ running = !!strcmp(inputhash, hash);
-+ if (running) {
-+ XBell(dpy, 100);
-+ failure = 1;
-+ }
-+ explicit_bzero(&passwd, sizeof(passwd));
-+ len = 0;
-+ break;
-+ case XK_Escape:
-+ explicit_bzero(&passwd, sizeof(passwd));
-+ len = 0;
-+ break;
-+ case XK_BackSpace:
-+ if (len)
-+ passwd[--len] = '\0';
-+ break;
-+ case XK_Caps_Lock:
-+ caps = !caps;
-+ break;
-+ case XF86XK_AudioLowerVolume:
-+ case XF86XK_AudioMute:
-+ case XF86XK_AudioRaiseVolume:
-+ case XF86XK_AudioPlay:
-+ case XF86XK_AudioStop:
-+ case XF86XK_AudioPrev:
-+ case XF86XK_AudioNext:
-+ XSendEvent(dpy, locks[0]->root, True, KeyPressMask, &ev);
-+ break;
-+ default:
-+ if (num && !iscntrl((int)buf[0]) &&
-+ (len + num < sizeof(passwd))) {
-+ memcpy(passwd + len, buf, num);
-+ len += num;
-+ }
-+ break;
-+ }
-+
-+ color = len ? (caps ? CAPS : INPUT) : (failure || failonclear ? FAILED : INIT);
-+ if (running && oldc != color) {
-+ for (screen = 0; screen < nscreens; screen++) {
-+ drawlogo(dpy, locks[screen], color);
-+ }
-+ oldc = color;
-+ }
-+ } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) {
-+ rre = (XRRScreenChangeNotifyEvent*)&ev;
-+ for (screen = 0; screen < nscreens; screen++) {
-+ if (locks[screen]->win == rre->window) {
-+ if (rre->rotation == RR_Rotate_90 ||
-+ rre->rotation == RR_Rotate_270)
-+ XResizeWindow(dpy, locks[screen]->win,
-+ rre->height, rre->width);
-+ else
-+ XResizeWindow(dpy, locks[screen]->win,
-+ rre->width, rre->height);
-+ XClearWindow(dpy, locks[screen]->win);
-+ break;
-+ }
-+ }
-+ } else {
-+ for (screen = 0; screen < nscreens; screen++)
-+ XRaiseWindow(dpy, locks[screen]->win);
-+ }
-+ }
-+}
-+
-+static struct lock *
-+lockscreen(Display *dpy, struct xrandr *rr, int screen)
-+{
-+ char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
-+ int i, ptgrab, kbgrab;
-+ struct lock *lock;
-+ XColor color, dummy;
-+ XSetWindowAttributes wa;
-+ Cursor invisible;
-+#ifdef XINERAMA
-+ XineramaScreenInfo *info;
-+ int n;
-+#endif
-+
-+ if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock))))
-+ return NULL;
-+
-+ lock->screen = screen;
-+ lock->root = RootWindow(dpy, lock->screen);
-+
-+ for (i = 0; i < NUMCOLS; i++) {
-+ XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen),
-+ colorname[i], &color, &dummy);
-+ lock->colors[i] = color.pixel;
-+ }
-+
-+ lock->x = DisplayWidth(dpy, lock->screen);
-+ lock->y = DisplayHeight(dpy, lock->screen);
-+#ifdef XINERAMA
-+ if ((info = XineramaQueryScreens(dpy, &n))) {
-+ lock->xoff = info[0].x_org;
-+ lock->yoff = info[0].y_org;
-+ lock->mw = info[0].width;
-+ lock->mh = info[0].height;
-+ } else
-+#endif
-+ {
-+ lock->xoff = lock->yoff = 0;
-+ lock->mw = lock->x;
-+ lock->mh = lock->y;
-+ }
-+ lock->drawable = XCreatePixmap(dpy, lock->root,
-+ lock->x, lock->y, DefaultDepth(dpy, screen));
-+ lock->gc = XCreateGC(dpy, lock->root, 0, NULL);
-+ XSetLineAttributes(dpy, lock->gc, 1, LineSolid, CapButt, JoinMiter);
-+
-+ /* init */
-+ wa.override_redirect = 1;
-+ wa.background_pixel = lock->colors[BACKGROUND];
-+ lock->win = XCreateWindow(dpy, lock->root, 0, 0,
-+ lock->x, lock->y,
-+ 0, DefaultDepth(dpy, lock->screen),
-+ CopyFromParent,
-+ DefaultVisual(dpy, lock->screen),
-+ CWOverrideRedirect | CWBackPixel, &wa);
-+ lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
-+ invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
-+ &color, &color, 0, 0);
-+ XDefineCursor(dpy, lock->win, invisible);
-+
-+ resizerectangles(lock);
-+
-+ /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
-+ for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
-+ if (ptgrab != GrabSuccess) {
-+ ptgrab = XGrabPointer(dpy, lock->root, False,
-+ ButtonPressMask | ButtonReleaseMask |
-+ PointerMotionMask, GrabModeAsync,
-+ GrabModeAsync, None, invisible, CurrentTime);
-+ }
-+ if (kbgrab != GrabSuccess) {
-+ kbgrab = XGrabKeyboard(dpy, lock->root, True,
-+ GrabModeAsync, GrabModeAsync, CurrentTime);
-+ }
-+
-+ /* input is grabbed: we can lock the screen */
-+ if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
-+ XMapRaised(dpy, lock->win);
-+ if (rr->active)
-+ XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
-+
-+ XSelectInput(dpy, lock->root, SubstructureNotifyMask);
-+ drawlogo(dpy, lock, INIT);
-+ return lock;
-+ }
-+
-+ /* retry on AlreadyGrabbed but fail on other errors */
-+ if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) ||
-+ (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
-+ break;
-+
-+ usleep(100000);
-+ }
-+
-+ /* we couldn't grab all input: fail out */
-+ if (ptgrab != GrabSuccess)
-+ fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n",
-+ screen);
-+ if (kbgrab != GrabSuccess)
-+ fprintf(stderr, "slock: unable to grab keyboard for screen %d\n",
-+ screen);
-+ return NULL;
-+}
-+
-+static void
-+usage(void)
-+{
-+ die("usage: slock [-v] [cmd [arg ...]]\n");
-+}
-+
-+int
-+main(int argc, char **argv) {
-+ struct xrandr rr;
-+ struct lock **locks;
-+ struct passwd *pwd;
-+ struct group *grp;
-+ uid_t duid;
-+ gid_t dgid;
-+ const char *hash;
-+ Display *dpy;
-+ int s, nlocks, nscreens;
-+
-+ ARGBEGIN {
-+ case 'v':
-+ fprintf(stderr, "slock-"VERSION"\n");
-+ return 0;
-+ default:
-+ usage();
-+ } ARGEND
-+
-+ /* validate drop-user and -group */
-+ errno = 0;
-+ if (!(pwd = getpwnam(user)))
-+ die("slock: getpwnam %s: %s\n", user,
-+ errno ? strerror(errno) : "user entry not found");
-+ duid = pwd->pw_uid;
-+ errno = 0;
-+ if (!(grp = getgrnam(group)))
-+ die("slock: getgrnam %s: %s\n", group,
-+ errno ? strerror(errno) : "group entry not found");
-+ dgid = grp->gr_gid;
-+
-+#ifdef __linux__
-+ dontkillme();
-+#endif
-+
-+ hash = gethash();
-+ errno = 0;
-+ if (!crypt("", hash))
-+ die("slock: crypt: %s\n", strerror(errno));
-+
-+ if (!(dpy = XOpenDisplay(NULL)))
-+ die("slock: cannot open display\n");
-+
-+ /* drop privileges */
-+ if (setgroups(0, NULL) < 0)
-+ die("slock: setgroups: %s\n", strerror(errno));
-+ if (setgid(dgid) < 0)
-+ die("slock: setgid: %s\n", strerror(errno));
-+ if (setuid(duid) < 0)
-+ die("slock: setuid: %s\n", strerror(errno));
-+
-+ /* check for Xrandr support */
-+ rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
-+
-+ /* get number of screens in display "dpy" and blank them */
-+ nscreens = ScreenCount(dpy);
-+ if (!(locks = calloc(nscreens, sizeof(struct lock *))))
-+ die("slock: out of memory\n");
-+ for (nlocks = 0, s = 0; s < nscreens; s++) {
-+ if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL)
-+ nlocks++;
-+ else
-+ break;
-+ }
-+ XSync(dpy, 0);
-+
-+ /* did we manage to lock everything? */
-+ if (nlocks != nscreens)
-+ return 1;
-+
-+ /* run post-lock command */
-+ if (argc > 0) {
-+ switch (fork()) {
-+ case -1:
-+ die("slock: fork failed: %s\n", strerror(errno));
-+ case 0:
-+ if (close(ConnectionNumber(dpy)) < 0)
-+ die("slock: close: %s\n", strerror(errno));
-+ execvp(argv[0], argv);
-+ fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno));
-+ _exit(1);
-+ }
-+ }
-+
-+ /* everything is now blank. Wait for the correct password */
-+ readpw(dpy, &rr, locks, nscreens, hash);
-+
-+ for (nlocks = 0, s = 0; s < nscreens; s++) {
-+ XFreePixmap(dpy, locks[s]->drawable);
-+ XFreeGC(dpy, locks[s]->gc);
-+ }
-+
-+ XSync(dpy, 0);
-+ XCloseDisplay(dpy);
-+ return 0;
-+}
-diff --git a/slock/util.h b/slock/util.h
-new file mode 100644
-index 0000000..6f748b8
---- /dev/null
-+++ b/slock/util.h
-@@ -0,0 +1,2 @@
-+#undef explicit_bzero
-+void explicit_bzero(void *, size_t);
-diff --git a/slstatus/LICENSE b/slstatus/LICENSE
-new file mode 100644
-index 0000000..8bee9c8
---- /dev/null
-+++ b/slstatus/LICENSE
-@@ -0,0 +1,43 @@
-+ISC License
-+
-+Copyright 2016-2022 Aaron Marcher <me@drkhsh.at>
-+
-+Copyright 2016 Roy Freytag <rfreytag@hs-mittweida.de>
-+Copyright 2016 Vincent Loupmon <vincentloupmon@gmail.com>
-+Copyright 2016 Daniel Walter <d.walter@0x90.at>
-+Copyright 2016-2018 Ali H. Fardan <raiz@firemail.cc>
-+Copyright 2016 Jody Leonard <me@jodyleonard.com>
-+Copyright 2016-2018 Quentin Rameau <quinq@fifth.space>
-+Copyright 2016 Mike Coddington <mike@coddington.us>
-+Copyright 2016-2018 Ivan J. <parazyd@dyne.org>
-+Copyright 2017 Tobias Stoeckmann <tobias@stoeckmann.org>
-+Copyright 2017-2018 Laslo Hunhold <dev@frign.de>
-+Copyright 2018 Darron Anderson <darronanderson@protonmail.com>
-+Copyright 2018 Josuah Demangeon <mail@josuah.net>
-+Copyright 2018 Tobias Tschinkowitz <tobias@he4d.net>
-+Copyright 2018 David Demelier <markand@malikania.fr>
-+Copyright 2018-2012 Michael Buch <michaelbuch12@gmail.com>
-+Copyright 2018 Ian Remmler <ian@remmler.org>
-+Copyright 2016-2019 Joerg Jung <jung@openbsd.org>
-+Copyright 2019 Ryan Kes <alrayyes@gmail.com>
-+Copyright 2019 Cem Keylan <cem@ckyln.com>
-+Copyright 2019 Dimitris Papastamos <dsp@2f30.org>
-+Copyright 2019-2022 Ingo Feinerer <feinerer@logic.at>
-+Copyright 2020 Alexandre Ratchov <alex@caoua.org>
-+Copyright 2020 Mart Lubbers <mart@martlubbers.net>
-+Copyright 2020 Daniel Moch <daniel@danielmoch.com>
-+Copyright 2022 Nickolas Raymond Kaczynski <nrk@disroot.org>
-+Copyright 2022 Patrick Iacob <iacobp@oregonstate.edu>
-+Copyright 2021-2022 Steven Ward <planet36@gmail.com>
-+
-+Permission to use, copy, modify, and/or distribute this software for any
-+purpose with or without fee is hereby granted, provided that the above
-+copyright notice and this permission notice appear in all copies.
-+
-+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-diff --git a/slstatus/Makefile b/slstatus/Makefile
-new file mode 100644
-index 0000000..43b8a3d
---- /dev/null
-+++ b/slstatus/Makefile
-@@ -0,0 +1,70 @@
-+# See LICENSE file for copyright and license details
-+# slstatus - suckless status monitor
-+.POSIX:
-+
-+include config.mk
-+
-+REQ = util
-+COM =\
-+ components/battery\
-+ components/cat\
-+ components/cpu\
-+ components/datetime\
-+ components/disk\
-+ components/entropy\
-+ components/hostname\
-+ components/ip\
-+ components/kanji\
-+ components/kernel_release\
-+ components/keyboard_indicators\
-+ components/keymap\
-+ components/load_avg\
-+ components/netspeeds\
-+ components/num_files\
-+ components/ram\
-+ components/run_command\
-+ components/swap\
-+ components/temperature\
-+ components/uptime\
-+ components/user\
-+ components/volume\
-+ components/wifi
-+
-+all: slstatus
-+
-+$(COM:=.o): config.mk $(REQ:=.h) slstatus.h
-+slstatus.o: slstatus.c slstatus.h arg.h config.h config.mk $(REQ:=.h)
-+
-+.c.o:
-+ $(CC) -o $@ -c $(CPPFLAGS) $(CFLAGS) $<
-+
-+config.h:
-+ cp config.def.h $@
-+
-+slstatus: slstatus.o $(COM:=.o) $(REQ:=.o)
-+ $(CC) -o $@ $(LDFLAGS) $(COM:=.o) $(REQ:=.o) slstatus.o $(LDLIBS)
-+
-+clean:
-+ rm -f config.h slstatus slstatus.o $(COM:=.o) $(REQ:=.o) slstatus-${VERSION}.tar.gz
-+
-+dist:
-+ rm -rf "slstatus-$(VERSION)"
-+ mkdir -p "slstatus-$(VERSION)/components"
-+ cp -R LICENSE Makefile README config.mk config.def.h \
-+ arg.h slstatus.h slstatus.c $(REQ:=.c) $(REQ:=.h) \
-+ slstatus.1 "slstatus-$(VERSION)"
-+ cp -R $(COM:=.c) "slstatus-$(VERSION)/components"
-+ tar -cf - "slstatus-$(VERSION)" | gzip -c > "slstatus-$(VERSION).tar.gz"
-+ rm -rf "slstatus-$(VERSION)"
-+
-+install: all
-+ mkdir -p "$(DESTDIR)$(PREFIX)/bin"
-+ cp -f slstatus "$(DESTDIR)$(PREFIX)/bin"
-+ chmod 755 "$(DESTDIR)$(PREFIX)/bin/slstatus"
-+ mkdir -p "$(DESTDIR)$(MANPREFIX)/man1"
-+ cp -f slstatus.1 "$(DESTDIR)$(MANPREFIX)/man1"
-+ chmod 644 "$(DESTDIR)$(MANPREFIX)/man1/slstatus.1"
-+
-+uninstall:
-+ rm -f "$(DESTDIR)$(PREFIX)/bin/slstatus"
-+ rm -f "$(DESTDIR)$(MANPREFIX)/man1/slstatus.1"
-diff --git a/slstatus/README b/slstatus/README
-new file mode 100644
-index 0000000..12d38bf
---- /dev/null
-+++ b/slstatus/README
-@@ -0,0 +1,65 @@
-+slstatus - suckless status
-+==========================
-+slstatus is a small tool for providing system status information to other
-+programs over the EWMH property of the root window (used by dwm(1)) or
-+standard input/output. It is designed to be as efficient as possible by
-+only issuing the minimum of system calls required.
-+
-+
-+Features
-+--------
-+- Battery percentage/state/time left
-+- Cat (read file)
-+- CPU usage
-+- CPU frequency
-+- Custom shell commands
-+- Date and time
-+- Disk status (free storage, percentage, total storage and used storage)
-+- Available entropy
-+- Username/GID/UID
-+- Hostname
-+- IP address (IPv4 and IPv6)
-+- Kernel version
-+- Keyboard indicators
-+- Keymap
-+- Load average
-+- Network speeds (RX and TX)
-+- Number of files in a directory (hint: Maildir)
-+- Memory status (free memory, percentage, total memory and used memory)
-+- Swap status (free swap, percentage, total swap and used swap)
-+- Temperature
-+- Uptime
-+- Volume percentage
-+- WiFi signal percentage and ESSID
-+
-+
-+Requirements
-+------------
-+Currently slstatus works on FreeBSD, Linux and OpenBSD.
-+In order to build slstatus you need the Xlib header files.
-+
-+- For volume percentage on Linux the kernel module `snd-mixer-oss` must be
-+ loaded.
-+- For volume percentage on FreeBSD, `sndio` must be installed.
-+
-+
-+Installation
-+------------
-+Edit config.mk to match your local setup (slstatus is installed into the
-+/usr/local namespace by default).
-+
-+Afterwards enter the following command to build and install slstatus (if
-+necessary as root):
-+
-+ make clean install
-+
-+
-+Running slstatus
-+----------------
-+See the man page for details.
-+
-+
-+Configuration
-+-------------
-+slstatus can be customized by creating a custom config.h and (re)compiling the
-+source code. This keeps it fast, secure and simple.
-diff --git a/slstatus/arg.h b/slstatus/arg.h
-new file mode 100644
-index 0000000..5f1f408
---- /dev/null
-+++ b/slstatus/arg.h
-@@ -0,0 +1,33 @@
-+/* See LICENSE file for copyright and license details. */
-+#ifndef ARG_H
-+#define ARG_H
-+
-+extern char *argv0;
-+
-+/* int main(int argc, char *argv[]) */
-+#define ARGBEGIN for (argv0 = *argv, *argv ? (argc--, argv++) : ((void *)0); \
-+ *argv && (*argv)[0] == '-' && (*argv)[1]; argc--, argv++) { \
-+ int i_, argused_; \
-+ if ((*argv)[1] == '-' && !(*argv)[2]) { \
-+ argc--, argv++; \
-+ break; \
-+ } \
-+ for (i_ = 1, argused_ = 0; (*argv)[i_]; i_++) { \
-+ switch ((*argv)[i_])
-+#define ARGEND if (argused_) { \
-+ if ((*argv)[i_ + 1]) { \
-+ break; \
-+ } else { \
-+ argc--, argv++; \
-+ break; \
-+ } \
-+ } \
-+ } \
-+ }
-+#define ARGC() ((*argv)[i_])
-+#define ARGF_(x) (((*argv)[i_ + 1]) ? (argused_ = 1, &((*argv)[i_ + 1])) : \
-+ (*(argv + 1)) ? (argused_ = 1, *(argv + 1)) : (x))
-+#define EARGF(x) ARGF_(((x), exit(1), (char *)0))
-+#define ARGF() ARGF_((char *)0)
-+
-+#endif
-diff --git a/slstatus/components/battery.c b/slstatus/components/battery.c
-new file mode 100644
-index 0000000..1c753f9
---- /dev/null
-+++ b/slstatus/components/battery.c
-@@ -0,0 +1,247 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdio.h>
-+#include <string.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+#if defined(__linux__)
-+/*
-+ * https://www.kernel.org/doc/html/latest/power/power_supply_class.html
-+ */
-+ #include <limits.h>
-+ #include <stdint.h>
-+ #include <unistd.h>
-+
-+ #define POWER_SUPPLY_CAPACITY "/sys/class/power_supply/%s/capacity"
-+ #define POWER_SUPPLY_STATUS "/sys/class/power_supply/%s/status"
-+ #define POWER_SUPPLY_CHARGE "/sys/class/power_supply/%s/charge_now"
-+ #define POWER_SUPPLY_ENERGY "/sys/class/power_supply/%s/energy_now"
-+ #define POWER_SUPPLY_CURRENT "/sys/class/power_supply/%s/current_now"
-+ #define POWER_SUPPLY_POWER "/sys/class/power_supply/%s/power_now"
-+
-+ static const char *
-+ pick(const char *bat, const char *f1, const char *f2, char *path,
-+ size_t length)
-+ {
-+ if (esnprintf(path, length, f1, bat) > 0 &&
-+ access(path, R_OK) == 0)
-+ return f1;
-+
-+ if (esnprintf(path, length, f2, bat) > 0 &&
-+ access(path, R_OK) == 0)
-+ return f2;
-+
-+ return NULL;
-+ }
-+
-+ const char *
-+ battery_perc(const char *bat)
-+ {
-+ int cap_perc;
-+ char path[PATH_MAX];
-+
-+ if (esnprintf(path, sizeof(path), POWER_SUPPLY_CAPACITY, bat) < 0)
-+ return NULL;
-+ if (pscanf(path, "%d", &cap_perc) != 1)
-+ return NULL;
-+
-+ return bprintf("%d", cap_perc);
-+ }
-+
-+ const char *
-+ battery_state(const char *bat)
-+ {
-+ static struct {
-+ char *state;
-+ char *symbol;
-+ } map[] = {
-+ { "Charging", "+" },
-+ { "Discharging", "-" },
-+ { "Full", "o" },
-+ { "Not charging", "o" },
-+ };
-+ size_t i;
-+ char path[PATH_MAX], state[12];
-+
-+ if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0)
-+ return NULL;
-+ if (pscanf(path, "%12[a-zA-Z ]", state) != 1)
-+ return NULL;
-+
-+ for (i = 0; i < LEN(map); i++)
-+ if (!strcmp(map[i].state, state))
-+ break;
-+
-+ return (i == LEN(map)) ? "?" : map[i].symbol;
-+ }
-+
-+ const char *
-+ battery_remaining(const char *bat)
-+ {
-+ uintmax_t charge_now, current_now, m, h;
-+ double timeleft;
-+ char path[PATH_MAX], state[12];
-+
-+ if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0)
-+ return NULL;
-+ if (pscanf(path, "%12[a-zA-Z ]", state) != 1)
-+ return NULL;
-+
-+ if (!pick(bat, POWER_SUPPLY_CHARGE, POWER_SUPPLY_ENERGY, path,
-+ sizeof(path)) ||
-+ pscanf(path, "%ju", &charge_now) < 0)
-+ return NULL;
-+
-+ if (!strcmp(state, "Discharging")) {
-+ if (!pick(bat, POWER_SUPPLY_CURRENT, POWER_SUPPLY_POWER, path,
-+ sizeof(path)) ||
-+ pscanf(path, "%ju", ¤t_now) < 0)
-+ return NULL;
-+
-+ if (current_now == 0)
-+ return NULL;
-+
-+ timeleft = (double)charge_now / (double)current_now;
-+ h = timeleft;
-+ m = (timeleft - (double)h) * 60;
-+
-+ return bprintf("%juh %jum", h, m);
-+ }
-+
-+ return "";
-+ }
-+#elif defined(__OpenBSD__)
-+ #include <fcntl.h>
-+ #include <machine/apmvar.h>
-+ #include <sys/ioctl.h>
-+ #include <unistd.h>
-+
-+ static int
-+ load_apm_power_info(struct apm_power_info *apm_info)
-+ {
-+ int fd;
-+
-+ fd = open("/dev/apm", O_RDONLY);
-+ if (fd < 0) {
-+ warn("open '/dev/apm':");
-+ return 0;
-+ }
-+
-+ memset(apm_info, 0, sizeof(struct apm_power_info));
-+ if (ioctl(fd, APM_IOC_GETPOWER, apm_info) < 0) {
-+ warn("ioctl 'APM_IOC_GETPOWER':");
-+ close(fd);
-+ return 0;
-+ }
-+ return close(fd), 1;
-+ }
-+
-+ const char *
-+ battery_perc(const char *unused)
-+ {
-+ struct apm_power_info apm_info;
-+
-+ if (load_apm_power_info(&apm_info))
-+ return bprintf("%d", apm_info.battery_life);
-+
-+ return NULL;
-+ }
-+
-+ const char *
-+ battery_state(const char *unused)
-+ {
-+ struct {
-+ unsigned int state;
-+ char *symbol;
-+ } map[] = {
-+ { APM_AC_ON, "+" },
-+ { APM_AC_OFF, "-" },
-+ };
-+ struct apm_power_info apm_info;
-+ size_t i;
-+
-+ if (load_apm_power_info(&apm_info)) {
-+ for (i = 0; i < LEN(map); i++)
-+ if (map[i].state == apm_info.ac_state)
-+ break;
-+
-+ return (i == LEN(map)) ? "?" : map[i].symbol;
-+ }
-+
-+ return NULL;
-+ }
-+
-+ const char *
-+ battery_remaining(const char *unused)
-+ {
-+ struct apm_power_info apm_info;
-+ unsigned int h, m;
-+
-+ if (load_apm_power_info(&apm_info)) {
-+ if (apm_info.ac_state != APM_AC_ON) {
-+ h = apm_info.minutes_left / 60;
-+ m = apm_info.minutes_left % 60;
-+ return bprintf("%uh %02um", h, m);
-+ } else {
-+ return "";
-+ }
-+ }
-+
-+ return NULL;
-+ }
-+#elif defined(__FreeBSD__)
-+ #include <sys/sysctl.h>
-+
-+ #define BATTERY_LIFE "hw.acpi.battery.life"
-+ #define BATTERY_STATE "hw.acpi.battery.state"
-+ #define BATTERY_TIME "hw.acpi.battery.time"
-+
-+ const char *
-+ battery_perc(const char *unused)
-+ {
-+ int cap_perc;
-+ size_t len;
-+
-+ len = sizeof(cap_perc);
-+ if (sysctlbyname(BATTERY_LIFE, &cap_perc, &len, NULL, 0) < 0 || !len)
-+ return NULL;
-+
-+ return bprintf("%d", cap_perc);
-+ }
-+
-+ const char *
-+ battery_state(const char *unused)
-+ {
-+ int state;
-+ size_t len;
-+
-+ len = sizeof(state);
-+ if (sysctlbyname(BATTERY_STATE, &state, &len, NULL, 0) < 0 || !len)
-+ return NULL;
-+
-+ switch (state) {
-+ case 0: /* FALLTHROUGH */
-+ case 2:
-+ return "+";
-+ case 1:
-+ return "-";
-+ default:
-+ return "?";
-+ }
-+ }
-+
-+ const char *
-+ battery_remaining(const char *unused)
-+ {
-+ int rem;
-+ size_t len;
-+
-+ len = sizeof(rem);
-+ if (sysctlbyname(BATTERY_TIME, &rem, &len, NULL, 0) < 0 || !len
-+ || rem < 0)
-+ return NULL;
-+
-+ return bprintf("%uh %02um", rem / 60, rem % 60);
-+ }
-+#endif
-diff --git a/slstatus/components/battery.o b/slstatus/components/battery.o
-new file mode 100644
-index 0000000..4c63c60
-Binary files /dev/null and b/slstatus/components/battery.o differ
-diff --git a/slstatus/components/cat.c b/slstatus/components/cat.c
-new file mode 100644
-index 0000000..07944cc
---- /dev/null
-+++ b/slstatus/components/cat.c
-@@ -0,0 +1,32 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdio.h>
-+#include <string.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+const char *
-+cat(const char *path)
-+{
-+ char *f;
-+ FILE *fp;
-+
-+ if (!(fp = fopen(path, "r"))) {
-+ warn("fopen '%s':", path);
-+ return NULL;
-+ }
-+
-+ f = fgets(buf, sizeof(buf) - 1, fp);
-+ if (fclose(fp) < 0) {
-+ warn("fclose '%s':", path);
-+ return NULL;
-+ }
-+ if (!f)
-+ return NULL;
-+
-+ if ((f = strrchr(buf, '\n')))
-+ f[0] = '\0';
-+
-+ return buf[0] ? buf : NULL;
-+}
-+
-diff --git a/slstatus/components/cat.o b/slstatus/components/cat.o
-new file mode 100644
-index 0000000..3b50ebc
-Binary files /dev/null and b/slstatus/components/cat.o differ
-diff --git a/slstatus/components/cpu.c b/slstatus/components/cpu.c
-new file mode 100644
-index 0000000..d0d03c7
---- /dev/null
-+++ b/slstatus/components/cpu.c
-@@ -0,0 +1,157 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdint.h>
-+#include <stdio.h>
-+#include <string.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+#if defined(__linux__)
-+ #define CPU_FREQ "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
-+
-+ const char *
-+ cpu_freq(const char *unused)
-+ {
-+ uintmax_t freq;
-+
-+ /* in kHz */
-+ if (pscanf(CPU_FREQ, "%ju", &freq) != 1)
-+ return NULL;
-+
-+ return fmt_human(freq * 1000, 1000);
-+ }
-+
-+ const char *
-+ cpu_perc(const char *unused)
-+ {
-+ static long double a[7];
-+ long double b[7], sum;
-+
-+ memcpy(b, a, sizeof(b));
-+ /* cpu user nice system idle iowait irq softirq */
-+ if (pscanf("/proc/stat", "%*s %Lf %Lf %Lf %Lf %Lf %Lf %Lf",
-+ &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6])
-+ != 7)
-+ return NULL;
-+
-+ if (b[0] == 0)
-+ return NULL;
-+
-+ sum = (b[0] + b[1] + b[2] + b[3] + b[4] + b[5] + b[6]) -
-+ (a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6]);
-+
-+ if (sum == 0)
-+ return NULL;
-+
-+ return bprintf("%d", (int)(100 *
-+ ((b[0] + b[1] + b[2] + b[5] + b[6]) -
-+ (a[0] + a[1] + a[2] + a[5] + a[6])) / sum));
-+ }
-+#elif defined(__OpenBSD__)
-+ #include <sys/param.h>
-+ #include <sys/sched.h>
-+ #include <sys/sysctl.h>
-+
-+ const char *
-+ cpu_freq(const char *unused)
-+ {
-+ int freq, mib[2];
-+ size_t size;
-+
-+ mib[0] = CTL_HW;
-+ mib[1] = HW_CPUSPEED;
-+
-+ size = sizeof(freq);
-+
-+ /* in MHz */
-+ if (sysctl(mib, 2, &freq, &size, NULL, 0) < 0) {
-+ warn("sysctl 'HW_CPUSPEED':");
-+ return NULL;
-+ }
-+
-+ return fmt_human(freq * 1E6, 1000);
-+ }
-+
-+ const char *
-+ cpu_perc(const char *unused)
-+ {
-+ int mib[2];
-+ static uintmax_t a[CPUSTATES];
-+ uintmax_t b[CPUSTATES], sum;
-+ size_t size;
-+
-+ mib[0] = CTL_KERN;
-+ mib[1] = KERN_CPTIME;
-+
-+ size = sizeof(a);
-+
-+ memcpy(b, a, sizeof(b));
-+ if (sysctl(mib, 2, &a, &size, NULL, 0) < 0) {
-+ warn("sysctl 'KERN_CPTIME':");
-+ return NULL;
-+ }
-+ if (b[0] == 0)
-+ return NULL;
-+
-+ sum = (a[CP_USER] + a[CP_NICE] + a[CP_SYS] + a[CP_INTR] + a[CP_IDLE]) -
-+ (b[CP_USER] + b[CP_NICE] + b[CP_SYS] + b[CP_INTR] + b[CP_IDLE]);
-+
-+ if (sum == 0)
-+ return NULL;
-+
-+ return bprintf("%d", 100 *
-+ ((a[CP_USER] + a[CP_NICE] + a[CP_SYS] +
-+ a[CP_INTR]) -
-+ (b[CP_USER] + b[CP_NICE] + b[CP_SYS] +
-+ b[CP_INTR])) / sum);
-+ }
-+#elif defined(__FreeBSD__)
-+ #include <devstat.h>
-+ #include <sys/param.h>
-+ #include <sys/sysctl.h>
-+
-+ const char *
-+ cpu_freq(const char *unused)
-+ {
-+ int freq;
-+ size_t size;
-+
-+ size = sizeof(freq);
-+ /* in MHz */
-+ if (sysctlbyname("hw.clockrate", &freq, &size, NULL, 0) < 0 || !size) {
-+ warn("sysctlbyname 'hw.clockrate':");
-+ return NULL;
-+ }
-+
-+ return fmt_human(freq * 1E6, 1000);
-+ }
-+
-+ const char *
-+ cpu_perc(const char *unused)
-+ {
-+ size_t size;
-+ static long a[CPUSTATES];
-+ long b[CPUSTATES], sum;
-+
-+ size = sizeof(a);
-+ memcpy(b, a, sizeof(b));
-+ if (sysctlbyname("kern.cp_time", &a, &size, NULL, 0) < 0 || !size) {
-+ warn("sysctlbyname 'kern.cp_time':");
-+ return NULL;
-+ }
-+ if (b[0] == 0)
-+ return NULL;
-+
-+ sum = (a[CP_USER] + a[CP_NICE] + a[CP_SYS] + a[CP_INTR] + a[CP_IDLE]) -
-+ (b[CP_USER] + b[CP_NICE] + b[CP_SYS] + b[CP_INTR] + b[CP_IDLE]);
-+
-+ if (sum == 0)
-+ return NULL;
-+
-+ return bprintf("%d", 100 *
-+ ((a[CP_USER] + a[CP_NICE] + a[CP_SYS] +
-+ a[CP_INTR]) -
-+ (b[CP_USER] + b[CP_NICE] + b[CP_SYS] +
-+ b[CP_INTR])) / sum);
-+ }
-+#endif
-diff --git a/slstatus/components/cpu.o b/slstatus/components/cpu.o
-new file mode 100644
-index 0000000..969d09c
-Binary files /dev/null and b/slstatus/components/cpu.o differ
-diff --git a/slstatus/components/datetime.c b/slstatus/components/datetime.c
-new file mode 100644
-index 0000000..5b10daf
---- /dev/null
-+++ b/slstatus/components/datetime.c
-@@ -0,0 +1,20 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdio.h>
-+#include <time.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+const char *
-+datetime(const char *fmt)
-+{
-+ time_t t;
-+
-+ t = time(NULL);
-+ if (!strftime(buf, sizeof(buf), fmt, localtime(&t))) {
-+ warn("strftime: Result string exceeds buffer size");
-+ return NULL;
-+ }
-+
-+ return buf;
-+}
-diff --git a/slstatus/components/datetime.o b/slstatus/components/datetime.o
-new file mode 100644
-index 0000000..0353444
-Binary files /dev/null and b/slstatus/components/datetime.o differ
-diff --git a/slstatus/components/disk.c b/slstatus/components/disk.c
-new file mode 100644
-index 0000000..e19a693
---- /dev/null
-+++ b/slstatus/components/disk.c
-@@ -0,0 +1,59 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdio.h>
-+#include <sys/statvfs.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+const char *
-+disk_free(const char *path)
-+{
-+ struct statvfs fs;
-+
-+ if (statvfs(path, &fs) < 0) {
-+ warn("statvfs '%s':", path);
-+ return NULL;
-+ }
-+
-+ return fmt_human(fs.f_frsize * fs.f_bavail, 1024);
-+}
-+
-+const char *
-+disk_perc(const char *path)
-+{
-+ struct statvfs fs;
-+
-+ if (statvfs(path, &fs) < 0) {
-+ warn("statvfs '%s':", path);
-+ return NULL;
-+ }
-+
-+ return bprintf("%d", (int)(100 *
-+ (1 - ((double)fs.f_bavail / (double)fs.f_blocks))));
-+}
-+
-+const char *
-+disk_total(const char *path)
-+{
-+ struct statvfs fs;
-+
-+ if (statvfs(path, &fs) < 0) {
-+ warn("statvfs '%s':", path);
-+ return NULL;
-+ }
-+
-+ return fmt_human(fs.f_frsize * fs.f_blocks, 1024);
-+}
-+
-+const char *
-+disk_used(const char *path)
-+{
-+ struct statvfs fs;
-+
-+ if (statvfs(path, &fs) < 0) {
-+ warn("statvfs '%s':", path);
-+ return NULL;
-+ }
-+
-+ return fmt_human(fs.f_frsize * (fs.f_blocks - fs.f_bfree), 1024);
-+}
-diff --git a/slstatus/components/disk.o b/slstatus/components/disk.o
-new file mode 100644
-index 0000000..5ec5c0a
-Binary files /dev/null and b/slstatus/components/disk.o differ
-diff --git a/slstatus/components/entropy.c b/slstatus/components/entropy.c
-new file mode 100644
-index 0000000..65010b0
---- /dev/null
-+++ b/slstatus/components/entropy.c
-@@ -0,0 +1,29 @@
-+/* See LICENSE file for copyright and license details. */
-+#include "../slstatus.h"
-+#if defined(__linux__)
-+ #include <stdint.h>
-+ #include <stdio.h>
-+
-+ #include "../util.h"
-+
-+ #define ENTROPY_AVAIL "/proc/sys/kernel/random/entropy_avail"
-+
-+ const char *
-+ entropy(const char *unused)
-+ {
-+ uintmax_t num;
-+
-+ if (pscanf(ENTROPY_AVAIL, "%ju", &num) != 1)
-+ return NULL;
-+
-+ return bprintf("%ju", num);
-+ }
-+#elif defined(__OpenBSD__) | defined(__FreeBSD__)
-+ const char *
-+ entropy(const char *unused)
-+ {
-+ // https://www.unicode.org/charts/PDF/U2200.pdf
-+ /* Unicode Character 'INFINITY' (U+221E) */
-+ return "\u221E";
-+ }
-+#endif
-diff --git a/slstatus/components/entropy.o b/slstatus/components/entropy.o
-new file mode 100644
-index 0000000..6af57ba
-Binary files /dev/null and b/slstatus/components/entropy.o differ
-diff --git a/slstatus/components/hostname.c b/slstatus/components/hostname.c
-new file mode 100644
-index 0000000..dab8b63
---- /dev/null
-+++ b/slstatus/components/hostname.c
-@@ -0,0 +1,17 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdio.h>
-+#include <unistd.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+const char *
-+hostname(const char *unused)
-+{
-+ if (gethostname(buf, sizeof(buf)) < 0) {
-+ warn("gethostbyname:");
-+ return NULL;
-+ }
-+
-+ return buf;
-+}
-diff --git a/slstatus/components/hostname.o b/slstatus/components/hostname.o
-new file mode 100644
-index 0000000..4384f7c
-Binary files /dev/null and b/slstatus/components/hostname.o differ
-diff --git a/slstatus/components/ip.c b/slstatus/components/ip.c
-new file mode 100644
-index 0000000..9476549
---- /dev/null
-+++ b/slstatus/components/ip.c
-@@ -0,0 +1,61 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <ifaddrs.h>
-+#include <netdb.h>
-+#include <stdio.h>
-+#include <string.h>
-+#if defined(__OpenBSD__)
-+ #include <sys/socket.h>
-+ #include <sys/types.h>
-+#elif defined(__FreeBSD__)
-+ #include <netinet/in.h>
-+ #include <sys/socket.h>
-+#endif
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+static const char *
-+ip(const char *interface, unsigned short sa_family)
-+{
-+ struct ifaddrs *ifaddr, *ifa;
-+ int s;
-+ char host[NI_MAXHOST];
-+
-+ if (getifaddrs(&ifaddr) < 0) {
-+ warn("getifaddrs:");
-+ return NULL;
-+ }
-+
-+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
-+ if (!ifa->ifa_addr)
-+ continue;
-+
-+ s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6),
-+ host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
-+ if (!strcmp(ifa->ifa_name, interface) &&
-+ (ifa->ifa_addr->sa_family == sa_family)) {
-+ freeifaddrs(ifaddr);
-+ if (s != 0) {
-+ warn("getnameinfo: %s", gai_strerror(s));
-+ return NULL;
-+ }
-+ return bprintf("%s", host);
-+ }
-+ }
-+
-+ freeifaddrs(ifaddr);
-+
-+ return NULL;
-+}
-+
-+const char *
-+ipv4(const char *interface)
-+{
-+ return ip(interface, AF_INET);
-+}
-+
-+const char *
-+ipv6(const char *interface)
-+{
-+ return ip(interface, AF_INET6);
-+}
-diff --git a/slstatus/components/ip.o b/slstatus/components/ip.o
-new file mode 100644
-index 0000000..22f306d
-Binary files /dev/null and b/slstatus/components/ip.o differ
-diff --git a/slstatus/components/kanji.c b/slstatus/components/kanji.c
-new file mode 100644
-index 0000000..3d156e4
---- /dev/null
-+++ b/slstatus/components/kanji.c
-@@ -0,0 +1,14 @@
-+/* Written by Madison Lynch <madi@mxdi.xyz> */
-+#include <time.h>
-+
-+const char *
-+kanji(const char *unused) {
-+ char *kanji[] = {"日", "月", "火", "水", "木", "金", "土"};
-+ time_t t=time(NULL);
-+ struct tm tm=*localtime(&t);
-+ int map[]={0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4},
-+ m=tm.tm_mon+1,
-+ y=tm.tm_year+1900-(m<3),
-+ wd=(y+y/4-y/100+y/400+map[m-1]+tm.tm_mday)%7;
-+ return kanji[wd];
-+}
-diff --git a/slstatus/components/kanji.o b/slstatus/components/kanji.o
-new file mode 100644
-index 0000000..304e0e5
-Binary files /dev/null and b/slstatus/components/kanji.o differ
-diff --git a/slstatus/components/kernel_release.c b/slstatus/components/kernel_release.c
-new file mode 100644
-index 0000000..36a6a44
---- /dev/null
-+++ b/slstatus/components/kernel_release.c
-@@ -0,0 +1,19 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdio.h>
-+#include <sys/utsname.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+const char *
-+kernel_release(const char *unused)
-+{
-+ struct utsname udata;
-+
-+ if (uname(&udata) < 0) {
-+ warn("uname:");
-+ return NULL;
-+ }
-+
-+ return bprintf("%s", udata.release);
-+}
-diff --git a/slstatus/components/kernel_release.o b/slstatus/components/kernel_release.o
-new file mode 100644
-index 0000000..be0be90
-Binary files /dev/null and b/slstatus/components/kernel_release.o differ
-diff --git a/slstatus/components/keyboard_indicators.c b/slstatus/components/keyboard_indicators.c
-new file mode 100644
-index 0000000..5f62bb7
---- /dev/null
-+++ b/slstatus/components/keyboard_indicators.c
-@@ -0,0 +1,50 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <ctype.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <X11/Xlib.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+/*
-+ * fmt consists of uppercase or lowercase 'c' for caps lock and/or 'n' for num
-+ * lock, each optionally followed by '?', in the order of indicators desired.
-+ * If followed by '?', the letter with case preserved is included in the output
-+ * if the corresponding indicator is on. Otherwise, the letter is always
-+ * included, lowercase when off and uppercase when on.
-+ */
-+const char *
-+keyboard_indicators(const char *fmt)
-+{
-+ Display *dpy;
-+ XKeyboardState state;
-+ size_t fmtlen, i, n;
-+ int togglecase, isset;
-+ char key;
-+
-+ if (!(dpy = XOpenDisplay(NULL))) {
-+ warn("XOpenDisplay: Failed to open display");
-+ return NULL;
-+ }
-+ XGetKeyboardControl(dpy, &state);
-+ XCloseDisplay(dpy);
-+
-+ fmtlen = strnlen(fmt, 4);
-+ for (i = n = 0; i < fmtlen; i++) {
-+ key = tolower(fmt[i]);
-+ if (key != 'c' && key != 'n')
-+ continue;
-+
-+ togglecase = (i + 1 >= fmtlen || fmt[i + 1] != '?');
-+ isset = (state.led_mask & (1 << (key == 'n')));
-+
-+ if (togglecase)
-+ buf[n++] = isset ? toupper(key) : key;
-+ else if (isset)
-+ buf[n++] = fmt[i];
-+ }
-+
-+ buf[n] = 0;
-+ return buf;
-+}
-diff --git a/slstatus/components/keyboard_indicators.o b/slstatus/components/keyboard_indicators.o
-new file mode 100644
-index 0000000..001b37f
-Binary files /dev/null and b/slstatus/components/keyboard_indicators.o differ
-diff --git a/slstatus/components/keymap.c b/slstatus/components/keymap.c
-new file mode 100644
-index 0000000..f8a2a47
---- /dev/null
-+++ b/slstatus/components/keymap.c
-@@ -0,0 +1,86 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <ctype.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <X11/XKBlib.h>
-+#include <X11/Xlib.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+static int
-+valid_layout_or_variant(char *sym)
-+{
-+ size_t i;
-+ /* invalid symbols from xkb rules config */
-+ static const char *invalid[] = { "evdev", "inet", "pc", "base" };
-+
-+ for (i = 0; i < LEN(invalid); i++)
-+ if (!strncmp(sym, invalid[i], strlen(invalid[i])))
-+ return 0;
-+
-+ return 1;
-+}
-+
-+static char *
-+get_layout(char *syms, int grp_num)
-+{
-+ char *tok, *layout;
-+ int grp;
-+
-+ layout = NULL;
-+ tok = strtok(syms, "+:");
-+ for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:")) {
-+ if (!valid_layout_or_variant(tok)) {
-+ continue;
-+ } else if (strlen(tok) == 1 && isdigit(tok[0])) {
-+ /* ignore :2, :3, :4 (additional layout groups) */
-+ continue;
-+ }
-+ layout = tok;
-+ grp++;
-+ }
-+
-+ return layout;
-+}
-+
-+const char *
-+keymap(const char *unused)
-+{
-+ Display *dpy;
-+ XkbDescRec *desc;
-+ XkbStateRec state;
-+ char *symbols;
-+ const char *layout;
-+
-+ layout = NULL;
-+
-+ if (!(dpy = XOpenDisplay(NULL))) {
-+ warn("XOpenDisplay: Failed to open display");
-+ return NULL;
-+ }
-+ if (!(desc = XkbAllocKeyboard())) {
-+ warn("XkbAllocKeyboard: Failed to allocate keyboard");
-+ goto end;
-+ }
-+ if (XkbGetNames(dpy, XkbSymbolsNameMask, desc)) {
-+ warn("XkbGetNames: Failed to retrieve key symbols");
-+ goto end;
-+ }
-+ if (XkbGetState(dpy, XkbUseCoreKbd, &state)) {
-+ warn("XkbGetState: Failed to retrieve keyboard state");
-+ goto end;
-+ }
-+ if (!(symbols = XGetAtomName(dpy, desc->names->symbols))) {
-+ warn("XGetAtomName: Failed to get atom name");
-+ goto end;
-+ }
-+ layout = bprintf("%s", get_layout(symbols, state.group));
-+ XFree(symbols);
-+end:
-+ XkbFreeKeyboard(desc, XkbSymbolsNameMask, 1);
-+ if (XCloseDisplay(dpy))
-+ warn("XCloseDisplay: Failed to close display");
-+
-+ return layout;
-+}
-diff --git a/slstatus/components/keymap.o b/slstatus/components/keymap.o
-new file mode 100644
-index 0000000..b9c0bca
-Binary files /dev/null and b/slstatus/components/keymap.o differ
-diff --git a/slstatus/components/load_avg.c b/slstatus/components/load_avg.c
-new file mode 100644
-index 0000000..f278a40
---- /dev/null
-+++ b/slstatus/components/load_avg.c
-@@ -0,0 +1,19 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdio.h>
-+#include <stdlib.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+const char *
-+load_avg(const char *unused)
-+{
-+ double avgs[3];
-+
-+ if (getloadavg(avgs, 3) < 0) {
-+ warn("getloadavg: Failed to obtain load average");
-+ return NULL;
-+ }
-+
-+ return bprintf("%.2f %.2f %.2f", avgs[0], avgs[1], avgs[2]);
-+}
-diff --git a/slstatus/components/load_avg.o b/slstatus/components/load_avg.o
-new file mode 100644
-index 0000000..0861fb7
-Binary files /dev/null and b/slstatus/components/load_avg.o differ
-diff --git a/slstatus/components/netspeeds.c b/slstatus/components/netspeeds.c
-new file mode 100644
-index 0000000..cde6fa9
---- /dev/null
-+++ b/slstatus/components/netspeeds.c
-@@ -0,0 +1,129 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <limits.h>
-+#include <stdio.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+#if defined(__linux__)
-+ #include <stdint.h>
-+
-+ #define NET_RX_BYTES "/sys/class/net/%s/statistics/rx_bytes"
-+ #define NET_TX_BYTES "/sys/class/net/%s/statistics/tx_bytes"
-+
-+ const char *
-+ netspeed_rx(const char *interface)
-+ {
-+ uintmax_t oldrxbytes;
-+ static uintmax_t rxbytes;
-+ extern const unsigned int interval;
-+ char path[PATH_MAX];
-+
-+ oldrxbytes = rxbytes;
-+
-+ if (esnprintf(path, sizeof(path), NET_RX_BYTES, interface) < 0)
-+ return NULL;
-+ if (pscanf(path, "%ju", &rxbytes) != 1)
-+ return NULL;
-+ if (oldrxbytes == 0)
-+ return NULL;
-+
-+ return fmt_human((rxbytes - oldrxbytes) * 1000 / interval,
-+ 1024);
-+ }
-+
-+ const char *
-+ netspeed_tx(const char *interface)
-+ {
-+ uintmax_t oldtxbytes;
-+ static uintmax_t txbytes;
-+ extern const unsigned int interval;
-+ char path[PATH_MAX];
-+
-+ oldtxbytes = txbytes;
-+
-+ if (esnprintf(path, sizeof(path), NET_TX_BYTES, interface) < 0)
-+ return NULL;
-+ if (pscanf(path, "%ju", &txbytes) != 1)
-+ return NULL;
-+ if (oldtxbytes == 0)
-+ return NULL;
-+
-+ return fmt_human((txbytes - oldtxbytes) * 1000 / interval,
-+ 1024);
-+ }
-+#elif defined(__OpenBSD__) | defined(__FreeBSD__)
-+ #include <ifaddrs.h>
-+ #include <net/if.h>
-+ #include <string.h>
-+ #include <sys/types.h>
-+ #include <sys/socket.h>
-+
-+ const char *
-+ netspeed_rx(const char *interface)
-+ {
-+ struct ifaddrs *ifal, *ifa;
-+ struct if_data *ifd;
-+ uintmax_t oldrxbytes;
-+ static uintmax_t rxbytes;
-+ extern const unsigned int interval;
-+ int if_ok = 0;
-+
-+ oldrxbytes = rxbytes;
-+
-+ if (getifaddrs(&ifal) < 0) {
-+ warn("getifaddrs failed");
-+ return NULL;
-+ }
-+ rxbytes = 0;
-+ for (ifa = ifal; ifa; ifa = ifa->ifa_next)
-+ if (!strcmp(ifa->ifa_name, interface) &&
-+ (ifd = (struct if_data *)ifa->ifa_data))
-+ rxbytes += ifd->ifi_ibytes, if_ok = 1;
-+
-+ freeifaddrs(ifal);
-+ if (!if_ok) {
-+ warn("reading 'if_data' failed");
-+ return NULL;
-+ }
-+ if (oldrxbytes == 0)
-+ return NULL;
-+
-+ return fmt_human((rxbytes - oldrxbytes) * 1000 / interval,
-+ 1024);
-+ }
-+
-+ const char *
-+ netspeed_tx(const char *interface)
-+ {
-+ struct ifaddrs *ifal, *ifa;
-+ struct if_data *ifd;
-+ uintmax_t oldtxbytes;
-+ static uintmax_t txbytes;
-+ extern const unsigned int interval;
-+ int if_ok = 0;
-+
-+ oldtxbytes = txbytes;
-+
-+ if (getifaddrs(&ifal) < 0) {
-+ warn("getifaddrs failed");
-+ return NULL;
-+ }
-+ txbytes = 0;
-+ for (ifa = ifal; ifa; ifa = ifa->ifa_next)
-+ if (!strcmp(ifa->ifa_name, interface) &&
-+ (ifd = (struct if_data *)ifa->ifa_data))
-+ txbytes += ifd->ifi_obytes, if_ok = 1;
-+
-+ freeifaddrs(ifal);
-+ if (!if_ok) {
-+ warn("reading 'if_data' failed");
-+ return NULL;
-+ }
-+ if (oldtxbytes == 0)
-+ return NULL;
-+
-+ return fmt_human((txbytes - oldtxbytes) * 1000 / interval,
-+ 1024);
-+ }
-+#endif
-diff --git a/slstatus/components/netspeeds.o b/slstatus/components/netspeeds.o
-new file mode 100644
-index 0000000..fe9aca1
-Binary files /dev/null and b/slstatus/components/netspeeds.o differ
-diff --git a/slstatus/components/num_files.c b/slstatus/components/num_files.c
-new file mode 100644
-index 0000000..df0acd1
---- /dev/null
-+++ b/slstatus/components/num_files.c
-@@ -0,0 +1,32 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <dirent.h>
-+#include <stdio.h>
-+#include <string.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+const char *
-+num_files(const char *path)
-+{
-+ struct dirent *dp;
-+ DIR *dir;
-+ int num;
-+
-+ if (!(dir = opendir(path))) {
-+ warn("opendir '%s':", path);
-+ return NULL;
-+ }
-+
-+ num = 0;
-+ while ((dp = readdir(dir))) {
-+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
-+ continue; /* skip self and parent */
-+
-+ num++;
-+ }
-+
-+ closedir(dir);
-+
-+ return bprintf("%d", num);
-+}
-diff --git a/slstatus/components/num_files.o b/slstatus/components/num_files.o
-new file mode 100644
-index 0000000..15d1365
-Binary files /dev/null and b/slstatus/components/num_files.o differ
-diff --git a/slstatus/components/ram.c b/slstatus/components/ram.c
-new file mode 100644
-index 0000000..15c4b74
---- /dev/null
-+++ b/slstatus/components/ram.c
-@@ -0,0 +1,212 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdio.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+#if defined(__linux__)
-+ #include <stdint.h>
-+
-+ const char *
-+ ram_free(const char *unused)
-+ {
-+ uintmax_t free;
-+
-+ if (pscanf("/proc/meminfo",
-+ "MemTotal: %ju kB\n"
-+ "MemFree: %ju kB\n"
-+ "MemAvailable: %ju kB\n",
-+ &free, &free, &free) != 3)
-+ return NULL;
-+
-+ return fmt_human(free * 1024, 1024);
-+ }
-+
-+ const char *
-+ ram_perc(const char *unused)
-+ {
-+ uintmax_t total, free, buffers, cached;
-+ int percent;
-+
-+ if (pscanf("/proc/meminfo",
-+ "MemTotal: %ju kB\n"
-+ "MemFree: %ju kB\n"
-+ "MemAvailable: %ju kB\n"
-+ "Buffers: %ju kB\n"
-+ "Cached: %ju kB\n",
-+ &total, &free, &buffers, &buffers, &cached) != 5)
-+ return NULL;
-+
-+ if (total == 0)
-+ return NULL;
-+
-+ percent = 100 * ((total - free) - (buffers + cached)) / total;
-+ return bprintf("%d", percent);
-+ }
-+
-+ const char *
-+ ram_total(const char *unused)
-+ {
-+ uintmax_t total;
-+
-+ if (pscanf("/proc/meminfo", "MemTotal: %ju kB\n", &total)
-+ != 1)
-+ return NULL;
-+
-+ return fmt_human(total * 1024, 1024);
-+ }
-+
-+ const char *
-+ ram_used(const char *unused)
-+ {
-+ uintmax_t total, free, buffers, cached, used;
-+
-+ if (pscanf("/proc/meminfo",
-+ "MemTotal: %ju kB\n"
-+ "MemFree: %ju kB\n"
-+ "MemAvailable: %ju kB\n"
-+ "Buffers: %ju kB\n"
-+ "Cached: %ju kB\n",
-+ &total, &free, &buffers, &buffers, &cached) != 5)
-+ return NULL;
-+
-+ used = (total - free - buffers - cached);
-+ return fmt_human(used * 1024, 1024);
-+ }
-+#elif defined(__OpenBSD__)
-+ #include <stdlib.h>
-+ #include <sys/sysctl.h>
-+ #include <sys/types.h>
-+ #include <unistd.h>
-+
-+ #define LOG1024 10
-+ #define pagetok(size, pageshift) (size_t)(size << (pageshift - LOG1024))
-+
-+ inline int
-+ load_uvmexp(struct uvmexp *uvmexp)
-+ {
-+ int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
-+ size_t size;
-+
-+ size = sizeof(*uvmexp);
-+
-+ if (sysctl(uvmexp_mib, 2, uvmexp, &size, NULL, 0) >= 0)
-+ return 1;
-+
-+ return 0;
-+ }
-+
-+ const char *
-+ ram_free(const char *unused)
-+ {
-+ struct uvmexp uvmexp;
-+ int free_pages;
-+
-+ if (!load_uvmexp(&uvmexp))
-+ return NULL;
-+
-+ free_pages = uvmexp.npages - uvmexp.active;
-+ return fmt_human(pagetok(free_pages, uvmexp.pageshift) *
-+ 1024, 1024);
-+ }
-+
-+ const char *
-+ ram_perc(const char *unused)
-+ {
-+ struct uvmexp uvmexp;
-+ int percent;
-+
-+ if (!load_uvmexp(&uvmexp))
-+ return NULL;
-+
-+ percent = uvmexp.active * 100 / uvmexp.npages;
-+ return bprintf("%d", percent);
-+ }
-+
-+ const char *
-+ ram_total(const char *unused)
-+ {
-+ struct uvmexp uvmexp;
-+
-+ if (!load_uvmexp(&uvmexp))
-+ return NULL;
-+
-+ return fmt_human(pagetok(uvmexp.npages,
-+ uvmexp.pageshift) * 1024, 1024);
-+ }
-+
-+ const char *
-+ ram_used(const char *unused)
-+ {
-+ struct uvmexp uvmexp;
-+
-+ if (!load_uvmexp(&uvmexp))
-+ return NULL;
-+
-+ return fmt_human(pagetok(uvmexp.active,
-+ uvmexp.pageshift) * 1024, 1024);
-+ }
-+#elif defined(__FreeBSD__)
-+ #include <sys/sysctl.h>
-+ #include <sys/vmmeter.h>
-+ #include <unistd.h>
-+ #include <vm/vm_param.h>
-+
-+ const char *
-+ ram_free(const char *unused) {
-+ struct vmtotal vm_stats;
-+ int mib[] = {CTL_VM, VM_TOTAL};
-+ size_t len;
-+
-+ len = sizeof(struct vmtotal);
-+ if (sysctl(mib, 2, &vm_stats, &len, NULL, 0) < 0
-+ || !len)
-+ return NULL;
-+
-+ return fmt_human(vm_stats.t_free * getpagesize(), 1024);
-+ }
-+
-+ const char *
-+ ram_total(const char *unused) {
-+ unsigned int npages;
-+ size_t len;
-+
-+ len = sizeof(npages);
-+ if (sysctlbyname("vm.stats.vm.v_page_count",
-+ &npages, &len, NULL, 0) < 0 || !len)
-+ return NULL;
-+
-+ return fmt_human(npages * getpagesize(), 1024);
-+ }
-+
-+ const char *
-+ ram_perc(const char *unused) {
-+ unsigned int npages;
-+ unsigned int active;
-+ size_t len;
-+
-+ len = sizeof(npages);
-+ if (sysctlbyname("vm.stats.vm.v_page_count",
-+ &npages, &len, NULL, 0) < 0 || !len)
-+ return NULL;
-+
-+ if (sysctlbyname("vm.stats.vm.v_active_count",
-+ &active, &len, NULL, 0) < 0 || !len)
-+ return NULL;
-+
-+ return bprintf("%d", active * 100 / npages);
-+ }
-+
-+ const char *
-+ ram_used(const char *unused) {
-+ unsigned int active;
-+ size_t len;
-+
-+ len = sizeof(active);
-+ if (sysctlbyname("vm.stats.vm.v_active_count",
-+ &active, &len, NULL, 0) < 0 || !len)
-+ return NULL;
-+
-+ return fmt_human(active * getpagesize(), 1024);
-+ }
-+#endif
-diff --git a/slstatus/components/ram.o b/slstatus/components/ram.o
-new file mode 100644
-index 0000000..2dbd845
-Binary files /dev/null and b/slstatus/components/ram.o differ
-diff --git a/slstatus/components/run_command.c b/slstatus/components/run_command.c
-new file mode 100644
-index 0000000..93bf6da
---- /dev/null
-+++ b/slstatus/components/run_command.c
-@@ -0,0 +1,31 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdio.h>
-+#include <string.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+const char *
-+run_command(const char *cmd)
-+{
-+ char *p;
-+ FILE *fp;
-+
-+ if (!(fp = popen(cmd, "r"))) {
-+ warn("popen '%s':", cmd);
-+ return NULL;
-+ }
-+
-+ p = fgets(buf, sizeof(buf) - 1, fp);
-+ if (pclose(fp) < 0) {
-+ warn("pclose '%s':", cmd);
-+ return NULL;
-+ }
-+ if (!p)
-+ return NULL;
-+
-+ if ((p = strrchr(buf, '\n')))
-+ p[0] = '\0';
-+
-+ return buf[0] ? buf : NULL;
-+}
-diff --git a/slstatus/components/run_command.o b/slstatus/components/run_command.o
-new file mode 100644
-index 0000000..7addb38
-Binary files /dev/null and b/slstatus/components/run_command.o differ
-diff --git a/slstatus/components/swap.c b/slstatus/components/swap.c
-new file mode 100644
-index 0000000..f270d93
---- /dev/null
-+++ b/slstatus/components/swap.c
-@@ -0,0 +1,274 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdint.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+#if defined(__linux__)
-+ static int
-+ get_swap_info(long *s_total, long *s_free, long *s_cached)
-+ {
-+ FILE *fp;
-+ struct {
-+ const char *name;
-+ const size_t len;
-+ long *var;
-+ } ent[] = {
-+ { "SwapTotal", sizeof("SwapTotal") - 1, s_total },
-+ { "SwapFree", sizeof("SwapFree") - 1, s_free },
-+ { "SwapCached", sizeof("SwapCached") - 1, s_cached },
-+ };
-+ size_t line_len = 0, i, left;
-+ char *line = NULL;
-+
-+ /* get number of fields we want to extract */
-+ for (i = 0, left = 0; i < LEN(ent); i++)
-+ if (ent[i].var)
-+ left++;
-+
-+ if (!(fp = fopen("/proc/meminfo", "r"))) {
-+ warn("fopen '/proc/meminfo':");
-+ return 1;
-+ }
-+
-+ /* read file line by line and extract field information */
-+ while (left > 0 && getline(&line, &line_len, fp) >= 0) {
-+ for (i = 0; i < LEN(ent); i++) {
-+ if (ent[i].var &&
-+ !strncmp(line, ent[i].name, ent[i].len)) {
-+ sscanf(line + ent[i].len + 1,
-+ "%ld kB\n", ent[i].var);
-+ left--;
-+ break;
-+ }
-+ }
-+ }
-+ free(line);
-+ if (ferror(fp)) {
-+ warn("getline '/proc/meminfo':");
-+ return 1;
-+ }
-+
-+ fclose(fp);
-+ return 0;
-+ }
-+
-+ const char *
-+ swap_free(const char *unused)
-+ {
-+ long free;
-+
-+ if (get_swap_info(NULL, &free, NULL))
-+ return NULL;
-+
-+ return fmt_human(free * 1024, 1024);
-+ }
-+
-+ const char *
-+ swap_perc(const char *unused)
-+ {
-+ long total, free, cached;
-+
-+ if (get_swap_info(&total, &free, &cached) || total == 0)
-+ return NULL;
-+
-+ return bprintf("%d", 100 * (total - free - cached) / total);
-+ }
-+
-+ const char *
-+ swap_total(const char *unused)
-+ {
-+ long total;
-+
-+ if (get_swap_info(&total, NULL, NULL))
-+ return NULL;
-+
-+ return fmt_human(total * 1024, 1024);
-+ }
-+
-+ const char *
-+ swap_used(const char *unused)
-+ {
-+ long total, free, cached;
-+
-+ if (get_swap_info(&total, &free, &cached))
-+ return NULL;
-+
-+ return fmt_human((total - free - cached) * 1024, 1024);
-+ }
-+#elif defined(__OpenBSD__)
-+ #include <stdlib.h>
-+ #include <sys/swap.h>
-+ #include <sys/types.h>
-+ #include <unistd.h>
-+
-+ static int
-+ getstats(int *total, int *used)
-+ {
-+ struct swapent *sep, *fsep;
-+ int rnswap, nswap, i;
-+
-+ if ((nswap = swapctl(SWAP_NSWAP, 0, 0)) < 1) {
-+ warn("swaptctl 'SWAP_NSWAP':");
-+ return 1;
-+ }
-+ if (!(fsep = sep = calloc(nswap, sizeof(*sep)))) {
-+ warn("calloc 'nswap':");
-+ return 1;
-+ }
-+ if ((rnswap = swapctl(SWAP_STATS, (void *)sep, nswap)) < 0) {
-+ warn("swapctl 'SWAP_STATA':");
-+ return 1;
-+ }
-+ if (nswap != rnswap) {
-+ warn("getstats: SWAP_STATS != SWAP_NSWAP");
-+ return 1;
-+ }
-+
-+ *total = 0;
-+ *used = 0;
-+
-+ for (i = 0; i < rnswap; i++) {
-+ *total += sep->se_nblks >> 1;
-+ *used += sep->se_inuse >> 1;
-+ }
-+
-+ free(fsep);
-+
-+ return 0;
-+ }
-+
-+ const char *
-+ swap_free(const char *unused)
-+ {
-+ int total, used;
-+
-+ if (getstats(&total, &used))
-+ return NULL;
-+
-+ return fmt_human((total - used) * 1024, 1024);
-+ }
-+
-+ const char *
-+ swap_perc(const char *unused)
-+ {
-+ int total, used;
-+
-+ if (getstats(&total, &used))
-+ return NULL;
-+
-+ if (total == 0)
-+ return NULL;
-+
-+ return bprintf("%d", 100 * used / total);
-+ }
-+
-+ const char *
-+ swap_total(const char *unused)
-+ {
-+ int total, used;
-+
-+ if (getstats(&total, &used))
-+ return NULL;
-+
-+ return fmt_human(total * 1024, 1024);
-+ }
-+
-+ const char *
-+ swap_used(const char *unused)
-+ {
-+ int total, used;
-+
-+ if (getstats(&total, &used))
-+ return NULL;
-+
-+ return fmt_human(used * 1024, 1024);
-+ }
-+#elif defined(__FreeBSD__)
-+ #include <fcntl.h>
-+ #include <kvm.h>
-+ #include <stdlib.h>
-+ #include <sys/types.h>
-+ #include <unistd.h>
-+
-+ static int getswapinfo(struct kvm_swap *swap_info, size_t size)
-+ {
-+ kvm_t *kd;
-+
-+ kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, NULL);
-+ if (kd == NULL) {
-+ warn("kvm_openfiles '/dev/null':");
-+ return 0;
-+ }
-+
-+ if (kvm_getswapinfo(kd, swap_info, size, 0 /* Unused flags */) < 0) {
-+ warn("kvm_getswapinfo:");
-+ kvm_close(kd);
-+ return 0;
-+ }
-+
-+ kvm_close(kd);
-+ return 1;
-+ }
-+
-+ const char *
-+ swap_free(const char *unused)
-+ {
-+ struct kvm_swap swap_info[1];
-+ long used, total;
-+
-+ if (!getswapinfo(swap_info, 1))
-+ return NULL;
-+
-+ total = swap_info[0].ksw_total;
-+ used = swap_info[0].ksw_used;
-+
-+ return fmt_human((total - used) * getpagesize(), 1024);
-+ }
-+
-+ const char *
-+ swap_perc(const char *unused)
-+ {
-+ struct kvm_swap swap_info[1];
-+ long used, total;
-+
-+ if (!getswapinfo(swap_info, 1))
-+ return NULL;
-+
-+ total = swap_info[0].ksw_total;
-+ used = swap_info[0].ksw_used;
-+
-+ return bprintf("%d", used * 100 / total);
-+ }
-+
-+ const char *
-+ swap_total(const char *unused)
-+ {
-+ struct kvm_swap swap_info[1];
-+ long total;
-+
-+ if (!getswapinfo(swap_info, 1))
-+ return NULL;
-+
-+ total = swap_info[0].ksw_total;
-+
-+ return fmt_human(total * getpagesize(), 1024);
-+ }
-+
-+ const char *
-+ swap_used(const char *unused)
-+ {
-+ struct kvm_swap swap_info[1];
-+ long used;
-+
-+ if (!getswapinfo(swap_info, 1))
-+ return NULL;
-+
-+ used = swap_info[0].ksw_used;
-+
-+ return fmt_human(used * getpagesize(), 1024);
-+ }
-+#endif
-diff --git a/slstatus/components/swap.o b/slstatus/components/swap.o
-new file mode 100644
-index 0000000..eff037e
-Binary files /dev/null and b/slstatus/components/swap.o differ
-diff --git a/slstatus/components/temperature.c b/slstatus/components/temperature.c
-new file mode 100644
-index 0000000..7cf1394
---- /dev/null
-+++ b/slstatus/components/temperature.c
-@@ -0,0 +1,73 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stddef.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+
-+#if defined(__linux__)
-+ #include <stdint.h>
-+
-+ const char *
-+ temp(const char *file)
-+ {
-+ uintmax_t temp;
-+
-+ if (pscanf(file, "%ju", &temp) != 1)
-+ return NULL;
-+
-+ return bprintf("%ju", temp / 1000);
-+ }
-+#elif defined(__OpenBSD__)
-+ #include <stdio.h>
-+ #include <sys/time.h> /* before <sys/sensors.h> for struct timeval */
-+ #include <sys/sensors.h>
-+ #include <sys/sysctl.h>
-+
-+ const char *
-+ temp(const char *unused)
-+ {
-+ int mib[5];
-+ size_t size;
-+ struct sensor temp;
-+
-+ mib[0] = CTL_HW;
-+ mib[1] = HW_SENSORS;
-+ mib[2] = 0; /* cpu0 */
-+ mib[3] = SENSOR_TEMP;
-+ mib[4] = 0; /* temp0 */
-+
-+ size = sizeof(temp);
-+
-+ if (sysctl(mib, 5, &temp, &size, NULL, 0) < 0) {
-+ warn("sysctl 'SENSOR_TEMP':");
-+ return NULL;
-+ }
-+
-+ /* kelvin to celsius */
-+ return bprintf("%d", (int)((float)(temp.value-273150000) / 1E6));
-+ }
-+#elif defined(__FreeBSD__)
-+ #include <stdio.h>
-+ #include <stdlib.h>
-+ #include <sys/sysctl.h>
-+
-+ #define ACPI_TEMP "hw.acpi.thermal.%s.temperature"
-+
-+ const char *
-+ temp(const char *zone)
-+ {
-+ char buf[256];
-+ int temp;
-+ size_t len;
-+
-+ len = sizeof(temp);
-+ snprintf(buf, sizeof(buf), ACPI_TEMP, zone);
-+ if (sysctlbyname(buf, &temp, &len, NULL, 0) < 0
-+ || !len)
-+ return NULL;
-+
-+ /* kelvin to decimal celcius */
-+ return bprintf("%d.%d", (temp - 2731) / 10, abs((temp - 2731) % 10));
-+ }
-+#endif
-diff --git a/slstatus/components/temperature.o b/slstatus/components/temperature.o
-new file mode 100644
-index 0000000..0856c7b
-Binary files /dev/null and b/slstatus/components/temperature.o differ
-diff --git a/slstatus/components/uptime.c b/slstatus/components/uptime.c
-new file mode 100644
-index 0000000..6227f73
---- /dev/null
-+++ b/slstatus/components/uptime.c
-@@ -0,0 +1,34 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdint.h>
-+#include <stdio.h>
-+#include <time.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+#if defined(CLOCK_BOOTTIME)
-+ #define UPTIME_FLAG CLOCK_BOOTTIME
-+#elif defined(CLOCK_UPTIME)
-+ #define UPTIME_FLAG CLOCK_UPTIME
-+#else
-+ #define UPTIME_FLAG CLOCK_MONOTONIC
-+#endif
-+
-+const char *
-+uptime(const char *unused)
-+{
-+ char warn_buf[256];
-+ uintmax_t h, m;
-+ struct timespec uptime;
-+
-+ if (clock_gettime(UPTIME_FLAG, &uptime) < 0) {
-+ snprintf(warn_buf, sizeof(warn_buf), "clock_gettime %d", UPTIME_FLAG);
-+ warn(warn_buf);
-+ return NULL;
-+ }
-+
-+ h = uptime.tv_sec / 3600;
-+ m = uptime.tv_sec % 3600 / 60;
-+
-+ return bprintf("%juh %jum", h, m);
-+}
-diff --git a/slstatus/components/uptime.o b/slstatus/components/uptime.o
-new file mode 100644
-index 0000000..ee47012
-Binary files /dev/null and b/slstatus/components/uptime.o differ
-diff --git a/slstatus/components/user.c b/slstatus/components/user.c
-new file mode 100644
-index 0000000..3517495
---- /dev/null
-+++ b/slstatus/components/user.c
-@@ -0,0 +1,33 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <pwd.h>
-+#include <stdio.h>
-+#include <sys/types.h>
-+#include <unistd.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+const char *
-+gid(const char *unused)
-+{
-+ return bprintf("%d", getgid());
-+}
-+
-+const char *
-+username(const char *unused)
-+{
-+ struct passwd *pw;
-+
-+ if (!(pw = getpwuid(geteuid()))) {
-+ warn("getpwuid '%d':", geteuid());
-+ return NULL;
-+ }
-+
-+ return bprintf("%s", pw->pw_name);
-+}
-+
-+const char *
-+uid(const char *unused)
-+{
-+ return bprintf("%d", geteuid());
-+}
-diff --git a/slstatus/components/user.o b/slstatus/components/user.o
-new file mode 100644
-index 0000000..83bb561
-Binary files /dev/null and b/slstatus/components/user.o differ
-diff --git a/slstatus/components/volume.c b/slstatus/components/volume.c
-new file mode 100644
-index 0000000..6cec556
---- /dev/null
-+++ b/slstatus/components/volume.c
-@@ -0,0 +1,219 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <fcntl.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <sys/ioctl.h>
-+#include <unistd.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+#if defined(__OpenBSD__) | defined(__FreeBSD__)
-+ #include <poll.h>
-+ #include <sndio.h>
-+ #include <stdlib.h>
-+ #include <sys/queue.h>
-+
-+ struct control {
-+ LIST_ENTRY(control) next;
-+ unsigned int addr;
-+ #define CTRL_NONE 0
-+ #define CTRL_LEVEL 1
-+ #define CTRL_MUTE 2
-+ unsigned int type;
-+ unsigned int maxval;
-+ unsigned int val;
-+ };
-+
-+ static LIST_HEAD(, control) controls = LIST_HEAD_INITIALIZER(controls);
-+ static struct pollfd *pfds;
-+ static struct sioctl_hdl *hdl;
-+ static int initialized;
-+
-+ /*
-+ * Call-back to obtain the description of all audio controls.
-+ */
-+ static void
-+ ondesc(void *unused, struct sioctl_desc *desc, int val)
-+ {
-+ struct control *c, *ctmp;
-+ unsigned int type = CTRL_NONE;
-+
-+ if (desc == NULL)
-+ return;
-+
-+ /* Delete existing audio control with the same address. */
-+ LIST_FOREACH_SAFE(c, &controls, next, ctmp) {
-+ if (desc->addr == c->addr) {
-+ LIST_REMOVE(c, next);
-+ free(c);
-+ break;
-+ }
-+ }
-+
-+ /* Only match output.level and output.mute audio controls. */
-+ if (desc->group[0] != 0 ||
-+ strcmp(desc->node0.name, "output") != 0)
-+ return;
-+ if (desc->type == SIOCTL_NUM &&
-+ strcmp(desc->func, "level") == 0)
-+ type = CTRL_LEVEL;
-+ else if (desc->type == SIOCTL_SW &&
-+ strcmp(desc->func, "mute") == 0)
-+ type = CTRL_MUTE;
-+ else
-+ return;
-+
-+ c = malloc(sizeof(struct control));
-+ if (c == NULL) {
-+ warn("sndio: failed to allocate audio control\n");
-+ return;
-+ }
-+
-+ c->addr = desc->addr;
-+ c->type = type;
-+ c->maxval = desc->maxval;
-+ c->val = val;
-+ LIST_INSERT_HEAD(&controls, c, next);
-+ }
-+
-+ /*
-+ * Call-back invoked whenever an audio control changes.
-+ */
-+ static void
-+ onval(void *unused, unsigned int addr, unsigned int val)
-+ {
-+ struct control *c;
-+
-+ LIST_FOREACH(c, &controls, next) {
-+ if (c->addr == addr)
-+ break;
-+ }
-+ c->val = val;
-+ }
-+
-+ static void
-+ cleanup(void)
-+ {
-+ struct control *c;
-+
-+ if (hdl) {
-+ sioctl_close(hdl);
-+ hdl = NULL;
-+ }
-+
-+ free(pfds);
-+ pfds = NULL;
-+
-+ while (!LIST_EMPTY(&controls)) {
-+ c = LIST_FIRST(&controls);
-+ LIST_REMOVE(c, next);
-+ free(c);
-+ }
-+ }
-+
-+ static int
-+ init(void)
-+ {
-+ hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
-+ if (hdl == NULL) {
-+ warn("sndio: cannot open device");
-+ goto failed;
-+ }
-+
-+ if (!sioctl_ondesc(hdl, ondesc, NULL)) {
-+ warn("sndio: cannot set control description call-back");
-+ goto failed;
-+ }
-+
-+ if (!sioctl_onval(hdl, onval, NULL)) {
-+ warn("sndio: cannot set control values call-back");
-+ goto failed;
-+ }
-+
-+ pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd));
-+ if (pfds == NULL) {
-+ warn("sndio: cannot allocate pollfd structures");
-+ goto failed;
-+ }
-+
-+ return 1;
-+ failed:
-+ cleanup();
-+ return 0;
-+ }
-+
-+ const char *
-+ vol_perc(const char *unused)
-+ {
-+ struct control *c;
-+ int n, v, value;
-+
-+ if (!initialized)
-+ initialized = init();
-+
-+ if (hdl == NULL)
-+ return NULL;
-+
-+ n = sioctl_pollfd(hdl, pfds, POLLIN);
-+ if (n > 0) {
-+ n = poll(pfds, n, 0);
-+ if (n > 0) {
-+ if (sioctl_revents(hdl, pfds) & POLLHUP) {
-+ warn("sndio: disconnected");
-+ cleanup();
-+ initialized = 0;
-+ return NULL;
-+ }
-+ }
-+ }
-+
-+ value = 100;
-+ LIST_FOREACH(c, &controls, next) {
-+ if (c->type == CTRL_MUTE && c->val == 1)
-+ value = 0;
-+ else if (c->type == CTRL_LEVEL) {
-+ v = (c->val * 100 + c->maxval / 2) / c->maxval;
-+ /* For multiple channels return the minimum. */
-+ if (v < value)
-+ value = v;
-+ }
-+ }
-+
-+ return bprintf("%d", value);
-+ }
-+#else
-+ #include <sys/soundcard.h>
-+
-+ const char *
-+ vol_perc(const char *card)
-+ {
-+ size_t i;
-+ int v, afd, devmask;
-+ char *vnames[] = SOUND_DEVICE_NAMES;
-+
-+ if ((afd = open(card, O_RDONLY | O_NONBLOCK)) < 0) {
-+ warn("open '%s':", card);
-+ return NULL;
-+ }
-+
-+ if (ioctl(afd, (int)SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
-+ warn("ioctl 'SOUND_MIXER_READ_DEVMASK':");
-+ close(afd);
-+ return NULL;
-+ }
-+ for (i = 0; i < LEN(vnames); i++) {
-+ if (devmask & (1 << i) && !strcmp("vol", vnames[i])) {
-+ if (ioctl(afd, MIXER_READ(i), &v) < 0) {
-+ warn("ioctl 'MIXER_READ(%ld)':", i);
-+ close(afd);
-+ return NULL;
-+ }
-+ }
-+ }
-+
-+ close(afd);
-+
-+ return bprintf("%d", v & 0xff);
-+ }
-+#endif
-diff --git a/slstatus/components/volume.o b/slstatus/components/volume.o
-new file mode 100644
-index 0000000..73b3122
-Binary files /dev/null and b/slstatus/components/volume.o differ
-diff --git a/slstatus/components/wifi.c b/slstatus/components/wifi.c
-new file mode 100644
-index 0000000..4543d32
---- /dev/null
-+++ b/slstatus/components/wifi.c
-@@ -0,0 +1,267 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <ifaddrs.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <sys/ioctl.h>
-+#include <sys/socket.h>
-+#include <unistd.h>
-+
-+#include "../slstatus.h"
-+#include "../util.h"
-+
-+#define RSSI_TO_PERC(rssi) \
-+ rssi >= -50 ? 100 : \
-+ (rssi <= -100 ? 0 : \
-+ (2 * (rssi + 100)))
-+
-+#if defined(__linux__)
-+ #include <limits.h>
-+ #include <linux/wireless.h>
-+
-+ #define NET_OPERSTATE "/sys/class/net/%s/operstate"
-+
-+ const char *
-+ wifi_perc(const char *interface)
-+ {
-+ int cur;
-+ size_t i;
-+ char *p, *datastart;
-+ char path[PATH_MAX];
-+ char status[5];
-+ FILE *fp;
-+
-+ if (esnprintf(path, sizeof(path), NET_OPERSTATE, interface) < 0)
-+ return NULL;
-+ if (!(fp = fopen(path, "r"))) {
-+ warn("fopen '%s':", path);
-+ return NULL;
-+ }
-+ p = fgets(status, 5, fp);
-+ fclose(fp);
-+ if (!p || strcmp(status, "up\n") != 0)
-+ return NULL;
-+
-+ if (!(fp = fopen("/proc/net/wireless", "r"))) {
-+ warn("fopen '/proc/net/wireless':");
-+ return NULL;
-+ }
-+
-+ for (i = 0; i < 3; i++)
-+ if (!(p = fgets(buf, sizeof(buf) - 1, fp)))
-+ break;
-+
-+ fclose(fp);
-+ if (i < 2 || !p)
-+ return NULL;
-+
-+ if (!(datastart = strstr(buf, interface)))
-+ return NULL;
-+
-+ datastart = (datastart+(strlen(interface)+1));
-+ sscanf(datastart + 1, " %*d %d %*d %*d\t\t %*d\t "
-+ "%*d\t\t%*d\t\t %*d\t %*d\t\t %*d", &cur);
-+
-+ /* 70 is the max of /proc/net/wireless */
-+ return bprintf("%d", (int)((float)cur / 70 * 100));
-+ }
-+
-+ const char *
-+ wifi_essid(const char *interface)
-+ {
-+ static char id[IW_ESSID_MAX_SIZE+1];
-+ int sockfd;
-+ struct iwreq wreq;
-+
-+ memset(&wreq, 0, sizeof(struct iwreq));
-+ wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
-+ if (esnprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "%s",
-+ interface) < 0)
-+ return NULL;
-+
-+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-+ warn("socket 'AF_INET':");
-+ return NULL;
-+ }
-+ wreq.u.essid.pointer = id;
-+ if (ioctl(sockfd,SIOCGIWESSID, &wreq) < 0) {
-+ warn("ioctl 'SIOCGIWESSID':");
-+ close(sockfd);
-+ return NULL;
-+ }
-+
-+ close(sockfd);
-+
-+ if (!strcmp(id, ""))
-+ return NULL;
-+
-+ return id;
-+ }
-+#elif defined(__OpenBSD__)
-+ #include <net/if.h>
-+ #include <net/if_media.h>
-+ #include <net80211/ieee80211.h>
-+ #include <sys/select.h> /* before <sys/ieee80211_ioctl.h> for NBBY */
-+ #include <net80211/ieee80211_ioctl.h>
-+ #include <stdlib.h>
-+ #include <sys/types.h>
-+
-+ static int
-+ load_ieee80211_nodereq(const char *interface, struct ieee80211_nodereq *nr)
-+ {
-+ struct ieee80211_bssid bssid;
-+ int sockfd;
-+ uint8_t zero_bssid[IEEE80211_ADDR_LEN];
-+
-+ memset(&bssid, 0, sizeof(bssid));
-+ memset(nr, 0, sizeof(struct ieee80211_nodereq));
-+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-+ warn("socket 'AF_INET':");
-+ return 0;
-+ }
-+ strlcpy(bssid.i_name, interface, sizeof(bssid.i_name));
-+ if ((ioctl(sockfd, SIOCG80211BSSID, &bssid)) < 0) {
-+ warn("ioctl 'SIOCG80211BSSID':");
-+ close(sockfd);
-+ return 0;
-+ }
-+ memset(&zero_bssid, 0, sizeof(zero_bssid));
-+ if (memcmp(bssid.i_bssid, zero_bssid,
-+ IEEE80211_ADDR_LEN) == 0) {
-+ close(sockfd);
-+ return 0;
-+ }
-+ strlcpy(nr->nr_ifname, interface, sizeof(nr->nr_ifname));
-+ memcpy(&nr->nr_macaddr, bssid.i_bssid, sizeof(nr->nr_macaddr));
-+ if ((ioctl(sockfd, SIOCG80211NODE, nr)) < 0 && nr->nr_rssi) {
-+ warn("ioctl 'SIOCG80211NODE':");
-+ close(sockfd);
-+ return 0;
-+ }
-+
-+ return close(sockfd), 1;
-+ }
-+
-+ const char *
-+ wifi_perc(const char *interface)
-+ {
-+ struct ieee80211_nodereq nr;
-+ int q;
-+
-+ if (load_ieee80211_nodereq(interface, &nr)) {
-+ if (nr.nr_max_rssi)
-+ q = IEEE80211_NODEREQ_RSSI(&nr);
-+ else
-+ q = RSSI_TO_PERC(nr.nr_rssi);
-+
-+ return bprintf("%d", q);
-+ }
-+
-+ return NULL;
-+ }
-+
-+ const char *
-+ wifi_essid(const char *interface)
-+ {
-+ struct ieee80211_nodereq nr;
-+
-+ if (load_ieee80211_nodereq(interface, &nr))
-+ return bprintf("%s", nr.nr_nwid);
-+
-+ return NULL;
-+ }
-+#elif defined(__FreeBSD__)
-+ #include <net/if.h>
-+ #include <net80211/ieee80211_ioctl.h>
-+
-+ int
-+ load_ieee80211req(int sock, const char *interface, void *data, int type, size_t *len)
-+ {
-+ char warn_buf[256];
-+ struct ieee80211req ireq;
-+ memset(&ireq, 0, sizeof(ireq));
-+ ireq.i_type = type;
-+ ireq.i_data = (caddr_t) data;
-+ ireq.i_len = *len;
-+
-+ strlcpy(ireq.i_name, interface, sizeof(ireq.i_name));
-+ if (ioctl(sock, SIOCG80211, &ireq) < 0) {
-+ snprintf(warn_buf, sizeof(warn_buf),
-+ "ioctl: 'SIOCG80211': %d", type);
-+ warn(warn_buf);
-+ return 0;
-+ }
-+
-+ *len = ireq.i_len;
-+ return 1;
-+ }
-+
-+ const char *
-+ wifi_perc(const char *interface)
-+ {
-+ union {
-+ struct ieee80211req_sta_req sta;
-+ uint8_t buf[24 * 1024];
-+ } info;
-+ uint8_t bssid[IEEE80211_ADDR_LEN];
-+ int rssi_dbm;
-+ int sockfd;
-+ size_t len;
-+ const char *fmt;
-+
-+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-+ warn("socket 'AF_INET':");
-+ return NULL;
-+ }
-+
-+ /* Retreive MAC address of interface */
-+ len = IEEE80211_ADDR_LEN;
-+ fmt = NULL;
-+ if (load_ieee80211req(sockfd, interface, &bssid, IEEE80211_IOC_BSSID, &len))
-+ {
-+ /* Retrieve info on station with above BSSID */
-+ memset(&info, 0, sizeof(info));
-+ memcpy(info.sta.is_u.macaddr, bssid, sizeof(bssid));
-+
-+ len = sizeof(info);
-+ if (load_ieee80211req(sockfd, interface, &info, IEEE80211_IOC_STA_INFO, &len)) {
-+ rssi_dbm = info.sta.info[0].isi_noise +
-+ info.sta.info[0].isi_rssi / 2;
-+
-+ fmt = bprintf("%d", RSSI_TO_PERC(rssi_dbm));
-+ }
-+ }
-+
-+ close(sockfd);
-+ return fmt;
-+ }
-+
-+ const char *
-+ wifi_essid(const char *interface)
-+ {
-+ char ssid[IEEE80211_NWID_LEN + 1];
-+ size_t len;
-+ int sockfd;
-+ const char *fmt;
-+
-+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-+ warn("socket 'AF_INET':");
-+ return NULL;
-+ }
-+
-+ fmt = NULL;
-+ len = sizeof(ssid);
-+ memset(&ssid, 0, len);
-+ if (load_ieee80211req(sockfd, interface, &ssid, IEEE80211_IOC_SSID, &len)) {
-+ if (len < sizeof(ssid))
-+ len += 1;
-+ else
-+ len = sizeof(ssid);
-+
-+ ssid[len - 1] = '\0';
-+ fmt = bprintf("%s", ssid);
-+ }
-+
-+ close(sockfd);
-+ return fmt;
-+ }
-+#endif
-diff --git a/slstatus/components/wifi.o b/slstatus/components/wifi.o
-new file mode 100644
-index 0000000..91e1508
-Binary files /dev/null and b/slstatus/components/wifi.o differ
-diff --git a/slstatus/config.def.h b/slstatus/config.def.h
-new file mode 100644
-index 0000000..aded4d7
---- /dev/null
-+++ b/slstatus/config.def.h
-@@ -0,0 +1,78 @@
-+/* See LICENSE file for copyright and license details. */
-+
-+/* interval between updates (in ms) */
-+const unsigned int interval = 1000;
-+
-+/* text to show if no value can be retrieved */
-+static const char unknown_str[] = "n/a";
-+
-+/* maximum output string length */
-+#define MAXLEN 2048
-+
-+/*
-+ * function description argument (example)
-+ *
-+ * battery_perc battery percentage battery name (BAT0)
-+ * NULL on OpenBSD/FreeBSD
-+ * battery_remaining battery remaining HH:MM battery name (BAT0)
-+ * NULL on OpenBSD/FreeBSD
-+ * battery_state battery charging state battery name (BAT0)
-+ * NULL on OpenBSD/FreeBSD
-+ * cat read arbitrary file path
-+ * cpu_freq cpu frequency in MHz NULL
-+ * cpu_perc cpu usage in percent NULL
-+ * datetime date and time format string (%F %T)
-+ * disk_free free disk space in GB mountpoint path (/)
-+ * disk_perc disk usage in percent mountpoint path (/)
-+ * disk_total total disk space in GB mountpoint path (/)
-+ * disk_used used disk space in GB mountpoint path (/)
-+ * entropy available entropy NULL
-+ * gid GID of current user NULL
-+ * hostname hostname NULL
-+ * ipv4 IPv4 address interface name (eth0)
-+ * ipv6 IPv6 address interface name (eth0)
-+ * kanji japanese day of week kanji NULL
-+ * kernel_release `uname -r` NULL
-+ * keyboard_indicators caps/num lock indicators format string (c?n?)
-+ * see keyboard_indicators.c
-+ * keymap layout (variant) of current NULL
-+ * keymap
-+ * load_avg load average NULL
-+ * netspeed_rx receive network speed interface name (wlan0)
-+ * netspeed_tx transfer network speed interface name (wlan0)
-+ * num_files number of files in a directory path
-+ * (/home/foo/Inbox/cur)
-+ * ram_free free memory in GB NULL
-+ * ram_perc memory usage in percent NULL
-+ * ram_total total memory size in GB NULL
-+ * ram_used used memory in GB NULL
-+ * run_command custom shell command command (echo foo)
-+ * swap_free free swap in GB NULL
-+ * swap_perc swap usage in percent NULL
-+ * swap_total total swap size in GB NULL
-+ * swap_used used swap in GB NULL
-+ * temp temperature in degree celsius sensor file
-+ * (/sys/class/thermal/...)
-+ * NULL on OpenBSD
-+ * thermal zone on FreeBSD
-+ * (tz0, tz1, etc.)
-+ * uid UID of current user NULL
-+ * uptime system uptime NULL
-+ * username username of current user NULL
-+ * vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer)
-+ * NULL on OpenBSD/FreeBSD
-+ * wifi_essid WiFi ESSID interface name (wlan0)
-+ * wifi_perc WiFi signal in percent interface name (wlan0)
-+ */
-+static const struct arg args[] = {
-+ /* function format argument */
-+ { ram_used, "[RAM %s]", NULL },
-+ { cpu_perc, "[CPU %s%%]", NULL },
-+ { disk_free, "[ %s free]" , "/" },
-+ { netspeed_rx, "[ %s]" , "wlan0"},
-+ { temp, "[%s C°]", "/sys/class/thermal/thermal_zone0/temp" },
-+ { run_command, "[%s% ]", "getvolume"},
-+ { datetime, "%s", "%r"},
-+ { kernel_release, " %s", },
-+ { kanji, "| %s |", },
-+};
-diff --git a/slstatus/config.mk b/slstatus/config.mk
-new file mode 100644
-index 0000000..07af883
---- /dev/null
-+++ b/slstatus/config.mk
-@@ -0,0 +1,22 @@
-+# slstatus version
-+VERSION = 1.0
-+
-+# customize below to fit your system
-+
-+# paths
-+PREFIX = /usr/local
-+MANPREFIX = $(PREFIX)/share/man
-+
-+X11INC = /usr/X11R6/include
-+X11LIB = /usr/X11R6/lib
-+
-+# flags
-+CPPFLAGS = -I$(X11INC) -D_DEFAULT_SOURCE -DVERSION=\"${VERSION}\"
-+CFLAGS = -std=c99 -pedantic -Wall -Wextra -Wno-unused-parameter -Os
-+LDFLAGS = -L$(X11LIB) -s
-+# OpenBSD: add -lsndio
-+# FreeBSD: add -lkvm -lsndio
-+LDLIBS = -lX11
-+
-+# compiler and linker
-+CC = cc
-diff --git a/slstatus/slstatus.1 b/slstatus/slstatus.1
-new file mode 100644
-index 0000000..73e7a60
---- /dev/null
-+++ b/slstatus/slstatus.1
-@@ -0,0 +1,47 @@
-+.Dd 2023-04-23
-+.Dt SLSTATUS 1
-+.Os
-+.Sh NAME
-+.Nm slstatus
-+.Nd suckless status
-+.Sh SYNOPSIS
-+.Nm
-+.Op Fl s
-+.Op Fl 1
-+.Sh DESCRIPTION
-+.Nm
-+is a small tool for providing system status information to other programs
-+over the EWMH
-+.Em WM_NAME
-+property of the root window (used by
-+.Xr dwm 1 ) or standard input/output. It is designed to be as efficient as possible by
-+only issuing the minimum of system calls required.
-+.P
-+By default,
-+.Nm
-+outputs to WM_NAME.
-+.Sh OPTIONS
-+.Bl -tag -width Ds
-+.It Fl v
-+Print version information to stderr, then exit.
-+.It Fl s
-+Write to stdout instead of WM_NAME.
-+.It Fl 1
-+Write once to stdout and quit.
-+.El
-+.Sh CUSTOMIZATION
-+.Nm
-+can be customized by creating a custom config.h and (re)compiling the source
-+code. This keeps it fast, secure and simple.
-+.Sh SIGNALS
-+.Nm
-+responds to the following signals:
-+.Pp
-+.Bl -tag -width TERM -compact
-+.It USR1
-+Triggers an instant redraw.
-+.El
-+.Sh AUTHORS
-+See the LICENSE file for the authors.
-+.Sh SEE ALSO
-+.Xr dwm 1
-diff --git a/slstatus/slstatus.c b/slstatus/slstatus.c
-new file mode 100644
-index 0000000..fd31313
---- /dev/null
-+++ b/slstatus/slstatus.c
-@@ -0,0 +1,134 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <errno.h>
-+#include <signal.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <time.h>
-+#include <X11/Xlib.h>
-+
-+#include "arg.h"
-+#include "slstatus.h"
-+#include "util.h"
-+
-+struct arg {
-+ const char *(*func)(const char *);
-+ const char *fmt;
-+ const char *args;
-+};
-+
-+char buf[1024];
-+static volatile sig_atomic_t done;
-+static Display *dpy;
-+
-+#include "config.h"
-+
-+static void
-+terminate(const int signo)
-+{
-+ if (signo != SIGUSR1)
-+ done = 1;
-+}
-+
-+static void
-+difftimespec(struct timespec *res, struct timespec *a, struct timespec *b)
-+{
-+ res->tv_sec = a->tv_sec - b->tv_sec - (a->tv_nsec < b->tv_nsec);
-+ res->tv_nsec = a->tv_nsec - b->tv_nsec +
-+ (a->tv_nsec < b->tv_nsec) * 1E9;
-+}
-+
-+static void
-+usage(void)
-+{
-+ die("usage: %s [-v] [-s] [-1]", argv0);
-+}
-+
-+int
-+main(int argc, char *argv[])
-+{
-+ struct sigaction act;
-+ struct timespec start, current, diff, intspec, wait;
-+ size_t i, len;
-+ int sflag, ret;
-+ char status[MAXLEN];
-+ const char *res;
-+
-+ sflag = 0;
-+ ARGBEGIN {
-+ case 'v':
-+ die("slstatus-"VERSION);
-+ case '1':
-+ done = 1;
-+ /* FALLTHROUGH */
-+ case 's':
-+ sflag = 1;
-+ break;
-+ default:
-+ usage();
-+ } ARGEND
-+
-+ if (argc)
-+ usage();
-+
-+ memset(&act, 0, sizeof(act));
-+ act.sa_handler = terminate;
-+ sigaction(SIGINT, &act, NULL);
-+ sigaction(SIGTERM, &act, NULL);
-+ act.sa_flags |= SA_RESTART;
-+ sigaction(SIGUSR1, &act, NULL);
-+
-+ if (!sflag && !(dpy = XOpenDisplay(NULL)))
-+ die("XOpenDisplay: Failed to open display");
-+
-+ do {
-+ if (clock_gettime(CLOCK_MONOTONIC, &start) < 0)
-+ die("clock_gettime:");
-+
-+ status[0] = '\0';
-+ for (i = len = 0; i < LEN(args); i++) {
-+ if (!(res = args[i].func(args[i].args)))
-+ res = unknown_str;
-+
-+ if ((ret = esnprintf(status + len, sizeof(status) - len,
-+ args[i].fmt, res)) < 0)
-+ break;
-+
-+ len += ret;
-+ }
-+
-+ if (sflag) {
-+ puts(status);
-+ fflush(stdout);
-+ if (ferror(stdout))
-+ die("puts:");
-+ } else {
-+ if (XStoreName(dpy, DefaultRootWindow(dpy), status) < 0)
-+ die("XStoreName: Allocation failed");
-+ XFlush(dpy);
-+ }
-+
-+ if (!done) {
-+ if (clock_gettime(CLOCK_MONOTONIC, ¤t) < 0)
-+ die("clock_gettime:");
-+ difftimespec(&diff, ¤t, &start);
-+
-+ intspec.tv_sec = interval / 1000;
-+ intspec.tv_nsec = (interval % 1000) * 1E6;
-+ difftimespec(&wait, &intspec, &diff);
-+
-+ if (wait.tv_sec >= 0 &&
-+ nanosleep(&wait, NULL) < 0 &&
-+ errno != EINTR)
-+ die("nanosleep:");
-+ }
-+ } while (!done);
-+
-+ if (!sflag) {
-+ XStoreName(dpy, DefaultRootWindow(dpy), NULL);
-+ if (XCloseDisplay(dpy) < 0)
-+ die("XCloseDisplay: Failed to close display");
-+ }
-+
-+ return 0;
-+}
-diff --git a/slstatus/slstatus.h b/slstatus/slstatus.h
-new file mode 100644
-index 0000000..dc927eb
---- /dev/null
-+++ b/slstatus/slstatus.h
-@@ -0,0 +1,87 @@
-+/* See LICENSE file for copyright and license details. */
-+
-+/* battery */
-+const char *battery_perc(const char *);
-+const char *battery_remaining(const char *);
-+const char *battery_state(const char *);
-+
-+/* cat */
-+const char *cat(const char *path);
-+
-+/* cpu */
-+const char *cpu_freq(const char *unused);
-+const char *cpu_perc(const char *unused);
-+
-+/* datetime */
-+const char *datetime(const char *fmt);
-+
-+/* disk */
-+const char *disk_free(const char *path);
-+const char *disk_perc(const char *path);
-+const char *disk_total(const char *path);
-+const char *disk_used(const char *path);
-+
-+/* entropy */
-+const char *entropy(const char *unused);
-+
-+/* hostname */
-+const char *hostname(const char *unused);
-+
-+/* ip */
-+const char *ipv4(const char *interface);
-+const char *ipv6(const char *interface);
-+
-+/* kanji */
-+const char *kanji(const char *unused);
-+
-+/* kernel_release */
-+const char *kernel_release(const char *unused);
-+
-+/* keyboard_indicators */
-+const char *keyboard_indicators(const char *fmt);
-+
-+/* keymap */
-+const char *keymap(const char *unused);
-+
-+/* load_avg */
-+const char *load_avg(const char *unused);
-+
-+/* netspeeds */
-+const char *netspeed_rx(const char *interface);
-+const char *netspeed_tx(const char *interface);
-+
-+/* num_files */
-+const char *num_files(const char *path);
-+
-+/* ram */
-+const char *ram_free(const char *unused);
-+const char *ram_perc(const char *unused);
-+const char *ram_total(const char *unused);
-+const char *ram_used(const char *unused);
-+
-+/* run_command */
-+const char *run_command(const char *cmd);
-+
-+/* swap */
-+const char *swap_free(const char *unused);
-+const char *swap_perc(const char *unused);
-+const char *swap_total(const char *unused);
-+const char *swap_used(const char *unused);
-+
-+/* temperature */
-+const char *temp(const char *);
-+
-+/* uptime */
-+const char *uptime(const char *unused);
-+
-+/* user */
-+const char *gid(const char *unused);
-+const char *uid(const char *unused);
-+const char *username(const char *unused);
-+
-+/* volume */
-+const char *vol_perc(const char *card);
-+
-+/* wifi */
-+const char *wifi_essid(const char *interface);
-+const char *wifi_perc(const char *interface);
-diff --git a/slstatus/util.c b/slstatus/util.c
-new file mode 100644
-index 0000000..bca9b2e
---- /dev/null
-+++ b/slstatus/util.c
-@@ -0,0 +1,141 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <errno.h>
-+#include <stdarg.h>
-+#include <stdint.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+#include "util.h"
-+
-+char *argv0;
-+
-+static void
-+verr(const char *fmt, va_list ap)
-+{
-+ vfprintf(stderr, fmt, ap);
-+
-+ if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
-+ fputc(' ', stderr);
-+ perror(NULL);
-+ } else {
-+ fputc('\n', stderr);
-+ }
-+}
-+
-+void
-+warn(const char *fmt, ...)
-+{
-+ va_list ap;
-+
-+ va_start(ap, fmt);
-+ verr(fmt, ap);
-+ va_end(ap);
-+}
-+
-+void
-+die(const char *fmt, ...)
-+{
-+ va_list ap;
-+
-+ va_start(ap, fmt);
-+ verr(fmt, ap);
-+ va_end(ap);
-+
-+ exit(1);
-+}
-+
-+static int
-+evsnprintf(char *str, size_t size, const char *fmt, va_list ap)
-+{
-+ int ret;
-+
-+ ret = vsnprintf(str, size, fmt, ap);
-+
-+ if (ret < 0) {
-+ warn("vsnprintf:");
-+ return -1;
-+ } else if ((size_t)ret >= size) {
-+ warn("vsnprintf: Output truncated");
-+ return -1;
-+ }
-+
-+ return ret;
-+}
-+
-+int
-+esnprintf(char *str, size_t size, const char *fmt, ...)
-+{
-+ va_list ap;
-+ int ret;
-+
-+ va_start(ap, fmt);
-+ ret = evsnprintf(str, size, fmt, ap);
-+ va_end(ap);
-+
-+ return ret;
-+}
-+
-+const char *
-+bprintf(const char *fmt, ...)
-+{
-+ va_list ap;
-+ int ret;
-+
-+ va_start(ap, fmt);
-+ ret = evsnprintf(buf, sizeof(buf), fmt, ap);
-+ va_end(ap);
-+
-+ return (ret < 0) ? NULL : buf;
-+}
-+
-+const char *
-+fmt_human(uintmax_t num, int base)
-+{
-+ double scaled;
-+ size_t i, prefixlen;
-+ const char **prefix;
-+ const char *prefix_1000[] = { "", "k", "M", "G", "T", "P", "E", "Z",
-+ "Y" };
-+ const char *prefix_1024[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei",
-+ "Zi", "Yi" };
-+
-+ switch (base) {
-+ case 1000:
-+ prefix = prefix_1000;
-+ prefixlen = LEN(prefix_1000);
-+ break;
-+ case 1024:
-+ prefix = prefix_1024;
-+ prefixlen = LEN(prefix_1024);
-+ break;
-+ default:
-+ warn("fmt_human: Invalid base");
-+ return NULL;
-+ }
-+
-+ scaled = num;
-+ for (i = 0; i < prefixlen && scaled >= base; i++)
-+ scaled /= base;
-+
-+ return bprintf("%.1f %s", scaled, prefix[i]);
-+}
-+
-+int
-+pscanf(const char *path, const char *fmt, ...)
-+{
-+ FILE *fp;
-+ va_list ap;
-+ int n;
-+
-+ if (!(fp = fopen(path, "r"))) {
-+ warn("fopen '%s':", path);
-+ return -1;
-+ }
-+ va_start(ap, fmt);
-+ n = vfscanf(fp, fmt, ap);
-+ va_end(ap);
-+ fclose(fp);
-+
-+ return (n == EOF) ? -1 : n;
-+}
-diff --git a/slstatus/util.h b/slstatus/util.h
-new file mode 100644
-index 0000000..cf4b027
---- /dev/null
-+++ b/slstatus/util.h
-@@ -0,0 +1,16 @@
-+/* See LICENSE file for copyright and license details. */
-+#include <stdint.h>
-+
-+extern char buf[1024];
-+
-+#define LEN(x) (sizeof(x) / sizeof((x)[0]))
-+
-+extern char *argv0;
-+
-+void warn(const char *, ...);
-+void die(const char *, ...);
-+
-+int esnprintf(char *str, size_t size, const char *fmt, ...);
-+const char *bprintf(const char *fmt, ...);
-+const char *fmt_human(uintmax_t num, int base);
-+int pscanf(const char *path, const char *fmt, ...);