bgdraw

X11 background animator
git clone https://git.sinitax.com/sinitax/bgdraw
Log | Files | Refs | LICENSE | sfeed.txt

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}