usrlib.de / CVS

Revision 1.14 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 <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;
	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, 32,
		    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 -n | -cCfFsStT 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 scr, scrnum, i, curscr = -1;
	int x, y, w, h;
	char opt = 'f';
	enum bgmode mode = BGM_FILL;
	Display *dpy;
	Window root;
	XineramaScreenInfo *info;

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

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

	/* parse command line and draw images */
	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-' && argv[i][1] != '\0') {
			if (argv[i][2] == '\0') {
				switch (argv[i][1]) {
				case 'c':
				case 'C':
					mode = BGM_CENTER;
					break;
				case 't':
				case 'T':
					mode = BGM_TILE;
					break;
				case 's':
				case 'S':
					mode = BGM_SCALE;
					break;
				case 'f':
				case 'F':
					mode = BGM_FILL;
					break;
				case 'n':
					curscr++;
					continue;
				default:
					usage(argv[0]);
					x_teardown(dpy, info);
					return 1;
				}
			} else {
				usage(argv[0]);
				x_teardown(dpy, info);
				return 1;
			}
			opt = argv[i][1];
			continue;
		}

		if (info && opt >= 'a') {
			/* Xinerama screen */
			curscr++;
			if (curscr >= scrnum) {
				break;
			}
			x = info[curscr].x_org;
			y = info[curscr].y_org;
			w = info[curscr].width;
			h = info[curscr].height;
		} else {
			/* default screen dimensions: entire display area */
			x = y = 0;
			w = DisplayWidth(dpy, scr);
			h = DisplayHeight(dpy, scr);
		}

		if (!(draw_image(dpy, scr, root, argv[i], mode, x, y, w, h))) {
			warn("%s", argv[i]);
		}
	}

	x_teardown(dpy, info);
	return 0;
}