sxalert.c (6563B)
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 #include <X11/extensions/Xrandr.h> 18 19 #include "config.h" 20 21 #define VERSION "v0.2" 22 23 static inline void 24 die(const char *msg, int code) 25 { 26 fprintf(stderr, "%s\n", msg); 27 exit(code); 28 } 29 30 static inline void 31 help(char *bin) 32 { 33 printf("sxalert %s - Alert utility for X\n\n" 34 " -t text hex color (eg FFFFFF)\n" 35 " -g background hex color\n" 36 " -r border hex color\n" 37 " -b border width in pixels\n" 38 " -d duration in milliseconds\n" 39 " -s read from stdin\n\n" 40 " -v print version & exit\n" 41 " -h print help panel & exit\n\n" 42 "Usage: %s [arguments] \"text\" \"text\" \"text\"\n", 43 VERSION, bin); 44 exit(0); 45 } 46 47 static int 48 hex2int(char *str) 49 { 50 int num; 51 sscanf(str, "%x", &num); 52 return num; 53 } 54 55 int 56 get_max(int arr[], int len) 57 { 58 int max = -1; 59 for (int i=0; i<len; i++) { 60 if (arr[i] > max) 61 max = arr[i]; 62 } 63 64 return max; 65 } 66 67 static int 68 get_width(Display *dpy, XftFont *font, struct alert *a) 69 { 70 XGlyphInfo ext; 71 int width = 0; 72 if (a->lines_len > 0) { 73 int width_lines[a->lines_len]; 74 int j = 0; 75 for (int i = 0; i < a->lines_len; i++) { 76 XftTextExtentsUtf8(dpy, font, 77 (XftChar8*)a->lines[i], 78 strlen(a->lines[i]), 79 &ext); 80 width_lines[i] = ext.xOff; 81 j++; 82 } 83 84 int text_width = get_max(width_lines, a->lines_len); 85 width = (a->text_x_padding * 2) + text_width; 86 } 87 88 if (width < a->min_width) 89 return a->min_width; 90 else if (width > a->max_width) 91 return a->max_width; 92 return width; 93 } 94 95 static int 96 find_window(struct xwin *windows, int count, Window id) 97 { 98 for (int i = 0; i < count; ++i) { 99 if (windows[i].win == id) 100 return i; 101 } 102 103 return -1; 104 } 105 106 static void 107 write_text(Display *dpy, struct xwin *windows, int count, 108 XftColor color, XftFont *font, struct alert *a) 109 { 110 while (1) { 111 struct pollfd pfd = { 112 .fd = ConnectionNumber(dpy), 113 .events = POLLIN, 114 }; 115 116 int pending = XPending(dpy) > 0 || 117 poll(&pfd, 1, a->duration) > 0; 118 119 if (!pending) 120 break; 121 122 XEvent ev; 123 XNextEvent(dpy, &ev); 124 125 int i = find_window(windows, count, ev.xany.window); 126 127 if (i < 0) 128 continue; 129 130 if (ev.type == Expose) { 131 int spacing = a->text_height * 2; 132 133 for (int line = 0; line < a->lines_len; ++line) { 134 XftDrawStringUtf8(windows[i].draw, &color, 135 font, a->text_x_padding, spacing, 136 (XftChar8 *)a->lines[line], 137 (int)strlen(a->lines[line]) 138 ); 139 140 spacing += a->text_height * 2; 141 } 142 } else if (ev.type == ButtonPress) { 143 switch (ev.xbutton.button) { 144 case Button1: /* left click */ 145 exit(0); 146 break; 147 case Button2: /* middle click */ 148 exit(1); 149 break; 150 case Button3: /* right click */ 151 exit(2); 152 break; 153 } 154 } 155 } 156 } 157 158 159 static void 160 init_render(Display *dpy, struct xrender *r, struct alert *a) 161 { 162 char text_color_pnd[8]; 163 snprintf(text_color_pnd, 8, "#%s", a->text_color); 164 165 r->scr = DefaultScreen(dpy); 166 r->visual = DefaultVisual(dpy, r->scr); 167 r->cmap = DefaultColormap(dpy, r->scr); 168 169 r->font = XftFontOpenName(dpy, r->scr, a->font); 170 if (!r->font) 171 die("Cannot load font\n", -1); 172 if (!XftColorAllocName(dpy, r->visual, r->cmap, 173 text_color_pnd, &(r->color))) 174 die("Cannot allocate Xft color\n", -1); 175 } 176 177 static void 178 init_window(Display *dpy, struct xrender *r, struct xwin *w, 179 struct alert *a, XRRMonitorInfo * monitor) 180 { 181 w->width = get_width(dpy, r->font, a); 182 w->height = a->lines_len * (a->text_height * 2) 183 + a->text_height; 184 185 Screen *screen = ScreenOfDisplay(dpy, 0); 186 w->x = monitor->x + monitor->width - w->width - a->x_offset; 187 w->y = monitor->y + a->y_offset; 188 189 190 w->win = XCreateSimpleWindow(dpy, RootWindow(dpy, r->scr), w->x, 191 a->y_offset, w->width, w->height, 192 a->border_width, 193 hex2int(a->border_color), 194 hex2int(a->bg_color)); 195 XSetWindowAttributes attr = { 196 .override_redirect = True 197 }; 198 XChangeWindowAttributes(dpy, w->win, CWOverrideRedirect, &attr); 199 XSelectInput(dpy, w->win, ExposureMask | KeyPressMask); 200 XMapWindow(dpy, w->win); 201 202 w->draw = XftDrawCreate(dpy, w->win, r->visual, r->cmap); 203 204 XSelectInput(dpy, w->win, ExposureMask | ButtonPressMask); 205 } 206 207 static void 208 draw(struct alert a) 209 { 210 Display *dpy = XOpenDisplay(NULL); 211 if (!dpy) 212 die("Cannot open X11 display\n", -1); 213 214 struct xrender r; 215 init_render(dpy, &r, &a); 216 217 struct mon m; 218 m.monitor_count = 1; 219 m.monitors = XRRGetMonitors(dpy, RootWindow(dpy, r.scr), 220 True, &m.monitor_count); 221 222 struct xwin *w = calloc(m.monitor_count, sizeof(struct xwin)); 223 if (!w) 224 die("Cannot allocate window array\n", -1); 225 226 for (int i = 0; i < m.monitor_count; i++) { 227 init_window(dpy, &r, &w[i], &a, &m.monitors[i]); 228 229 } 230 231 write_text(dpy, w, m.monitor_count, r.color, r.font, &a); 232 } 233 234 void 235 read_stdin(struct alert *a, FILE *f) 236 { 237 int buffer = 264; 238 char *p = malloc(buffer); 239 240 int i = 0; 241 while (fgets(p, buffer, f) != NULL) { 242 for (int j=0; j<264; j++) { 243 if (p[j] == '\n') 244 p[j] = '\0'; 245 } 246 247 a->lines = realloc(a->lines, (i + 1)*sizeof(*a->lines)); 248 if (a->lines == NULL) { 249 die("Cannot allocate memory\n", -1); 250 } 251 a->lines[i] = calloc(buffer, 1); 252 strncpy(a->lines[i], p, buffer); 253 i++; 254 } 255 a->lines_len = i; 256 free(p); 257 } 258 259 260 int 261 main(int argc, char **argv) 262 { 263 int c = 3; 264 extern char *optarg; 265 266 struct alert a = default_alert; 267 268 int s = 0; 269 while ((c = getopt(argc, argv, "d:b:t:g:r:svh")) != -1 ) { 270 switch (c) { 271 case 'v': 272 die(VERSION, EXIT_SUCCESS); 273 case 'h': 274 help(argv[0]); 275 case 'd': 276 a.duration=atoi(optarg); 277 if (a.duration == 0) 278 a.duration = -1; 279 break; 280 case 'b': 281 a.border_width=atoi(optarg); 282 break; 283 case 't': 284 strncpy(a.text_color, optarg, 7); 285 break; 286 case 'g': 287 strncpy(a.bg_color, optarg, 7); 288 break; 289 case 'r': 290 strncpy(a.border_color, optarg, 7); 291 break; 292 case 's': 293 s = 1; 294 break; 295 } 296 } 297 298 int cmd_lines_len = argc - optind; 299 char **lines = argv + optind; 300 301 if (!s) { 302 a.lines = lines; 303 a.lines_len = cmd_lines_len; 304 draw(a); 305 return 4; 306 } 307 308 FILE *f = tmpfile(); 309 int cc; 310 311 while ((cc = getchar()) != EOF) 312 fputc(cc, f); 313 314 for (int i=0; i<cmd_lines_len; i++) 315 fprintf(f, "%s\n", lines[i]); 316 //fputs(lines[i], f); 317 318 a.lines = NULL; 319 a.lines_len = 0; 320 rewind(f); 321 read_stdin(&a, f); 322 323 draw(a); 324 325 return 4; 326 }