nfy

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

commit e87c72750e2e4955e2dfaccb89dcf6966c8e969e
parent 6d94996de9f50d43c78b6bef4bc8364f28b0a94b
Author: Christos Margiolis <christos@margiolis.net>
Date:   Fri,  1 Apr 2022 22:42:21 +0300

added linebreak, fixed calcs, now reads from stdin only

Diffstat:
Mconfig.h | 9+++++----
Mconfig.mk | 4+++-
Mnfy.1 | 42++++++++++++++++++------------------------
Mnfy.c | 116++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
4 files changed, 106 insertions(+), 65 deletions(-)

diff --git a/config.h b/config.h @@ -2,19 +2,20 @@ #ifndef _NFY_CONFIG_H_ #define _NFY_CONFIG_H_ +#define MAXLEN 82 /* Max length per line (characters) */ + enum { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; /* Window positions */ static const char *bgcolor = "#201f1c"; /* Background color */ static const char *bordercolor = "#f18f19"; /* Border color */ static const char *fontcolor = "#e6e5e3"; /* Font color */ /* TODO: add fallbacks */ 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 padding = 10; /* Padding (pixels) */ +static const unsigned int borderw = 3; /* 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 my = 10; /* Margin Y (pixels) */ static const unsigned int linespace = 10; /* Line spacing (pixels) */ static const char *lockfile = "/tmp/nfy.lock"; diff --git a/config.mk b/config.mk @@ -1,10 +1,12 @@ # See LICENSE file for copyright and license details. # nfy version -VERSION = 0.1 +VERSION = 0.2 # paths PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man +# OpenBSD +#MANPREFIX = ${PREFIX}/man # includes and libs # FreeBSD diff --git a/nfy.1 b/nfy.1 @@ -7,44 +7,38 @@ .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. 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 +reads input from stdin (only), creates a temporary popup window and \ +displays it. Configuration is done by editing the 'config.h' file in the \ +source code. .Pp -If the string is not surrounded in quotes, .Nm -will print one line for each word. -.Pp -.Nm -has a non-blocking behaviour, meaning that commands -coming after it can execute normally without them having -to wait for +has non-blocking behaviour, meaning that commands coming after it can execute \ +normally without them having to wait for .Nm -to finish first. This is especially helpful inside scripts. +to finish first. +This is especially useful inside scripts. .Pp .Nm handles multiple notifications by queuing them using a lock file. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl v +prints version to stdout and exits. +.El .Sh SIGNALS .Nm -handles only SIGALRM, SIGTERM and SIGINT. It receives a -SIGALRM signal by default after the specified duration +handles only SIGALRM, SIGTERM and SIGINT. +It receives a SIGALRM signal by default after the specified duration \ of the notification. .Sh SEE ALSO -.Xr signal 3 , +.Xr fcntl 2 , .Xr alarm 3 , -.Xr fcntl 3 , +.Xr signal 3 , .Xr Xft 3 , .Xr Xrandr 3 -.Sh BUGS -Perhaps. .Sh AUTHORS .An Christos Margiolis Aq Mt christos@margiolis.net +.Sh BUGS +Perhaps. diff --git a/nfy.c b/nfy.c @@ -14,12 +14,39 @@ #include "config.h" +struct line { + char data[MAXLEN+1]; + size_t len; +}; + +#undef strlcpy /* compat with openbsd */ +size_t strlcpy(char *, const char *, size_t); static void sighandler(int); static char *argv0; static Display *dpy; static Window win; +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + if (nleft != 0) { + while (--nleft != 0) + if ((*dst++ = *src++) == '\0') + break; + } + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; + while (*src++) + ; + } + return (src - osrc - 1); +} + static void sighandler(int sig) { @@ -49,18 +76,18 @@ main(int argc, char *argv[]) XftColor color; XftDraw *drw; XftFont *font; + XGlyphInfo gi; struct flock fl; - struct sigaction sig; + struct sigaction sa; + struct line *lines, *lp; pid_t pid; int lockfd; int scr, scrw, scrh; - int x, y, w, h, th; - int i, j, len, argi; - char ch; + int x, y, w = 0, h, th; + int i, len = 0, nlines = 0; + char buf[MAXLEN+1], ch; argv0 = *argv; - if (argc < 2) - usage(); while ((ch = getopt(argc, argv, "v")) != -1) { switch (ch) { case 'v': @@ -71,10 +98,10 @@ main(int argc, char *argv[]) usage(); } } - /*argc -= optind;*/ - /*argv += optind;*/ - argi = optind; + argc -= optind; + argv += optind; + /* detach from tty */ if ((pid = fork()) < 0) err(1, "fork"); if (pid > 0) @@ -85,6 +112,8 @@ main(int argc, char *argv[]) if ((lockfd = open(lockfile, O_CREAT | O_RDWR, 0600)) < 0) err(1, "open"); + + /* init x11 */ if (!(dpy = XOpenDisplay(NULL))) errx(1, "XOpenDisplay"); @@ -106,38 +135,50 @@ main(int argc, char *argv[]) th = font->ascent - font->descent; XftColorAllocName(dpy, vis, colormap, fontcolor, &color); - w = 0; - for (i = argi; i < argc; i++) { - len = strlen(argv[i]); - j = len; - while (j--) - if (**argv == '\n') - **argv++ = ' '; - /* TODO: handle maxlen */ - if (len > w) - w = len; - } - w *= th + borderw; - h = th * (argc - 1) + (linespace * (argc - 2)) + (padding << 1); + /* read stdin into buffer */ + if ((lines = malloc(sizeof(struct line))) == NULL) + err(1, "malloc"); + while (read(STDIN_FILENO, &ch, 1) > 0) { + if (ch == '\n' || len == MAXLEN) { + buf[len] = '\0'; + if ((lines = realloc(lines, + (nlines + 1) * sizeof(struct line))) == NULL) + err(1, "realloc"); + lp = &lines[nlines]; + lp->len = len; + strlcpy(lp->data, buf, sizeof(lp->data)); + /* determine window width based on largest line */ + XftTextExtentsUtf8(dpy, font, (XftChar8 *)lp->data, + lp->len, &gi); + if (gi.width > w) + w = gi.width; + nlines++; + len = 0; + } else + buf[len++] = ch; + } + w += padding * 2; + h = (nlines - 1) * linespace + nlines * th + 2 * padding; + /* calculate position coordinates */ switch (pos) { case TOP_LEFT: x = mx; y = my; break; - case TOP_RIGHT: + case TOP_RIGHT: /* FALLTHROUGH */ default: - x = scrw - w - my; + x = scrw - w - mx - borderw * 2; y = my; break; case BOTTOM_LEFT: x = mx; - y = scrh - h - my; + y = scrh - h - my - borderw * 2; break; case BOTTOM_RIGHT: - x = scrw - w - mx; - y = scrh - h - my; + x = scrw - w - mx - borderw * 2; + y = scrh - h - my - borderw * 2; break; } @@ -148,16 +189,17 @@ main(int argc, char *argv[]) XSelectInput(dpy, win, ExposureMask | ButtonPress); XMapWindow(dpy, win); - sig.sa_handler = sighandler; - sig.sa_flags = SA_RESTART; - (void)sigfillset(&sig.sa_mask); - if (sigaction(SIGALRM, &sig, NULL) < 0) + sa.sa_handler = sighandler; + sa.sa_flags = SA_RESTART; + (void)sigfillset(&sa.sa_mask); + if (sigaction(SIGALRM, &sa, NULL) < 0) err(1, "sigaction(SIGALRM)"); - if (sigaction(SIGTERM, &sig, NULL) < 0) + if (sigaction(SIGTERM, &sa, NULL) < 0) err(1, "sigaction(SIGTERM)"); - if (sigaction(SIGINT, &sig, NULL) < 0) + if (sigaction(SIGINT, &sa, NULL) < 0) err(1, "sigaction(SIGINT)"); + /* setup lock file */ fl.l_len = 0; fl.l_start = 0; fl.l_type = F_WRLCK; @@ -172,14 +214,16 @@ main(int argc, char *argv[]) XNextEvent(dpy, &ev); if (ev.type == Expose) { XClearWindow(dpy, win); - for (i = argi; i < argc; i++) + for (i = 0; i < nlines; i++) XftDrawStringUtf8(drw, &color, font, - w >> 3, linespace * (i - 1) + th * i + padding, - (FcChar8 *)argv[i], strlen(argv[i])); + padding, + linespace * i + th * (i + 1) + padding, + (XftChar8 *)lines[i].data, lines[i].len); } else if (ev.type == ButtonPress) break; } + free(lines); XftDrawDestroy(drw); XftColorFree(dpy, vis, colormap, &color); XftFontClose(dpy, font);