usrlib.de / CVS

Revision 1.5 of bgset/bgset.c

/*
 * bgset
 * draws images on the root window
 * 
 * Copyright (c) 2016 Lukas Hofmann <lhofmann@fsfe.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */


#include <stdio.h>
#include <unistd.h>
#include <err.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xinerama.h>
#include <Imlib2.h>


enum bgmode {
	BGM_CENTER,  /* centered, original size */
	BGM_TILE,    /* tiled, original size */
	BGM_SCALE,   /* scaled to fit scrind with borders */
	BGM_FILL     /* scaled and cropped to fill scrind */
};


Imlib_Image
prepare_image(char *filename, enum bgmode mode, int scr_x, int scr_y, int scr_w, int scr_h) {
	int orig_w, orig_h, prep_x, prep_y, scr_scl_w, scr_scl_h;
	double orig_ratio, scr_ratio;
	Imlib_Image orig, prep;

	if (!(orig = imlib_load_image(filename))) {
		return NULL;
	}

	imlib_context_set_image(orig);
	orig_w = imlib_image_get_width();
	orig_h = imlib_image_get_height();

	switch (mode) {
	case BGM_CENTER:
		prep_x = (orig_w - scr_w) / 2;
		prep_y = (orig_h - scr_h) / 2;
		prep = imlib_create_cropped_image(prep_x, prep_y, scr_w, scr_h);
		break;
	case BGM_TILE:
		/* n/a */
		break;
	case BGM_SCALE:
	case BGM_FILL:
		orig_ratio = (double) orig_w / orig_h;
		scr_ratio = (double) scr_w / scr_h;
		if (mode == BGM_SCALE && orig_ratio >= scr_ratio || mode == BGM_FILL && orig_ratio < scr_ratio) {
			scr_scl_w = orig_w;
			scr_scl_h = orig_w / scr_ratio;
		} else {
			scr_scl_h = orig_h;
			scr_scl_w = orig_h * scr_ratio;
		}
		prep_x = (orig_w - scr_scl_w) / 2;
		prep_y = (orig_h - scr_scl_h) / 2;
		prep = imlib_create_cropped_scaled_image(prep_x, prep_y, scr_scl_w, scr_scl_h, scr_w, scr_h);
		break;
	}
	imlib_free_image();
	return prep;
}

void
draw_image(char *filename, enum bgmode mode, int scr_x, int scr_y, int scr_w, int scr_h, Display *dpy, int scr) {
	Screen *scr_o;
	Pixmap pxm;
	Window root_win;
	Imlib_Image image;

	if (!(image = prepare_image(filename, mode, scr_x, scr_y, scr_w, scr_h))) {
		warnx("%s: unable to open file", filename);
		return;
	}

	imlib_context_set_image(image);

	root_win = DefaultRootWindow(dpy);
	scr_o = ScreenOfDisplay(dpy, scr);
	pxm = XCreatePixmap(dpy, root_win, DisplayWidth(dpy, scr), DisplayHeight(dpy, scr), DefaultDepthOfScreen(scr_o));
	imlib_context_set_display(dpy);
	imlib_context_set_drawable(pxm);
	imlib_context_set_visual(DefaultVisualOfScreen(scr_o));
	imlib_context_set_colormap(DefaultColormapOfScreen(scr_o));

	imlib_render_image_on_drawable(scr_x, scr_y);
	XSetWindowBackgroundPixmap(dpy, root_win, pxm);
	XClearWindow(dpy, root_win);
	while (XPending(dpy)) {
		XEvent ev;
		XNextEvent(dpy, &ev);
	}

	XFreePixmap(dpy, pxm);
	imlib_free_image();
}

int
main(int argc, char *argv[]) {

	int opt, scrind = 0, scrnum = 0;
	int x, y, w, h;
	int scr;
	enum bgmode mode;
	Display *dpy;
	XineramaScreenInfo *info = NULL;

	if (argc == 1) {
		goto usage;
	}

	/* initialize X11 stuff */
	if (!(dpy = XOpenDisplay(NULL))) {
		errx(1, "error opening display");
	}
	scr = DefaultScreen(dpy);
	info = XineramaQueryScreens(dpy, &scrnum);

	/* default screen dimensions */
	x = 0;
	y = 0;
	w = DisplayWidth(dpy, scr);
	h = DisplayHeight(dpy, scr);

	/* parse command line and draw images */
	while ((opt = getopt(argc, argv, "c:t:s:f:")) != -1) {
		if (info) {
			x = info[scrind].x_org;
			y = info[scrind].y_org;
			w = info[scrind].width;
			h = info[scrind].height;
		}

		switch (opt) {
		case 'c':
			mode = BGM_CENTER;
			break;
		case 't':
			mode = BGM_TILE;
			break;
		case 's':
			mode = BGM_SCALE;
			break;
		case 'f':
			mode = BGM_FILL;
			break;
		default:
			goto usage;
		}

		draw_image(optarg, mode, x, y, w, h, dpy, scr);

		scrind++;
		if (scrind >= scrnum) {
			if (info) {
				XFree(info);
			}
			return 0;
		}
	}

	if (optind != argc) {
		fprintf(stderr, "%s: ignoring superfluous arguments\n", argv[0]);
	}

	return 0;

usage:
	fprintf(stderr, "usage: %s [-c file] [-t file] [-s file] [-f file]\n", argv[0]);
	if (info) {
		XFree(info);
	}
	return 1;
}