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