diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 49 | ||||
-rw-r--r-- | config.h | 22 | ||||
-rw-r--r-- | config.mk | 30 | ||||
-rw-r--r-- | slstatus.c | 302 |
5 files changed, 405 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0622c05 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +slstatus +slstatus.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f31d1c7 --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = ${NAME}.c +OBJ = ${SRC:.c=.o} + +all: options ${NAME} + +options: + @echo ${NAME} build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: config.mk + +${NAME}: ${OBJ} + @echo CC -o $@ + @${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + @echo cleaning + @rm -f ${NAME} ${OBJ} ${NAME}-${VERSION}.tar.gz + +dist: clean + @echo creating dist tarball + @mkdir -p ${NAME}-${VERSION} + @cp -R Makefile config.mk LICENSE \ + ${SRC} ${NAME}-${VERSION} + @tar -cf ${NAME}-${VERSION}.tar ${NAME}-${VERSION} + @gzip ${NAME}-${VERSION}.tar + @rm -rf ${NAME}-${VERSION} + +install: all + @echo installing executable file to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${NAME} ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME} + +uninstall: + @echo removing executable file from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/${NAME} + +.PHONY: all options clean dist install uninstall diff --git a/config.h b/config.h new file mode 100644 index 0000000..b92a20d --- /dev/null +++ b/config.h @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ + +/* alsa sound */ +static const char soundcard[] = "default"; +static const char channel[] = "Master"; + +/* cpu temperature */ +static const char tempfile[] = "/sys/devices/platform/coretemp.0/hwmon/hwmon2/temp1_input"; + +/* wifi */ +static const char wificard[] = "wlp3s0"; + +/* battery */ +static const char batterynowfile[] = "/sys/class/power_supply/BAT0/energy_now"; +static const char batteryfullfile[] = "/sys/class/power_supply/BAT0/energy_full_design"; + +/* time */ +static const char timeformat[] = "%y-%m-%d %H:%M:%S"; + +/* statusbar */ +#define FORMATSTRING "wifi %4s | bat %4s | cpu %4s %3s | ram %3s | vol %4s | %3s" +#define ARGUMENTS wifi_signal(), battery(), cpu_usage(), cpu_temperature(), ram_usage(), volume(), datetime() diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..75ba482 --- /dev/null +++ b/config.mk @@ -0,0 +1,30 @@ +NAME = slstatus +VERSION = 1.0 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# includes and libs +INCS = -I. -I/usr/include -I${X11INC} +LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lasound + +# flags +CPPFLAGS = -DVERSION=\"${VERSION}\" +CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +#CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} +LDFLAGS = -g ${LIBS} +#LDFLAGS = -s ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc + diff --git a/slstatus.c b/slstatus.c new file mode 100644 index 0000000..77f900d --- /dev/null +++ b/slstatus.c @@ -0,0 +1,302 @@ +/* See LICENSE file for copyright and license details. */ + +#include <alsa/asoundlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <X11/Xlib.h> + +#include "config.h" + +char *smprintf(char *fmt, ...); + +void *setstatus(char *str); + +char *wifi_signal(); +char *battery(); +char *cpu_usage(); +char *cpu_temperature(); +char *ram_usage(); +char *volume(); +char *datetime(); + +static Display *dpy; + +/* smprintf function */ +char * +smprintf(char *fmt, ...) +{ + va_list fmtargs; + char *ret; + int len; + + va_start(fmtargs, fmt); + len = vsnprintf(NULL, 0, fmt, fmtargs); + va_end(fmtargs); + + ret = malloc(++len); + if (ret == NULL) { + fprintf(stderr, "Malloc error."); + exit(1); + } + + va_start(fmtargs, fmt); + vsnprintf(ret, len, fmt, fmtargs); + va_end(fmtargs); + + return ret; +} + +/* set statusbar (WM_NAME) */ +void +setstatus(char *str) +{ + XStoreName(dpy, DefaultRootWindow(dpy), str); + XSync(dpy, False); +} + +/* alsa volume percentage */ +char * +volume() +{ + int mute = 0; + long vol = 0, max = 0, min = 0; + + snd_mixer_t *handle; + snd_mixer_elem_t *pcm_mixer, *mas_mixer; + snd_mixer_selem_id_t *vol_info, *mute_info; + snd_mixer_open(&handle, 0); + snd_mixer_attach(handle, soundcard); + snd_mixer_selem_register(handle, NULL, NULL); + snd_mixer_load(handle); + snd_mixer_selem_id_malloc(&vol_info); + snd_mixer_selem_id_malloc(&mute_info); + snd_mixer_selem_id_set_name(vol_info, channel); + snd_mixer_selem_id_set_name(mute_info, channel); + pcm_mixer = snd_mixer_find_selem(handle, vol_info); + mas_mixer = snd_mixer_find_selem(handle, mute_info); + snd_mixer_selem_get_playback_volume_range((snd_mixer_elem_t *)pcm_mixer, + &min, &max); + snd_mixer_selem_get_playback_volume((snd_mixer_elem_t *)pcm_mixer, + SND_MIXER_SCHN_MONO, &vol); + snd_mixer_selem_get_playback_switch(mas_mixer, SND_MIXER_SCHN_MONO, + &mute); + + if (vol_info) + snd_mixer_selem_id_free(vol_info); + if (mute_info) + snd_mixer_selem_id_free(mute_info); + if (handle) + snd_mixer_close(handle); + + if (!mute) + return "mute"; + else + return smprintf("%d%%", (vol * 100) / max); +} + +/* cpu temperature */ +char * +cpu_temperature() +{ + int temperature; + FILE *fp; + + /* open temperature file */ + if (!(fp = fopen(tempfile, "r"))) { + fprintf(stderr, "Could not open temperature file.\n"); + exit(1); + } + + /* extract temperature, close file */ + fscanf(fp, "%d", &temperature); + fclose(fp); + + /* return temperature in degrees */ + return smprintf("%d°C", temperature / 1000); +} + +/* wifi percentage */ +char * +wifi_signal() +{ + int bufsize = 255; + int strength; + char buf[bufsize]; + char *datastart; + char path_start[16] = "/sys/class/net/"; + char path_end[11] = "/operstate"; + char path[32]; + FILE *fp; + + /* generate the path name */ + strcat(path, path_start); + strcat(path, wificard); + strcat(path, path_end); + + /* open wifi file, extract status, close file */ + if(!(fp = fopen(path, "r"))) { + fprintf(stderr, "Error opening wifi operstate file."); + exit(1); + } + char status[5]; + fgets(status, 5, fp); + fclose(fp); + + /* check if interface down */ + if(strcmp(status, "up\n") != 0){ + return "n/a"; + } + + /* open wifi file */ + if (!(fp = fopen("/proc/net/wireless", "r"))) { + fprintf(stderr, "Error opening wireless file."); + exit(1); + } + + /* extract the signal strength and close the file */ + fgets(buf, bufsize, fp); + fgets(buf, bufsize, fp); + fgets(buf, bufsize, fp); + if ((datastart = strstr(buf, "wlp3s0:")) != NULL) { + datastart = strstr(buf, ":"); + sscanf(datastart + 1, " %*d %d %*d %*d %*d %*d %*d %*d %*d %*d", + &strength); + } + fclose(fp); + + /* return strength in percent */ + return smprintf("%d%%", strength); +} + +/* battery percentage */ +char * +battery() +{ + int batt_now; + int batt_full; + int batt_perc; + FILE *fp; + + /* open battery now file, extract and close */ + if (!(fp = fopen(batterynowfile, "r"))) { + fprintf(stderr, "Error opening battery file."); + exit(1); + } + fscanf(fp, "%i", &batt_now); + fclose(fp); + + /* extract battery full file, extract and close */ + if (!(fp = fopen(batteryfullfile, "r"))) { + fprintf(stderr, "Error opening battery file."); + exit(1); + } + fscanf(fp, "%i", &batt_full); + fclose(fp); + + /* calculate percent */ + batt_perc = batt_now / (batt_full / 100); + + /* return percent */ + return smprintf("%d%%", batt_perc); +} + +/* date and time */ +char * +datetime() +{ + time_t tm; + size_t bufsize = 19; + char *buf = malloc(bufsize); + + /* get time in format */ + time(&tm); + if(!strftime(buf, bufsize, timeformat, localtime(&tm))) { + fprintf(stderr, "Strftime failed.\n"); + exit(1); + } + + /* return time */ + return buf; +} + +/* cpu percentage */ +char * +cpu_usage() +{ + FILE *fp; + long double a[4], b[4], cpu_perc; + + /* open stat file, read and close, do same after 1 second */ + if (!(fp = fopen("/proc/stat","r"))) { + fprintf(stderr, "Error opening stat file."); + exit(1); + } + fscanf(fp,"%*s %Lf %Lf %Lf %Lf",&a[0],&a[1],&a[2],&a[3]); + fclose(fp); + sleep(1); + if (!(fp = fopen("/proc/stat","r"))) { + fprintf(stderr, "Error opening stat file."); + exit(1); + } + fscanf(fp,"%*s %Lf %Lf %Lf %Lf",&b[0],&b[1],&b[2],&b[3]); + fclose(fp); + + /* calculate average in 1 second */ + cpu_perc = 100 * ((b[0]+b[1]+b[2]) - (a[0]+a[1]+a[2])) / ((b[0]+b[1]+b[2]+b[3]) - (a[0]+a[1]+a[2]+a[3])); + + /* return avg cpu percentage */ + return smprintf("%d%%", (int)cpu_perc); +} + +/* ram percentage */ +char * +ram_usage() +{ + FILE *fp; + long total, free, available; + int ram_perc; + + /* read meminfo file, extract and close */ + if (!(fp = fopen("/proc/meminfo", "r"))) { + fprintf(stderr, "Error opening meminfo file."); + exit(1); + } + fscanf(fp, "MemTotal: %ld kB\n", &total); + fscanf(fp, "MemFree: %ld kB\n", &free); + fscanf(fp, "MemAvailable: %ld kB\n", &available); + fclose(fp); + + /* calculate percentage */ + ram_perc = 100 * (total - available) / total; + + /* return in percent */ + return smprintf("%d%%",ram_perc); +} + +int +main() +{ + char status[1024]; + + /* open display */ + if (( dpy = XOpenDisplay(0x0)) == NULL ) { + fprintf(stderr, "Cannot open display!\n"); + exit(1); + } + + /* return status every second */ + for (;;) { + sprintf(status, FORMATSTRING, ARGUMENTS); + setstatus(status); + } + + /* close display */ + XCloseDisplay(dpy); + + /* exit successfully */ + return 0; +} |