usrlib.de / CVS

Revision 1.9 of bgset/bgset.c

/*
 * bgset
 * 
 * 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/Xatom.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 screen with borders */
	BGM_FILL     /* scaled and cropped to fill screen */
};


Imlib_Image prepare_image(char *, enum bgmode, int, int);
int draw_image(Display *, int, Window, char *, enum bgmode, int, int, int, int);
void usage(char *);
void x_init(Display **, int *, Window *, XineramaScreenInfo **, int *);
void x_teardown(Display *, XineramaScreenInfo *);
int main(int, char **);


Imlib_Image
prepare_image(char *filename, enum bgmode mode, 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;
}

int
draw_image(Display *dpy, int scr, Window win, char *filename, enum bgmode mode,
    int scr_x, int scr_y, int scr_w, int scr_h) {
	int format = 32;
	unsigned long nitems, bytes_after;
	unsigned char *data;
	Atom pxm_prop, type;
	Pixmap pxm;
	Imlib_Image image;

	if (!(image = prepare_image(filename, mode, scr_w, scr_h))) {
		return 0;
	}

	/* init pixmap, set window property for compositors */
	pxm_prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
	XGetWindowProperty(dpy, win, pxm_prop, 0L, 1L, False,
	    AnyPropertyType, &type, &format, &nitems, &bytes_after, &data);
	if (data) {
		if (type == XA_PIXMAP) {
			pxm = *((Pixmap *) data);
		}
		XFree(data);
	} else {
		pxm = XCreatePixmap(dpy, win, DisplayWidth(dpy, scr),
		    DisplayHeight(dpy, scr), DefaultDepth(dpy, scr));
		XChangeProperty(dpy, win, pxm_prop, XA_PIXMAP, format,
		    PropModeReplace, (unsigned char *) &pxm, 1);
	}

	/* init imlib */
	imlib_context_set_image(image);
	imlib_context_set_display(dpy);
	imlib_context_set_drawable(pxm);
	imlib_context_set_visual(DefaultVisual(dpy, scr));
	imlib_context_set_colormap(DefaultColormap(dpy, scr));

	/* draw image */
	imlib_render_image_on_drawable(scr_x, scr_y);
	XSetWindowBackgroundPixmap(dpy, win, pxm);
	XClearWindow(dpy, win);

	/* clean up */
	imlib_free_image();

	return 1;
}

void
usage(char *myname) {
	fprintf(stderr, "usage: %s [-c file] [-t file] [-s file] [-f file]\n",
	    myname);
}

void
x_init(Display **dpy, int *scr, Window *root, XineramaScreenInfo **info,
    int *scrnum) {
	if (!(*dpy = XOpenDisplay(NULL))) {
		errx(1, "error opening display");
	}
	*scr = DefaultScreen(*dpy);
	*root = DefaultRootWindow(*dpy);
	*info = XineramaQueryScreens(*dpy, scrnum);
	XSetCloseDownMode(*dpy, RetainPermanent);
}

void
x_teardown(Display *dpy, XineramaScreenInfo *info) {
	if (info) {
		XFree(info);
	}
	XCloseDisplay(dpy);
}

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

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

	if (argc == 1) {
		usage(argv[0]);
		return 1;
	}

	x_init(&dpy, &scr, &root, &info, &scrnum);

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

	/* parse command line and draw images */
	for (scrind = 0; scrind < scrnum; scrind++) {
		if ((opt = getopt(argc, argv, "c:t:s:f:n")) == -1) {
			break;
		}

		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;
		case 'n':
			continue;
		default:
			usage(argv[0]);
			x_teardown(dpy, info);
			return 1;
		}

		if (!(draw_image(dpy, scr, root, optarg, mode, x, y, w, h))) {
			warn("%s on screen %d", optarg, scrind);
		}
	}

	x_teardown(dpy, info);
	return 0;
}