herbe

[fork] notifications
git clone https://hhvn.uk/herbe
git clone git://hhvn.uk/herbe
Log | Files | Refs | README | LICENSE

herbe.c (6397B)


      1 #include <X11/Xlib.h>
      2 #include <X11/Xft/Xft.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <signal.h>
      6 #include <unistd.h>
      7 #include <string.h>
      8 #include <stdarg.h>
      9 #include <limits.h>
     10 #include <errno.h>
     11 #include <fcntl.h>
     12 #include <semaphore.h>
     13 
     14 #ifdef XINERAMA
     15 #include <X11/extensions/Xinerama.h>
     16 #endif /* XINERAMA */
     17 
     18 #define EXIT_ACTION EXIT_SUCCESS
     19 #define EXIT_DISMISS 2
     20 
     21 enum colours { COLOR_FONT, COLOR_BACKGROUND, COLOR_BORDER, COLOR_LAST };
     22 enum corners { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT };
     23 
     24 /* allow references to enum */
     25 #include "config.h"
     26 
     27 struct {
     28 	size_t len;
     29 	size_t size;
     30 	size_t width;
     31 	char **lines;
     32 } body = {0, 0, 0, NULL};
     33 
     34 Display *display = NULL;
     35 XftDraw *draw = NULL;
     36 XftFont *font = NULL;
     37 Visual *visual = NULL;
     38 Colormap colormap = 0;
     39 Window window = 0;
     40 sem_t *mutex = NULL;
     41 XftColor color[COLOR_LAST];
     42 
     43 void
     44 cleanup(void) {
     45 	size_t i;
     46 
     47 	if (mutex) {
     48 		sem_post(mutex);
     49 		sem_close(mutex);
     50 	}
     51 	for (i = 0; i < body.len; i++)
     52 		free(body.lines[i]);
     53 	free(body.lines);
     54 	if (draw)
     55 		XftDrawDestroy(draw);
     56 	if (display) {
     57 		XCloseDisplay(display);
     58 		if (visual)
     59 			for (i = 0; i < COLOR_LAST; i++)
     60 				XftColorFree(display, visual, colormap, &color[i]);
     61 	}
     62 }
     63 
     64 void
     65 die(const char *str) {
     66 	perror(str);
     67 	cleanup();
     68 	exit(EXIT_FAILURE);
     69 }
     70 
     71 size_t
     72 fontlen(char *string) {
     73 	size_t eol, tmp, i;
     74 	XGlyphInfo info;
     75 
     76 	eol = strlen(string);
     77 	XftTextExtentsUtf8(display, font, (FcChar8 *)string, eol, &info);
     78 
     79 	if (info.width > body.width)
     80 	{
     81 		eol = body.width / font->max_advance_width;
     82 		info.width = 0;
     83 
     84 		while (info.width < body.width)
     85 		{
     86 			eol++;
     87 			XftTextExtentsUtf8(display, font, (FcChar8 *)string, eol, &info);
     88 		}
     89 
     90 		eol--;
     91 	}
     92 
     93 	for (i = 0; i < eol; i++)
     94 		if (string[i] == '\n')
     95 		{
     96 			string[i] = ' ';
     97 			return ++i;
     98 		}
     99 
    100 	if (info.width <= body.width)
    101 		return eol;
    102 
    103 	tmp = eol;
    104 
    105 	while (string[eol] != ' ' && eol)
    106 		--eol;
    107 
    108 	if (eol == 0)
    109 		return tmp;
    110 	else
    111 		return ++eol;
    112 }
    113 
    114 void
    115 expire(int sig) {
    116 	XEvent event;
    117 	event.type = ButtonPress;
    118 	event.xbutton.button = (sig == SIGUSR2) ? ACTION_BUTTON : DISMISS_BUTTON;
    119 	XSendEvent(display, window, 0, 0, &event);
    120 	XFlush(display);
    121 }
    122 
    123 void
    124 getpos(Display *dpy, int scr, int h, int *rx, int *ry) {
    125 #ifdef XINERAMA
    126 	XineramaScreenInfo *info;
    127 	XWindowAttributes win;
    128 	Window focus;
    129 	int mx, my, mw, mh;
    130 	int revert;
    131 	int i;
    132 	int n;
    133 
    134 	info = XineramaQueryScreens(dpy, &n);
    135 
    136 	if (info) {
    137 		XGetInputFocus(dpy, &focus, &revert);
    138 		XGetWindowAttributes(dpy, focus, &win);
    139 
    140 		for (i = 0; i < n; i++) {
    141 			if (win.x >= info[i].x_org && win.x <= info[i].x_org + info[i].width &&
    142 					win.y >= info[i].y_org && win.y <= info[i].y_org + info[i].height) {
    143 				mx = info[i].x_org;
    144 				my = info[i].y_org;
    145 				mw = info[i].width;
    146 				mh = info[i].height;
    147 				goto end;
    148 			}
    149 		}
    150 	}
    151 #endif /* XINERAMA */
    152 
    153 	mx = 0;
    154 	my = 0;
    155 	mw = DisplayWidth(dpy, scr);
    156 	mh = DisplayHeight(dpy, scr);
    157 
    158 end:
    159 	if (corner == TOP_RIGHT || corner == BOTTOM_RIGHT)
    160 		*rx = mw - w - border * 2 - x;
    161 	else
    162 		*rx = x;
    163 
    164 	if (corner == BOTTOM_LEFT || corner == BOTTOM_RIGHT)
    165 		*ry = mh - h - border * 2 - y;
    166 	else
    167 		*ry = y;
    168 
    169 	*rx += mx;
    170 	*ry += my;
    171 
    172 	XFree(info);
    173 }
    174 
    175 int
    176 main(int argc, char *argv[]) {
    177 	struct sigaction act_expire, act_ignore;
    178 	int screen;
    179 	int h, th;
    180 	int wx, wy;
    181 	size_t eol;
    182 	size_t i;
    183 	XSetWindowAttributes attributes;
    184 	char spath[NAME_MAX];
    185 
    186 	snprintf(spath, sizeof(spath), "/herbe-%d", getuid());
    187 
    188 	if (argc == 1) {
    189 		sem_unlink(spath);
    190 		fprintf(stderr, "usage: herbe body...\n");
    191 		return EXIT_FAILURE;
    192 	}
    193 
    194 	act_expire.sa_handler = expire;
    195 	act_expire.sa_flags = SA_RESTART;
    196 	sigemptyset(&act_expire.sa_mask);
    197 
    198 	act_ignore.sa_handler = SIG_IGN;
    199 	act_ignore.sa_flags = 0;
    200 	sigemptyset(&act_ignore.sa_mask);
    201 
    202 	sigaction(SIGHUP, &act_expire, 0);
    203 	sigaction(SIGTERM, &act_expire, 0);
    204 	sigaction(SIGINT, &act_expire, 0);
    205 
    206 	sigaction(SIGUSR1, &act_ignore, 0);
    207 	sigaction(SIGUSR2, &act_ignore, 0);
    208 
    209 	if (!(display = XOpenDisplay(NULL)))
    210 		die("XOpenDisplay()");
    211 
    212 	screen = DefaultScreen(display);
    213 	visual = DefaultVisual(display, screen);
    214 	colormap = DefaultColormap(display, screen);
    215 
    216 	XftColorAllocName(display, visual, colormap, font_color, &color[COLOR_FONT]);
    217 	XftColorAllocName(display, visual, colormap, background_color, &color[COLOR_BACKGROUND]);
    218 	XftColorAllocName(display, visual, colormap, border_color, &color[COLOR_BORDER]);
    219 
    220 	attributes.override_redirect = True;
    221 	attributes.background_pixel = color[COLOR_BACKGROUND].pixel;
    222 	attributes.border_pixel = color[COLOR_BORDER].pixel;
    223 
    224 	body.len = 0;
    225 	body.width = w - 2 * pad;
    226 	body.size = 5;
    227 	body.lines = malloc(body.size * sizeof(char *));
    228 	if (!body.lines)
    229 		die("malloc()");
    230 
    231 	font = XftFontOpenName(display, screen, font_pattern);
    232 
    233 	for (i = 1; i < (size_t)argc; i++) {
    234 		for (eol = fontlen(argv[i]); eol; argv[i] += eol, body.len++, eol = fontlen(argv[i])) {
    235 			if (body.size <= body.len) {
    236 				body.lines = realloc(body.lines, (body.size += 5) * sizeof(char *));
    237 				if (!body.lines)
    238 					die("realloc()");
    239 			}
    240 
    241 			body.lines[body.len] = malloc((eol + 1) * sizeof(char));
    242 			if (!body.lines[body.len])
    243 				die("malloc()");
    244 
    245 			strncpy(body.lines[body.len], argv[i], eol);
    246 			body.lines[body.len][eol] = '\0';
    247 		}
    248 	}
    249 
    250 	th = font->ascent - font->descent;
    251 	h = (body.len - 1) * lpad + body.len * th + 2 * pad;
    252 
    253 	getpos(display, screen, h, &wx, &wy);
    254 
    255 	window = XCreateWindow(display, RootWindow(display, screen), wx, wy, w, h, border, DefaultDepth(display, screen),
    256 						   CopyFromParent, visual, CWOverrideRedirect | CWBackPixel | CWBorderPixel, &attributes);
    257 
    258 	draw = XftDrawCreate(display, window, visual, colormap);
    259 
    260 	XSelectInput(display, window, ExposureMask | ButtonPress);
    261 	XMapWindow(display, window);
    262 
    263 	if ((mutex = sem_open(spath, O_CREAT, 0644, 1)) == SEM_FAILED)
    264 		die("sem_open()");
    265 	sem_wait(mutex);
    266 
    267 	sigaction(SIGUSR1, &act_expire, 0);
    268 	sigaction(SIGUSR2, &act_expire, 0);
    269 
    270 	if (duration <= 0)
    271 		alarm(duration);
    272 
    273 	for (;;) {
    274 		XEvent event;
    275 		XNextEvent(display, &event);
    276 
    277 		if (event.type == Expose) {
    278 			XClearWindow(display, window);
    279 			for (i = 0; i < body.len; i++)
    280 				XftDrawStringUtf8(draw, &color[COLOR_FONT], font, pad, lpad * i + th * (i + 1) + pad,
    281 								  (FcChar8 *)body.lines[i], strlen(body.lines[i]));
    282 		} else if (event.type == ButtonPress) {
    283 			switch (event.xbutton.button) {
    284 			case DISMISS_BUTTON:
    285 				cleanup();
    286 				return EXIT_DISMISS;
    287 			case ACTION_BUTTON:
    288 				cleanup();
    289 				return EXIT_ACTION;
    290 			}
    291 		}
    292 	}
    293 }