xsetwallpaper.c revision e6987d98
1/* $NetBSD: xsetwallpaper.c,v 1.1 2011/10/22 22:05:27 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__RCSID("$NetBSD: xsetwallpaper.c,v 1.1 2011/10/22 22:05:27 jmcneill Exp $");
30
31#include <err.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <unistd.h>
35
36#include <X11/Xlib.h>
37
38#include "stbi.h"
39
40#define DEFAULT_FILL_COLOR	"#000000"
41
42static void
43usage(const char *pn)
44{
45	fprintf(stderr, "usage: %s [-f fillcolor] filename\n", pn);
46	exit(EXIT_FAILURE);
47}
48
49int
50main(int argc, char *argv[])
51{
52	const char *fill_color = DEFAULT_FILL_COLOR;
53	const char *pn = argv[0];
54	uint8_t *data;
55	unsigned int root_width, root_height, root_border_width, root_depth;
56	int root_x, root_y;
57	int bitmap_pad, srcx, srcy, dstx, dsty;
58	int imagew, imageh, imagebpp;
59	int ch, i;
60	int screen, default_depth;
61	Display *display;
62	Colormap colormap;
63	XImage *image;
64	Pixmap pixmap;
65	XColor color;
66	Window window;
67	GC gc;
68
69	while ((ch = getopt(argc, argv, "f:h")) != -1) {
70		switch (ch) {
71		case 'f':
72			fill_color = optarg;
73			break;
74		case 'h':
75		default:
76			usage(pn);
77			/* NOTREACHED */
78		}
79	}
80	argc -= optind;
81	argv += optind;
82
83	if (argc != 1)
84		usage(pn);
85
86	/* Load the image */
87	data = stbi_load(argv[0], &imagew, &imageh, &imagebpp, 4);
88	if (data == NULL)
89		errx(EXIT_FAILURE, "failed to load %s", argv[0]);
90
91	/* swap red and blue */
92	for (i = 0; i < imagew * imageh * 4; i += 4) {
93		uint8_t p;
94		p = data[i + 0];
95		data[i + 0] = data[i + 2];
96		data[i + 2] = p;
97	}
98
99#ifdef DEBUG
100	printf("%s: %dx%d %dbpp\n", argv[0], imagew, imageh, imagebpp * 8);
101#endif
102
103	/* open the display */
104	display = XOpenDisplay(NULL);
105	if (display == NULL) {
106		errx(EXIT_FAILURE, "couldn't open display: %s",
107		    getenv("DISPLAY"));
108	}
109	screen = DefaultScreen(display);
110	default_depth = DefaultDepth(display, screen);
111	colormap = DefaultColormap(display, 0);
112
113	/* get root window geometry */
114	if (!XGetGeometry(display, XDefaultRootWindow(display), &window,
115	    &root_x, &root_y, &root_width, &root_height,
116	    &root_border_width, &root_depth)) {
117		errx(EXIT_FAILURE, "couldn't get screen dimensions\n");
118	}
119
120#ifdef DEBUG
121	printf("screen is %dx%d\n", root_width, root_height);
122#endif
123
124	XSync(display, False);
125
126	/* Parse the fill colour and allocate it */
127	if (!XParseColor(display, colormap, fill_color, &color)) {
128		errx(EXIT_FAILURE, "couldn't parse color '%s'", fill_color);
129	}
130	if (!XAllocColor(display, colormap, &color)) {
131		errx(EXIT_FAILURE, "XAllocColor failed");
132	}
133
134	/* Create an XImage from our raw image data */
135	if (default_depth >= 24)
136		bitmap_pad = 32;
137	else if (default_depth >= 16)
138		bitmap_pad = 16;
139	else
140		bitmap_pad = 8;
141	image = XCreateImage(display, CopyFromParent, imagebpp * 8,
142	    ZPixmap, 0, (char *)data, imagew, imageh, bitmap_pad, 0);
143	if (image == NULL) {
144		errx(EXIT_FAILURE, "XCreateImage failed");
145	}
146	XInitImage(image);
147	image->byte_order = LSBFirst;	/* ??? */
148
149	/* Create a graphics context for our new pixmap */
150	gc = XCreateGC(display, window, 0, NULL);
151
152	/* Create a pixmap the size of the root window */
153	pixmap = XCreatePixmap(display, window,
154	    root_width, root_height, root_depth);
155
156	/* Fill the background with the specified fill colour */
157	XSetForeground(display, gc, color.pixel);
158	XFillRectangle(display, pixmap, gc, 0, 0, root_width, root_height);
159
160	/* Copy the image to the pixmal, centering it on screen */
161	if ((unsigned int)imagew > root_width) {
162		srcx = (imagew - root_width) / 2;
163		dstx = 0;
164	} else {
165		srcx = 0;
166		dstx = (root_width - imagew) / 2;
167	}
168	if ((unsigned int)imageh > root_height) {
169		srcy = (imageh - root_height) / 2;
170		dsty = 0;
171	} else {
172		srcy = 0;
173		dsty = (root_height - imageh) / 2;
174	}
175	XPutImage(display, pixmap, gc, image,
176	    srcx, srcy, dstx, dsty,
177	    root_width, root_height);
178
179	/* Set the background pixmap for the window */
180	XSetWindowBackgroundPixmap(display, window, pixmap);
181	XClearWindow(display, window);
182
183	/* Cleanup */
184	XFreePixmap(display, pixmap);
185	XCloseDisplay(display);
186
187	return EXIT_SUCCESS;
188}
189