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 }