birdwm

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit fd287578b0f927111ffc78d23497bd8295f496e3
parent 72b689a7e89692fbaf67bd994459ba87d10fe09b
Author: grouse <bdmfegys@duck.com>
Date:   Wed,  6 Dec 2023 21:13:01 -0500

remove deprecated birdwm.patch

Diffstat:
Dextras/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", &current_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, &current) < 0) -+ die("clock_gettime:"); -+ difftimespec(&diff, &current, &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, ...);