birde.c (6936B)
1 #include <X11/Xlib.h> 2 #include <X11/Xft/Xft.h> 3 #include <X11/extensions/Xrandr.h> 4 #include <X11/Xresource.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <signal.h> 8 #include <unistd.h> 9 #include <string.h> 10 #include <stdarg.h> 11 #include <fcntl.h> 12 #include <sys/ipc.h> 13 #include <sys/shm.h> 14 15 #include "config.h" 16 17 #define EXIT_ACTION 0 18 #define EXIT_FAIL 1 19 #define EXIT_DISMISS 2 20 21 #define XRES_STR(name) \ 22 if (XrmGetResource(db, "birde." #name, "*", &type, &val)) \ 23 name = val.addr 24 #define XRES_INT(name) \ 25 if (XrmGetResource(db, "birde." #name, "*", &type, &val)) \ 26 name = strtoul(val.addr, 0, 10) 27 28 Display *display; 29 Window window; 30 int exit_code = EXIT_DISMISS; 31 32 static void die(const char *format, ...) 33 { 34 va_list ap; 35 va_start(ap, format); 36 vfprintf(stderr, format, ap); 37 fprintf(stderr, "\n"); 38 va_end(ap); 39 exit(EXIT_FAIL); 40 } 41 42 int get_max_len(char *string, XftFont *font, int max_text_width) 43 { 44 int eol = strlen(string); 45 XGlyphInfo info; 46 XftTextExtentsUtf8(display, font, (FcChar8 *)string, eol, &info); 47 48 if (info.width > max_text_width) 49 { 50 eol = max_text_width / font->max_advance_width; 51 info.width = 0; 52 53 while (info.width < max_text_width) 54 { 55 eol++; 56 XftTextExtentsUtf8(display, font, (FcChar8 *)string, eol, &info); 57 } 58 59 eol--; 60 } 61 62 for (int i = 0; i < eol; i++) 63 if (string[i] == '\n') 64 { 65 string[i] = ' '; 66 return ++i; 67 } 68 69 if (info.width <= max_text_width) 70 return eol; 71 72 int temp = eol; 73 74 while (string[eol] != ' ' && eol) 75 --eol; 76 77 if (eol == 0) 78 return temp; 79 else 80 return ++eol; 81 } 82 83 void expire(int sig) 84 { 85 XEvent event; 86 event.type = ButtonPress; 87 event.xbutton.button = (sig == SIGUSR2) ? (ACTION_BUTTON) : (DISMISS_BUTTON); 88 XSendEvent(display, window, 0, 0, &event); 89 XFlush(display); 90 } 91 92 void read_y_offset(unsigned int **offset, int *id) { 93 int shm_id = shmget(8432, sizeof(unsigned int), IPC_CREAT | 0660); 94 if (shm_id == -1) die("shmget failed"); 95 96 *offset = (unsigned int *)shmat(shm_id, 0, 0); 97 if (*offset == (unsigned int *)-1) die("shmat failed\n"); 98 *id = shm_id; 99 } 100 101 void free_y_offset(int id) { 102 shmctl(id, IPC_RMID, NULL); 103 } 104 105 int main(int argc, char *argv[]) 106 { 107 if (argc == 1) 108 die("Usage: %s body", argv[0]); 109 110 struct sigaction act_expire, act_ignore; 111 112 act_expire.sa_handler = expire; 113 act_expire.sa_flags = SA_RESTART; 114 sigemptyset(&act_expire.sa_mask); 115 116 act_ignore.sa_handler = SIG_IGN; 117 act_ignore.sa_flags = 0; 118 sigemptyset(&act_ignore.sa_mask); 119 120 sigaction(SIGALRM, &act_expire, 0); 121 sigaction(SIGTERM, &act_expire, 0); 122 sigaction(SIGINT, &act_expire, 0); 123 124 sigaction(SIGUSR1, &act_ignore, 0); 125 sigaction(SIGUSR2, &act_ignore, 0); 126 127 if (!(display = XOpenDisplay(0))) 128 die("Cannot open display"); 129 130 XrmInitialize(); 131 132 char *res_man = XResourceManagerString(display); 133 XrmDatabase db = XrmGetStringDatabase(res_man); 134 135 char *type; 136 XrmValue val; 137 138 XRES_STR(background_color); 139 XRES_STR(border_color); 140 XRES_STR(font_color); 141 XRES_STR(font_pattern); 142 143 XRES_INT(line_spacing); 144 XRES_INT(padding); 145 XRES_INT(width); 146 XRES_INT(border_size); 147 XRES_INT(pos_x); 148 XRES_INT(pos_y); 149 XRES_INT(corner); 150 XRES_INT(duration); 151 152 int screen = DefaultScreen(display); 153 Visual *visual = DefaultVisual(display, screen); 154 Colormap colormap = DefaultColormap(display, screen); 155 156 int screen_x = 0; 157 int screen_y = 0; 158 int screen_width = DisplayWidth(display, screen); 159 int screen_height = DisplayHeight(display, screen); 160 if(use_primary_monitor) { 161 int nMonitors; 162 XRRMonitorInfo* info = XRRGetMonitors(display, RootWindow(display, screen), 1, &nMonitors); 163 for(int i = 0; i < nMonitors; i++) { 164 if(info[i].primary) { 165 screen_x = info[i].x; 166 screen_y = info[i].y; 167 screen_width = info[i].width; 168 screen_height = info[i].height; 169 } 170 } 171 } 172 173 XSetWindowAttributes attributes; 174 attributes.override_redirect = True; 175 XftColor color; 176 XftColorAllocName(display, visual, colormap, background_color, &color); 177 attributes.background_pixel = color.pixel; 178 XftColorAllocName(display, visual, colormap, border_color, &color); 179 attributes.border_pixel = color.pixel; 180 181 int num_of_lines = 0; 182 int max_text_width = width - 2 * padding; 183 int lines_size = 5; 184 char **lines = malloc(lines_size * sizeof(char *)); 185 if (!lines) 186 die("malloc failed"); 187 188 XftFont *font = XftFontOpenName(display, screen, font_pattern); 189 190 for (int i = 1; i < argc; i++) 191 { 192 for (unsigned int eol = get_max_len(argv[i], font, max_text_width); eol; argv[i] += eol, num_of_lines++, eol = get_max_len(argv[i], font, max_text_width)) 193 { 194 if (lines_size <= num_of_lines) 195 { 196 lines = realloc(lines, (lines_size += 5) * sizeof(char *)); 197 if (!lines) 198 die("realloc failed"); 199 } 200 201 lines[num_of_lines] = malloc((eol + 1) * sizeof(char)); 202 if (!lines[num_of_lines]) 203 die("malloc failed"); 204 205 strncpy(lines[num_of_lines], argv[i], eol); 206 lines[num_of_lines][eol] = '\0'; 207 } 208 } 209 210 int y_offset_id; 211 unsigned int *y_offset; 212 read_y_offset(&y_offset, &y_offset_id); 213 214 unsigned int text_height = font->ascent - font->descent; 215 unsigned int height = (num_of_lines - 1) * line_spacing + num_of_lines * text_height + 2 * padding; 216 unsigned int x = pos_x; 217 unsigned int y = pos_y + *y_offset; 218 219 unsigned int used_y_offset = (*y_offset) += height + padding; 220 221 if (corner == TOP_RIGHT || corner == BOTTOM_RIGHT) 222 x = screen_x + screen_width - width - border_size * 2 - x; 223 224 if (corner == BOTTOM_LEFT || corner == BOTTOM_RIGHT) 225 y = screen_y + screen_height - height - border_size * 2 - y; 226 227 window = XCreateWindow(display, RootWindow(display, screen), x, y, width, height, border_size, DefaultDepth(display, screen), 228 CopyFromParent, visual, CWOverrideRedirect | CWBackPixel | CWBorderPixel, &attributes); 229 230 XftDraw *draw = XftDrawCreate(display, window, visual, colormap); 231 XftColorAllocName(display, visual, colormap, font_color, &color); 232 233 XSelectInput(display, window, ExposureMask | ButtonPress); 234 XMapWindow(display, window); 235 236 sigaction(SIGUSR1, &act_expire, 0); 237 sigaction(SIGUSR2, &act_expire, 0); 238 239 if (duration != 0) 240 alarm(duration); 241 242 for (;;) 243 { 244 XEvent event; 245 XNextEvent(display, &event); 246 247 if (event.type == Expose) 248 { 249 XClearWindow(display, window); 250 for (int i = 0; i < num_of_lines; i++) 251 XftDrawStringUtf8(draw, &color, font, padding, line_spacing * i + text_height * (i + 1) + padding, 252 (FcChar8 *)lines[i], strlen(lines[i])); 253 } 254 else if (event.type == ButtonPress) 255 { 256 if (event.xbutton.button == DISMISS_BUTTON) 257 break; 258 else if (event.xbutton.button == ACTION_BUTTON) 259 { 260 exit_code = EXIT_ACTION; 261 break; 262 } 263 } 264 } 265 266 267 for (int i = 0; i < num_of_lines; i++) 268 free(lines[i]); 269 270 if (used_y_offset == *y_offset) free_y_offset(y_offset_id); 271 free(lines); 272 XftDrawDestroy(draw); 273 XftColorFree(display, visual, colormap, &color); 274 XftFontClose(display, font); 275 XrmDestroyDatabase(db); 276 XCloseDisplay(display); 277 278 return exit_code; 279 }