bgdraw.c (9142B)
1#include <X11/Xlib.h> 2#include <X11/Xutil.h> 3#include <X11/XKBlib.h> 4#include <X11/keysym.h> 5#include <X11/cursorfont.h> 6#include <X11/extensions/Xinerama.h> 7 8#define STB_IMAGE_IMPLEMENTATION 9#include "stb_image.h" 10 11#define STB_IMAGE_RESIZE_IMPLEMENTATION 12#include "stb_image_resize.h" 13 14#include <sys/time.h> 15#include <unistd.h> 16#include <err.h> 17#include <stdarg.h> 18#include <stdbool.h> 19#include <stdio.h> 20#include <stdlib.h> 21#include <string.h> 22 23#define MIN(a, b) ((a) < (b) ? (a) : (b)) 24#define MAX(a, b) ((a) >= (b) ? (a) : (b)) 25#define ARRLEN(x) (sizeof(x) / sizeof((x)[0])) 26 27struct image { 28 char *name; 29 char *path; 30 int ox, oy; 31 int x, y; 32 int w, h; 33 int channels; 34 35 XImage **frames; 36 int *durations; 37 int framei, framecnt; 38 float frametime; 39 time_t last_frame; 40 41 struct image *next; 42}; 43 44static void monitor_offset(const char *name, int num, int *x, int *y); 45static void convert_rgb_bgr(uint8_t *pix, int width, int height, 46 int stride, int pixlen); 47 48static void draw_img(struct image *img); 49static void draw(void); 50 51static void load(const char *path); 52 53static bool load_attrs(FILE *file, struct image *img, int *lineno); 54 55static void load_img(struct image *img); 56static void free_img(struct image *img); 57 58static const char *config_path; 59static bool debug; 60 61static struct image *images; 62 63static Display *display; 64static Screen *screen; 65static Window root; 66static Visual *vis; 67static GC gc; 68static int depth; 69 70void 71monitor_offset(const char *name, int num, int *x, int *y) 72{ 73 XineramaScreenInfo *info; 74 int mcount, screen; 75 76 if (!XineramaIsActive(display)) { 77 if (debug) fprintf(stderr, "No monitor support\n"); 78 *x = *y = 0; 79 return; 80 } 81 82 if (!(info = XineramaQueryScreens(display, &mcount))) 83 errx(1, "Failed to query xinerama"); 84 85 if (num < 0 || num > mcount) 86 errx(1, "IMG %s: Monitor number out of range", name); 87 88 *x = info[num].x_org; 89 *y = info[num].y_org; 90} 91 92void 93convert_rgb_bgr(uint8_t *pix, int width, int height, int stride, int pixlen) 94{ 95 uint8_t tmp, *cpix; 96 int x, y; 97 98 for (y = 0; y < height; y++) { 99 for (x = 0; x < width; x++) { 100 cpix = &pix[y * stride + x * pixlen]; 101 tmp = cpix[0]; 102 cpix[0] = cpix[2]; 103 cpix[2] = tmp; 104 } 105 } 106} 107 108void 109draw_img(struct image *img) 110{ 111 if (debug) fprintf(stderr, "DRAW %s\n", img->name); 112 XPutImage(display, root, gc, img->frames[img->framei], 0, 0, 113 img->x, img->y, img->w, img->h); 114} 115 116void 117draw(void) 118{ 119 struct image *img; 120 121 for (img = images; img; img = img->next) 122 draw_img(img); 123} 124 125void 126load(const char *path) 127{ 128 struct image img; 129 struct image **next; 130 int lineno; 131 FILE *file; 132 133 file = fopen(path, "r"); 134 if (!file) err(1, "fopen %s", path); 135 136 images = NULL; 137 next = &images; 138 lineno = 1; 139 while (!feof(file)) { 140 memset(&img, 0, sizeof(struct image)); 141 img.last_frame = 0; 142 143 if (!load_attrs(file, &img, &lineno)) 144 break; /* EOF */ 145 146 img.x += img.ox; 147 img.y += img.oy; 148 149 load_img(&img); 150 151 *next = malloc(sizeof(struct image)); 152 if (!*next) err(1, "malloc"); 153 memcpy(*next, &img, sizeof(struct image)); 154 next = &((*next)->next); 155 } 156 157 fclose(file); 158} 159 160bool 161load_attrs(FILE *file, struct image *img, int *lineno) 162{ 163 char *key, *val; 164 char line[256]; 165 char *tok, end; 166 bool partial; 167 168 partial = false; 169 while (fgets(line, sizeof(line), file)) { 170 tok = strchr(line, '\n'); 171 if (tok) *tok = '\0'; 172 173 if (!strcmp(line, "")) { 174 if (partial) return true; 175 continue; 176 } 177 178 if (line[0] == '#') 179 continue; 180 181 partial = true; 182 183 if (!img->name) { 184 end = '\0'; 185 if (sscanf(line, "[%m[-_a-zA-Z0-9]]%c", 186 &img->name, &end) != 1 || end) 187 errx(1, "Invalid title line %i:\n%s %c", 188 *lineno, line, end); 189 continue; 190 } 191 192 tok = strchr(line, ' '); 193 if (!tok) errx(1, "IMG %s: Invalid line %i: %s", 194 img->name, *lineno, line); 195 *tok = '\0'; 196 197 key = line; 198 val = tok + 1; 199 200 if (!strcmp(key, "pos")) { 201 tok = strchr(val, 'x'); 202 if (!tok) errx(1, "IMG %s: Invalid pos: %s", 203 img->name, val); 204 *tok = '\0'; 205 206 img->x = atoi(val); 207 img->y = atoi(tok + 1); 208 } else if (!strcmp(key, "size")) { 209 tok = strchr(val, 'x'); 210 if (!tok) errx(1, "IMG %s: Invalid size: %s", 211 img->name, val); 212 *tok = '\0'; 213 214 if ((img->w = atoi(val)) <= 0) 215 errx(1, "IMG %s: Invalid width: %s", 216 img->name, val); 217 218 if ((img->h = atoi(tok + 1)) <= 0) 219 errx(1, "IMG %s: Invalid height: %s", 220 img->name, tok + 1); 221 } else if (!strcmp(key, "monitor")) { 222 monitor_offset(img->name, atoi(val), &img->ox, &img->oy); 223 } else if (!strcmp(key, "frametime")) { 224 img->frametime = atof(val); 225 } else if (!strcmp(key, "path")) { 226 img->path = strdup(val); 227 if (!img->path) err(1, "strdup"); 228 } else { 229 errx(1, "IMG %s: Invalid line %i:\n%s", 230 img->name, *lineno, line); 231 } 232 233 (*lineno)++; 234 } 235 236 return partial; 237} 238 239void 240load_img(struct image *img) 241{ 242 char *args[4] = { NULL }; 243 int width, height, i; 244 void *pix, *rpix; 245 stbi__context ctx; 246 stbi__result_info info; 247 stbi__gif gif; 248 uint8_t *frames; 249 FILE *file; 250 251 if (debug) fprintf(stderr, "LOAD %s\n", img->name); 252 253 if (!(file = stbi__fopen(img->path, "rb"))) 254 errx(1, "IMG %s: Path does not exist", img->name); 255 256 stbi__start_file(&ctx, file); 257 258 if (stbi__gif_test(&ctx)) { 259 frames = stbi__load_gif_main(&ctx, &img->durations, 260 &width, &height, &img->framecnt, &img->channels, 4); 261 if (!frames || !img->framecnt) 262 errx(1, "IMG %s: Failed to load gif image", img->name); 263 264 if (!img->w || !img->h) { 265 img->w = width; 266 img->h = height; 267 } 268 269 img->frames = malloc(sizeof(XImage*) * img->framecnt); 270 if (!img->frames) err(1, "malloc"); 271 272 for (i = 0; i < img->framecnt; i++) { 273 pix = frames + width * height * 4 * i; 274 275 rpix = malloc(img->w * img->h * 4); 276 if (!rpix) err(1, "malloc"); 277 if (!stbir_resize_uint8(pix, width, height, 0, 278 rpix, img->w, img->h, 0, 4)) 279 errx(1, "IMG %s: Failed to resize image", 280 img->name); 281 282 convert_rgb_bgr(rpix, img->w, img->h, img->w * 4, 4); 283 284 img->frames[i] = XCreateImage(display, vis, 285 24, ZPixmap, 0, rpix, 286 img->w, img->h, 32, 0); 287 if (!img->frames[i]) 288 errx(1, "IMG %s: Failed to create XImage", 289 img->name); 290 } 291 292 STBI_FREE(frames); 293 } else { 294 pix = stbi__load_main(&ctx, &width, &height, 295 &img->channels, 4, &info, 8); 296 if (!pix) errx(1, "IMG %s: Failed to load image", img->name); 297 298 if (!img->w || !img->h) { 299 img->w = width; 300 img->h = height; 301 } 302 303 rpix = malloc(img->w * img->h * 4); 304 if (!rpix) err(1, "malloc"); 305 if (!stbir_resize_uint8(pix, width, height, 0, 306 rpix, img->w, img->h, 0, 4)) 307 errx(1, "IMG %s: Failed to resize image", img->name); 308 309 img->durations = NULL; 310 img->frames = malloc(sizeof(XImage*)); 311 if (!img->frames) err(1, "malloc"); 312 img->frames[0] = XCreateImage(display, vis, depth, 313 ZPixmap, 0, rpix, img->w, img->h, 32, 0); 314 if (!img->frames[0]) 315 errx(1, "IMG %s: Failed to create XImage", img->name); 316 317 free(pix); 318 } 319 320 for (i = 0; i < img->framecnt; i++) { 321 if (img->frametime) 322 img->durations[i] = img->frametime * 100; 323 else 324 img->durations[i] = img->durations[i] / 10; 325 } 326 327 img->framei = 0; 328 329 fclose(file); 330} 331 332void 333free_img(struct image *img) 334{ 335 int i; 336 337 free(img->name); 338 free(img->path); 339 for (i = 0; i < img->framecnt; i++) { 340 free(img->frames[i]->data); 341 XFree(img->frames[i]); 342 } 343 STBI_FREE(img->durations); 344} 345 346void 347init(void) 348{ 349 XWindowAttributes attrs; 350 351 display = XOpenDisplay(NULL); 352 if (!display) errx(1, "Failed to open display"); 353 354 screen = XDefaultScreenOfDisplay(display); 355 if (!screen) errx(1, "Failed to get screen"); 356 357 vis = DefaultVisualOfScreen(screen); 358 root = RootWindowOfScreen(screen); 359 360 XGetWindowAttributes(display, root, &attrs); 361 depth = attrs.depth; 362 363 gc = XCreateGC(display, root, 0, NULL); 364 365 XSelectInput(display, root, ExposureMask); 366 367 XSync(display, True); 368 369 load(config_path); 370} 371 372void 373deinit(void) 374{ 375 struct image *img, *next; 376 377 for (img = images; img; ) { 378 next = img->next; 379 free_img(img); 380 free(img); 381 img = next; 382 } 383 384 XCloseDisplay(display); 385} 386 387int 388main(int argc, const char *argv[]) 389{ 390 struct image *img; 391 const char **arg; 392 struct timeval tv, nowtv; 393 XEvent event; 394 time_t now; 395 bool done; 396 fd_set fds; 397 int xfd; 398 399 debug = false; 400 config_path = "drawrc"; 401 for (arg = argv + 1; *arg; arg++) { 402 if (!strcmp(*arg, "-d")) { 403 debug = true; 404 } else if (!strcmp(*arg, "-c") && arg[1]) { 405 config_path = *(++arg); 406 } else { 407 printf("USAGE: bgdraw [-d] [-c CONFIG]\n"); 408 return 0; 409 } 410 } 411 412 init(); 413 414 draw(); 415 416 done = false; 417 xfd = XConnectionNumber(display); 418 while (!done) { 419 FD_ZERO(&fds); 420 FD_SET(xfd, &fds); 421 422 tv.tv_usec = 50000; 423 tv.tv_sec = 0; 424 425 /* wait for X event or timer */ 426 select(xfd + 1, &fds, NULL, NULL, &tv); 427 428 /* handle exposure events */ 429 while (XPending(display)) { 430 XNextEvent(display, &event); 431 switch (event.type) { 432 case Expose: 433 draw(); 434 break; 435 } 436 } 437 438 /* handle reloads */ 439 gettimeofday(&nowtv, NULL); 440 now = nowtv.tv_sec * 100 + nowtv.tv_usec / 10000; 441 for (img = images; img; img = img->next) { 442 if (img->durations && img->last_frame 443 + img->durations[img->framei] < now) { 444 img->last_frame = now; 445 draw_img(img); 446 447 img->framei++; 448 if (img->framei == img->framecnt) 449 img->framei = 0; 450 } 451 } 452 } 453 454 deinit(); 455}