xsetwallpaper.c revision 3b633e1e
1/* $NetBSD: xsetwallpaper.c,v 1.2 2018/03/24 19:43:31 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.2 2018/03/24 19:43:31 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 void
45usage(const char *pn)
46{
47	fprintf(stderr, "usage: %s [-f fillcolor] filename\n", pn);
48	exit(EXIT_FAILURE);
49}
50
51int
52main(int argc, char *argv[])
53{
54	const char *fill_color = DEFAULT_FILL_COLOR;
55	const char *pn = argv[0];
56	uint8_t *data;
57	unsigned int root_width, root_height, root_border_width, root_depth;
58	int root_x, root_y;
59	int bitmap_pad, srcx, srcy, dstx, dsty;
60	int imagew, imageh, imagebpp;
61	int ch, i;
62	int screen, default_depth, byte_order;
63	Display *display;
64	Colormap colormap;
65	XImage *image;
66	Pixmap pixmap;
67	XColor color;
68	Window window;
69	GC gc;
70
71	while ((ch = getopt(argc, argv, "f:h")) != -1) {
72		switch (ch) {
73		case 'f':
74			fill_color = optarg;
75			break;
76		case 'h':
77		default:
78			usage(pn);
79			/* NOTREACHED */
80		}
81	}
82	argc -= optind;
83	argv += optind;
84
85	if (argc != 1)
86		usage(pn);
87
88	/* Load the image */
89	data = stbi_load(argv[0], &imagew, &imageh, &imagebpp, 4);
90	if (data == NULL)
91		errx(EXIT_FAILURE, "failed to load %s", argv[0]);
92
93	/* swap red and blue */
94	for (i = 0; i < imagew * imageh * 4; i += 4) {
95		uint8_t p;
96		p = data[i + 0];
97		data[i + 0] = data[i + 2];
98		data[i + 2] = p;
99	}
100
101#if _BYTE_ORDER == _BIG_ENDIAN
102	for (i = 0; i < imagew * imageh * 4; i += 4) {
103		uint32_t *p = (uint32_t *)&data[i];
104		*p = bswap32(*p);
105	}
106#endif
107
108#ifdef DEBUG
109	printf("%s: %dx%d %dbpp\n", argv[0], imagew, imageh, imagebpp * 8);
110#endif
111
112	/* open the display */
113	display = XOpenDisplay(NULL);
114	if (display == NULL) {
115		errx(EXIT_FAILURE, "couldn't open display: %s",
116		    getenv("DISPLAY"));
117	}
118	screen = DefaultScreen(display);
119	default_depth = DefaultDepth(display, screen);
120	colormap = DefaultColormap(display, 0);
121	byte_order = ImageByteOrder(display);
122
123	/* get root window geometry */
124	if (!XGetGeometry(display, XDefaultRootWindow(display), &window,
125	    &root_x, &root_y, &root_width, &root_height,
126	    &root_border_width, &root_depth)) {
127		errx(EXIT_FAILURE, "couldn't get screen dimensions\n");
128	}
129
130#ifdef DEBUG
131	printf("screen is %dx%d\n", root_width, root_height);
132#endif
133
134	XSync(display, False);
135
136	/* Parse the fill colour and allocate it */
137	if (!XParseColor(display, colormap, fill_color, &color)) {
138		errx(EXIT_FAILURE, "couldn't parse color '%s'", fill_color);
139	}
140	if (!XAllocColor(display, colormap, &color)) {
141		errx(EXIT_FAILURE, "XAllocColor failed");
142	}
143
144	/* Create an XImage from our raw image data */
145	if (default_depth >= 24)
146		bitmap_pad = 32;
147	else if (default_depth >= 16)
148		bitmap_pad = 16;
149	else
150		bitmap_pad = 8;
151	image = XCreateImage(display, CopyFromParent, default_depth,
152	    ZPixmap, 0, (char *)data, imagew, imageh, bitmap_pad, 0);
153	if (image == NULL) {
154		errx(EXIT_FAILURE, "XCreateImage failed");
155	}
156	XInitImage(image);
157	image->byte_order = byte_order;
158
159	/* Create a graphics context for our new pixmap */
160	gc = XCreateGC(display, window, 0, NULL);
161
162	/* Create a pixmap the size of the root window */
163	pixmap = XCreatePixmap(display, window,
164	    root_width, root_height, root_depth);
165
166	/* Fill the background with the specified fill colour */
167	XSetForeground(display, gc, color.pixel);
168	XFillRectangle(display, pixmap, gc, 0, 0, root_width, root_height);
169
170	/* Copy the image to the pixmal, centering it on screen */
171	if ((unsigned int)imagew > root_width) {
172		srcx = (imagew - root_width) / 2;
173		dstx = 0;
174	} else {
175		srcx = 0;
176		dstx = (root_width - imagew) / 2;
177	}
178	if ((unsigned int)imageh > root_height) {
179		srcy = (imageh - root_height) / 2;
180		dsty = 0;
181	} else {
182		srcy = 0;
183		dsty = (root_height - imageh) / 2;
184	}
185	XPutImage(display, pixmap, gc, image,
186	    srcx, srcy, dstx, dsty,
187	    root_width, root_height);
188
189	/* Set the background pixmap for the window */
190	XSetWindowBackgroundPixmap(display, window, pixmap);
191	XClearWindow(display, window);
192
193	/* Cleanup */
194	XFreePixmap(display, pixmap);
195	XCloseDisplay(display);
196
197	return EXIT_SUCCESS;
198}
199