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 }