From bd73709f9ac24cd41fb74590f5409f807f84ad09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Gon=C3=A7alves?= Date: Sat, 27 Jan 2024 12:10:20 -0300 Subject: [PATCH] major refactor - added some cool patches i did this mostly because some patches that required manual intervention to be applied were actually unappliable, also now it's distro agnostic ig --- .gitignore | 3 + LICENSE | 38 + dmenu/Makefile | 64 + dmenu/PKGBUILD | 52 - dmenu/arg.h | 49 + dmenu/{config.h => config.def.h} | 0 dmenu/config.mk | 32 + dmenu/dmenu.1 | 197 ++ dmenu/dmenu.c | 821 +++++++ dmenu/dmenu_path | 13 + dmenu/dmenu_run | 2 + dmenu/drw.c | 450 ++++ dmenu/drw.h | 58 + dmenu/stest.1 | 90 + dmenu/stest.c | 109 + dmenu/util.c | 36 + dmenu/util.h | 8 + dwm/Makefile | 51 + dwm/PKGBUILD | 49 - dwm/config.def.h | 142 ++ dwm/config.h | 73 +- dwm/config.mk | 40 + dwm/drw.c | 450 ++++ dwm/drw.h | 58 + dwm/drw.o | Bin 0 -> 11072 bytes dwm/dwm | Bin 0 -> 73272 bytes dwm/dwm-loop | 5 - dwm/dwm.1 | 209 ++ dwm/dwm.c | 2668 +++++++++++++++++++++ dwm/dwm.desktop | 7 - dwm/dwm.install | 5 - dwm/dwm.o | Bin 0 -> 67864 bytes dwm/transient.c | 42 + dwm/util.c | 36 + dwm/util.h | 8 + dwm/util.o | Bin 0 -> 2224 bytes slstatus/LICENSE | 43 + slstatus/Makefile | 69 + slstatus/PKGBUILD | 41 - slstatus/README | 65 + slstatus/arg.h | 33 + slstatus/components/battery.c | 247 ++ slstatus/components/battery.o | Bin 0 -> 5136 bytes slstatus/components/cat.c | 32 + slstatus/components/cat.o | Bin 0 -> 2088 bytes slstatus/components/cpu.c | 157 ++ slstatus/components/cpu.o | Bin 0 -> 3136 bytes slstatus/components/datetime.c | 20 + slstatus/components/datetime.o | Bin 0 -> 1920 bytes slstatus/components/disk.c | 59 + slstatus/components/disk.o | Bin 0 -> 3128 bytes slstatus/components/entropy.c | 29 + slstatus/components/entropy.o | Bin 0 -> 1784 bytes slstatus/components/hostname.c | 17 + slstatus/components/hostname.o | Bin 0 -> 1640 bytes slstatus/components/ip.c | 61 + slstatus/components/ip.o | Bin 0 -> 2552 bytes slstatus/components/kernel_release.c | 19 + slstatus/components/kernel_release.o | Bin 0 -> 1832 bytes slstatus/components/keyboard_indicators.c | 50 + slstatus/components/keyboard_indicators.o | Bin 0 -> 2400 bytes slstatus/components/keymap.c | 86 + slstatus/components/keymap.o | Bin 0 -> 4024 bytes slstatus/components/load_avg.c | 19 + slstatus/components/load_avg.o | Bin 0 -> 1864 bytes slstatus/components/netspeeds.c | 129 + slstatus/components/netspeeds.o | Bin 0 -> 2760 bytes slstatus/components/num_files.c | 32 + slstatus/components/num_files.o | Bin 0 -> 2152 bytes slstatus/components/ram.c | 212 ++ slstatus/components/ram.o | Bin 0 -> 3080 bytes slstatus/components/run_command.c | 31 + slstatus/components/run_command.o | Bin 0 -> 2104 bytes slstatus/components/swap.c | 274 +++ slstatus/components/swap.o | Bin 0 -> 3888 bytes slstatus/components/temperature.c | 73 + slstatus/components/temperature.o | Bin 0 -> 1704 bytes slstatus/components/uptime.c | 34 + slstatus/components/uptime.o | Bin 0 -> 1968 bytes slstatus/components/user.c | 33 + slstatus/components/user.o | Bin 0 -> 2192 bytes slstatus/components/volume.c | 219 ++ slstatus/components/volume.o | Bin 0 -> 3616 bytes slstatus/components/wifi.c | 267 +++ slstatus/components/wifi.o | Bin 0 -> 4264 bytes slstatus/config.def.h | 71 + slstatus/config.mk | 22 + slstatus/slstatus | Bin 0 -> 31800 bytes slstatus/slstatus.1 | 47 + slstatus/slstatus.c | 134 ++ slstatus/slstatus.h | 84 + slstatus/slstatus.o | Bin 0 -> 6008 bytes slstatus/util.c | 141 ++ slstatus/util.h | 16 + slstatus/util.o | Bin 0 -> 5552 bytes 95 files changed, 8543 insertions(+), 188 deletions(-) create mode 100644 LICENSE create mode 100644 dmenu/Makefile delete mode 100644 dmenu/PKGBUILD create mode 100644 dmenu/arg.h rename dmenu/{config.h => config.def.h} (100%) create mode 100644 dmenu/config.mk create mode 100644 dmenu/dmenu.1 create mode 100644 dmenu/dmenu.c create mode 100755 dmenu/dmenu_path create mode 100755 dmenu/dmenu_run create mode 100644 dmenu/drw.c create mode 100644 dmenu/drw.h create mode 100644 dmenu/stest.1 create mode 100644 dmenu/stest.c create mode 100644 dmenu/util.c create mode 100644 dmenu/util.h create mode 100644 dwm/Makefile delete mode 100644 dwm/PKGBUILD create mode 100644 dwm/config.def.h create mode 100644 dwm/config.mk create mode 100644 dwm/drw.c create mode 100644 dwm/drw.h create mode 100644 dwm/drw.o create mode 100755 dwm/dwm delete mode 100644 dwm/dwm-loop create mode 100644 dwm/dwm.1 create mode 100644 dwm/dwm.c delete mode 100644 dwm/dwm.desktop delete mode 100644 dwm/dwm.install create mode 100644 dwm/dwm.o create mode 100644 dwm/transient.c create mode 100644 dwm/util.c create mode 100644 dwm/util.h create mode 100644 dwm/util.o create mode 100644 slstatus/LICENSE create mode 100644 slstatus/Makefile delete mode 100644 slstatus/PKGBUILD create mode 100644 slstatus/README create mode 100644 slstatus/arg.h create mode 100644 slstatus/components/battery.c create mode 100644 slstatus/components/battery.o create mode 100644 slstatus/components/cat.c create mode 100644 slstatus/components/cat.o create mode 100644 slstatus/components/cpu.c create mode 100644 slstatus/components/cpu.o create mode 100644 slstatus/components/datetime.c create mode 100644 slstatus/components/datetime.o create mode 100644 slstatus/components/disk.c create mode 100644 slstatus/components/disk.o create mode 100644 slstatus/components/entropy.c create mode 100644 slstatus/components/entropy.o create mode 100644 slstatus/components/hostname.c create mode 100644 slstatus/components/hostname.o create mode 100644 slstatus/components/ip.c create mode 100644 slstatus/components/ip.o create mode 100644 slstatus/components/kernel_release.c create mode 100644 slstatus/components/kernel_release.o create mode 100644 slstatus/components/keyboard_indicators.c create mode 100644 slstatus/components/keyboard_indicators.o create mode 100644 slstatus/components/keymap.c create mode 100644 slstatus/components/keymap.o create mode 100644 slstatus/components/load_avg.c create mode 100644 slstatus/components/load_avg.o create mode 100644 slstatus/components/netspeeds.c create mode 100644 slstatus/components/netspeeds.o create mode 100644 slstatus/components/num_files.c create mode 100644 slstatus/components/num_files.o create mode 100644 slstatus/components/ram.c create mode 100644 slstatus/components/ram.o create mode 100644 slstatus/components/run_command.c create mode 100644 slstatus/components/run_command.o create mode 100644 slstatus/components/swap.c create mode 100644 slstatus/components/swap.o create mode 100644 slstatus/components/temperature.c create mode 100644 slstatus/components/temperature.o create mode 100644 slstatus/components/uptime.c create mode 100644 slstatus/components/uptime.o create mode 100644 slstatus/components/user.c create mode 100644 slstatus/components/user.o create mode 100644 slstatus/components/volume.c create mode 100644 slstatus/components/volume.o create mode 100644 slstatus/components/wifi.c create mode 100644 slstatus/components/wifi.o create mode 100644 slstatus/config.def.h create mode 100644 slstatus/config.mk create mode 100755 slstatus/slstatus create mode 100644 slstatus/slstatus.1 create mode 100644 slstatus/slstatus.c create mode 100644 slstatus/slstatus.h create mode 100644 slstatus/slstatus.o create mode 100644 slstatus/util.c create mode 100644 slstatus/util.h create mode 100644 slstatus/util.o diff --git a/.gitignore b/.gitignore index ab80e36..6f257bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.tar.* +*.diff +*/src +!dwm/dwm-restartsig.diff diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..995172f --- /dev/null +++ b/LICENSE @@ -0,0 +1,38 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2009 Jukka Salmi +© 2006-2007 Sander van Dijk +© 2007-2011 Peter Hartlich +© 2007-2009 Szabolcs Nagy +© 2007-2009 Christof Musik +© 2007-2009 Premysl Hruby +© 2007-2008 Enno Gottox Boland +© 2008 Martin Hurton +© 2008 Neale Pickett +© 2009 Mate Nagy +© 2010-2016 Hiltjo Posthuma +© 2010-2012 Connor Lane Smith +© 2011 Christoph Lohmann <20h@r-36.net> +© 2015-2016 Quentin Rameau +© 2015-2016 Eric Pruitt +© 2016-2017 Markus Teich +© 2020-2022 Chris Down + +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/dmenu/Makefile b/dmenu/Makefile new file mode 100644 index 0000000..e731a2c --- /dev/null +++ b/dmenu/Makefile @@ -0,0 +1,64 @@ +# dmenu - dynamic menu +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dmenu.c stest.c util.c +OBJ = $(SRC:.c=.o) + +all: options dmenu stest + +options: + @echo dmenu build options: + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" + +.c.o: + $(CC) -c $(CFLAGS) $< + +config.h: + cp config.def.h $@ + +$(OBJ): arg.h config.h config.mk drw.h + +dmenu: dmenu.o drw.o util.o + $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) + +stest: stest.o + $(CC) -o $@ stest.o $(LDFLAGS) + +clean: + rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz *.orig *.rej config.h + +dist: clean + mkdir -p dmenu-$(VERSION) + cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ + drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ + dmenu-$(VERSION) + tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) + gzip dmenu-$(VERSION).tar + rm -rf dmenu-$(VERSION) + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run + chmod 755 $(DESTDIR)$(PREFIX)/bin/stest + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ + $(DESTDIR)$(PREFIX)/bin/dmenu_path\ + $(DESTDIR)$(PREFIX)/bin/dmenu_run\ + $(DESTDIR)$(PREFIX)/bin/stest\ + $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ + $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +.PHONY: all options clean dist install uninstall diff --git a/dmenu/PKGBUILD b/dmenu/PKGBUILD deleted file mode 100644 index 5eea39c..0000000 --- a/dmenu/PKGBUILD +++ /dev/null @@ -1,52 +0,0 @@ -# my custom dmenu!! - -pkgname=dmenu -pkgver=5.2 -pkgrel=1 -pkgdesc='Generic menu for X' -url='http://git.vern.cc/vitorg/suckless' -arch=('x86_64') -license=('MIT') -options=(zipman) -depends=('sh' 'glibc' 'coreutils' 'libx11' 'libxinerama' 'libxft' 'freetype2' 'fontconfig' 'libfontconfig.so') -_patchname=("center" "gruvbox" "border") -source=(https://dl.suckless.org/tools/$pkgname-${pkgver}.tar.gz - center.diff::https://tools.suckless.org/$pkgname/patches/center/$pkgname-center-$pkgver.diff - gruvbox.diff::https://tools.suckless.org/$pkgname/patches/gruvbox/$pkgname-gruvbox-20210329-9ae8ea5.diff - border.diff::https://tools.suckless.org/$pkgname/patches/border/$pkgname-border-$pkgver.diff - config.h) -sha512sums=('7be9bb76b11225ec86a30e643e9b884f6d13af581526085212cb7401383d448a72fe4dffc3ce84ffb8319dbf36ca8384597a89070cd27d39d5618f239a2da6e3' - 'b98695c14063b9555c6964e675512bcdc3a4dc97ece8a882fabe9261b9081bd31dbd884885d9ad0b2c1305bdcc50170bb0641659e076333c7a1e29aed516a476' - '8a860d57a550bef787de1d7be28ce9531340c01af296f0c6da6485ec0bb6e16293dd2ba13806280dd26d4a07c3d0f067fd84ed2abb9a8c66546d90c04489e234' - 'b241c668bb18a2bdcc4bc76492ff34ab24cc35d7af49a0f84069ab9fa3d4ce81bcc30bf8b14c02f1403e52370c050e6b210330752245301ba479073aa0e40d61' - 'a9f3f35ed258ff430584d7b21493cbb1f76bae40d2c58b0bcf30fdeb14fc1616f92d4e0c5696f9f30117e659f1c231e037b5a1517d71052848a069e17e013911') - -prepare() { - cd ${pkgname}-${pkgver} - echo "CPPFLAGS+=${CPPFLAGS}" >> config.mk - echo "CFLAGS+=${CFLAGS}" >> config.mk - echo "LDFLAGS+=${LDFLAGS}" >> config.mk - if [[ -f "${srcdir}/config.h" ]]; then - cp -fv "${srcdir}/config.h" config.h - fi - for p in ${_patchname[@]}; do - printf "\npatching %s\n\n" "$p" - patch --forward --strip=1 --input="${srcdir}/$p.diff" - done -} - -build() { - cd ${pkgname}-${pkgver} - make \ - X11INC=/usr/include/X11 \ - X11LIB=/usr/lib/X11 \ - FREETYPEINC=/usr/include/freetype2 -} - -package() { - cd ${pkgname}-${pkgver} - make PREFIX=/usr DESTDIR="${pkgdir}" install - install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}" -} - -# vim: ts=2 sw=2 et: diff --git a/dmenu/arg.h b/dmenu/arg.h new file mode 100644 index 0000000..e94e02b --- /dev/null +++ b/dmenu/arg.h @@ -0,0 +1,49 @@ +/* + * 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_) + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#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]))) + +#endif diff --git a/dmenu/config.h b/dmenu/config.def.h similarity index 100% rename from dmenu/config.h rename to dmenu/config.def.h diff --git a/dmenu/config.mk b/dmenu/config.mk new file mode 100644 index 0000000..566348b --- /dev/null +++ b/dmenu/config.mk @@ -0,0 +1,32 @@ +# dmenu version +VERSION = 5.2 + +# 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 = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = $(X11INC)/freetype2 +#MANPREFIX = ${PREFIX}/man + +# includes and libs +INCS = -I$(X11INC) -I$(FREETYPEINC) +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) +CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) +LDFLAGS = $(LIBS) + +# compiler and linker +CC = cc diff --git a/dmenu/dmenu.1 b/dmenu/dmenu.1 new file mode 100644 index 0000000..c036baa --- /dev/null +++ b/dmenu/dmenu.1 @@ -0,0 +1,197 @@ +.TH DMENU 1 dmenu\-VERSION +.SH NAME +dmenu \- dynamic menu +.SH SYNOPSIS +.B dmenu +.RB [ \-bfiv ] +.RB [ \-l +.IR lines ] +.RB [ \-m +.IR monitor ] +.RB [ \-p +.IR prompt ] +.RB [ \-fn +.IR font ] +.RB [ \-nb +.IR color ] +.RB [ \-nf +.IR color ] +.RB [ \-sb +.IR color ] +.RB [ \-sf +.IR color ] +.RB [ \-w +.IR windowid ] +.P +.BR dmenu_run " ..." +.SH DESCRIPTION +.B dmenu +is a dynamic menu for X, which reads a list of newline\-separated items from +stdin. When the user selects an item and presses Return, their choice is printed +to stdout and dmenu terminates. Entering text will narrow the items to those +matching the tokens in the input. +.P +.B dmenu_run +is a script used by +.IR dwm (1) +which lists programs in the user's $PATH and runs the result in their $SHELL. +.SH OPTIONS +.TP +.B \-b +dmenu appears at the bottom of the screen. +.TP +.B \-c +dmenu appears centered on the screen. +.TP +.B \-f +dmenu grabs the keyboard before reading stdin if not reading from a tty. This +is faster, but will lock up X until stdin reaches end\-of\-file. +.TP +.B \-i +dmenu matches menu items case insensitively. +.TP +.BI \-l " lines" +dmenu lists items vertically, with the given number of lines. +.TP +.BI \-m " monitor" +dmenu is displayed on the monitor number supplied. Monitor numbers are starting +from 0. +.TP +.BI \-p " prompt" +defines the prompt to be displayed to the left of the input field. +.TP +.BI \-fn " font" +defines the font or font set used. +.TP +.BI \-nb " color" +defines the normal background color. +.IR #RGB , +.IR #RRGGBB , +and X color names are supported. +.TP +.BI \-nf " color" +defines the normal foreground color. +.TP +.BI \-sb " color" +defines the selected background color. +.TP +.BI \-sf " color" +defines the selected foreground color. +.TP +.B \-v +prints version information to stdout, then exits. +.TP +.BI \-w " windowid" +embed into windowid. +.SH USAGE +dmenu is completely controlled by the keyboard. Items are selected using the +arrow keys, page up, page down, home, and end. +.TP +.B Tab +Copy the selected item to the input field. +.TP +.B Return +Confirm selection. Prints the selected item to stdout and exits, returning +success. +.TP +.B Ctrl-Return +Confirm selection. Prints the selected item to stdout and continues. +.TP +.B Shift\-Return +Confirm input. Prints the input text to stdout and exits, returning success. +.TP +.B Escape +Exit without selecting an item, returning failure. +.TP +.B Ctrl-Left +Move cursor to the start of the current word +.TP +.B Ctrl-Right +Move cursor to the end of the current word +.TP +.B C\-a +Home +.TP +.B C\-b +Left +.TP +.B C\-c +Escape +.TP +.B C\-d +Delete +.TP +.B C\-e +End +.TP +.B C\-f +Right +.TP +.B C\-g +Escape +.TP +.B C\-h +Backspace +.TP +.B C\-i +Tab +.TP +.B C\-j +Return +.TP +.B C\-J +Shift-Return +.TP +.B C\-k +Delete line right +.TP +.B C\-m +Return +.TP +.B C\-M +Shift-Return +.TP +.B C\-n +Down +.TP +.B C\-p +Up +.TP +.B C\-u +Delete line left +.TP +.B C\-w +Delete word left +.TP +.B C\-y +Paste from primary X selection +.TP +.B C\-Y +Paste from X clipboard +.TP +.B M\-b +Move cursor to the start of the current word +.TP +.B M\-f +Move cursor to the end of the current word +.TP +.B M\-g +Home +.TP +.B M\-G +End +.TP +.B M\-h +Up +.TP +.B M\-j +Page down +.TP +.B M\-k +Page up +.TP +.B M\-l +Down +.SH SEE ALSO +.IR dwm (1), +.IR stest (1) diff --git a/dmenu/dmenu.c b/dmenu/dmenu.c new file mode 100644 index 0000000..ba4132c --- /dev/null +++ b/dmenu/dmenu.c @@ -0,0 +1,821 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef XINERAMA +#include +#endif +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ + * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ + +struct item { + char *text; + struct item *left, *right; + int out; +}; + +static char text[BUFSIZ] = ""; +static char *embed; +static int bh, mw, mh; +static int inputw = 0, promptw; +static int lrpad; /* sum of left and right padding */ +static size_t cursor; +static struct item *items = NULL; +static struct item *matches, *matchend; +static struct item *prev, *curr, *next, *sel; +static int mon = -1, screen; + +static Atom clip, utf8; +static Display *dpy; +static Window root, parentwin, win; +static XIC xic; + +static Drw *drw; +static Clr *scheme[SchemeLast]; + +#include "config.h" + +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; +static char *(*fstrstr)(const char *, const char *) = strstr; + +static unsigned int +textw_clamp(const char *str, unsigned int n) +{ + unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad; + return MIN(w, n); +} + +static void +appenditem(struct item *item, struct item **list, struct item **last) +{ + if (*last) + (*last)->right = item; + else + *list = item; + + item->left = *last; + item->right = NULL; + *last = item; +} + +static void +calcoffsets(void) +{ + int i, n; + + if (lines > 0) + n = lines * bh; + else + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); + /* calculate which items will begin the next page and previous page */ + for (i = 0, next = curr; next; next = next->right) + if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n) + break; +} + +static int +max_textw(void) +{ + int len = 0; + for (struct item *item = items; item && item->text; item++) + len = MAX(TEXTW(item->text), len); + return len; +} + +static void +cleanup(void) +{ + size_t i; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + for (i = 0; items && items[i].text; ++i) + free(items[i].text); + free(items); + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +} + +static char * +cistrstr(const char *h, const char *n) +{ + size_t i; + + if (!n[0]) + return (char *)h; + + for (; *h; ++h) { + for (i = 0; n[i] && tolower((unsigned char)n[i]) == + tolower((unsigned char)h[i]); ++i) + ; + if (n[i] == '\0') + return (char *)h; + } + return NULL; +} + +static int +drawitem(struct item *item, int x, int y, int w) +{ + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); +} + +static void +drawmenu(void) +{ + unsigned int curpos; + struct item *item; + int x = 0, y = 0, w; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); + + if (prompt && *prompt) { + drw_setscheme(drw, scheme[SchemeSel]); + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); + } + /* draw input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); + + curpos = TEXTW(text) - TEXTW(&text[cursor]); + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); + } + + if (lines > 0) { + /* draw vertical list */ + for (item = curr; item != next; item = item->right) + drawitem(item, x, y += bh, mw - x); + } else if (matches) { + /* draw horizontal list */ + x += inputw; + w = TEXTW("<"); + if (curr->left) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); + } + x += w; + for (item = curr; item != next; item = item->right) + x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">"))); + if (next) { + w = TEXTW(">"); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); + } + } + drw_map(drw, win, 0, 0, mw, mh); +} + +static void +grabfocus(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; + Window focuswin; + int i, revertwin; + + for (i = 0; i < 100; ++i) { + XGetInputFocus(dpy, &focuswin, &revertwin); + if (focuswin == win) + return; + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + nanosleep(&ts, NULL); + } + die("cannot grab focus"); +} + +static void +grabkeyboard(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + if (embed) + return; + /* try to grab keyboard, we may have to wait for another process to ungrab */ + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, NULL); + } + die("cannot grab keyboard"); +} + +static void +match(void) +{ + static char **tokv = NULL; + static int tokn = 0; + + char buf[sizeof text], *s; + int i, tokc = 0; + size_t len, textsize; + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; + + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) + die("cannot realloc %zu bytes:", tokn * sizeof *tokv); + len = tokc ? strlen(tokv[0]) : 0; + + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; + textsize = strlen(text) + 1; + for (item = items; item && item->text; item++) { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + if (i != tokc) /* not all tokens match */ + continue; + /* exact matches go first, then prefixes, then substrings */ + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); + else if (!fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lprefix, &prefixend); + else + appenditem(item, &lsubstr, &substrend); + } + if (lprefix) { + if (matches) { + matchend->right = lprefix; + lprefix->left = matchend; + } else + matches = lprefix; + matchend = prefixend; + } + if (lsubstr) { + if (matches) { + matchend->right = lsubstr; + lsubstr->left = matchend; + } else + matches = lsubstr; + matchend = substrend; + } + curr = sel = matches; + calcoffsets(); +} + +static void +insert(const char *str, ssize_t n) +{ + if (strlen(text) + n > sizeof text - 1) + return; + /* move existing text out of the way, insert new text, and update cursor */ + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); + if (n > 0) + memcpy(&text[cursor], str, n); + cursor += n; + match(); +} + +static size_t +nextrune(int inc) +{ + ssize_t n; + + /* return location of next utf8 rune in the given direction (+1 or -1) */ + for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) + ; + return n; +} + +static void +movewordedge(int dir) +{ + if (dir < 0) { /* move cursor to the start of the word*/ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + } else { /* move cursor to the end of the word */ + while (text[cursor] && strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + while (text[cursor] && !strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + } +} + +static void +keypress(XKeyEvent *ev) +{ + char buf[32]; + int len; + KeySym ksym; + Status status; + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: + goto insert; + case XLookupKeySym: + case XLookupBoth: + break; + } + + if (ev->state & ControlMask) { + switch(ksym) { + case XK_a: ksym = XK_Home; break; + case XK_b: ksym = XK_Left; break; + case XK_c: ksym = XK_Escape; break; + case XK_d: ksym = XK_Delete; break; + case XK_e: ksym = XK_End; break; + case XK_f: ksym = XK_Right; break; + case XK_g: ksym = XK_Escape; break; + case XK_h: ksym = XK_BackSpace; break; + case XK_i: ksym = XK_Tab; break; + case XK_j: /* fallthrough */ + case XK_J: /* fallthrough */ + case XK_m: /* fallthrough */ + case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; + case XK_n: ksym = XK_Down; break; + case XK_p: ksym = XK_Up; break; + + case XK_k: /* delete right */ + text[cursor] = '\0'; + match(); + break; + case XK_u: /* delete left */ + insert(NULL, 0 - cursor); + break; + case XK_w: /* delete word */ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + break; + case XK_y: /* paste selection */ + case XK_Y: + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + return; + case XK_Left: + case XK_KP_Left: + movewordedge(-1); + goto draw; + case XK_Right: + case XK_KP_Right: + movewordedge(+1); + goto draw; + case XK_Return: + case XK_KP_Enter: + break; + case XK_bracketleft: + cleanup(); + exit(1); + default: + return; + } + } else if (ev->state & Mod1Mask) { + switch(ksym) { + case XK_b: + movewordedge(-1); + goto draw; + case XK_f: + movewordedge(+1); + goto draw; + case XK_g: ksym = XK_Home; break; + case XK_G: ksym = XK_End; break; + case XK_h: ksym = XK_Up; break; + case XK_j: ksym = XK_Next; break; + case XK_k: ksym = XK_Prior; break; + case XK_l: ksym = XK_Down; break; + default: + return; + } + } + + switch(ksym) { + default: +insert: + if (!iscntrl((unsigned char)*buf)) + insert(buf, len); + break; + case XK_Delete: + case XK_KP_Delete: + if (text[cursor] == '\0') + return; + cursor = nextrune(+1); + /* fallthrough */ + case XK_BackSpace: + if (cursor == 0) + return; + insert(NULL, nextrune(-1) - cursor); + break; + case XK_End: + case XK_KP_End: + if (text[cursor] != '\0') { + cursor = strlen(text); + break; + } + if (next) { + /* jump to end of list and position items in reverse */ + curr = matchend; + calcoffsets(); + curr = prev; + calcoffsets(); + while (next && (curr = curr->right)) + calcoffsets(); + } + sel = matchend; + break; + case XK_Escape: + cleanup(); + exit(1); + case XK_Home: + case XK_KP_Home: + if (sel == matches) { + cursor = 0; + break; + } + sel = curr = matches; + calcoffsets(); + break; + case XK_Left: + case XK_KP_Left: + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Up: + case XK_KP_Up: + if (sel && sel->left && (sel = sel->left)->right == curr) { + curr = prev; + calcoffsets(); + } + break; + case XK_Next: + case XK_KP_Next: + if (!next) + return; + sel = curr = next; + calcoffsets(); + break; + case XK_Prior: + case XK_KP_Prior: + if (!prev) + return; + sel = curr = prev; + calcoffsets(); + break; + case XK_Return: + case XK_KP_Enter: + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + if (!(ev->state & ControlMask)) { + cleanup(); + exit(0); + } + if (sel) + sel->out = 1; + break; + case XK_Right: + case XK_KP_Right: + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Down: + case XK_KP_Down: + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + break; + case XK_Tab: + if (!sel) + return; + cursor = strnlen(sel->text, sizeof text - 1); + memcpy(text, sel->text, cursor); + text[cursor] = '\0'; + match(); + break; + } + +draw: + drawmenu(); +} + +static void +paste(void) +{ + char *p, *q; + int di; + unsigned long dl; + Atom da; + + /* we have been given the current selection, now insert it into input */ + if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, + utf8, &da, &di, &dl, &dl, (unsigned char **)&p) + == Success && p) { + insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); + XFree(p); + } + drawmenu(); +} + +static void +readstdin(void) +{ + char *line = NULL; + size_t i, junk, size = 0; + ssize_t len; + + /* read each line from stdin and add it to the item list */ + for (i = 0; (len = getline(&line, &junk, stdin)) != -1; i++, line = NULL) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %zu bytes:", size); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + items[i].text = line; + items[i].out = 0; + } + if (items) + items[i].text = NULL; + lines = MIN(lines, i); +} + +static void +run(void) +{ + XEvent ev; + + while (!XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, win)) + continue; + switch(ev.type) { + case DestroyNotify: + if (ev.xdestroywindow.window != win) + break; + cleanup(); + exit(1); + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); + break; + case FocusIn: + /* regrab focus from parent window */ + if (ev.xfocus.window != win) + grabfocus(); + break; + case KeyPress: + keypress(&ev.xkey); + break; + case SelectionNotify: + if (ev.xselection.property == utf8) + paste(); + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dpy, win); + break; + } + } +} + +static void +setup(void) +{ + int x, y, i, j; + unsigned int du; + XSetWindowAttributes swa; + XIM xim; + Window w, dw, *dws; + XWindowAttributes wa; + XClassHint ch = {"dmenu", "dmenu"}; +#ifdef XINERAMA + XineramaScreenInfo *info; + Window pw; + int a, di, n, area = 0; +#endif + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(drw, colors[j], 2); + + clip = XInternAtom(dpy, "CLIPBOARD", False); + utf8 = XInternAtom(dpy, "UTF8_STRING", False); + + /* calculate menu geometry */ + bh = drw->fonts->h + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; +#ifdef XINERAMA + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { + XGetInputFocus(dpy, &w, &di); + if (mon >= 0 && mon < n) + i = mon; + else if (w != root && w != PointerRoot && w != None) { + /* find top-level window containing current input focus */ + do { + if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) + XFree(dws); + } while (w != root && w != pw); + /* find xinerama screen with which the window intersects most */ + if (XGetWindowAttributes(dpy, pw, &wa)) + for (j = 0; j < n; j++) + if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { + area = a; + i = j; + } + } + /* no focused window is on screen, so use pointer location instead */ + if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) + for (i = 0; i < n; i++) + if (INTERSECT(x, y, 1, 1, info[i]) != 0) + break; + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); + x = info[i].x_org + ((info[i].width - mw) / 2); + y = info[i].y_org + ((info[i].height - mh) / 2); + } else { + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + } + + XFree(info); + } else +#endif + { + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); + x = (wa.width - mw) / 2; + y = (wa.height - mh) / 2; + } else { + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + } + } + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + inputw = mw / 3; /* input width: ~33% of monitor width */ + match(); + + /* create menu window */ + swa.override_redirect = True; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + if (border_width) + XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); + XSetClassHint(dpy, win, &ch); + + + /* input methods */ + if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed: could not open input device"); + + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, NULL); + + XMapRaised(dpy, win); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); + if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { + for (i = 0; i < du && dws[i] != win; ++i) + XSelectInput(dpy, dws[i], FocusChangeMask); + XFree(dws); + } + grabfocus(); + } + drw_resize(drw, mw, mh); + drawmenu(); +} + +static void +usage(void) +{ + die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); +} + +int +main(int argc, char *argv[]) +{ + XWindowAttributes wa; + int i, fast = 0; + + for (i = 1; i < argc; i++) + /* these options take no arguments */ + if (!strcmp(argv[i], "-v")) { /* prints version information */ + puts("dmenu-"VERSION); + exit(0); + } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; + else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ + centered = 1; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; + } else if (i + 1 == argc) + usage(); + /* these options take one argument */ + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + lines = atoi(argv[++i]); + else if (!strcmp(argv[i], "-m")) + mon = atoi(argv[++i]); + else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ + prompt = argv[++i]; + else if (!strcmp(argv[i], "-fn")) /* font or font set */ + fonts[0] = argv[++i]; + else if (!strcmp(argv[i], "-nb")) /* normal background color */ + colors[SchemeNorm][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ + colors[SchemeNorm][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-sb")) /* selected background color */ + colors[SchemeSel][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ + colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; + else if (!strcmp(argv[i], "-bw")) + border_width = atoi(argv[++i]); /* border width */ + else + usage(); + + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("cannot open display"); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + if (!embed || !(parentwin = strtol(embed, NULL, 0))) + parentwin = root; + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + drw = drw_create(dpy, screen, root, wa.width, wa.height); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + +#ifdef __OpenBSD__ + if (pledge("stdio rpath", NULL) == -1) + die("pledge"); +#endif + + if (fast && !isatty(0)) { + grabkeyboard(); + readstdin(); + } else { + readstdin(); + grabkeyboard(); + } + setup(); + run(); + + return 1; /* unreachable */ +} diff --git a/dmenu/dmenu_path b/dmenu/dmenu_path new file mode 100755 index 0000000..3a7cda7 --- /dev/null +++ b/dmenu/dmenu_path @@ -0,0 +1,13 @@ +#!/bin/sh + +cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}" +cache="$cachedir/dmenu_run" + +[ ! -e "$cachedir" ] && mkdir -p "$cachedir" + +IFS=: +if stest -dqr -n "$cache" $PATH; then + stest -flx $PATH | sort -u | tee "$cache" +else + cat "$cache" +fi diff --git a/dmenu/dmenu_run b/dmenu/dmenu_run new file mode 100755 index 0000000..834ede5 --- /dev/null +++ b/dmenu/dmenu_run @@ -0,0 +1,2 @@ +#!/bin/sh +dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & diff --git a/dmenu/drw.c b/dmenu/drw.c new file mode 100644 index 0000000..a58a2b4 --- /dev/null +++ b/dmenu/drw.c @@ -0,0 +1,450 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + 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); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); +} + +/* 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) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + int i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; + + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) + return 0; + + if (!render) { + w = invert ? invert : ~invert; + } 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)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); + while (1) { + ew = ellipsis_len = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + ew += tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } + x += ew; + w -= ew; + } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); + + if (!*text || overflow) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + goto no_match; + } + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; +no_match: + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +unsigned int +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) +{ + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/dmenu/drw.h b/dmenu/drw.h new file mode 100644 index 0000000..fd7631b --- /dev/null +++ b/dmenu/drw.h @@ -0,0 +1,58 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); +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); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* 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); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/dmenu/stest.1 b/dmenu/stest.1 new file mode 100644 index 0000000..2667d8a --- /dev/null +++ b/dmenu/stest.1 @@ -0,0 +1,90 @@ +.TH STEST 1 dmenu\-VERSION +.SH NAME +stest \- filter a list of files by properties +.SH SYNOPSIS +.B stest +.RB [ -abcdefghlpqrsuwx ] +.RB [ -n +.IR file ] +.RB [ -o +.IR file ] +.RI [ file ...] +.SH DESCRIPTION +.B stest +takes a list of files and filters by the files' properties, analogous to +.IR test (1). +Files which pass all tests are printed to stdout. If no files are given, stest +reads files from stdin. +.SH OPTIONS +.TP +.B \-a +Test hidden files. +.TP +.B \-b +Test that files are block specials. +.TP +.B \-c +Test that files are character specials. +.TP +.B \-d +Test that files are directories. +.TP +.B \-e +Test that files exist. +.TP +.B \-f +Test that files are regular files. +.TP +.B \-g +Test that files have their set-group-ID flag set. +.TP +.B \-h +Test that files are symbolic links. +.TP +.B \-l +Test the contents of a directory given as an argument. +.TP +.BI \-n " file" +Test that files are newer than +.IR file . +.TP +.BI \-o " file" +Test that files are older than +.IR file . +.TP +.B \-p +Test that files are named pipes. +.TP +.B \-q +No files are printed, only the exit status is returned. +.TP +.B \-r +Test that files are readable. +.TP +.B \-s +Test that files are not empty. +.TP +.B \-u +Test that files have their set-user-ID flag set. +.TP +.B \-v +Invert the sense of tests, only failing files pass. +.TP +.B \-w +Test that files are writable. +.TP +.B \-x +Test that files are executable. +.SH EXIT STATUS +.TP +.B 0 +At least one file passed all tests. +.TP +.B 1 +No files passed all tests. +.TP +.B 2 +An error occurred. +.SH SEE ALSO +.IR dmenu (1), +.IR test (1) diff --git a/dmenu/stest.c b/dmenu/stest.c new file mode 100644 index 0000000..e27d3a5 --- /dev/null +++ b/dmenu/stest.c @@ -0,0 +1,109 @@ +/* See LICENSE file for copyright and license details. */ +#include + +#include +#include +#include +#include +#include +#include + +#include "arg.h" +char *argv0; + +#define FLAG(x) (flag[(x)-'a']) + +static void test(const char *, const char *); +static void usage(void); + +static int match = 0; +static int flag[26]; +static struct stat old, new; + +static void +test(const char *path, const char *name) +{ + struct stat st, ln; + + if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */ + && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */ + && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */ + && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */ + && (!FLAG('e') || access(path, F_OK) == 0) /* exists */ + && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */ + && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */ + && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */ + && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */ + && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */ + && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */ + && (!FLAG('r') || access(path, R_OK) == 0) /* readable */ + && (!FLAG('s') || st.st_size > 0) /* not empty */ + && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */ + && (!FLAG('w') || access(path, W_OK) == 0) /* writable */ + && (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */ + if (FLAG('q')) + exit(0); + match = 1; + puts(name); + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] " + "[-n file] [-o file] [file...]\n", argv0); + exit(2); /* like test(1) return > 1 on error */ +} + +int +main(int argc, char *argv[]) +{ + struct dirent *d; + char path[PATH_MAX], *line = NULL, *file; + size_t linesiz = 0; + ssize_t n; + DIR *dir; + int r; + + ARGBEGIN { + case 'n': /* newer than file */ + case 'o': /* older than file */ + file = EARGF(usage()); + if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old)))) + perror(file); + break; + default: + /* miscellaneous operators */ + if (strchr("abcdefghlpqrsuvwx", ARGC())) + FLAG(ARGC()) = 1; + else + usage(); /* unknown flag */ + } ARGEND; + + if (!argc) { + /* read list from stdin */ + while ((n = getline(&line, &linesiz, stdin)) > 0) { + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + test(line, line); + } + free(line); + } else { + for (; argc; argc--, argv++) { + if (FLAG('l') && (dir = opendir(*argv))) { + /* test directory contents */ + while ((d = readdir(dir))) { + r = snprintf(path, sizeof path, "%s/%s", + *argv, d->d_name); + if (r >= 0 && (size_t)r < sizeof path) + test(path, d->d_name); + } + closedir(dir); + } else { + test(*argv, *argv); + } + } + } + return match ? 0 : 1; +} diff --git a/dmenu/util.c b/dmenu/util.c new file mode 100644 index 0000000..96b82c9 --- /dev/null +++ b/dmenu/util.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} diff --git a/dmenu/util.h b/dmenu/util.h new file mode 100644 index 0000000..f633b51 --- /dev/null +++ b/dmenu/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/dwm/Makefile b/dwm/Makefile new file mode 100644 index 0000000..c479ecf --- /dev/null +++ b/dwm/Makefile @@ -0,0 +1,51 @@ +# dwm - dynamic window manager +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dwm.c util.c +OBJ = ${SRC:.c=.o} + +all: options dwm + +options: + @echo dwm build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +config.h: + cp config.def.h $@ + +dwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz config.h *.orig *.rej + +dist: clean + mkdir -p dwm-${VERSION} + cp -R LICENSE Makefile README config.def.h config.mk\ + dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} + tar -cf dwm-${VERSION}.tar dwm-${VERSION} + gzip dwm-${VERSION}.tar + rm -rf dwm-${VERSION} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f dwm ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dwm\ + ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +.PHONY: all options clean dist install uninstall diff --git a/dwm/PKGBUILD b/dwm/PKGBUILD deleted file mode 100644 index 8a9051c..0000000 --- a/dwm/PKGBUILD +++ /dev/null @@ -1,49 +0,0 @@ -# my custom dwm!! - -pkgname=dwm -pkgver=6.4 -pkgrel=1 -pkgdesc="A dynamic window manager for X" -url="http://git.vern.cc/vitorg/suckless" -arch=('x86_64') -license=('MIT') -options=(zipman) -depends=('libx11' 'libxinerama' 'libxft' 'freetype2') -install=dwm.install -source=(https://dl.suckless.org/dwm/dwm-$pkgver.tar.gz - https://dwm.suckless.org/patches/swallow/dwm-swallow-6.3.diff - https://dwm.suckless.org/patches/cool_autostart/dwm-cool-autostart-6.2.diff - https://dwm.suckless.org/patches/pertag/dwm-pertag-20200914-61bb8b2.diff - https://dwm.suckless.org/patches/uselessgap/dwm-uselessgap-20211119-58414bee958f2.diff - dwm-restartsig.diff - config.h - dwm.desktop - dwm-loop) -sha256sums=('fa9c0d69a584485076cfc18809fd705e5c2080dafb13d5e729a3646ca7703a6e' - 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP') - -prepare() { - cd "$srcdir/$pkgname-$pkgver" - if [[ -f "$srcdir/config.h" ]]; then - cp -fv "$srcdir/config.h" config.h - fi - # patch --forward --strip=1 --input="${srcdir}/dwm-restartsig.diff" - patch --forward --strip=1 --input="${srcdir}/dwm-swallow-6.3.diff" - patch --forward --strip=1 --input="${srcdir}/dwm-cool-autostart-6.2.diff" - patch --forward --strip=1 --input="${srcdir}/dwm-pertag-20200914-61bb8b2.diff" - patch --forward --strip=1 --input="${srcdir}/dwm-uselessgap-20211119-58414bee958f2.diff" -} - -build() { - cd "$srcdir/$pkgname-$pkgver" - make X11INC=/usr/include/X11 X11LIB=/usr/lib/X11 FREETYPEINC=/usr/include/freetype2 -} - -package() { - cd "$srcdir/$pkgname-$pkgver" - make PREFIX=/usr DESTDIR="$pkgdir" install - install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" - install -Dm644 README "$pkgdir/usr/share/doc/$pkgname/README" - install -Dm644 "$srcdir/dwm.desktop" "$pkgdir/usr/share/xsessions/dwm.desktop" - install -m755 "$srcdir/dwm-loop" "$pkgdir/usr/bin/dwm-loop" -} diff --git a/dwm/config.def.h b/dwm/config.def.h new file mode 100644 index 0000000..ffda70c --- /dev/null +++ b/dwm/config.def.h @@ -0,0 +1,142 @@ +/* See LICENSE file for copyright and license details. */ + +#define SESSION_FILE "/tmp/dwm-session" + +/* appearance */ +static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int gappx = 4; /* gaps between windows*/ +static const unsigned int snap = 32; /* snap pixel */ +static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const char *fonts[] = { "monospace:size=10" }; +static const char col_gray1[] = "#282828"; +static const char col_gray2[] = "#504945"; +static const char col_gray3[] = "#bdae93"; +static const char col_gray4[] = "#ebdbb2"; +static const char col_cyan[] = "#cc241d"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray1 }, + [SchemeSel] = { col_gray4, col_gray2, col_gray2 }, +}; + +typedef struct { + const char *name; + const void *cmd; +} Sp; +const char *spcmd1[] = {"st", "-n", "spterm", "-g", "120x34", NULL }; +const char *spcmd2[] = {"st", "-n", "spfm", "-g", "144x41", "-e", "nnn", NULL }; +const char *spcmd3[] = {"st", "-n", "spmtrx", "-g", "144x41", "-e", "gomuks", NULL }; +const char *spcmd4[] = {"st", "-n", "sprss", "-g", "144x41", "-e", "newsboat", NULL }; +static Sp scratchpads[] = { + /* name cmd */ + {"spterm", spcmd1}, + {"spfm", spcmd2}, + {"spmtrx", spcmd3}, + {"sprss", spcmd4}, +}; + +/* tagging */ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +static const int momentaryalttags = 0; /* 1 means alttags will show only when key is held down*/ +static const Rule rules[] = { + /* class instance title tags mask isfloating isterminal noswallow monitor */ + { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 }, + { "Cromite", NULL, NULL, 1<<1, 0, 0, 0, -1 }, + { "st", NULL, NULL, 0, 0, 1, 0, -1 }, + { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */ + { NULL, "spterm", NULL, SPTAG(0), 1, 0, 0, -1 }, + { NULL, "spfm", NULL, SPTAG(1), 1, 0, 0, -1 }, + { NULL, "spmtrx", NULL, SPTAG(2), 1, 0, 0, -1 }, + { NULL, "sprss", NULL, SPTAG(3), 1, 0, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.5; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +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[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "[M]", monocle }, + { "><>", NULL }, /* no layout function means floating behavior */ +}; + +/* key definitions */ +#define MODKEY Mod4Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +static const char *menucmd[] = { "dmenu_run", "-c", "-l", "10", "-bw", "2", NULL }; +static const char *passcmd[] = { "passmenu", "-c", "-l", "10", "-bw", "2", NULL }; +static const char *termcmd[] = { "st", NULL }; +static const char *wwwcmd[] = { "cromite", NULL }; +static const char *sscmd[] = { "ss", NULL }; + +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_r, spawn, {.v = menucmd } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, spawn, {.v = wwwcmd } }, + { MODKEY, XK_p, spawn, {.v = sscmd } }, + { MODKEY|ShiftMask, XK_p, spawn, {.v = passcmd } }, + { MODKEY|ShiftMask, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_Return, zoom, {0} }, + { MODKEY, XK_w, killclient, {0} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + { MODKEY, XK_c, togglescratch, {.ui = 0 } }, + { MODKEY, XK_e, togglescratch, {.ui = 1 } }, + { MODKEY, XK_g, togglescratch, {.ui = 2 } }, + { MODKEY, XK_n, togglescratch, {.ui = 3 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY|ShiftMask, XK_c, quit, {1} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/dwm/config.h b/dwm/config.h index 61ef190..ffda70c 100644 --- a/dwm/config.h +++ b/dwm/config.h @@ -1,5 +1,7 @@ /* See LICENSE file for copyright and license details. */ +#define SESSION_FILE "/tmp/dwm-session" + /* appearance */ static const unsigned int borderpx = 1; /* border pixel of windows */ static const unsigned int gappx = 4; /* gaps between windows*/ @@ -8,35 +10,46 @@ static const int swallowfloating = 0; /* 1 means swallow floating wind 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 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 col_gray1[] = "#282828"; +static const char col_gray2[] = "#504945"; +static const char col_gray3[] = "#bdae93"; +static const char col_gray4[] = "#ebdbb2"; +static const char col_cyan[] = "#cc241d"; static const char *colors[][3] = { /* fg bg border */ - [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, - [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + [SchemeNorm] = { col_gray3, col_gray1, col_gray1 }, + [SchemeSel] = { col_gray4, col_gray2, col_gray2 }, }; -static const char *autostart[] = { - "xwallpaper", "--maximize", "/usr/share/wallpapers/arch-gruvbox-dark.png", NULL, - "cromite", "--no-startup-window", NULL, - "picom", "--backend", "glx", NULL, - "slstatus", NULL, - NULL +typedef struct { + const char *name; + const void *cmd; +} Sp; +const char *spcmd1[] = {"st", "-n", "spterm", "-g", "120x34", NULL }; +const char *spcmd2[] = {"st", "-n", "spfm", "-g", "144x41", "-e", "nnn", NULL }; +const char *spcmd3[] = {"st", "-n", "spmtrx", "-g", "144x41", "-e", "gomuks", NULL }; +const char *spcmd4[] = {"st", "-n", "sprss", "-g", "144x41", "-e", "newsboat", NULL }; +static Sp scratchpads[] = { + /* name cmd */ + {"spterm", spcmd1}, + {"spfm", spcmd2}, + {"spmtrx", spcmd3}, + {"sprss", spcmd4}, }; + /* tagging */ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; - +static const int momentaryalttags = 0; /* 1 means alttags will show only when key is held down*/ 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 }, + /* class instance title tags mask isfloating isterminal noswallow monitor */ + { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 }, + { "Cromite", NULL, NULL, 1<<1, 0, 0, 0, -1 }, + { "st", NULL, NULL, 0, 0, 1, 0, -1 }, + { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */ + { NULL, "spterm", NULL, SPTAG(0), 1, 0, 0, -1 }, + { NULL, "spfm", NULL, SPTAG(1), 1, 0, 0, -1 }, + { NULL, "spmtrx", NULL, SPTAG(2), 1, 0, 0, -1 }, + { NULL, "sprss", NULL, SPTAG(3), 1, 0, 0, -1 }, }; /* layout(s) */ @@ -48,8 +61,8 @@ static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen win static const Layout layouts[] = { /* symbol arrange function */ { "[]=", tile }, /* first entry is default */ - { "><>", NULL }, /* no layout function means floating behavior */ { "[M]", monocle }, + { "><>", NULL }, /* no layout function means floating behavior */ }; /* key definitions */ @@ -64,7 +77,8 @@ static const Layout layouts[] = { #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } /* commands */ -static const char *menucmd[] = { "dmenu_run", NULL }; +static const char *menucmd[] = { "dmenu_run", "-c", "-l", "10", "-bw", "2", NULL }; +static const char *passcmd[] = { "passmenu", "-c", "-l", "10", "-bw", "2", NULL }; static const char *termcmd[] = { "st", NULL }; static const char *wwwcmd[] = { "cromite", NULL }; static const char *sscmd[] = { "ss", NULL }; @@ -75,6 +89,7 @@ static const Key keys[] = { { MODKEY, XK_Return, spawn, {.v = termcmd } }, { MODKEY, XK_b, spawn, {.v = wwwcmd } }, { MODKEY, XK_p, spawn, {.v = sscmd } }, + { MODKEY|ShiftMask, XK_p, spawn, {.v = passcmd } }, { MODKEY|ShiftMask, XK_b, togglebar, {0} }, { MODKEY, XK_j, focusstack, {.i = +1 } }, { MODKEY, XK_k, focusstack, {.i = -1 } }, @@ -82,12 +97,8 @@ static const Key keys[] = { { MODKEY, XK_d, incnmaster, {.i = -1 } }, { MODKEY, XK_h, setmfact, {.f = -0.05} }, { MODKEY, XK_l, setmfact, {.f = +0.05} }, - { MODKEY|ShiftMask, XK_Return, zoom, {0} }, - { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_Return, zoom, {0} }, { MODKEY, XK_w, killclient, {0} }, - { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, - { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, - { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, { MODKEY, XK_space, setlayout, {0} }, { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, { MODKEY, XK_0, view, {.ui = ~0 } }, @@ -96,6 +107,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_c, togglescratch, {.ui = 0 } }, + { MODKEY, XK_e, togglescratch, {.ui = 1 } }, + { MODKEY, XK_g, togglescratch, {.ui = 2 } }, + { MODKEY, XK_n, togglescratch, {.ui = 3 } }, TAGKEYS( XK_1, 0) TAGKEYS( XK_2, 1) TAGKEYS( XK_3, 2) @@ -106,6 +121,7 @@ static const Key keys[] = { TAGKEYS( XK_8, 7) TAGKEYS( XK_9, 8) { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY|ShiftMask, XK_c, quit, {1} }, }; /* button definitions */ @@ -115,7 +131,6 @@ static const Button buttons[] = { { ClkLtSymbol, 0, Button1, setlayout, {0} }, { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, { ClkWinTitle, 0, Button2, zoom, {0} }, - { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, { ClkClientWin, MODKEY, Button1, movemouse, {0} }, { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, diff --git a/dwm/config.mk b/dwm/config.mk new file mode 100644 index 0000000..c1434f2 --- /dev/null +++ b/dwm/config.mk @@ -0,0 +1,40 @@ +# dwm version +VERSION = 6.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 = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = ${X11INC}/freetype2 +#MANPREFIX = ${PREFIX}/man +#KVMLIB = -lkvm + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res ${KVMLIB} + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/dwm/drw.c b/dwm/drw.c new file mode 100644 index 0000000..a58a2b4 --- /dev/null +++ b/dwm/drw.c @@ -0,0 +1,450 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + 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); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); +} + +/* 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) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + int i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; + + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) + return 0; + + if (!render) { + w = invert ? invert : ~invert; + } 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)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); + while (1) { + ew = ellipsis_len = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + ew += tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } + x += ew; + w -= ew; + } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); + + if (!*text || overflow) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + goto no_match; + } + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; +no_match: + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +unsigned int +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) +{ + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/dwm/drw.h b/dwm/drw.h new file mode 100644 index 0000000..6471431 --- /dev/null +++ b/dwm/drw.h @@ -0,0 +1,58 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); +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); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* 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); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/dwm/drw.o b/dwm/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..eb6ee1a9c9f5e11a38ee2dc8ca0d4fbe2252e873 GIT binary patch literal 11072 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 A8~^|S literal 0 HcmV?d00001 diff --git a/dwm/dwm b/dwm/dwm new file mode 100755 index 0000000000000000000000000000000000000000..0db4b644b09e94fc5f72dd2238ca45db493d7113 GIT binary patch literal 73272 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

gW^jM-P)D&)I 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

ioP&MDL9WlExZV)? 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 literal 0 HcmV?d00001 diff --git a/dwm/dwm-loop b/dwm/dwm-loop deleted file mode 100644 index 9074a16..0000000 --- a/dwm/dwm-loop +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -while true - do - /usr/bin/dwm 2> /dev/null -done diff --git a/dwm/dwm.1 b/dwm/dwm.1 new file mode 100644 index 0000000..0f3a6ba --- /dev/null +++ b/dwm/dwm.1 @@ -0,0 +1,209 @@ +.TH DWM 1 dwm\-VERSION +.SH NAME +dwm \- dynamic window manager +.SH SYNOPSIS +.B dwm +.RB [ \-v ] +.SH DESCRIPTION +dwm is a dynamic window manager for X. 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 +In tiled layouts windows are managed in a master and stacking area. The master +area on the left contains one window by default, and the stacking area on the +right contains all other windows. The number of master area windows can be +adjusted from zero to an arbitrary number. In monocle layout all windows are +maximised to the screen size. In floating layout windows can be resized and +moved freely. Dialog windows are always managed floating, regardless of the +layout applied. +.P +Windows are grouped by tags. Each window can be tagged with one or multiple +tags. Selecting certain tags displays all windows with these tags. +.P +Each screen contains a small status bar which displays all available tags, the +layout, the title of the focused window, and the text read from the root window +name property, if the screen is focused. A floating window is indicated with an +empty square and a maximised floating window is indicated with a filled square +before the windows title. The selected tags are indicated with a different +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. +.P +On start, dwm 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 dwm will wait for its +termination before starting. The latter is executed in the background before +dwm enters its handler loop. +.P +Either of these files may be omitted. +.SH OPTIONS +.TP +.B \-v +prints version information to stderr, then exits. +.SH USAGE +.SS Status bar +.TP +.B X root window name +is read and displayed in the status text area. It can be set with the +.BR xsetroot (1) +command. +.TP +.B Button1 +click on a tag label to display all windows with that tag, click on the layout +label toggles between tiled and floating layout. +.TP +.B Button3 +click on a tag label adds/removes all windows with that tag to/from the view. +.TP +.B Mod1\-Button1 +click on a tag label applies that tag to the focused window. +.TP +.B Mod1\-Button3 +click on a tag label adds/removes that tag to/from the focused window. +.SS Keyboard commands +.TP +.B Mod1\-Shift\-Return +Start +.BR st(1). +.TP +.B Mod1\-p +Spawn +.BR dmenu(1) +for launching other programs. +.TP +.B Mod1\-, +Focus previous screen, if any. +.TP +.B Mod1\-. +Focus next screen, if any. +.TP +.B Mod1\-Shift\-, +Send focused window to previous screen, if any. +.TP +.B Mod1\-Shift\-. +Send focused window to next screen, if any. +.TP +.B Mod1\-b +Toggles bar on and off. +.TP +.B Mod1\-t +Sets tiled layout. +.TP +.B Mod1\-f +Sets floating layout. +.TP +.B Mod1\-m +Sets monocle layout. +.TP +.B Mod1\-space +Toggles between current and previous layout. +.TP +.B Mod1\-j +Focus next window. +.TP +.B Mod1\-k +Focus previous window. +.TP +.B Mod1\-i +Increase number of windows in master area. +.TP +.B Mod1\-d +Decrease number of windows in master area. +.TP +.B Mod1\-l +Increase master area size. +.TP +.B Mod1\-h +Decrease master area size. +.TP +.B Mod1\-Return +Zooms/cycles focused window to/from master area (tiled layouts only). +.TP +.B Mod1\-Shift\-c +Close focused window. +.TP +.B Mod1\-Shift\-space +Toggle focused window between tiled and floating state. +.TP +.B Mod1\-Tab +Toggles to the previously selected tags. +.TP +.B Mod1\-Shift\-[1..n] +Apply nth tag to focused window. +.TP +.B Mod1\-Shift\-0 +Apply all tags to focused window. +.TP +.B Mod1\-Control\-Shift\-[1..n] +Add/remove nth tag to/from focused window. +.TP +.B Mod1\-[1..n] +View all windows with nth tag. +.TP +.B Mod1\-0 +View all windows with any tag. +.TP +.B Mod1\-Control\-[1..n] +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 +Move focused window while dragging. Tiled windows will be toggled to the floating state. +.TP +.B Mod1\-Button2 +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 dwm are searched for in +the following directories: +.IP "1. $XDG_DATA_HOME/dwm" +.IP "2. $HOME/.local/share/dwm" +.IP "3. $HOME/.dwm" +.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 dwm enters its handler +loop. +.TP 15 +autostart_blocking.sh +This file is started before any autostart.sh; dwm waits for its termination. +.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) +.SH ISSUES +Java applications which use the XToolkit/XAWT backend may draw grey windows +only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early +JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds +are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the +environment variable +.BR AWT_TOOLKIT=MToolkit +(to use the older Motif backend instead) or running +.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D +or +.B wmname LG3D +(to pretend that a non-reparenting window manager is running that the +XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable +.BR _JAVA_AWT_WM_NONREPARENTING=1 . +.SH BUGS +Send all bug reports with a patch to hackers@suckless.org. diff --git a/dwm/dwm.c b/dwm/dwm.c new file mode 100644 index 0000000..cc9e53b --- /dev/null +++ b/dwm/dwm.c @@ -0,0 +1,2668 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include +#include +#include +#ifdef __OpenBSD__ +#include +#include +#endif /* __OpenBSD */ + +#include "drw.h" +#include "util.h" + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define NUMTAGS (LENGTH(tags) + LENGTH(scratchpads)) +#define WIDTH(X) ((X)->w + 2 * (X)->bw + gappx) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw + gappx) +#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) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { SchemeNorm, SchemeSel }; /* color schemes */ +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow; + pid_t pid; + Client *next; + Client *snext; + Client *swallowing; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +typedef struct Pertag Pertag; +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + const Layout *lt[2]; + Pertag *pertag; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int isterminal; + int noswallow; + int monitor; +} Rule; + +/* function declarations */ +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(void); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmon(const Arg *arg); +static void focusstack(const Arg *arg); +static Atom getatomprop(Client *c, Atom prop); +static int getrootptr(int *x, int *y); +static long getstate(Window w); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +static void grabkeys(void); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void monocle(Monitor *m); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *c); +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Monitor *recttomon(int x, int y, int w, int h); +static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void runautostart(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void sigchld(int unused); +static void 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); +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 unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updatetitle(Client *c); +static void updatewindowtype(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void zoom(const Arg *arg); + +static pid_t getparentprocess(pid_t p); +static int isdescprocess(pid_t p, pid_t c); +static Client *swallowingclient(Window w); +static Client *termforwin(const Client *c); +static pid_t winpid(Window w); + +/* variables */ +static const char 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 */ +static int bh; /* bar height */ +static int lrpad; /* sum of left and right padding for text */ +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static int restart = 0; +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +static xcb_connection_t *xcon; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +struct Pertag { + unsigned int curtag, prevtag; /* current and previous tag */ + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ + const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ + int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ +}; + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isterminal = r->isterminal; + c->noswallow = r->noswallow; + 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; + } + } + if (ch.res_class) + 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] & ~SPTAGMASK); +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +swallow(Client *p, Client *c) +{ + + if (c->noswallow || c->isterminal) + return; + if (c->noswallow && !swallowfloating && c->isfloating) + return; + + detach(c); + detachstack(c); + + setclientstate(c, WithdrawnState); + XUnmapWindow(dpy, p->win); + + p->swallowing = c; + c->mon = p->mon; + + Window w = p->win; + p->win = c->win; + c->win = w; + updatetitle(p); + XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h); + arrange(p->mon); + configure(p); + updateclientlist(); +} + +void +unswallow(Client *c) +{ + c->win = c->swallowing->win; + + free(c->swallowing); + c->swallowing = NULL; + + /* unfullscreen the client */ + setfullscreen(c, 0); + updatetitle(c); + arrange(c->mon); + XMapWindow(dpy, c->win); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + setclientstate(c, NormalState); + focus(NULL); + arrange(c->mon); +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + for (i = 0; i < LENGTH(buttons); i++) + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) + free(scheme[i]); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m; + unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + m->pertag = ecalloc(1, sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + + for (i = 0; i <= LENGTH(tags); i++) { + m->pertag->nmasters[i] = m->nmaster; + m->pertag->mfacts[i] = m->mfact; + + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + m->pertag->showbars[i] = m->showbar; + } + + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); + + else if ((c = swallowingclient(ev->window))) + unmanage(c->swallowing, 1); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +drawbar(Monitor *m) +{ + int x, w, tw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, + urg & 1 << i); + x += w; + } + w = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} + +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +enternotify(XEvent *e) +{ + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + focus(c); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void +focusmon(const Arg *arg) +{ + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); +} + +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + if (c) { + focus(c); + restack(selmon); + } +} + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keys); i++) + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +keypress(XEvent *e) +{ + unsigned int i; + KeySym keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL, *term = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + c->pid = winpid(w); + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + term = termforwin(c); + } + + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + if (term) + swallow(term, c); + focus(NULL); +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +monocle(Monitor *m) +{ + unsigned int n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + mon = m; +} + +void +movemouse(const Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +} + +void +saveSession(void) +{ + FILE *fw = fopen(SESSION_FILE, "w"); + for (Client *c = selmon->clients; c != NULL; c = c->next) { // get all the clients with their tags and write them to the file + fprintf(fw, "%lu %u\n", c->win, c->tags); + } + fclose(fw); +} + +void +restoreSession(void) +{ + // restore session + FILE *fr = fopen(SESSION_FILE, "r"); + if (!fr) + return; + + char *str = malloc(23 * sizeof(char)); // allocate enough space for excepted input from text file + while (fscanf(fr, "%[^\n] ", str) != EOF) { // read file till the end + long unsigned int winId; + unsigned int tagsForWin; + int check = sscanf(str, "%lu %u", &winId, &tagsForWin); // get data + if (check != 2) // break loop if data wasn't read correctly + break; + + for (Client *c = selmon->clients; c ; c = c->next) { // add tags to every window by winId + if (c->win == winId) { + c->tags = tagsForWin; + break; + } + } + } + + for (Client *c = selmon->clients; c ; c = c->next) { // refocus on windows + focus(c); + restack(c->mon); + } + + for (Monitor *m = selmon; m; m = m->next) // rearrange all monitors + arrange(m); + + free(str); + fclose(fr); + + // delete a file + remove(SESSION_FILE); +} + +void +quit(const Arg *arg) +{ + if(arg->i) restart = 1; + running = 0; + + if (restart == 1) + saveSession(); +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +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; + + 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); +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + 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) +{ + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int +sendevent(Client *c, Atom proto) +{ + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + + /* clean up any zombies immediately */ + sigchld(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); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ + updatebars(); + updatestatus(); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +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) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + while (0 < waitpid(-1, NULL, WNOHANG)); +} + +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 (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +tag(const Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +} + +void +tagmon(const Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + +void +tile(Monitor *m) +{ + unsigned int i, n, h, mw, my, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2*c->bw) + (n > 1 ? gappx : 0), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } +} + +void +togglebar(const Arg *arg) +{ + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + 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) +{ + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); + } +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + int i; + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + + if (newtagset == ~0) { + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = 0; + } + + /* test if the user did not select the same tag */ + if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { + selmon->pertag->prevtag = selmon->pertag->curtag; + for (i = 0; !(newtagset & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + + /* apply settings for this view */ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + + focus(NULL); + arrange(selmon); + } +} + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void +unmanage(Client *c, int destroyed) +{ + Monitor *m = c->mon; + XWindowChanges wc; + + if (c->swallowing) { + unswallow(c); + return; + } + + Client *s = swallowingclient(c->win); + if (s) { + free(s->swallowing); + s->swallowing = NULL; + arrange(m); + focus(NULL); + return; + } + + detach(c); + detachstack(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XSelectInput(dpy, c->win, NoEventMask); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + free(c); + + if (!s) { + arrange(m); + focus(NULL); + updateclientlist(); + } +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void +updatebars(void) +{ + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void +updatebarpos(Monitor *m) +{ + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; + m->wy = m->topbar ? m->wy + bh : m->wy; + } else + m->by = -bh; +} + +void +updateclientlist() +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int +updategeom(void) +{ + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + + /* new monitors if nn > n */ + for (i = n; i < nn; i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + /* removed monitors if n > nn */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attach(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +void +updatetitle(Client *c) +{ + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); +} + +void +updatewindowtype(Client *c) +{ + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void +view(const Arg *arg) +{ + int i; + unsigned int tmptag; + + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) { + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + selmon->pertag->prevtag = selmon->pertag->curtag; + + if (arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for (i = 0; !(arg->ui & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + + focus(NULL); + arrange(selmon); +} + +pid_t +winpid(Window w) +{ + + pid_t result = 0; + +#ifdef __linux__ + xcb_res_client_id_spec_t spec = {0}; + spec.client = w; + spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; + + xcb_generic_error_t *e = NULL; + xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); + xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); + + if (!r) + return (pid_t)0; + + xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); + for (; i.rem; xcb_res_client_id_value_next(&i)) { + spec = i.data->spec; + if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { + uint32_t *t = xcb_res_client_id_value_value(i.data); + result = *t; + break; + } + } + + free(r); + + if (result == (pid_t)-1) + result = 0; + +#endif /* __linux__ */ + +#ifdef __OpenBSD__ + Atom type; + int format; + unsigned long len, bytes; + unsigned char *prop; + pid_t ret; + + if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) + return 0; + + ret = *(pid_t*)prop; + XFree(prop); + result = ret; + +#endif /* __OpenBSD__ */ + return result; +} + +pid_t +getparentprocess(pid_t p) +{ + unsigned int v = 0; + +#ifdef __linux__ + FILE *f; + char buf[256]; + snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); + + if (!(f = fopen(buf, "r"))) + return 0; + + fscanf(f, "%*u %*s %*c %u", &v); + fclose(f); +#endif /* __linux__*/ + +#ifdef __OpenBSD__ + int n; + kvm_t *kd; + struct kinfo_proc *kp; + + kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); + if (!kd) + return 0; + + kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); + v = kp->p_ppid; +#endif /* __OpenBSD__ */ + + return (pid_t)v; +} + +int +isdescprocess(pid_t p, pid_t c) +{ + while (p != c && c != 0) + c = getparentprocess(c); + + return (int)c; +} + +Client * +termforwin(const Client *w) +{ + Client *c; + Monitor *m; + + if (!w->pid || w->isterminal) + return NULL; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + } + } + + return NULL; +} + +Client * +swallowingclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->swallowing && c->swallowing->win == w) + return c; + } + } + + return NULL; +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* 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. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + if (!(xcon = XGetXCBConnection(dpy))) + die("dwm: cannot get xcb connection\n"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec ps", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + runautostart(); + restoreSession(); + run(); + if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/dwm/dwm.desktop b/dwm/dwm.desktop deleted file mode 100644 index e44ff32..0000000 --- a/dwm/dwm.desktop +++ /dev/null @@ -1,7 +0,0 @@ -[Desktop Entry] -Encoding=UTF-8 -Name=Dwm -Comment=Dynamic window manager -Exec=dwm-loop -Icon=dwm -Type=XSession diff --git a/dwm/dwm.install b/dwm/dwm.install deleted file mode 100644 index 782c02a..0000000 --- a/dwm/dwm.install +++ /dev/null @@ -1,5 +0,0 @@ -post_install() { - echo "-- Edit config.h in dwm's PKGBUILD directory" - echo "-- and rebuild package if you want to change settings." - rm $srcdir/dwm-6.4* dwm-swallow* -} diff --git a/dwm/dwm.o b/dwm/dwm.o new file mode 100644 index 0000000000000000000000000000000000000000..6ebd43ab699173c7dee4440d193e5dae949eba07 GIT binary patch literal 67864 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 +#include +#include +#include + +int main(void) { + Display *d; + Window r, f, t = None; + XSizeHints h; + XEvent e; + + d = XOpenDisplay(NULL); + if (!d) + exit(1); + r = DefaultRootWindow(d); + + f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); + h.min_width = h.max_width = h.min_height = h.max_height = 400; + h.flags = PMinSize | PMaxSize; + XSetWMNormalHints(d, f, &h); + XStoreName(d, f, "floating"); + XMapWindow(d, f); + + XSelectInput(d, f, ExposureMask); + while (1) { + XNextEvent(d, &e); + + if (t == None) { + sleep(5); + t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); + XSetTransientForHint(d, t, f); + XStoreName(d, t, "transient"); + XMapWindow(d, t); + XSelectInput(d, t, ExposureMask); + } + } + + XCloseDisplay(d); + exit(0); +} diff --git a/dwm/util.c b/dwm/util.c new file mode 100644 index 0000000..96b82c9 --- /dev/null +++ b/dwm/util.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} diff --git a/dwm/util.h b/dwm/util.h new file mode 100644 index 0000000..f633b51 --- /dev/null +++ b/dwm/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/dwm/util.o b/dwm/util.o new file mode 100644 index 0000000000000000000000000000000000000000..8e55fa18d6ada0bfba70d02362b40ffaf2d35ebf GIT binary patch literal 2224 zcmbtUO=uHQ5T0$TjWtbT?V(Z+5mrf{E}Ik~q$<%yyGoE+D{3#ACR-cKpPNldRWM>b zgb?V#qX!Qj6g(94C^YE7t4B}Pqfig^ASe~$%?e!*H_wR z+#gQLxBTIhT=s{b%5|js!_VYZOTUnxTl%%UX6d(b+tTmkPpGq6v+rmK4Gjy1B5kdC z3LW&mXss1_iv>os)~p=RT1i>`n3Mw^gi#NxEFAoX2Gl1&Yn~v>9Mc*b)Ud}2Xic>0 z4|+TC>cRCO#w|3`vDD_k`T&R4>1-_YvYq&(UG-`$t0N1T`;bZJ^QBB2U}|zQ9t=%g zx*85f#+9fN2}afExEfO#KRRyF-kt7tqjojnaLGz| z^@i91Tmi-r6@7;rexwT?>Vhj>aGJVXeVmikC-2WXC&VVY@S$qv@=6Aj)T9d8oDLN; zs~ZL^EtHL1(Of{^$m>N|C|Au4lrdB?K(FOY(Cv$XbxEqW!nxab;7`YvR}81SuooA{ z-!RcV5AE=@9h6{5Yy=>Xttsx zm@~xAiN_o+jADS!FTn{+*XXcOJS<^9+GN`A)BaCT{20R-oghnQoEtIR36CL8HUBlA z{(V$4ic1u}Kkt{v`2JicFhDl_*hQIR1pY7NF`mcu1q^VTb4V|_fiBz|h|xO}?<%@b zWBJa$oZoX7xkPp5zhwE-R7? + +Copyright 2016 Roy Freytag +Copyright 2016 Vincent Loupmon +Copyright 2016 Daniel Walter +Copyright 2016-2018 Ali H. Fardan +Copyright 2016 Jody Leonard +Copyright 2016-2018 Quentin Rameau +Copyright 2016 Mike Coddington +Copyright 2016-2018 Ivan J. +Copyright 2017 Tobias Stoeckmann +Copyright 2017-2018 Laslo Hunhold +Copyright 2018 Darron Anderson +Copyright 2018 Josuah Demangeon +Copyright 2018 Tobias Tschinkowitz +Copyright 2018 David Demelier +Copyright 2018-2012 Michael Buch +Copyright 2018 Ian Remmler +Copyright 2016-2019 Joerg Jung +Copyright 2019 Ryan Kes +Copyright 2019 Cem Keylan +Copyright 2019 Dimitris Papastamos +Copyright 2019-2022 Ingo Feinerer +Copyright 2020 Alexandre Ratchov +Copyright 2020 Mart Lubbers +Copyright 2020 Daniel Moch +Copyright 2022 Nickolas Raymond Kaczynski +Copyright 2022 Patrick Iacob +Copyright 2021-2022 Steven Ward + +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..1893d38 --- /dev/null +++ b/slstatus/Makefile @@ -0,0 +1,69 @@ +# 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/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 slstatus slstatus.o $(COM:=.o) $(REQ:=.o) slstatus-${VERSION}.tar.gz *.rej *.orig config.h + +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/PKGBUILD b/slstatus/PKGBUILD deleted file mode 100644 index 73e6e27..0000000 --- a/slstatus/PKGBUILD +++ /dev/null @@ -1,41 +0,0 @@ -# my cool slstatus!! - -pkgname=slstatus -pkgver=1.0 -pkgrel=1 -pkgdesc="A suckless status monitor for dwm" -url="http://git.vern.cc/vitorg/suckless" -arch=('x86_64') -license=('MIT') -options=(zipman) -depends=() -install= -provides=("$pkgname") -conflicts=("$pkgname") -source=(http://dl.suckless.org/tools/slstatus-1.0.tar.gz - config.h) -sha256sums=('6d6d0a16c08dd9d211172c30c4720701267a3f40cdc938db3f386f6a2b6cff54' - '6a2df67ebdb5480644ec2b9176bfcf771aabc99c1329c8d16c3ef2cc0f56dad1') - -prepare() { - cd "${pkgname}-${pkgver}" || exit 1 - echo "CPPFLAGS+=${CPPFLAGS}" >> config.mk - echo "CFLAGS+=${CFLAGS}" >> config.mk - echo "LDFLAGS+=${LDFLAGS}" >> config.mk - if [[ -f "${srcdir}/config.h" ]]; then - cp -fv "${srcdir}/config.h" config.h - fi -} - -build() { - cd ${pkgname}-${pkgver} - make \ - X11INC=/usr/include/X11 \ - X11LIB=/usr/lib/X11 -} - -package() { - cd ${pkgname}-${pkgver} - make PREFIX=/usr DESTDIR="${pkgdir}" install - install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}" -} 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 +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__linux__) +/* + * https://www.kernel.org/doc/html/latest/power/power_supply_class.html + */ + #include + #include + #include + + #define POWER_SUPPLY_CAPACITY "/sys/class/power_supply/%s/capacity" + #define POWER_SUPPLY_STATUS "/sys/class/power_supply/%s/status" + #define POWER_SUPPLY_CHARGE "/sys/class/power_supply/%s/charge_now" + #define POWER_SUPPLY_ENERGY "/sys/class/power_supply/%s/energy_now" + #define POWER_SUPPLY_CURRENT "/sys/class/power_supply/%s/current_now" + #define POWER_SUPPLY_POWER "/sys/class/power_supply/%s/power_now" + + static const char * + pick(const char *bat, const char *f1, const char *f2, char *path, + size_t length) + { + if (esnprintf(path, length, f1, bat) > 0 && + access(path, R_OK) == 0) + return f1; + + if (esnprintf(path, length, f2, bat) > 0 && + access(path, R_OK) == 0) + return f2; + + return NULL; + } + + const char * + battery_perc(const char *bat) + { + int cap_perc; + char path[PATH_MAX]; + + if (esnprintf(path, sizeof(path), POWER_SUPPLY_CAPACITY, bat) < 0) + return NULL; + if (pscanf(path, "%d", &cap_perc) != 1) + return NULL; + + return bprintf("%d", cap_perc); + } + + const char * + battery_state(const char *bat) + { + static struct { + char *state; + char *symbol; + } map[] = { + { "Charging", "+" }, + { "Discharging", "-" }, + { "Full", "o" }, + { "Not charging", "o" }, + }; + size_t i; + char path[PATH_MAX], state[12]; + + if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0) + return NULL; + if (pscanf(path, "%12[a-zA-Z ]", state) != 1) + return NULL; + + for (i = 0; i < LEN(map); i++) + if (!strcmp(map[i].state, state)) + break; + + return (i == LEN(map)) ? "?" : map[i].symbol; + } + + const char * + battery_remaining(const char *bat) + { + uintmax_t charge_now, current_now, m, h; + double timeleft; + char path[PATH_MAX], state[12]; + + if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0) + return NULL; + if (pscanf(path, "%12[a-zA-Z ]", state) != 1) + return NULL; + + if (!pick(bat, POWER_SUPPLY_CHARGE, POWER_SUPPLY_ENERGY, path, + sizeof(path)) || + pscanf(path, "%ju", &charge_now) < 0) + return NULL; + + if (!strcmp(state, "Discharging")) { + if (!pick(bat, POWER_SUPPLY_CURRENT, POWER_SUPPLY_POWER, path, + sizeof(path)) || + pscanf(path, "%ju", ¤t_now) < 0) + return NULL; + + if (current_now == 0) + return NULL; + + timeleft = (double)charge_now / (double)current_now; + h = timeleft; + m = (timeleft - (double)h) * 60; + + return bprintf("%juh %jum", h, m); + } + + return ""; + } +#elif defined(__OpenBSD__) + #include + #include + #include + #include + + 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 + + #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 0000000000000000000000000000000000000000..4c63c608ace60a0b1b5364bb6912ad58a0e17cc0 GIT binary patch literal 5136 zcmb_gZEPGz8J;`awN1m`r6j?SL_Hz9y*BaL>!hvuYVK;M-oQ1q#1UyexUSFZ*yO&n zyQkU}G>sfrUAZR(fzk>I6@Ksoi68lsA5?9)I#h%rkX=;}D56m#p`-@2H4>tk4pPM{?R-4*ydN|3?(Ezh-<^Fh8i_C|5q3Xo^d!pIVyw|l^Jbc@XK|LqXL#=#-apO}fn^@P0{&e6_-4JH^{!^Uw>eG}Ik0j0&U3BA zX+YICmN!W#>s=N^H>$sR(C{uEI+8h@IdYzyePGl!!aBFO&p7e7F7h~T%(wO=xxJI! z9R%ogqgLqdXqcaA)$7aKjM`S{Vsx~-m52Nn?rHTx^{sgq&|7+)BgXvqxutPq+Ximh zZS(OHzl6s_N6u#ujXyCzq|E4k1e6R11U_6I?(QP{+{#>?2Xr7?8}D9+v3n2~jnY~L z0U5Pk9_^cZyuTas-(gwruR__~WUQVyW?zjPvmdrrFB`Qs5SKfRS1z~inIGQTf$05( zxgmOlCzi9>3=HoYCNUVE1l0UVB$Y|D>-DS8QubtNwYOY9k9Xue{%m-E6q^>z^W7_F zudS{cb9MJzw${UE$@heIjFv!7bNa_ZAuBS+TM$#7dp$O{f&4Z#eb6@@JDFQuT}4o% z-ElJ72s`%7ZW=M>hr8nxLhR&yyf;euGWYs)`-%~}dtrH_;VsR+9Wj=!8l6jkURjA9 zs1w~MTmWHV_7V}^F*+AOc#R0ZUWt8^h;g2x6S2Mp2j;fFx4b@K$n+je9AP)(TP4drI+H7vAHNwZKWW-WEzausR+SA;IUJcP9le>BKZ*Zk zx@y~2$(6Pz6Xc-ChTst9x3OOKV6{+SWwy8MCIYuu-2by&8uAV9O|y}qp)VwoBYXGX zmC$#j22y%rpnqUT|6sq)DL5t*^dJd{#G))x2PV(#i0F3`2HD_OEb_$HS!6mM*}Sg3 z;}ns1623)@eH--yTD%q=(Yl^(8PO6?w~lDZ*|vwY{^(vUIjkizS{D%>&^nSt9Tj>t z)EO=Qbo61ZYnJrRB%-#~MMy^LprJu}o}ve`fP(vY1U@qGk%5m9Z7~sjK}!-6I3enn z=y?e`Bp+dNA>CnL_fn$e_mLZvxmNQ6rdrwitIbARf>msc-bzhc;{f1mC3-D<7lI+) z*g|E5-4cN5pv72=f=l>wyun{-3Gw<)fp=erGPYgdcZG02JKES+L-@Ud@7rq(eo^3$ zHXzbHTbUz z{%-`IzY~P9G1GM|dnT1auv&_Ae_8enqXsslFQLoA%85F zpFEbEFsBLu86OLaIj)^ARsztrisn=aXD{o)+(>Y?ZKCHR`Y1#02S^geAMs(1f128o zPo}tluj1HWNR*y-dL$qF0twsyg%5K+a!&G>1P=aZ6#TM+e^$XiRPfCTzDevW;=$Q1 z?PmlYj^`T+{}T#-OyR5X6cs*BCu#plg^$~?#7`@HoPiSmxx!cDzo_ul_;104NW$^o zE^t(8{C6vSHU2?`zm9ZdJi8S9lM231!8HZ9*05hu`1q+K?N2Lwb$w1Ke4HPW|2+j? zui!sW@Y@vpWyQWa-#c1}M1tW@(If4<3XX5P#7`?YuA377p}?{JY95|daCIMv#s%96 zom<7e%5zrYxNbu*cZkI0cc9CM16<~JB7}DdJIN5fRp9+0{C0ukZUK>gB|pJYf8E5= zazuWIfZ>tn`pFPqp2zhNF3(+-a%PIIIYzZ>`}L%3*;c_!xz@DHQsbs;veX!zjw!p0 z4(NJ{&Qng}2(@T1RVe4pLO{zqE*-`BapW z2wHw4O;xk?i(|n^>bztOUF=bK{gCJIk26MZnZM0^L*=4)p{|I)@QuN}CajNdXE=8VVs#9V{K2n+GQ58H<}}mq P7ysY=DA4p9Vg3ICAH>jB literal 0 HcmV?d00001 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 +#include + +#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 0000000000000000000000000000000000000000..3b50ebc08386b9f7fa978c6a5d87cf7c074db078 GIT binary patch literal 2088 zcmbuAOHUI~6vt0n5LBELqK1fzS*TPZleVZK7)c8XC8@>-EEI_u>J$>8#hD?3#$aMF zX<`#Meg{7VX?(>m;KGe77bY&yl?zPp{Acbd!#Hg8B)9jR-+A1*_uQMVT`rErV=)qm z(J<{;LWy3t?C3d9&5=fjC_h!GpM#yBoVr=4ZC(Ecqfq-&s9*H7^@cl8toQr$eWCVd zV>>}qtZk~m&57DOPhD@U9@)vuG(4DTeDAxm{?+}paeyl{v$$yD1JP4&335Ld>aAGd z_Vi5tPJZUCP37v6RndD-I=w?wE-h3YOBpmiIy$7M#;;DD)-wY}*2w7Dbao&;n9iUA zVVgP+)Z-wEQ>+o#I-iYi%NZT!I7sZ#4T`NKV?7;h?JwDRmf3O7L-Y#sh?cC!C$#Pt ziM*yiZ^>(^XRVjCbo_~yVvyImIbcLYU$$Q^|CV5*fwOG=4Jp$pZRU zfo7M>RGzb3M_mVcXxbHWT-z?q+gwvl3g^Q2lYVde(#1LicK~N9TzZRm_=Klehk&D* z!Y=@_Aa*|w;bVX-;JD(#CjnVd2j3=q8juC?2YCp80LTK49t&RsWRdULQv0g;xXC46 z4{+o(#c);^U2~QUJ~Mxw7tOX7OvANSTr%#Nu1UtMR9oH<~SN2?G*;umq(c11Rr=p-Wf46L#i&pas{!7t08*gY|s7g-Ra<9dW*r4>V zfwRp8A?xk9ESOaSdID7*ayOhTr+9#?a3D`Ui4vN_>z`piYW{0J-V&u*DC-ihMi1i=JmiHiT$&-149imGN=8h;B5qpan-rue}$#zoZhrdhCpF=#s`#@+o zKd17q`C-l9aQ*^&qWYf%WRX6j|9Ib$FFa~( +#include +#include + +#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 + #include + #include + + 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 + #include + #include + + 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 0000000000000000000000000000000000000000..969d09c68200f9e6fccf9170774d1a2122c28002 GIT binary patch literal 3136 zcma)7O>A355Z>n`#I(UNQ4kR=!V=FYh345#QWUCEStKFv5Kx3l8_|O8ICh=Zc3k@z znkq_Lg-VSgOA)6^aDWTyfkU|>QRVLtDh_}+I5M#cToSda+FK4tVZP@b_hpqx9qaAP zd^58%-PkB74!PT z1@#>*Q-qHsTx4iXk9YvQeR!_tVyIjFJzCjn{d&1|@b@qOXuYvSSq(4lDE%}@&2_`IjUEe*MFfscxx_Xwx_8sl~>BkdU3s)oz2dUYtWWDR)m3@m z$@u|&hQhD?+LELrpAIcnSJ&@Vm*}@uegP*{eX^d`rz?kbVl(r-x_N!|Cc#^COffbhRr@cD<6`+Xvi%TLAw1uJF=|2VC`_MRB(??3MA9QyF%Bfe0N(QSl$-NEjj zU|%pK8p*>)AIgLrgiC0Pz_^w*sZ-V2)GoPp5%7^<~ry3RvYx(rtgk+*0Z3w{ygAu4<3wp+_vK$140|clbtJC6Nd2 z2jrp;xAEUjjXVH}faA0ne*+*9?x%%5#!mty0*+p0{0u-M;Fy!~3jm2QKWhCP6rxKt zX5+|>{l9SF@MnC+fuooi$GZXI`H?H=s~W50+(X|eIQj_meT~)qPe}caf)7jmCk5wy z-Ius}ZZFHm=R7(8R~4M|_bWK(-=*N3Pmh9gKBpC&_xqWG^M0>7@aJhSoac8A{D1@h zS>o6)>YM$2B!jSDd`x{(U_8nOqcD}RVq?N6Sos=Hv6N4wV}_NuYzbpLX2patRwxJ~ zKM4iyA%j{*s_}x=D~$MLCX>jLe|FMJ81(*j*)1aV^Vy<7k2$r7HAR7#J&~g3ie=PS zgp+%A?ju(m33|y%VX6Ep&x>t}Dh`nWXBlP($w%cAnv;i#IzcQy>>bh%eG44D!Us}ry3uY@i|8Cb!Q{6dU&5Q33Yk=_1VSP1zTLMwCIZ2J5 zBI925vnKxlczx6l`k3Rne)d`$>y65SxFrt +#include + +#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 0000000000000000000000000000000000000000..035344419cb0c514053dd063a9b39bf57566d85c GIT binary patch literal 1920 zcmb_cO=}ZT6n&GXHfjy2R1m8$i>MX#kx4;HK}o6FDHIpgF4RSsCNE8BKI+T_)4EV7 zE{0O@XZRlki66hfmAG+P7j9fD)O#lHrqeK?ARc&m_nv$1eKYsI$;*4o_Y;O87>2kb z4n1aRPafKZ&@70wI3pg~!Efup-uam++q=oj(-g9UyV;c0{Q;F7?At*ytL<<0-X*Z_ zQOyoM*ui%l`Dh1U?bl!08LMmW-G(L%khl9Nn=0GaC&SYIXivZMIrH?B=erfZ)=+ci zit^fZ-z1{eTsKvxq*U26*V>hea!s$ct%O)ySeP@X7ay!%Gp!k!mzJ5&M5Ty`kVm;;?`h_?b^oN?XCwOX8HhVaD|1vDdKZT9*&+P&5FTxetAA?< zFUpRuFg;lka(N-w_eH%`a_Wc(8ld1Ii|3Bp6vd+FJEe_csk%|DIJLSEQ&b7V~=CG+!acBK$nr0Q49V>sLV%pdX>d{1cD_=ugsO{uxLD_*1l)H^~N2 z2dfY1Z8dynO-Opv-d<(PRdq-Ds^beu-ExFn^E@Hl797YeX$e_sH5#f(l4i?SlCJK} zutnNmZ?@&8+uBsFzeTAisH(+^>ri|D3rwnlyNe!2QP2+GKaN2g1?God6`~~BUEQR& znmE8a~|(Y6kwY**hk{X80}-?@EshvljNs#{@` +#include + +#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 0000000000000000000000000000000000000000..5ec5c0a4c269a7d8b55cf45f4823fc7442fd27a3 GIT binary patch literal 3128 zcmcIl&2Jk;6rXjI#)aZFQ7bHp&>kFWXuGnV163u!RRY;V=%G;53WU3{*En_kA@8O* zD1t~otK?%9jGGpNpIfF?|sd@ zeKY&nn~QHH3_~y(;&lO5zSIA4z3OaC%;ZSn4Cb@F!qB-M zWRtgGcy+jXnAU^eo$Ie*Ey2R@&%@5fS~l&N->zkI--!vOa5mn}_NvaYLo!$}DXpFU zU+eGxu=i9~oQ?Tx&bd15O^qD$733al_5U!Oo41_1S{jb)>>@WPY$cGA~GQWFEg8Zu?1`MJrq837748e4&hUY%OcaO!8Z$|LRJwLU-?=ujwx@yS_X^wP$WgHy>(GPVeRLFpQhr&H;EVj;C@B-~W` zbSg=ZMO-2V{emH~+0t$JDE}4b8vKbm7@{~ejstic;an5q?t^iUseVCq;%l#sZ7wJ&5X)UijGhr_grz5kSnY{}>y99 zC2Gx}TwiIrEm1C$qS7c=>Wy;EUA`Q8bi8gwEOolet)LbnK|64ja>e&#+q{U}MCf7E z`kx0Op>CiA@jhb4Q8y6m(c_FS0we%jhTR~a`|-GQSU8P6yjG0>}wpU`l9|K~JZubZdE*Xwpkv)Aj^*KoaVH#A(& zJJfJJ@2?7vf3M#(dp+;p8m{L}A^;KUpy#y!5<#s@pbgvmt8yXdV#R3Zr}=g$@hibZDRrRf=#zYEh~O-QrMMtv+1?SzSRyq zo8IXY@`9-KN39jR({0oJ2v*4v(Y-odFt_Qc-R)giM$H3 zM(s<)I@Qs{L^5-k5mr5G3wkQ$#DF*crL*44^;8`m6n4V?!n5{{I3HNd1%m literal 0 HcmV?d00001 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 + #include + + #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 0000000000000000000000000000000000000000..6af57bab6c3c7347e4c86e3fa0338dd668244991 GIT binary patch literal 1784 zcmbtUPiqrV5T8vG8!?6`6$(|@Ln`s#OE&dTgqD<8yFwAM_8=l*v)P!MWD|EclIX!h zP9YTh0)CgEg5W3cq@tG|ym<2<)S2Bk>2^skIxsskzxns}&FsG3+`bc26kt+d9gZwU z0s3=CddXKykcP9cqwilLQ{P|n#psJZ{F$lgQyVhyvE!%TC z-9g28VeGX4w#wyOYHsV^?iE#A5k;Y?#X@nVuv*ZlFF+hwMNS}N6vRN85XPiPo8Nsw zI+T|WKwa~M$Yo&iqtVEiiTM1-9}W_+DMB*N#B8Q&og ziEtVM<0|Sz)R%Zl!X30dqYA?HoIo~MbF7vjJge`4s2QFCqUyRJoDLZjt_cl<*=e`& zPC(cl&l0#&H~g2D_EWnjaB;hq;|(yC3R;ay-7(tM^aTHm@=(pf|-b#N|MEld8QO$qNr_Y*d7Lmim{ds@b z$NgEB&_Q~E1El;YC4b}W<~){n(ZM>$ko*xgLV7gz5&CF +#include + +#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 0000000000000000000000000000000000000000..4384f7c2f60d00ba3f9621fe5939431c179c7f5c GIT binary patch literal 1640 zcmb_cL2DCH5S~q|jaXw81)&Nnf>f}N-Skol%1Y2iC?bjn@t~WyZNO$X?!IE9haSX3 z4h8RC{3R0f;!!+#^V*}{g*xBnP5ZKLJ%}Gn-pu#DnfEeplI@$Tw_y8&E6wJzJHK-0!4=nER1M|34X?52EqOlD2vx~tJHo~)s*p1RCvZ$GcRph_oR{~= z*{VBpvoqDVym_8^7~tXKMy>w364$Em{{qyVrk{IdlBtqT&E`Vfb`s|}JZO ztv)HuqqB$b7Z2eHT}4k-htz7*qxR++nP!8g7hxK6uJjJiiCg~K8w2+Muz!?I_)8!e z&`+@mKM#@t{S2G%F8cuV*2oa4Y|ztTn^&EV*RFVJv=J&DZD~?ZLmiT8XBnw9K>)e0 zd{UjH*NbA7#EFg+kHSKx!_G4kug9CJpC>F~ +#include +#include +#include +#if defined(__OpenBSD__) + #include + #include +#elif defined(__FreeBSD__) + #include + #include +#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 0000000000000000000000000000000000000000..22f306de2a6adac31eb47dc2f12d3bb3af24b08a GIT binary patch literal 2552 zcmbuAU1%It6vxkOl6JM}5N*YU6f$5cE9$VbDN-ZKPPQ>q*-FtyXiZ%vyR*sK?1!_H zc4Nd+>=uVL;)4&xr@s3lJ_v$@1wTNa>YGps?TaW;ih>^x#r6DW@6Aq!-4{LN-h1xv zo^$TG_nypNoXxOsH zYHud2R@b|8@^gg)z81Fmp+_c&s28SqGj~@(9<&E`XUHS@# zef6xlWS%u&`*J(RiT%9uPq({$KeXGdtvFt}WEYEWW0qL2+7+ijGopFEuBiwq>iQxjsW2eNaHrfjm;=VN(W4eBIfeV4vGck&6h}NPAHS( zd*dJQ;z{PXILJO2pd$L7?;*2ui7y*GMHOj8S)+Mng?g$ z*?j@QE!`_>Fdd`udodjlT;7RXPOyS=Mdc9&gLrZXKQe?rGlVY;;irc1r6Ih+{6W4S zc`x!^T)`g_S0_1@>w1AZ(sS7qIs;uA#VQJyF4%50T)foq+(M;JCD(DJ&5B*lGj?3J z=F)QAEmyq~Mhk)vgwQcU*6S zT@lb(&6iyM{>hC0->9)WN;&ki6a>r!J<(ME6A_TelqQ+tEi%DsziR@r92E|D6R3D& z2)+-HN4bHIU=osibR)4A#ZNFt%>U|--v`7@a|?^ye<}=Fm;1{xBUmb8_qH&Ieix;m z9FK=$hC_TL4Z?7M?P+HCzYo_iT&@ZKXkPOEVFP)C8%(4A*M +#include + +#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 0000000000000000000000000000000000000000..be0be906e0109bf991f48dd21218f2f2fc204ebe GIT binary patch literal 1832 zcmbtU&ubG=5S~pN8?}~NiVF3RLo1QmM>Yj11xujRt`HOi51zs%*_e`K6L+`RsGx-k zN-6ZV?XLDX`>%C=#aW!y1*2pS-~Eg}pSFFs>8;X2NVK`WzM9T#-oAG=Z7-{w zveUV2ZaKS>wYlmzbDBBH%psH@s|!rXGB(%LG;5^w_%2yH)7ISNMCy>8ml+3x+W6U= zdOMb=O{Jj!g-~~#ejSYp85?oKGdd8b9j1%gOwzskERF&F7k(IYqTsnia({HRLBVi>vQda+!q7pv}8 zqc>y*mJDluJQsH z`P^L6p|Q`hM6SG#O9sDU@PW^W_PjU#xQO?^#sR4Luld-2)Rg^URg?5TBY=_8Uu2$f z=vm_z@zE&qjSyV&MBZYD=n@0@WzZPV +#include +#include +#include + +#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 0000000000000000000000000000000000000000..001b37f767fae561908eca0f028112650aa2b570 GIT binary patch literal 2400 zcmbu9O>7%Q6vt;1rwPvrlOUy0fL+^7Hmb{KQw*hSH}(ccv3JdS ztk!TKA_Ubnh6;hyV^0Wf+>wH%QgVe`5fb!PP(i2{E-hN#ThF+|%D%vpcIVCS{ok9} zosV~q=9fk!iExw1GvscMQ$ntf-Szd*)rm|F5aSi&q_Jw8SZUqSg4LJJ?cd8KvmKjG zfyQj*k_D~%E6UB*6?3QCc);vDja^26`x-+Xnw&CQd)gmIwSb9#$#eJ@MI|)`eL6L^7WEv>S9E{eCB)5ASs8WN-Y$?oUukzc6>nI6tS}oPfwD z#`(Bv23K#k%+4b?weMnwi3w9Fl+44u@19()=D4|y zdaYtN7nCJ?qvDhl>M0&zrPSkuEN8O|N^1GJ)yEZWR-I8bWhOl{o1RZ=FdF^>1s+@m zkdP4~b&+wCSf8)mgDfDu{SuMRO-cvj;}aJlmz}6U)e{ z5670|^auM4d4795CtsFEW0E|dlhcNr0trYVB_mI;B10N19V54bLE+H>JM5uQC~Pv8 zh;zMP(1&vdmWF4NX~YJ1*C3u5!cPw2uMOelA-pz(zdwXu8p6q@(_HgxzhrH=rH!IZ zJ-@CNiJH%*2Ze~Zwa8i9cS&K{p~qN}tmjhStB|$EIw@o;Ufp54kkqN~RvedDR*^Pq zjzztSch>Q(idPg^qgLy)I<4%nNE$&5Jy*okAW*HxWc2wXCjH7;^ zz+V*jxWG>_j&*R$@%m>N$2#{2yejZ}1^%AE_Y3?7fyV{@7vs@!6YPNDxCaEiBIq%v zZ)&|+rS=+8;g9#)GWUF^Vyo0SM~PapsZG?idY!1ghaHq_sz%hJSFJiONL-IPDjfYM z!-Zh|H{FI>^Wh%xX%kYhpi{QieY@)PXYjuq1+wvzajittS%;rT9%H|UcgaEU@L%bD z(sOi#U=u6>*92XDtxRDM>@j$d;~o;ti4MeqQTzmOtodK_@$SW%Gw_JU$DbA<_WtxiCE5ih;CC^P@jTwHK!E#PgYD;o-~jEb!0`oj~B(D^*7m7&(|5(AI~w*=X}uEIGD~R^a7g)o?RR#nvXT3YY&KAWbjMu Y2*%3w$$j7r%0I*A|4v`obE5hG071EAFaQ7m literal 0 HcmV?d00001 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 +#include +#include +#include +#include + +#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 0000000000000000000000000000000000000000..b9c0bcae8d300a3d45ff945ced6067d035475739 GIT binary patch literal 4024 zcmb`JZD<@t7{}-G(nO7$*cXjfWyNcW)Gl{dYtvFCn_SZ=9<|y=16r?pcbg{MYxg#> zDa96Ih2isg!ue-XOg+~7DRktcV>R` zf1a70nSFMT?Z}L_#bQiaj6KYjTaq&N$xX}kA-_7r`dBYBpG+>APncuozOlW0?kX?v zM#s7V5^ygaA55OP3@!IA@DpdISMm9W&=mB_EDaR&2Yi=zo@DM??wvi|;k#Y)yNRu7 zc1>Qqf;n^V8lT@6Uw;}?RwJ3-1sd<$U5#`+0a2sYr*?C%BaV?i?ENSmU(Xxc<2#bq z`TS-~E)&d!X5SOIc<16FZ=pjCTGgQ2h`QE`BjR6V!T7ui_1MJ-sK^?dHWCl8bWag*oxy8SlDWOqwqD z&iRUr_v@$FEq}R$k6g3qPVQY==x<)Qg#~l*!Y=Qph1WQ(&0v9V2zJZu47}&gbM9S! zYQOokx!-)o%$^&6v}RY*Q}tTOn%$<4T2m#vpu1JQN?0!hoQ)TAW~o%oKVr}3s+Lnw zg%&z2*Vc=o6Y8C|yW1+;byeutt}|uN_(HutTh3KWb)ntsQh+F}KcZfTgpE^R)2)^f zT-6`6UELzCSEz$+*v8gT=#f&jZnt=}JowVH*)s)uhD}v$m(}ttXVq=&|G&4LBO}}N zft|a@?$eW-jFgeoQ;F23#9$)n$G|z)>FT75M68X)&VWgai8gi_wmK+S?8xIRcDOIL zwx_H6W$Mfjzf<)60`aic_flJ0>wmF*Skvb_hP8o{ou-y}VTGv;&UK}=Nrd zB}{FAB*R)ixeROFB1lAm&>ifz#a7+zEi(sW?f%m>d)!FP7+bLf^`#p0%Ut5%xv@&k zcDvu~Wb2wSyIb(RA%3sm$3lES@OMLequ}3#cuMf^Lwt+iKZf{rYOhq*$KNAjBXsqJ zv}o>$;A0VdB7!>+{J99;h~Te8@Yf^w8xj1y2>x*d|15%k9lyN(Nonobhpzb ze_7?{O7bq@kC-sYdj!WC!X)n%T=!|PkC^L{8T2dSS~+Imwe`zMS##`?Ww`cXml*}i zwV08s*O}o|(SST@P-gK(KA{?Q8u%S5F(Y3sm+cCT!F`c5|(ZgTbA!0E3eF|EER@PBs?{T(==u+(mBm=T<#3EwPF^=3P zx&G?~$6l(YH!c2hkJzRqa6i=$^S}Dz?+Y +#include + +#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 0000000000000000000000000000000000000000..0861fb70162773fd39d6c4e6013b1814cb1f2574 GIT binary patch literal 1864 zcmbu9&ubGw6vyADiH%rmEJX@cx`$NauQ8h*tOX^|+OAMMlnQzZn`D!eCfT@IYpVwj z#X~7V{{;Vp{sj`yga3(&9@Mi?-*+=FNw+D8KA6mVpZ8-X^JeGugVp7jVUUPH_o;6Q zCHged*Yly8r*S$@_U`x0d-%%setb5=ioLg#p0Yaj-sdlfd(+vOVC(HY7$LDb-)4SY zNUf(+{JWY?YTw}qoAGoQV(+h~r^@z?$*^LO=)&EPnGaPrsC!P?*{Uv>%g#pKEt`R7 zdd0xmXqxbt&X(&tRhMQ}u449nh*lOC7tGAcqsP}wYfj~qW#+QEx$Jz_!p`D6Odbgy z978cOI$&}ZhPKZ#gG_cOGG4Bcv7Iue$3~L}91MP0`&yWn#u{|+RC)^XW86K%fEKKANBF*p&dA-%|fZRS*UP(=?d3}pciL&{66Z6{LdIOTtDI1 zCj{}MJcKU;vLGHl;cI{_U}%!?Dj*9udM?~#9fCR-JyGqQM&J}lwFAF9*F^K(x}ySj zJ0Mkd0*6$w-6rLGFc5Aji&V*LG~6bKG`+x8dnZ68BZx@+OE|@??If^TjW7LIMQn zIf|b$$9+F`C)rNw!05i@{B=2c#tK5C`SX97U+3Im{m<%#NT1Pvd{45z@L_9n7}fOp qKVY!n{ln`Q%}32>?-|h({fBHA1ES0J2;R?O`L>3ydXZfvn*Rr{+QMM~ literal 0 HcmV?d00001 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 +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__linux__) + #include + + #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 + #include + #include + #include + #include + + 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 0000000000000000000000000000000000000000..fe9aca11d3ac84ba0f9eef6f66354043c64b08c0 GIT binary patch literal 2760 zcmdUw&x>1C6vxlYOxsc0Fv^I5szjKP*4mf66rrd{%3$XzqZCU;DH7jgUYs~dCcb+| z5=FFVg&_>MaM6E2aVfYdE=oYbu3Wehy3l2zqI9K#FrM$rJ(J6wj9ql)ftz#g=bUqY zo_F)%^UG&*K|mq_JxAl7P$G3`Y*#b0N_jd$7u43@YWqQuYcxxAUS0aFz*-F+0b5mr z>4jr#sI5=tXP8GrwcVPZQCol9g#YreKOGHky$GBQqPCv`7r(lE!~C*yee;GI8~`62 z!Ef}_HJaw2<3FqB@a7a-f8JJGKV&1S!ROgTtLnsDHnTY};+MY1x-P!)-T!dOL?zwV zrFtvYy3|QbX+f8?iA__RW?h%kUTvjs5={$lZ~k|rkw{uvTztBCZ0Y3-$BWUauo6ba zO1W~Xe7YRrgmJu7Y+$4er4j&f`t=SMi>m`ma>T8?q42>4-cGR`(23&ugM z3I76+1w7A9__u&87zZ^c{6|0*nOFGlBfMw7@?rS#2knOp?(R2lxxe2>op_wH%scOJ zcfVC<9CyF3Io#cE(XJosK@)NB3w*a8L)~wic!ff3(ve;hD@|H)Xp)|xa5XkDg)3T9 zm~Nnh@Q4dHtZ%g2Nryu^8zu?)$vc@9pzXia*$lhsMmI@KpHnelvQ}%PaXXQf-JLFS zI*tOh>`K*k8upyT=XD8nk2|VLfYnz4S$?$%s9BiuZWZ8U!TSV`yL9DCeA=zUfK%VE)}B|Fo^Y572SMKfn9gH!1(RUH|(|E=)iFUp=gqApigX literal 0 HcmV?d00001 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 +#include +#include + +#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 0000000000000000000000000000000000000000..15d1365cd6285091389dddbfd68aaa69d7f2d084 GIT binary patch literal 2152 zcmbu9TWb?h5P&D?)n1m^dI{8r^`SOWyJQoDT2YcFwOb6Ov{qVcDBEOfg1NcbVyh1# z#X`V<5B>muNXdL(dyU~D4-}Xngy6mB&Ouh?ELb1S&b?YMU^ArNHjbdR2uA~(9&-Y0oB$^aVN`Yyqccy<)3MC{tE(I|Kfhf{uqW9>8p9ihn9W6RIXa{y- z&yLEF;DMtUP76Nl;m+RZl#a?-G>2U4+IvSfj>B7Xr`5MzsoxStf(RRRz10DHn@I2}4R{%TVwWov2!R=B}{sl(v+Li5)Yo9ZaQyMs9u6 z)Qd**4gO1!F`K?QIvcmdgu(ZneH81K5j5xwV{VALMwbPpS0qrcNafEzNKTYUWoM<#7y5J?bDl2HNCw@1V6-vR>*LPDAy#%QEQc-6Yu}98R;nA{J++s1WW(` literal 0 HcmV?d00001 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 + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__linux__) + #include + + 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 + #include + #include + #include + + #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 + #include + #include + #include + + 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 0000000000000000000000000000000000000000..2dbd84593c0ee3945c7f835806b25159ecd06858 GIT binary patch literal 3080 zcmcgt&2Jk;6dyYYT|&BqDngOk!yX)P6uR{~t%xE%+$K$?!bK4Zq7?{dWAEY+ukEha zsD(-(MMzjCQtu!*aN*d$0J(tpxlxnosXT~)}KS|L$do=<|8poYS(@mjkjcXHDgrd%tUM*jr75-PbWX2 zAanl^B&k`q^pAnYHt_W94)C z+1y;tK`(-+0fj|8LQHxhEmA#TOerPvo0w1=saKy7sf**O!(#`GE3^sxh}!coFPc_* z%`_}p9*0cyE`~+3aKV&I=881sNwbiC&NN_q4tqpv7-0K+-?j;v=xq#1+hx-LJcw}K zVR3V3(B9C17k(gCL>BI(u~-}tkKPH32NZrffj_A5cM|wvg};};Es_su=ZSMi*fjAt zaguTb48rH{fvYH%sN}X33{F6RS48uq@V_;YL03V<5tN zi&yQIR}rFLCqaEG#1QdcJAE>779!y2U&bE+NCfO&V#b#N5&<72W*qki0l$x!@ftuP z_(zv8-U3LJjQc?!-V^s*g+u=yV)pZcrhibwZ)v!G9z)duevlLPbH9e`d3adi$vix% z;d&m8xE}XM4cFsZDi63Xm}yQe80S9cIx^1v93}L* zpM~AJ6h!Vi%~9B&tGo)mnrlbi#Yor{H*$r&-f9UuY(RjzV^iCg8$saJNu}P1JexkH zN8|QZ@=Nu$O@9Gu@8}Yl!l75Chv5c%V7|~kXN&BRI`ouI1tt8Z^2&P&QQ-T8x)oSK z%~w%mqFc%UHNlcUypz}@dWaY}-lR@P^W_EK_FVr#v0>6v@!wF18dJ7Od-zP&9`*&*e+>v4ao!oi|9FtS Np#r_eifT^U{|y>y^&tQN literal 0 HcmV?d00001 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 +#include + +#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 0000000000000000000000000000000000000000..7addb3815164233c09db3965c5bb7fb0372a729b GIT binary patch literal 2104 zcmbtVOKTHR6h28>TeXg{C`2kU8*MChNK%DTQIb~M6k_qwF0_`CrqdQ|9?nd$RiuJc z2pDkVe{ofUFZ=^8+_-WfxTvmNDAe=K+>=hG$woi8bI&Gv4D zC|BK4!kx+LJJ;N*Z=Bf6OV(X7S^qw8ee0|9Yx@vaXk>BG$OodQ?hxdB&el3Hz}?xo z%)QLqTZ<~?idoY8&)EGVR4J^MZBsckF*Y`$$0w#{&gsb^BV{D@R3bH$7)~Tnfv`-S z8|tx$!W63emd<!Fuz0C%E`oA7eh&?6>% zoNWL+4p;a~AQ{jee{nknyEYa}YM(yB|N4Bkw_Ck|+Hn2gDW)`kq!~3+)}|@cx?aB>Pd7ADox$KTBNT zeLxz_PpkYJo~-c==C5+3)%oin8FJ1zf4pzW7rxcl$isgJFsypKUEI-NK57Qfo+|^u_xS literal 0 HcmV?d00001 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 +#include +#include +#include + +#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 + #include + #include + #include + + 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 + #include + #include + #include + #include + + 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 0000000000000000000000000000000000000000..eff037e9bda2548ed1289fd5d02d72e97e847cbe GIT binary patch literal 3888 zcmb`JZHQD=7{|}-i;i#C&9o*Inc$jR);P0j?Mlhc?z*0h-ICl@qjVf+?l{ZNTQhfX zwT*;zipw}9LG*$_LEj?K>O&OBl%`f+!cT!;0x7rFk|2ttoSy%^=ggkH%s@~NoVVZo zKhJr)=iK}J_Tj-6kB3R~ux)IvB`IU4m(10B?P@R6*fJL06W$#j3y+TN3SHsT*C*q= z+_tWh1l$_Xlf2RnEw`rhAh%4N^NRy|=OQ4g^m;{Q?_ygtsvm%tzwR}lcPsUsp(-!c zc;yiiv*oXzj7=RqFIATxe;+FA3tqZnF5wk;T5bAl zH!n}=K1XcbuuJP46^+-Hj;BGQskp5KZ;^YUh$SIC!JKRNED>f{H* z<&^FluEcf!aK+SgVe2NZeQiJgad)j& z3spbGX4qiX%f;w;nU*%rH#iGzwmb2r&re$4KD96Wba-EQe>g(RbKMuZ9IDOR5ihNw z^~22Ity<0cJX9@RF0SC^hxN0pji&n9wZ(HoqxdWBjhu_`L(I;3@q84oGKwuQbsFEb znd^VoBL{hTsm@DZn(MiBiEkJn0c9P0q8b;g%HQCB{K>5gp=!NfUxR)-Dd9YlinTCL6-=5(wB^}fDIR!^KEXVb9iG)o7kW?9 zLr#2+c=jg5eOkxMEq$8rrPe;pUuo;pI#0BRwcra2!dmzA!a?n{r}a6H*4?iK!&)av zNJ=h!nwK&3krc5;Q4VPSmd7$-)=1IX;08o6TC=*`o;$Vfu!()k)t{lVT5#NKs`>3kJG)(lR9`2!-^K3}e65Rb z6g=SKn*|?n@gBjqyZAQ26E5CQ?G6`1^OYuis0rWMgzs&_4>aMqCcM~$Pc`8c;wxw$ z<^I|45wX{r=-(0gU&Pki?<2AIn&>|i`a}y=$YlJpR7by`2yVZp5ESTQ2*hx>BdA8~ zn;3=U5~%UdYCJdLo5F zESgEM!_j<(C1R;;!JuaaGa8$S#F7({M07l5k0p}8EtN={kz_F)%{VM)7; zl>99~GW5BOlH~6Kl0lFE4avU(B!ga~B>81PGVtY;B)aKyo{l+=$Y`V|UKC>(c%)aMkwQsE_quTuCo3cpj~-z!|z_h5izD3(%^{c^!E z-@6pPPvL5uQwmr6J)>|{&sr%c8RDz@RSH-2eu&8sPK31EDLbFz@|+)cae1Ckxwt&H zAGx?Zw=7UNk~X6UsWkIWnUuA>k%|UPW71@Sc+`xtz`;U+1@c)mAP)uTJjb%>w2>i6 zCTki2`V>E4A41VSlqm-2pMcJrd4yaM&`8pejHV4Sp#NC-$sb#fGpAawXsi|MEuV?k zL^kd38uqBSPMiuCU)!A647}s;l^qY?+{{k zQBqC%kE@XCvVU3j3Kn$fE{XxkeXN13d zTyp)PfjvVBG2Q+?Y%19$r>N&{f1IyI`~R`{(UIRr>_6^Z=`Z)cQQzq7JJJ8!Vqmy? kaG$&V5wp=!9mArKxySEbKitWU{DT-U*|Sbj&)xq200!!}3jhEB literal 0 HcmV?d00001 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 + +#include "../slstatus.h" +#include "../util.h" + + +#if defined(__linux__) + #include + + 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 + #include /* before for struct timeval */ + #include + #include + + 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 + #include + #include + + #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 0000000000000000000000000000000000000000..0856c7b85c661292ba932b9976188ef833331b16 GIT binary patch literal 1704 zcmbtTO=}Zj5T4zpCSr}N^q|zjf~iE*m)-PIJxEHbU7_^g7l=r@Nj6Pw^Wp9WT0MBk zDHNgqLcw3)EvTq?^am(-D(JzU^dQ8Ux9_BzCB5jt?94p#%*?y*$Fu88H*`${77eb! zLBuFPcI?2k_MGl}yC(_XbtG@!$)qaI#Yx#A9G3~F^UiqicIHtVV6jlRYGxOg?_MzN zS&brGsu!8?` zOsu9mLVFOEybAc7Imh>u<0*7#Z}SW&YpG3IQSmXfRLd9jw8GCTJo1cX=7q|~O?>_( zBv8+Pt;Z)S*OiM6;`w~9IL-6f=2gAL1Z7_-%Wsy`T*vk%CfH{V*$>em;>Wr7F_%Og zx|0}xR5kH+@&2ncJVu9l#_NA8!W$AMH_q oGJ#5S;`P)se)b5#PgQ?~f`}-1fSg2cRQ*e}{-*=xz7wzi3r97g_5c6? literal 0 HcmV?d00001 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 +#include +#include + +#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 0000000000000000000000000000000000000000..ee47012f9373b7f45c0d314d3fec0718b18c6bc7 GIT binary patch literal 1968 zcmbtUOKTHR6h4!-HfoJgDirEs7OAwTmzi{-_)MwVQHqEUP!z&6nKrR`IhkUs3n?lX zO2CDSx{)s32;Fw0LB+NIz^$Nyu0#;(`6l&B&_7Is(tirtM=Ax z_}SLVGrw!rIymTW!|QAE%kQzfo9{A(a%Mg~=XhSe=;$L^8o9lY)A>;(nwp$ErjJft zyf&tr<02_cJ!vGzjiZK%&V<+3dG+z)Ac~OI2pkE&MAPOzJ_e=K?p-ErF|G{{^hckv zGr|0R>3an^5sN>JOvlzEkr%P}L@X+O-303Q(vQtf{vI4<2s?ZmyKcvB-|VA3?KENE zdjoa+Ju)}UD;|o4Io5?wcHxh7;ZJwrX`zB)i42M9Ndpg4Qq(q0s+KBlzU0l)UE3|u zj5N~es%P`eGr9Tnter2=0qz4}cH4OU-fZGHNJfNL4==@E0Lef+?ycglf@FZl%oLvo z$v_?W6#oPy1Kf6AieF+mtz_xlX|rp0ntY$l9Jw zVy0Rp;g(?_+!Q8>Ou1NeN*q!udye3%eAquBY0s4wM8z#v9M@aoR1|b_=~>qhjmAC1S*28fl;7ncy_EJ&mdI7#(~#sbfAE-b5Z{7EKo9pn z#6Hyg*L?g?$EiB4pP*c;5@ zJ7~L;Y>&$PzxKtxAnve%&~W~BnV$* +#include +#include +#include + +#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 0000000000000000000000000000000000000000..83bb561ba7b4f5ae20d35c63d3ad64022c64b4c7 GIT binary patch literal 2192 zcmb`HTWb?R6vxlT*4AD^jEa|v>`SeJuGv(u6qQJ*jZmeCmbRd>ZMJD?b8)k!tv>kV zLlL3h!!HpK)DPikuZ~cO`pz!|JB>KJ;XG!s1IyG5YwK9Xw zbfzlrN)LXOIr>Pw+7o8CdgR~f!3#b3By;rd*i&Y--3%jdmAtuP4v#z@J`PM}qf9G} zXrmUdsiay^4Z%Y`N9_YeJ3&-Kn-m@_I3oi*{v0N93XlbH?49t7fGo%#;vw8+8v;Je zLpb&Uf_bDb=<>L%X{+#B>A2IlX!Oiu&(A-}C86zKON&4fvK+xah6-AG8 zS9A2o5Sh}3a5)!?7MJfyUURP+2P>@ODADU;Md4Q9#o=yDUO9*Z@>ZHn@}fE%r2C#v z-fF#C4QrfItH+_oSN>)*4puj{U#V?-ji}xTqj--?(J)+pwHDz_{1 +#include +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__OpenBSD__) | defined(__FreeBSD__) + #include + #include + #include + #include + + 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 + + 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 0000000000000000000000000000000000000000..73b312237407eb73c1e2f476d0bf501fb0c7b1b9 GIT binary patch literal 3616 zcmds(O>7%g5P-+|s|&#iP(-S7Fp@(OMP1oWfe@g?P10^52`E1(2#V{hH?bAlYu1}I z0VGnjRG45XhYIz;0f7)4Kx&0lfrKiHQraUDmx@a{R%$3hTsTm|%lS8*WhPI8jzh&WeZ@rH95sx*SK5vK{`7hqbg2S9~3j_Tg+IC4lHHUrUiB?owXou z6|#>2 z5qNU~0?X}zeXYBK>)81W@<+I@hnx?E+A9s?p^gt46QS^#rr}Wcspcc0^@hflK&Ts0 zBGiE{heAOP+M%ITP4LHNz3#@kI~<@9fAyphTJFN$ad0Z<*2{S?)GQSwR_P1^w4S8yU_7rYGvM$RhXrf_T{UxeABoh>+XkW7}G zl(CRBG7HJMR5piW)4C*Sz(Qa3?@dphBZxw4mA-{=dJYour&!@r1X0Mp6^n3(APV`@ zDiHn_K@{@84~y_K1X0L;7Z%|af+%YKvmSPN2zBxQ%E#M${ECnJ$NiZ(jZ0fb^8DiS z_dnN$kNd~{i#g@ljYaav2BZ1YIurgdZ9o*__hS)GjKtfYP}?W6orRBiZnd>5e1drg zx58gxJFM^|+uaH`*rt6$B6$`(6Z*LoUSxYv;j#{Ng`Z&iC56lSm{IsD+s74tn(d6j z-)CFqCG%WozO49v$o8tjKVthmg@3}fNo4J(cn9B}mu9{9cDbQx|?$vCI42p?q zG0+SvpEnEWSFjvY!~1T(d*HA=Us%+NHl9?rv%;y&Omfan<;~hO{;LwkKzjG;O*4p$ zM9zQD@_a?7mhKnYgM(Q7a-9#zbCeu~&Hz$_WT13-4fJA5T)i1rIZoa`yF*wMZXd(8 z-uOj21W{)AW6}6DhB`joi^SCN^=%&+9awl;V!{)s5u<6SQY$9*})huQ<`197LsiSO&E(5q4(x1ZzHdCB{e_$5>*ry8H(_!)8JR*ioH zdA;v$O6C*w{n6c1FaB4a L|Ee#RR5kuzxDdZJ literal 0 HcmV?d00001 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 +#include +#include +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +#define RSSI_TO_PERC(rssi) \ + rssi >= -50 ? 100 : \ + (rssi <= -100 ? 0 : \ + (2 * (rssi + 100))) + +#if defined(__linux__) + #include + #include + + #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 + #include + #include + #include /* before for NBBY */ + #include + #include + #include + + 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 + #include + + 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 0000000000000000000000000000000000000000..91e15088843157aa4a6889ca01b03d9270631ab4 GIT binary patch literal 4264 zcmbW3Yitx%6vxlDU7?7x6;KvYGu17vJhnSU`W~+ zDv}n$q+KD=n3$OO!9-0=j34xaA1o`BVEiO%;v+^9gNlNpX^asx^_)BR?%odFFP`k) z$M62nxsRDSGe1BR@ai{BHdx|x89KURRVHDPpXT)}fG2TZQ zpU3Oeer3vtBjOV-n?2jr)8U!(;n9DqEx%#^GC5OSj)pVQ3NIPRmp_O0w7!nZx`J4l;^Y2TB)Uj4h{ zKmHOs(1fvS2|HoS>+oY!bnyFo^aFa2e(;>$ZP+(Hn2#YQ5d&VF(Q6^;vUCKRv|`x4 zjK@FywqakynqSM*g^qx~9#~YoP7T^;lT{eu3>6I8=Z+Ug7L}IDm{z>H-Ec0beIa6h zuN5bkkJIJP1nPLCk)^X;Z0A6(PVvU`Z0lgXXV_Pa@$Nv=En6I&3eo<~L9;nxHpeqD z(`?Qrt>y-^IX9Thn^w$9LLO4s$*PSFX5%Ip8e9O)gZW&X35V19WF~2vLUQW}x~L5+ z6RN5 zysxvXE8Kw;V4@A+<2KmV-o8m~+O~7gL#nnm*b>y#mQc&u(1wr(HK*U;~)V)x(Tk#jF+Z1)8Mpv3n zcy%Q-dfQ{lr)u?xM+qU)6}3(AqqI$#4-i7PLp;W}RO7%z)lS8Kq8f#xH66<1Rc94d zSNuBn;Nm$dT0#x{F*C;yRF`%)d$gLJ#%zYmLN%R;WA(hggVpT8;l!yHaW2k63@+vT zegSyx=)ADf#hrP)5OnbstnUTg#aD6Ov2iz+pI@N(bYRP6R^`jI^^tqgD$Q zYpHmcigi>xLd8ZZ)>E;83e9n0K3WTl7sZqrhoqSu%%`(f%E^f^9Lr}RbvS8Zl8k3^ zW)e&*A0HTm-k}sV@kdoAnT2RHjmd7~#;9q=W7!n+@<7ojX5x5%G~U-AO~uj~XEsc` z=>*S?PD%uDo>RcQ=H$44@I^`Hya^~r04&D8;0CtINKa3P;9UgKNKZLXaJoOqNIwt% zf@cV#k)FO^1gHCljQHL77yLznXv8U32>u#DG>T8TNbolaqLJQ@f5G1;h(>zK*@Aya z5RLTm`llFm^vkd<^q)(7fyBSzoZ`s)`cvZazPyx((MT`v>p_X%gJWXe^%7q$@hzOw zzHXQJE=fFEq-Vi)1aYf?8QvCZQeoEq$(?y)Gr8qu`{~+n*`|XCr zRY^aOHi||SdxKOpIMSK#|Nr;65d z(|PP>@{(RYHx)HxG|VNepI~K~i~I*cb7a7Z^O8s6%osh)Iffjp6rXJ z^05K&^z%jjk4i-!x&_>C;#Op2N9Du7KEGYq7!~x4(7n)#f7wn_vOs%_4j+x40rKUT z*4}#QXRG8+;v< pYv~%f{plUWCDaFQfKfq@c7g#+$nemBL^6+$ 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 F`9F=c9-#mL literal 0 HcmV?d00001 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 +#include +#include +#include +#include +#include +#include + +#include "arg.h" +#include "slstatus.h" +#include "util.h" + +struct arg { + const char *(*func)(const char *); + const char *fmt; + const char *args; +}; + +char buf[1024]; +static volatile sig_atomic_t done; +static Display *dpy; + +#include "config.h" + +static void +terminate(const int signo) +{ + if (signo != SIGUSR1) + done = 1; +} + +static void +difftimespec(struct timespec *res, struct timespec *a, struct timespec *b) +{ + res->tv_sec = a->tv_sec - b->tv_sec - (a->tv_nsec < b->tv_nsec); + res->tv_nsec = a->tv_nsec - b->tv_nsec + + (a->tv_nsec < b->tv_nsec) * 1E9; +} + +static void +usage(void) +{ + die("usage: %s [-v] [-s] [-1]", argv0); +} + +int +main(int argc, char *argv[]) +{ + struct sigaction act; + struct timespec start, current, diff, intspec, wait; + size_t i, len; + int sflag, ret; + char status[MAXLEN]; + const char *res; + + sflag = 0; + ARGBEGIN { + case 'v': + die("slstatus-"VERSION); + case '1': + done = 1; + /* FALLTHROUGH */ + case 's': + sflag = 1; + break; + default: + usage(); + } ARGEND + + if (argc) + usage(); + + memset(&act, 0, sizeof(act)); + act.sa_handler = terminate; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + act.sa_flags |= SA_RESTART; + sigaction(SIGUSR1, &act, NULL); + + if (!sflag && !(dpy = XOpenDisplay(NULL))) + die("XOpenDisplay: Failed to open display"); + + do { + if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) + die("clock_gettime:"); + + status[0] = '\0'; + for (i = len = 0; i < LEN(args); i++) { + if (!(res = args[i].func(args[i].args))) + res = unknown_str; + + if ((ret = esnprintf(status + len, sizeof(status) - len, + args[i].fmt, res)) < 0) + break; + + len += ret; + } + + if (sflag) { + puts(status); + fflush(stdout); + if (ferror(stdout)) + die("puts:"); + } else { + if (XStoreName(dpy, DefaultRootWindow(dpy), status) < 0) + die("XStoreName: Allocation failed"); + XFlush(dpy); + } + + if (!done) { + if (clock_gettime(CLOCK_MONOTONIC, ¤t) < 0) + die("clock_gettime:"); + difftimespec(&diff, ¤t, &start); + + intspec.tv_sec = interval / 1000; + intspec.tv_nsec = (interval % 1000) * 1E6; + difftimespec(&wait, &intspec, &diff); + + if (wait.tv_sec >= 0 && + nanosleep(&wait, NULL) < 0 && + errno != EINTR) + die("nanosleep:"); + } + } while (!done); + + if (!sflag) { + XStoreName(dpy, DefaultRootWindow(dpy), NULL); + if (XCloseDisplay(dpy) < 0) + die("XCloseDisplay: Failed to close display"); + } + + return 0; +} diff --git a/slstatus/slstatus.h b/slstatus/slstatus.h new file mode 100644 index 0000000..8ef5874 --- /dev/null +++ b/slstatus/slstatus.h @@ -0,0 +1,84 @@ +/* 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); + +/* 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/slstatus.o b/slstatus/slstatus.o new file mode 100644 index 0000000000000000000000000000000000000000..e8cd9a3a4eb26d66053a3e53bb7f72406d756a68 GIT binary patch literal 6008 zcmbtYZ){sv6~B(1#4W+j+HK7!u*Yy9*&5F;ZJ8Sth@GUn5hhe5Wq~d@ug=RK)v+zV z=aOt)mnz9N&oiq8f_)fB`!Xgbq&)Zp*{y6RTdZ!k zxBsFM5p;W1G?BteTee{rtS&tqVFk;WGJCXkOFPVn>U9HK(tsI7=}HQ&1jP% z1j`titd%y+91#1`S<9@`2ZXiEjZO+V8^?E2W9?RZd#z^ehqU$)7JDG}mDrKk$Pr%r z$8hoA{PdsS^fNZWOTP9Fm@zLdg)RfYOHI5e62g}&c2o5#U%coa(BDp!yD%@jG{B%C zxU>MqLbR*!@RNr5@PP_1^@m#dJ-!e>@LF&1*-O0h9bWt;?|glo z2k(9zz(@S+!B)`))FLU!|27uJKLE5O-v8G|fe34VWtcdqc_W&Y1Md2uA9q5rR- z;S}-T^s4Y;f2e~mug-PoH+cDENRyeJZxhO5)Mq=EaATfJt^ci7aZki$8ek*2Cy%D8 zULtEx-~^;VJd$=}ZQV})9G_IZa&#>AU~DY*^;q(za|*Kkfh2RU!LEB%h<84ZntAP7 zjNRYY*Q0gZKXjy1)AxqE!@AZT>E0WOMsz{;L8-wH7X?VHfq5#3;r2)INPAn&gfq7$ zo`=57Jaes{-7U@jGeB+y_@lB7brcT2h|n0jhGUBdTAyx+2im^rjR&-{FCOT4sxcOb zJl517h%Pn{2F~p8{?HqU_5~ubKnGBO3NG;gB+pH7oIdt$wO+e-V-{Omz2M=L764%G z8?c(g?SOcI@0Tvr(<`#J;j*E4S<{BDl-;0U9M!d&(wzcVRxM}f6B0NcU^Us=?P~?x z3%XFMx&Ve#%r^70Ga17+*+e!2MQ)xM`6-JPGRHI7Z)B2|ooC@hUpE$evDk;jek?wN z#eG;ru!v%D01G|BW{sH)209U86Eh~WW~Pi3UIMq=D!1MqnQ3Km`I(G8DXx*svL~_y zn@vup3)VE7H1qjvo?I>2=poUtah+tzBycj>q`Gi!yBjU=FEJGO+Xd|0%I`qCyX>ZR+z+vX_l*lw=3~Fk$LPQcpK>N!|e?vPH;R+6#ksV z(ftmB|3Ko-__&Wz5U25RAEO{1fS-!Lf*^=9K3-c;oN<0%gKvt1`xwO;XFKdC6pVvE z)$bh=NB&0$-r<7BUGO1-ZH5gpa4T;-4irzB?7ZO5)({SmEyy{Nn`w7m0)KLdE|N;cEoS?9r&9o7=%{iNe;eBJU@G;9`;d~Yir0ZcFOhp33 z$lHaSgaip1fv~-wQH-y^S>{<`4UD=5G`*jOm=e`lRE~Mn$6r==i--um9_E;a`w^>>vAf z_K%wo`=`$sn2gJDD?VZ7hTdDVnm z6jd)0T{Otzr1Ym4>dH^y-vR~C5$F6Dq`z}qYTU~IMNlxN)Bh#uKc|SYcKSaLc)j&I zg9`?t`bQJ*TgqQOfA#uCzYe*fqjJG;eRkFH-v-!Cdk?U2`3c|UNSMQfZ=m}A6*>O1 J&`!na|38PO29W>& literal 0 HcmV?d00001 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 +#include +#include +#include +#include +#include + +#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 + +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, ...); diff --git a/slstatus/util.o b/slstatus/util.o new file mode 100644 index 0000000000000000000000000000000000000000..361d63fe39044fb9966c60a61a596af4ccca1e5d GIT binary patch literal 5552 zcmdUyU2I!L7J$c&OWo35yZn^!v*JG7(54lxo$}KL6%yi*0g@6R1=;|qH|WR0I3oP^#LIv0dW3GRM@Zg|@X``ZW;U+5nI#%Dwj{i`${;>l0=#?R~(gkmM(_SE~Z^L=9HFB6AX z-FaL}|J?nRMg2D-<{!do_Z)XmRKw@k+bjT!(_`q*R=a}l=bOq6jE#jW>y3Tx#Z;X@ z^G@z)KL*=Da09=^pCG1}QA|H=?FSnBfY0p4T^C>D8g8}^ix+rkP3iRI+?|~|?&F`H;!9=`%`Su< zDlLT8d>AWbLhEMjLX}vLJfJ zet>5lnrrX7)BSM^cYmv%`QLm&h)v|~_uj#{bFl2j#v7VLwD!CxUvx~z zF6NR6C!J!M5ochiIFiV&ddNEbGg%-Yo0PvTa$mHOp+48O^72=BlQ`s#eCjIyzd-)m@vnG@G&Yk(NlzY>Bq4 zk8X&@X#XJKnF(nTBrs!s<|`qSZ=sLsOMszV<|*cT;y&gZ5BsiOwm7&Cj4j~TP+JOh zyAhu9_Za3=OO0@c5$wD;ZiMeJf_U}C$?kR3UB+>r|5YOhMI3uOB$O90!!}SRq;W_n zM==v)--R7NGQ#bmuL<}$Yy(-qOxTFQzc#{gVT1maunpvA%({$2KK~a>(YwuH5e0M{ zImmg;Za2bD`{PFAzZ>F)ITh$K&iMQ%4YR{&gwbyoW1?9tLB9j+hjOj9hRW_<*xJD0 z8qx&!1U#Sez7zpeI@Z#8o!qKZ5nSPp4a$V;>ksPoa5~2_aPK8q z9-2mN_ISe1v4MetlSu9yNDl2B$Rx7EER{{O^mx``X?b^6uTC~d!wnWQ)k~A*3&}(d z0A77CV74%t+_-UIEJHXsJPLPd!rn0!mCKPo>+v|2LHZSb)}pK=1KeKxmLxZc>-Ou2 zqrCx1+IK2Eq;P}c1?}+%iL`%1;dl>9eh;A+Qd)1$<3RnFZRK`*Ld0gSDKd&m|U{U%%tMP}S4dsNwF@~l6 zN#f|gI`04i5L_?5b<*A@j!O0CtYVM<*rfe0iv86JUyctf2#$|;u(ZE|ICfM0=}|bo zThjhPh2sx!$%hrL`gt$dpy2p;KS=xSP)0$n#;1UgVL0NDT?FJkAUP*4?;OeRB91YG zB6+u0WhQCK@%~0x?Wu}=FV*D-EA8(ij_(JGkF;_aaNj$AR4x8V)2on16kdT1bHCq~lb)n8X8bj6s` ze_6EwLwS*; zUcuDr*FnA3{EhhWgB-%aL2!PYL!UoJOrL*isuR z|FAZp-v6J_x7PW+ANIUC=qYi2_#VmrGJa~!ja@hy&XZ&Kn+Ib+?~iYye)fP^6dyB? a4?I=LwdIJntycd$K6)S%BClq8|K9