commit 3dd4fe5416c2f9050723fc1c0ebf50f9e0a77094
parent eb435af87ca87831003675138764932e9aa2fd12
Author: Christos Margiolis <christos@margiolis.net>
Date: Wed, 28 Apr 2021 21:02:37 +0300
added queuing using a lockfile
Diffstat:
M | LICENSE | | | 43 | ++++++++++++++++--------------------------- |
M | Makefile | | | 6 | +++--- |
M | README | | | 2 | +- |
M | config.h | | | 24 | +++++++++++++----------- |
M | nfy.c | | | 309 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
5 files changed, 198 insertions(+), 186 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -1,31 +1,20 @@
-BSD 3-Clause License
+MIT License
-Copyright (c) 2021-present, Christos Margiolis.
-All rights reserved.
+(c) 2021-Present Christos Margiolis <christos@christosmarg.xyz>
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+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:
-* Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
-THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
-NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+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/Makefile b/Makefile
@@ -15,9 +15,9 @@ all: options ${BIN}
options:
@echo ${BIN} build options:
- @echo "CFLAGS = ${CFLAGS}"
- @echo "LDFLAGS = ${LDFLAGS}"
- @echo "CC = ${CC}"
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
${BIN}: ${OBJ}
${CC} ${LDFLAGS} ${OBJ} -o $@
diff --git a/README b/README
@@ -18,7 +18,7 @@ setup and preferences.
# make install clean
$ nfy str...
-nfy will be installed in /usr/local/bin by default.
+nfy will be installed in `/usr/local/bin` by default.
Configuration is done by editing config.h and recompiling
the source code.
diff --git a/config.h b/config.h
@@ -1,14 +1,16 @@
/* See LICENSE file for copyright and license details. */
enum { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; /* Window positions */
-static const char *bgcol = "#453588"; /* Background color */
-static const char *bordercol = "#282828"; /* Border color */
-static const char *fontcol = "#ebdbb2"; /* Font color */
+static const char *bgcolor = "#201f1c"; /* Background color */
+static const char *bordercolor = "#f28f19"; /* 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 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 *lockfile = "/tmp/nfy.lock";
diff --git a/nfy.c b/nfy.c
@@ -1,4 +1,6 @@
/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
@@ -12,164 +14,183 @@
#include "config.h"
+static void die(const char *, ...);
+static void recvalrm(int);
+
+static char *argv0;
static Display *dpy;
static Window win;
static void
die(const char *fmt, ...)
{
- va_list args;
-
- 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);
+ 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)
{
- XEvent ev;
-
- ev.type = ButtonPress;
- XSendEvent(dpy, win, 0, 0, &ev);
- XFlush(dpy);
+ XEvent ev;
+
+ ev.type = ButtonPress;
+ XSendEvent(dpy, win, 0, 0, &ev);
+ XFlush(dpy);
}
int
main(int argc, char *argv[])
{
- Visual *vis;
- Colormap colormap;
- XEvent ev;
- XSetWindowAttributes attrs;
- XftColor color;
- XftFont *font;
- XftDraw *drw;
- XRRScreenResources *screens;
- XRRCrtcInfo *info = NULL;
- struct sigaction sig;
- pid_t pid;
- int scr, scrw, scrh;
- int x, y, w, h, th;
- int i, j, len;
-
- if (argc < 2)
- die("usage: %s str...", argv[0]);
-
- if (!(dpy = XOpenDisplay(NULL)))
- die("cannot open display");
-
- pid = fork();
- if (pid < 0)
- die("fork:");
- if (pid > 0)
- exit(EXIT_SUCCESS);
- umask(0);
- if (setsid() < 0)
- die("setsid:");
- close(STDIN_FILENO);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
-
- scr = DefaultScreen(dpy);
- screens = XRRGetScreenResources(dpy, RootWindow(dpy, scr));
- info = XRRGetCrtcInfo(dpy, screens, screens->crtcs[0]);
- scrw = info->width;
- scrh = info->height;
-
- vis = DefaultVisual(dpy, scr);
- colormap = DefaultColormap(dpy, scr);
- XftColorAllocName(dpy, vis, colormap, bgcol, &color);
- attrs.background_pixel = color.pixel;
- XftColorAllocName(dpy, vis, colormap, bordercol, &color);
- attrs.border_pixel = color.pixel;
- attrs.override_redirect = True;
-
- font = XftFontOpenName(dpy, scr, fonts);
- th = font->ascent - font->descent;
-
- w = 0;
- for (i = 1; i < argc; i++) {
- len = strlen(argv[i]);
- for (j = 0; j < len; j++)
- if (argv[i][j] == '\n')
- argv[i][j] = ' ';
- /* TODO: handle maxlen */
- if (len > w)
- w = len;
- }
-
- w *= th + borderw;
- h = th * (argc - 1) + (linespace * (argc - 2)) + (padding << 1);
-
- switch (pos) {
- case TOP_LEFT:
- x = mx;
- y = my;
- break;
- case TOP_RIGHT:
- default:
- x = scrw - w - my;
- y = my;
- break;
- case BOTTOM_LEFT:
- x = mx;
- y = scrh - h - my;
- break;
- case BOTTOM_RIGHT:
- x = scrw - w - mx;
- y = scrh - h - my;
- break;
- }
-
- win = XCreateWindow(dpy, RootWindow(dpy, scr), x, y, w, h, borderw,
- DefaultDepth(dpy, scr), CopyFromParent, vis,
- CWOverrideRedirect | CWBackPixel | CWBorderPixel, &attrs);
-
- drw = XftDrawCreate(dpy, win, vis, colormap);
- XftColorAllocName(dpy, vis, colormap, fontcol, &color);
-
- XSelectInput(dpy, win, ExposureMask | ButtonPress);
- XMapWindow(dpy, win);
-
- sig.sa_handler = recvalrm;
- 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? */
-
- if (duration > 0)
- alarm(duration);
-
- for (;;) {
- XNextEvent(dpy, &ev);
-
- if (ev.type == Expose) {
- XClearWindow(dpy, win);
- for (i = 1; i < argc; i++)
- XftDrawStringUtf8(drw, &color, font,
- w >> 3, linespace * (i - 1) + th * i + padding,
- (FcChar8 *)argv[i], strlen(argv[i]));
- } else if (ev.type == ButtonPress)
- break;
- }
-
- XftDrawDestroy(drw);
- XftColorFree(dpy, vis, colormap, &color);
- XftFontClose(dpy, font);
- XRRFreeCrtcInfo(info);
- XRRFreeScreenResources(screens);
- XCloseDisplay(dpy);
-
- return 0;
+ Colormap colormap;
+ Visual *vis;
+ XEvent ev;
+ XRRCrtcInfo *info = NULL;
+ XRRScreenResources *screens;
+ XSetWindowAttributes attrs;
+ XftColor color;
+ XftDraw *drw;
+ XftFont *font;
+ struct sigaction sig;
+ pid_t pid;
+ int lockfd;
+ int scr, scrw, scrh;
+ int x, y, w, h, th;
+ int i, j, len;
+
+ argv0 = *argv;
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s str...\n", argv0);
+ return 1;
+ }
+
+ if ((lockfd = open(lockfile, O_CREAT | O_RDWR, 0600)) == -1)
+ die("open:");
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("XOpenDisplay:");
+
+ if ((pid = fork()) < 0)
+ die("fork:");
+ if (pid > 0)
+ exit(EXIT_SUCCESS);
+ umask(0);
+ if (setsid() < 0)
+ die("setsid:");
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ scr = DefaultScreen(dpy);
+ screens = XRRGetScreenResources(dpy, RootWindow(dpy, scr));
+ info = XRRGetCrtcInfo(dpy, screens, screens->crtcs[0]);
+ scrw = info->width;
+ scrh = info->height;
+
+ vis = DefaultVisual(dpy, scr);
+ colormap = DefaultColormap(dpy, scr);
+ XftColorAllocName(dpy, vis, colormap, bgcolor, &color);
+ attrs.background_pixel = color.pixel;
+ XftColorAllocName(dpy, vis, colormap, bordercolor, &color);
+ attrs.border_pixel = color.pixel;
+ attrs.override_redirect = True;
+
+ font = XftFontOpenName(dpy, scr, fonts);
+ th = font->ascent - font->descent;
+
+ w = 0;
+ for (i = 1; 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);
+
+ switch (pos) {
+ case TOP_LEFT:
+ x = mx;
+ y = my;
+ break;
+ case TOP_RIGHT:
+ default:
+ x = scrw - w - my;
+ y = my;
+ break;
+ case BOTTOM_LEFT:
+ x = mx;
+ y = scrh - h - my;
+ break;
+ case BOTTOM_RIGHT:
+ x = scrw - w - mx;
+ y = scrh - h - my;
+ break;
+ }
+
+ win = XCreateWindow(dpy, RootWindow(dpy, scr), x, y, w, h, borderw,
+ DefaultDepth(dpy, scr), CopyFromParent, vis,
+ CWOverrideRedirect | CWBackPixel | CWBorderPixel, &attrs);
+
+ drw = XftDrawCreate(dpy, win, vis, colormap);
+ XftColorAllocName(dpy, vis, colormap, fontcolor, &color);
+
+ XSelectInput(dpy, win, ExposureMask | ButtonPress);
+ XMapWindow(dpy, win);
+
+ sig.sa_handler = recvalrm;
+ 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)
+ ;
+ if (duration > 0)
+ (void)alarm(duration);
+
+ for (;;) {
+ XNextEvent(dpy, &ev);
+
+ if (ev.type == Expose) {
+ XClearWindow(dpy, win);
+ for (i = 1; i < argc; i++)
+ XftDrawStringUtf8(drw, &color, font,
+ w >> 3, linespace * (i - 1) + th * i + padding,
+ (FcChar8 *)argv[i], strlen(argv[i]));
+ } else if (ev.type == ButtonPress)
+ break;
+ }
+
+ XftDrawDestroy(drw);
+ XftColorFree(dpy, vis, colormap, &color);
+ XftFontClose(dpy, font);
+ XRRFreeCrtcInfo(info);
+ XRRFreeScreenResources(screens);
+ XCloseDisplay(dpy);
+
+ (void)lockf(lockfd, F_ULOCK, 0);
+ (void)close(lockfd);
+ (void)unlink(lockfile);
+
+ return 0;
}