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