nfy

Minimal and daemonless notification program for X
git clone git://git.christosmarg.xyz/nfy.git
Log | Files | Refs | README | LICENSE

commit db2c03b9dda742d584185290a66913910b223bd2
parent 3dd4fe5416c2f9050723fc1c0ebf50f9e0a77094
Author: Christos Margiolis <christos@margiolis.net>
Date:   Mon, 24 May 2021 20:45:29 +0300

Fixed lockfile bug, made Makefile portable

Diffstat:
MMakefile | 28++++++++++++++--------------
MREADME | 2+-
Mconfig.h | 25+++++++++++++++----------
Mconfig.mk | 18++++--------------
Mnfy.1 | 31++++++++++++++++++-------------
Mnfy.c | 108+++++++++++++++++++++++++++++++++++++++----------------------------------------
6 files changed, 105 insertions(+), 107 deletions(-)

diff --git a/Makefile b/Makefile @@ -26,28 +26,28 @@ ${BIN}: ${OBJ} ${CC} -c ${CFLAGS} $< dist: clean - ${MKDIR} ${DIST} - ${CP} -R nfy.c config.h Makefile config.mk ${DIST} - ${TAR} ${DIST}.tar ${DIST} - ${GZIP} ${DIST}.tar - ${RM_DIR} ${DIST} + mkdir -p ${DIST} + cp -R config.h config.mk LICENSE Makefile nfy.1 nfy.c README ${DIST} + tar -cf ${DIST}.tar ${DIST} + gzip ${DIST}.tar + rm -rf ${DIST} run: ./${BIN} install: all - ${MKDIR} ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN_DIR} - ${CP} ${BIN} ${BIN_DIR} - ${CP} ${MAN1} ${DESTDIR}${MAN_DIR} - sed "s/VERSION/${VERSION}/g" < ${MAN1} > ${DESTDIR}${MAN_DIR}/${MAN1} - chmod 755 ${DESTDIR}${BIN_DIR}/${BIN} - chmod 644 ${DESTDIR}${MAN_DIR}/${MAN1} + mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${MANPREFIX}/man1 + cp -f ${BIN} ${DESTDIR}${PREFIX}/bin + cp -f ${MAN1} ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < ${MAN1} > ${DESTDIR}${MANPREFIX}/man1/${MAN1} + chmod 755 ${DESTDIR}${PREFIX}/bin/${BIN} + chmod 644 ${DESTDIR}${MANPREFIX}/man1/${MAN1} uninstall: - ${RM} ${DESTDIR}${BIN_DIR}/${BIN} - ${RM} ${DESTDIR}${MAN_DIR}/${MAN1} + rm -f ${DESTDIR}${PREFIX}/bin/${BIN} \ + ${DESTDIR}${MANPREFIX}/man1/${MAN1} clean: - ${RM} ${BIN} ${OBJ} ${DIST}.tar.gz + rm -f ${BIN} ${OBJ} ${DIST}.tar.gz *.core .PHONY: all options clean dist install uninstall run diff --git a/README b/README @@ -10,7 +10,7 @@ Dependencies: - libXrandr Usage: ------ +------ Edit config.mk and config.h to match your particular setup and preferences. diff --git a/config.h b/config.h @@ -1,16 +1,21 @@ /* See LICENSE file for copyright and license details. */ +#ifndef _NFY_CONFIG_H_ +#define _NFY_CONFIG_H_ + enum { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; /* Window positions */ static const char *bgcolor = "#201f1c"; /* Background color */ -static const char *bordercolor = "#f28f19"; /* Border color */ +static const char *bordercolor = "#f18f19"; /* Border color */ static const char *fontcolor = "#e6e5e3"; /* Font color */ /* TODO: add fallbacks */ -static const char *fonts = "monospace:size=15"; /* Font */ -static const int padding = 15; /* Padding (pixels) */ -static const int borderw = 2; /* Border width (pixels) */ -static const int duration = 3; /* Notification duration (seconds) */ -static const int pos = TOP_RIGHT; /* Window position */ -static const int mx = 10; /* Margin X (pixels) */ -static const int my = 25; /* Margin Y (pixels) */ -static const int maxlen = 250; /* Max window length (pixels) */ -static const int linespace = 10; /* Line spacing (pixels) */ +static const char *fonts = "monospace:size=12"; /* Fonts */ +static const unsigned int padding = 15; /* Padding (pixels) */ +static const unsigned int borderw = 2; /* Border width (pixels) */ +static const unsigned int duration = 3; /* Notification duration (seconds) */ +static const unsigned int pos = TOP_RIGHT; /* Window position */ +static const unsigned int mx = 10; /* Margin X (pixels) */ +static const unsigned int my = 25; /* Margin Y (pixels) */ +static const unsigned int maxlen = 250; /* Max window length (pixels) */ +static const unsigned int linespace = 10; /* Line spacing (pixels) */ static const char *lockfile = "/tmp/nfy.lock"; + +#endif /* _NFY_CONFIG_H_ */ diff --git a/config.mk b/config.mk @@ -1,17 +1,17 @@ # See LICENSE file for copyright and license details. # nfy version -VERSION = 0 +VERSION = 0.1 # paths PREFIX = /usr/local -MAN_DIR = ${PREFIX}/share/man/man1 -BIN_DIR = ${PREFIX}/bin +MANPREFIX = ${PREFIX}/share/man # includes and libs +# FreeBSD X11INC = /usr/local/include X11LIB = /usr/local/lib -# Linux +# Linux/OpenBSD #X11INC = /usr/X11R6/include #X11LIB = /usr/X11R6/lib @@ -26,15 +26,5 @@ CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \ CFLAGS = -std=c99 -pedantic -Wall -Os -I${X11INC} -I${FREETYPEINC} ${CPPFLAGS} LDFLAGS = -L${X11LIB} ${FREETYPELIBS} -lX11 -lXrandr -# utils -CP = cp -f -RM = rm -f -RM_DIR = rm -rf -MV = mv -MKDIR = mkdir -p -RM_DIR = rm -rf -TAR = tar -cf -GZIP = gzip - # compiler CC = cc diff --git a/nfy.1 b/nfy.1 @@ -6,19 +6,18 @@ .Nd a minimal and daemonless notification program for X .Sh SYNOPSIS .Nm +.Op Fl v .Ar str... .Sh DESCRIPTION .Pp .Nm -creates a temporary notification popup window and displays -all the arguments that were passed to it. It uses the -.Xr Xft 3 -library to render fonts and colors, and -.Xr Xrandr 3 -to handle screen sizes. -.Pp -All configuration is done by -editing the 'config.h' file in the source code. +creates a temporary notification popup window and displays all the arguments +that were passed to it. Configuration is done by editing the 'config.h' file +in the source code. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl v +prints version to stdout and exits. .Sh USAGE .Pp If the string is not surrounded in quotes, @@ -30,9 +29,10 @@ has a non-blocking behaviour, meaning that commands coming after it can execute normally without them having to wait for .Nm -to finish first. This is helpful since it's a notification -program and there's no reason to wait for the notification -to die off before other commands can execute. +to finish first. This is especially helpful inside scripts. +.Pp +.Nm +handles multiple notifications by queuing them using a lock file. .Sh SIGNALS .Nm handles only SIGALRM, SIGTERM and SIGINT. It receives a @@ -40,6 +40,11 @@ SIGALRM signal by default after the specified duration of the notification. .Sh SEE ALSO .Xr signal 3 , -.Xr alarm 3 +.Xr alarm 3 , +.Xr fcntl 3 , +.Xr Xft 3 , +.Xr Xrandr 3 +.Sh BUGS +Perhaps. .Sh AUTHORS .An Christos Margiolis Aq Mt christos@christosmarg.xyz diff --git a/nfy.c b/nfy.c @@ -1,5 +1,5 @@ /* See LICENSE file for copyright and license details. */ -#include <errno.h> +#include <err.h> #include <fcntl.h> #include <signal.h> #include <stdarg.h> @@ -14,35 +14,14 @@ #include "config.h" -static void die(const char *, ...); -static void recvalrm(int); +static void sighandler(int); static char *argv0; static Display *dpy; static Window win; static void -die(const char *fmt, ...) -{ - va_list args; - - fprintf(stderr, "%s: ", argv0); - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - - if (fmt[0] && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else - fputc('\n', stderr); - - exit(EXIT_FAILURE); -} - -static void -recvalrm(int sig) +sighandler(int sig) { XEvent ev; @@ -51,6 +30,13 @@ recvalrm(int sig) XFlush(dpy); } +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-v] str...\n", argv0); + exit(1); +} + int main(int argc, char *argv[]) { @@ -63,34 +49,44 @@ main(int argc, char *argv[]) XftColor color; XftDraw *drw; XftFont *font; + struct flock fl; struct sigaction sig; pid_t pid; int lockfd; int scr, scrw, scrh; int x, y, w, h, th; - int i, j, len; + int i, j, len, argi; + char ch; argv0 = *argv; - if (argc < 2) { - fprintf(stderr, "usage: %s str...\n", argv0); - return 1; + if (argc < 2) + usage(); + while ((ch = getopt(argc, argv, "v")) != -1) { + switch (ch) { + case 'v': + puts("nfy-"VERSION); + exit(0); + case '?': + default: + usage(); + } } - - if ((lockfd = open(lockfile, O_CREAT | O_RDWR, 0600)) == -1) - die("open:"); - if (!(dpy = XOpenDisplay(NULL))) - die("XOpenDisplay:"); + /*argc -= optind;*/ + /*argv += optind;*/ + argi = optind; if ((pid = fork()) < 0) - die("fork:"); + err(1, "fork"); if (pid > 0) - exit(EXIT_SUCCESS); + _exit(0); umask(0); if (setsid() < 0) - die("setsid:"); - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); + err(1, "setsid"); + + if ((lockfd = open(lockfile, O_CREAT | O_RDWR, 0600)) < 0) + err(1, "open"); + if (!(dpy = XOpenDisplay(NULL))) + errx(1, "XOpenDisplay"); scr = DefaultScreen(dpy); screens = XRRGetScreenResources(dpy, RootWindow(dpy, scr)); @@ -110,7 +106,7 @@ main(int argc, char *argv[]) th = font->ascent - font->descent; w = 0; - for (i = 1; i < argc; i++) { + for (i = argi; i < argc; i++) { len = strlen(argv[i]); j = len; while (j--) @@ -154,26 +150,31 @@ main(int argc, char *argv[]) XSelectInput(dpy, win, ExposureMask | ButtonPress); XMapWindow(dpy, win); - sig.sa_handler = recvalrm; + sig.sa_handler = sighandler; sig.sa_flags = SA_RESTART; - sigemptyset(&sig.sa_mask); - sigaction(SIGALRM, &sig, 0); - sigaction(SIGTERM, &sig, 0); - sigaction(SIGINT, &sig, 0); - /* XXX: replace with just singal? */ - - /* XXX: when being called from another process this one succeeds */ - while (lockf(lockfd, F_LOCK, 0L) == -1) - ; + (void)sigemptyset(&sig.sa_mask); + if (sigaction(SIGALRM, &sig, NULL) < 0) + err(1, "sigaction(SIGALRM)"); + if (sigaction(SIGTERM, &sig, NULL) < 0) + err(1, "sigaction(SIGTERM)"); + if (sigaction(SIGINT, &sig, NULL) < 0) + err(1, "sigaction(SIGINT)"); + + fl.l_len = 0; + fl.l_start = 0; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + if (fcntl(lockfd, F_SETLKW, &fl) < 0) + err(1, "fcntl(F_SETLKW)"); + if (duration > 0) (void)alarm(duration); for (;;) { XNextEvent(dpy, &ev); - if (ev.type == Expose) { XClearWindow(dpy, win); - for (i = 1; i < argc; i++) + for (i = argi; i < argc; i++) XftDrawStringUtf8(drw, &color, font, w >> 3, linespace * (i - 1) + th * i + padding, (FcChar8 *)argv[i], strlen(argv[i])); @@ -187,10 +188,7 @@ main(int argc, char *argv[]) XRRFreeCrtcInfo(info); XRRFreeScreenResources(screens); XCloseDisplay(dpy); - - (void)lockf(lockfd, F_ULOCK, 0); (void)close(lockfd); - (void)unlink(lockfile); return 0; }