nfy

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

nfy.c (5472B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <err.h>
      3 #include <errno.h>
      4 #include <fcntl.h>
      5 #include <signal.h>
      6 #include <stdarg.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <unistd.h>
     11 
     12 #include <X11/Xlib.h>
     13 #include <X11/Xft/Xft.h>
     14 #include <X11/extensions/Xrandr.h>
     15 
     16 #include "config.h"
     17 
     18 struct line {
     19 	char data[MAXLEN+1];
     20 	size_t len;
     21 };
     22 
     23 #undef strlcpy	/* compat with openbsd */
     24 size_t strlcpy(char *, const char *, size_t);
     25 static void sighandler(int);
     26 
     27 static char *argv0;
     28 static Display *dpy;
     29 static Window win;
     30 
     31 size_t
     32 strlcpy(char *dst, const char *src, size_t dsize)
     33 {
     34 	const char *osrc = src;
     35 	size_t nleft = dsize;
     36 
     37 	if (nleft != 0) {
     38 		while (--nleft != 0)
     39 			if ((*dst++ = *src++) == '\0')
     40 				break;
     41 	}
     42 	if (nleft == 0) {
     43 		if (dsize != 0)
     44 			*dst = '\0';
     45 		while (*src++)
     46 			;
     47 	}
     48 	return (src - osrc - 1);
     49 }
     50 
     51 static void
     52 sighandler(int sig)
     53 {
     54 	XEvent ev;
     55 	
     56 	ev.type = ButtonPress;
     57 	XSendEvent(dpy, win, 0, 0, &ev);
     58 	XFlush(dpy);
     59 }
     60 
     61 static void
     62 usage(void)
     63 {
     64 	fprintf(stderr, "usage: %s [-d duration] [-v]\n", argv0);
     65 	exit(1);
     66 }
     67 
     68 int
     69 main(int argc, char *argv[])
     70 {
     71 	Colormap colormap;
     72 	Visual *vis;
     73 	XEvent ev;
     74 	XRRCrtcInfo *info = NULL;
     75 	XRRScreenResources *screens;
     76 	XSetWindowAttributes attrs = {0};
     77 	XftColor color;
     78 	XftDraw *drw;
     79 	XftFont *font;
     80 	XGlyphInfo gi;
     81 	struct flock fl;
     82 	struct sigaction sa;
     83 	struct line *lines, *lp;
     84 	pid_t pid;
     85 	int lockfd;
     86 	int scr, scrw, scrh;
     87 	int x, y, w = 0, h, th;
     88 	int i, len = 0, nlines = 0;
     89 	char buf[MAXLEN+1], ch;
     90 
     91 	argv0 = *argv;
     92 	while ((ch = getopt(argc, argv, "d:v")) != -1) {
     93 		switch (ch) {
     94 		case 'd':
     95 			duration = strtol(optarg, NULL, 10);
     96 			if (errno == EINVAL || errno == ERANGE)
     97 				err(1, "strtol(%s)", optarg);
     98 			if (duration < 1)
     99 				errx(1, "duration has to be more than 1 second");
    100 			break;
    101 		case 'v':
    102 			fprintf(stderr, "%s-"VERSION"\n", argv0);
    103 			exit(1);
    104 		case '?':
    105 		default:
    106 			usage();
    107 		}
    108 	}
    109 	argc -= optind;
    110 	argv += optind;
    111 
    112 	if (isatty(STDIN_FILENO))
    113 		errx(1, "stdin is empty");
    114 
    115 	/* detach from tty */
    116 	if ((pid = fork()) < 0)
    117 		err(1, "fork");
    118 	if (pid > 0)
    119 		_exit(0);
    120 	umask(0);
    121 	if (setsid() < 0)
    122 		err(1, "setsid");
    123 
    124 	if ((lockfd = open(lockfile, O_CREAT | O_WRONLY, 0600)) < 0)
    125 		err(1, "open(%s)", lockfile);
    126 
    127 	/* init x11 */
    128 	if (!(dpy = XOpenDisplay(NULL)))
    129 		errx(1, "XOpenDisplay");
    130 
    131 	scr = DefaultScreen(dpy);
    132 	screens = XRRGetScreenResources(dpy, RootWindow(dpy, scr));
    133 	info = XRRGetCrtcInfo(dpy, screens, screens->crtcs[0]);
    134 	scrw = info->width;
    135 	scrh = info->height;
    136 
    137 	vis = DefaultVisual(dpy, scr);
    138 	colormap = DefaultColormap(dpy, scr);
    139 	XftColorAllocName(dpy, vis, colormap, bgcolor, &color);
    140 	attrs.background_pixel = color.pixel;
    141 	XftColorAllocName(dpy, vis, colormap, bordercolor, &color);
    142 	attrs.border_pixel = color.pixel;
    143 	attrs.override_redirect = True;
    144 
    145 	font = XftFontOpenName(dpy, scr, fonts);
    146 	th = font->ascent - font->descent;
    147 	XftColorAllocName(dpy, vis, colormap, fontcolor, &color);
    148 
    149 	/* read stdin into buffer */
    150 	if ((lines = malloc(sizeof(struct line))) == NULL)
    151 		err(1, "malloc");
    152 	while (read(STDIN_FILENO, &ch, 1) > 0) {
    153 		buf[len++] = ch;
    154 		if (ch == '\n' || len == MAXLEN) {
    155 			/* FIXME: is this stupid? */
    156 			if (ch == '\n')
    157 				len--;
    158 			buf[len] = '\0';
    159 			if ((lines = realloc(lines,
    160 			    (nlines + 1) * sizeof(struct line))) == NULL)
    161 				err(1, "realloc");
    162 			lp = &lines[nlines];
    163 			lp->len = len;
    164 			strlcpy(lp->data, buf, sizeof(lp->data));
    165 			/* determine window width based on largest line */
    166 			XftTextExtentsUtf8(dpy, font, (XftChar8 *)lp->data,
    167 			    lp->len, &gi);
    168 			if (gi.width > w)
    169 				w = gi.width;
    170 			nlines++;
    171 			len = 0;
    172 		}
    173 	}
    174 	w += padding * 2;
    175 	h = (nlines - 1) * linespace + nlines * th + 2 * padding;
    176 
    177 	/* calculate position coordinates */
    178 	switch (pos) {
    179 	case TOP_LEFT:
    180 		x = mx;
    181 		y = my;
    182 		break;
    183 	case TOP_RIGHT: /* FALLTHROUGH */
    184 	default:
    185 		x = scrw - w - mx - borderw * 2;
    186 		y = my;
    187 		break;
    188 	case BOTTOM_LEFT:
    189 		x = mx;
    190 		y = scrh - h - my - borderw * 2;
    191 		break;
    192 	case BOTTOM_RIGHT:
    193 		x = scrw - w - mx - borderw * 2;
    194 		y = scrh - h - my - borderw * 2;
    195 		break;
    196 	}
    197 	
    198 	win = XCreateWindow(dpy, RootWindow(dpy, scr), x, y, w, h, borderw,
    199 	    DefaultDepth(dpy, scr), CopyFromParent, vis,
    200 	    CWOverrideRedirect | CWBackPixel | CWBorderPixel, &attrs);
    201 	drw = XftDrawCreate(dpy, win, vis, colormap);
    202 	XSelectInput(dpy, win, ExposureMask | ButtonPress);
    203 	XMapWindow(dpy, win);
    204 
    205 	sa.sa_handler = sighandler;
    206 	sa.sa_flags = SA_RESTART;
    207 	(void)sigfillset(&sa.sa_mask);
    208 	if (sigaction(SIGALRM, &sa, NULL) < 0)
    209 		err(1, "sigaction(SIGALRM)");
    210 	if (sigaction(SIGTERM, &sa, NULL) < 0)
    211 		err(1, "sigaction(SIGTERM)");
    212 	if (sigaction(SIGINT, &sa, NULL) < 0)
    213 		err(1, "sigaction(SIGINT)");
    214 
    215 	/* setup lock file */
    216 	fl.l_len = 0;
    217 	fl.l_start = 0;
    218 	fl.l_type = F_WRLCK;
    219 	fl.l_whence = SEEK_SET;
    220 	if (fcntl(lockfd, F_SETLKW, &fl) < 0)
    221 		err(1, "fcntl(F_SETLKW)");
    222 
    223 	if (duration > 0)
    224 		(void)alarm(duration);
    225 
    226 	for (;;) {
    227 		XNextEvent(dpy, &ev);
    228 		if (ev.type == Expose) {
    229 			XClearWindow(dpy, win);
    230 			for (i = 0; i < nlines; i++)
    231 				XftDrawStringUtf8(drw, &color, font,
    232 				    padding,
    233 				    linespace * i + th * (i + 1) + padding,
    234 				    (XftChar8 *)lines[i].data, lines[i].len);
    235 		} else if (ev.type == ButtonPress)
    236 			break;
    237 	}
    238 
    239 	free(lines);
    240 	XftDrawDestroy(drw);
    241 	XftColorFree(dpy, vis, colormap, &color);
    242 	XftFontClose(dpy, font);
    243 	XRRFreeCrtcInfo(info);
    244 	XRRFreeScreenResources(screens);
    245 	XCloseDisplay(dpy);
    246 	(void)close(lockfd);
    247 
    248 	return (0);
    249 }