commit 6ca8386e0b7936572b9e144eeb44033218dbe855
Author: Christos Margiolis <christos@margiolis.net>
Date: Tue, 26 Jan 2021 23:26:37 +0200
initial commit
Diffstat:
A | Makefile | | | 53 | +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | config.h | | | 11 | +++++++++++ |
A | config.mk | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
A | nfy.1 | | | 15 | +++++++++++++++ |
A | nfy.c | | | 152 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 271 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,53 @@
+# See LICENSE file for copyright and license details.
+# nfy - minimal notification program
+.POSIX:
+
+include config.mk
+
+BIN = nfy
+DIST = ${BIN}-${VERSION}
+MAN1 = ${BIN}.1
+
+SRC = nfy.c
+OBJ = ${SRC:.c=.o}
+
+all: options ${BIN}
+
+options:
+ @echo ${BIN} build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+${BIN}: ${OBJ}
+ ${CC} ${LDFLAGS} ${OBJ} -o $@
+
+.c.o:
+ ${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}
+
+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}
+
+uninstall:
+ ${RM} ${DESTDIR}${BIN_DIR}/${BIN}
+ ${RM} ${DESTDIR}${MAN_DIR}/${MAN1}
+
+clean:
+ ${RM} ${BIN} ${OBJ} ${DIST}.tar.gz
+
+.PHONY: all options clean dist install uninstall run
diff --git a/config.h b/config.h
@@ -0,0 +1,11 @@
+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 *fonts = "monospace:size=15"; /* Font */
+static const int borderw = 2; /* Border width */
+static const int duration = 3; /* Notification duration (seconds) */
+static const int pos = TOP_RIGHT; /* Window position */
+static const int mx = 10; /* Margin X */
+static const int my = 25; /* Margin Y */
+static const int maxlen = 250; /* Max window length (pixels) */
diff --git a/config.mk b/config.mk
@@ -0,0 +1,40 @@
+# See LICENSE file for copyright and license details.
+# nfy version
+VERSION = 0
+
+# paths
+PREFIX = /usr/local
+MAN_DIR = ${PREFIX}/share/man/man1
+BIN_DIR = ${PREFIX}/bin
+
+# includes and libs
+#X11INC = /usr/local/include
+#X11LIB = /usr/local/lib
+
+# Linux
+X11INC = /usr/X11R6/include
+X11LIB = /usr/X11R6/lib
+
+FREETYPELIBS = -lfontconfig -lXft
+#FREETYPEINC = ${X11INC}/freetype2
+# Linux
+FREETYPEINC = /usr/include/freetype2
+
+# flags
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \
+ -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\"
+CFLAGS = -std=c99 -pedantic -Wall -Os -I${X11INC} -I${FREETYPEINC} ${CPPFLAGS}
+LDFLAGS = -L${X11LIB} ${FREETYPELIBS} -lX11
+
+# 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
@@ -0,0 +1,15 @@
+.Dd nfy\-VERSRION
+.Dt NFY 1
+.Os
+.Sh NAME
+.Nm nfy
+.Nd a minimal notification program for X
+.Sh SYNOPSIS
+.Nm
+.Ar str...
+.Sh DESCRIPTION
+.Nm
+creates a temporary notification popup window and displays
+all the arguments that were passed to it.
+.Sh AUTHORS
+.An Christos Margiolis Aq Mt christos@christosmarg.xyz
diff --git a/nfy.c b/nfy.c
@@ -0,0 +1,152 @@
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xft/Xft.h>
+
+#include "config.h"
+
+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);
+}
+
+static void
+recvalrm(int sig)
+{
+ 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;
+ struct sigaction sig;
+ 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");
+ scr = DefaultScreen(dpy);
+ 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 = w * (font->descent << 1) + borderw;
+ h = (th + (borderw << 2)) * argc;
+ scrw = DisplayWidth(dpy, scr);
+ scrh = DisplayHeight(dpy, scr);
+
+ 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);
+
+ 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,
+ borderw, (th + (borderw << 2)) * i,
+ (FcChar8 *)argv[i], strlen(argv[i]));
+ } else if (ev.type == ButtonPress)
+ break;
+ }
+
+ XftDrawDestroy(drw);
+ XftColorFree(dpy, vis, colormap, &color);
+ XftFontClose(dpy, font);
+ XCloseDisplay(dpy);
+
+ return 0;
+}