xsetwallpaper.c revision a3c4ad5d
1/* $NetBSD: xsetwallpaper.c,v 1.3 2020/02/08 20:29:30 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.3 2020/02/08 20:29:30 jmcneill Exp $");
30
31#include <sys/endian.h>
32
33#include <err.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <unistd.h>
37
38#include <X11/Xlib.h>
39
40#include "stbi.h"
41
42#define DEFAULT_FILL_COLOR	"#000000"
43
44static uint8_t *	resize_nn(const uint8_t *, int, int, int, int);
45
46static void
47usage(const char *pn)
48{
49	fprintf(stderr, "usage: %s [-f fillcolor] [-s] filename\n", pn);
50	exit(EXIT_FAILURE);
51}
52
53int
54main(int argc, char *argv[])
55{
56	const char *fill_color = DEFAULT_FILL_COLOR;
57	const char *pn = argv[0];
58	uint8_t *data;
59	unsigned int root_width, root_height, root_border_width, root_depth;
60	int root_x, root_y;
61	int bitmap_pad, srcx, srcy, dstx, dsty;
62	int imagew, imageh, imagebpp;
63	int ch, i;
64	int screen, default_depth, byte_order;
65	int scale = 0;
66	Display *display;
67	Colormap colormap;
68	XImage *image;
69	Pixmap pixmap;
70	XColor color;
71	Window window;
72	GC gc;
73
74	while ((ch = getopt(argc, argv, "f:sh")) != -1) {
75		switch (ch) {
76		case 'f':
77			fill_color = optarg;
78			break;
79		case 's':
80			scale = 1;
81			break;
82		case 'h':
83		default:
84			usage(pn);
85			/* NOTREACHED */
86		}
87	}
88	argc -= optind;
89	argv += optind;
90
91	if (argc != 1)
92		usage(pn);
93
94	/* Load the image */
95	data = stbi_load(argv[0], &imagew, &imageh, &imagebpp, 4);
96	if (data == NULL)
97		errx(EXIT_FAILURE, "failed to load %s", argv[0]);
98
99	/* swap red and blue */
100	for (i = 0; i < imagew * imageh * 4; i += 4) {
101		uint8_t p;
102		p = data[i + 0];
103		data[i + 0] = data[i + 2];
104		data[i + 2] = p;
105	}
106
107#if _BYTE_ORDER == _BIG_ENDIAN
108	for (i = 0; i < imagew * imageh * 4; i += 4) {
109		uint32_t *p = (uint32_t *)&data[i];
110		*p = bswap32(*p);
111	}
112#endif
113
114#ifdef DEBUG
115	printf("%s: %dx%d %dbpp\n", argv[0], imagew, imageh, imagebpp * 8);
116#endif
117
118	/* open the display */
119	display = XOpenDisplay(NULL);
120	if (display == NULL) {
121		errx(EXIT_FAILURE, "couldn't open display: %s",
122		    getenv("DISPLAY"));
123	}
124	screen = DefaultScreen(display);
125	default_depth = DefaultDepth(display, screen);
126	colormap = DefaultColormap(display, 0);
127	byte_order = ImageByteOrder(display);
128
129	/* get root window geometry */
130	if (!XGetGeometry(display, XDefaultRootWindow(display), &window,
131	    &root_x, &root_y, &root_width, &root_height,
132	    &root_border_width, &root_depth)) {
133		errx(EXIT_FAILURE, "couldn't get screen dimensions\n");
134	}
135
136#ifdef DEBUG
137	printf("screen is %dx%d\n", root_width, root_height);
138#endif
139
140	XSync(display, False);
141
142	if (scale) {
143		data = resize_nn(data, imagew, imageh, root_width, root_height);
144		if (data == NULL)
145			err(EXIT_FAILURE, "couldn't resize image\n");
146		imagew = root_width;
147		imageh = root_height;
148	}
149
150	/* Parse the fill colour and allocate it */
151	if (!XParseColor(display, colormap, fill_color, &color)) {
152		errx(EXIT_FAILURE, "couldn't parse color '%s'", fill_color);
153	}
154	if (!XAllocColor(display, colormap, &color)) {
155		errx(EXIT_FAILURE, "XAllocColor failed");
156	}
157
158	/* Create an XImage from our raw image data */
159	if (default_depth >= 24)
160		bitmap_pad = 32;
161	else if (default_depth >= 16)
162		bitmap_pad = 16;
163	else
164		bitmap_pad = 8;
165	image = XCreateImage(display, CopyFromParent, default_depth,
166	    ZPixmap, 0, (char *)data, imagew, imageh, bitmap_pad, 0);
167	if (image == NULL) {
168		errx(EXIT_FAILURE, "XCreateImage failed");
169	}
170	XInitImage(image);
171	image->byte_order = byte_order;
172
173	/* Create a graphics context for our new pixmap */
174	gc = XCreateGC(display, window, 0, NULL);
175
176	/* Create a pixmap the size of the root window */
177	pixmap = XCreatePixmap(display, window,
178	    root_width, root_height, root_depth);
179
180	/* Fill the background with the specified fill colour */
181	XSetForeground(display, gc, color.pixel);
182	XFillRectangle(display, pixmap, gc, 0, 0, root_width, root_height);
183
184	/* Copy the image to the pixmal, centering it on screen */
185	if ((unsigned int)imagew > root_width) {
186		srcx = (imagew - root_width) / 2;
187		dstx = 0;
188	} else {
189		srcx = 0;
190		dstx = (root_width - imagew) / 2;
191	}
192	if ((unsigned int)imageh > root_height) {
193		srcy = (imageh - root_height) / 2;
194		dsty = 0;
195	} else {
196		srcy = 0;
197		dsty = (root_height - imageh) / 2;
198	}
199	XPutImage(display, pixmap, gc, image,
200	    srcx, srcy, dstx, dsty,
201	    root_width, root_height);
202
203	/* Set the background pixmap for the window */
204	XSetWindowBackgroundPixmap(display, window, pixmap);
205	XClearWindow(display, window);
206
207	/* Cleanup */
208	XFreePixmap(display, pixmap);
209	XCloseDisplay(display);
210
211	return EXIT_SUCCESS;
212}
213
214static uint8_t *
215resize_nn(const uint8_t *data, int src_w, int src_h, int dst_w, int dst_h)
216{
217	const uint32_t *src;
218	uint32_t *dst;
219	int src_x, src_y, dst_x, dst_y;
220
221	src = (const uint32_t *)data;
222	dst = malloc(src_w * src_h * 4);
223	if (dst == NULL)
224		return NULL;
225
226	for (dst_y = 0; dst_y < dst_h; dst_y++) {
227		src_y = dst_y * src_h / dst_h;
228		for (dst_x = 0; dst_x < dst_w; dst_x++) {
229			src_x = dst_x * src_w / dst_w;
230			dst[dst_y * dst_w + dst_x] = src[src_y * src_w + src_x];
231		}
232	}
233
234	return (uint8_t *)dst;
235}
236