sxalert

Simple notification system for X
git clone git://mcdim.xyz/sxalert.git
Log | Files | Refs | README | LICENSE

sxalert.c (4871B)


      1 /* Simple X Alert v0.1
      2  * Simple notification program for X
      3  * GNU GPL v3.0+
      4  *
      5  * sxalert.c - main program
      6  */
      7 
      8 #include <stdlib.h>
      9 #include <stdio.h>
     10 #include <unistd.h>
     11 #include <string.h>
     12 #include <stdbool.h>
     13 #include <getopt.h>
     14 #include <time.h>
     15 #include <poll.h>
     16 #include <X11/Xft/Xft.h>
     17 
     18 #include "config.h"
     19 
     20 #define VERSION "v0.1"
     21 
     22 static void
     23 die(const char *msg, int code)
     24 {
     25 	fprintf(stderr, "%s\n", msg);
     26 	exit(code);
     27 }
     28 
     29 static void
     30 help(char *bin)
     31 {
     32 	printf("sxalert %s - Alert utility for X\n\n", VERSION);
     33 	printf("	-t text hex color (eg FFFFFF)\n");
     34 	printf("	-g background hex color\n");
     35 	printf("	-r border hex color\n");
     36 	printf("	-b border width in pixels\n");
     37 	printf("	-d duration in milliseconds\n");
     38 	printf("	-v print version & exit\n\n");
     39 	printf("	-h print help panel & exit\n\n");
     40 	printf("Usage: %s [arguments] \"text\" \"text\" \"text\"\n", bin);
     41 	exit(0);
     42 }
     43 
     44 static int
     45 hex2int(char *str)
     46 {
     47 	int num;
     48 	sscanf(str, "%x", &num);
     49 	return num;
     50 }
     51 
     52 static char *
     53 convert_text_color_code(void) /* adds a '#' before the color hex, as XftColorAllocName requires it */
     54 {
     55 	char* text_color_pnd = (char*)malloc(8);
     56 	strncpy(text_color_pnd+1, text_color, 6);
     57 	text_color_pnd[0] = '#';
     58 	text_color_pnd[7] = '\0';
     59 	return text_color_pnd;
     60 }
     61 
     62 int
     63 get_max(int arr[], int len)
     64 {
     65 	int max = -1;
     66 	for (int i=0; i<len; i++) {
     67 		if (arr[i] > max)
     68 			max = arr[i];
     69 	}
     70 
     71 	return max;
     72 }
     73 
     74 static int
     75 get_width(Display *dpy, XftFont *font, char **lines, int length)
     76 {
     77 	XGlyphInfo ext;
     78 	int width = 0;
     79 	if (length > 0) {
     80 		int width_lines[length];
     81 		int j = 0;
     82 		for (int i = 0; i < length; i++) {
     83 			XftTextExtentsUtf8(dpy, font, (XftChar8*)lines[i], strlen(lines[i]), &ext);
     84     			width_lines[i] = ext.xOff;
     85 			j++;
     86 		}
     87 
     88 		int text_width = get_max(width_lines, length);
     89 		width = (text_x_padding * 2) + text_width;
     90 	}
     91 
     92 	if (width < min_width)
     93 		return min_width;
     94 	else if (width > max_width)
     95 		return max_width;
     96 	else
     97 		return width;
     98 }
     99 
    100 static void
    101 write_text(Display *dpy, Window w, XftDraw *draw, XftColor color, XftFont *font, int text_height, char **lines, int length)
    102 {
    103 	while (1) {
    104     		struct pollfd pfd = {
    105         		.fd = ConnectionNumber(dpy),
    106         		.events = POLLIN,
    107     		};
    108 		int pending = XPending(dpy) > 0 || poll(&pfd, 1, duration) > 0;
    109 
    110 		if (!pending)
    111 			break;
    112 
    113 		XEvent ev;
    114 		XSelectInput(dpy, w, ExposureMask | ButtonPressMask);
    115 		XNextEvent(dpy, &ev);
    116 
    117 		if (ev.type == Expose) {
    118 		    	int spacing = text_height * 2;
    119 		    	for (int i = 0; i < length; i++) {
    120 				if (length != 0)
    121 		    			XftDrawStringUtf8(draw, &color, font, text_x_padding, spacing, (XftChar8 *)lines[i], strlen(lines[i]));
    122 		    		spacing += text_height * 2;
    123 			}
    124 		} else if (ev.type == ButtonPress) {
    125 			return;
    126 		}
    127 	}
    128 }
    129 
    130 static void
    131 draw(int border, int duration, char **lines, int length)
    132 {
    133 	char text_color_pnd[8];
    134 	strncpy(text_color_pnd, convert_text_color_code(), 8);
    135 
    136 	Display *dpy = XOpenDisplay(NULL);
    137 	if (!dpy)
    138 		die("Cannot open X11 display\n", EXIT_FAILURE);
    139 	int scr = DefaultScreen(dpy);
    140 	Visual *visual = DefaultVisual(dpy, scr);
    141 	Colormap cmap = DefaultColormap(dpy, scr);
    142 
    143 	XftColor color;
    144 	XGlyphInfo extents;
    145 	XftFont *font = XftFontOpenName(dpy, scr, fontname);
    146 	if (!font)
    147 		die("Cannot load font\n", EXIT_FAILURE);
    148 	if (!XftColorAllocName(dpy, visual, cmap, text_color_pnd, &color))
    149 		die("Cannot allocate Xft color\n", EXIT_FAILURE);
    150 	
    151 	int width = get_width(dpy, font, lines, length);
    152 	int height = length * (text_height * 2) + text_height;
    153 
    154 	int count_screens = ScreenCount(dpy);
    155         Screen *screen = ScreenOfDisplay(dpy, 0);
    156 	int x = screen->width - width - x_offset;
    157 
    158 	Window win = XCreateSimpleWindow(dpy, RootWindow(dpy, scr), x, y_offset, width, height, border, hex2int(border_color), hex2int(bg_color));
    159 	XSetWindowAttributes attributes;
    160 	attributes.override_redirect = True;
    161 	XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attributes);
    162 	XSelectInput(dpy, win, ExposureMask | KeyPressMask);
    163 	XMapWindow(dpy, win);
    164 
    165 	XftDraw *draw = XftDrawCreate(dpy, win, visual, cmap);
    166 
    167 	write_text(dpy, win, draw, color, font, text_height, lines, length);
    168 
    169 	XftColorFree(dpy, visual, cmap, &color);
    170 	XftDrawDestroy(draw);
    171 	XDestroyWindow(dpy, win);
    172 	XCloseDisplay(dpy);
    173 }
    174 
    175 int
    176 main(int argc, char **argv)
    177 {
    178 	int c, s = 3;
    179 	char text[BUFFER];
    180 	extern char *optarg;
    181 
    182 	while ((c = getopt(argc, argv, "d:b:t:g:r:vh")) != -1 ) {
    183 		switch (c) {
    184 		case 'v':
    185 			die(VERSION, EXIT_SUCCESS);
    186 		case 'h':
    187 			help(argv[0]);
    188 		case 'd':
    189 			duration=atoi(optarg);
    190 			break;
    191 		case 'b':
    192 			border_width=atoi(optarg);
    193 			break;
    194 		case 't':
    195 			strncpy(text_color, optarg, 7);
    196 			break;
    197 		case 'g':
    198 			strncpy(bg_color, optarg, 7);
    199 			break;
    200 		case 'r':
    201 			strncpy(border_color, optarg, 7);
    202 			break;
    203 		}
    204 	}
    205 
    206 	int lines_len=argc-optind;
    207 	char **lines = argv + optind; /* get lines to print */
    208 	draw(border_width, duration, lines, lines_len);
    209 
    210 	return 0;
    211 }