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