Hack of the day:
- Problem
-
I wanted to be able to fade to a new background image. But how could I get the current background image so that the two images can blended together?
- Solution
-
I dived into Xlib for the first time since 2003 and came up with a small C program that reads the current background image and writes it to a JPEG file.
I now have a script that downloads a new background image every hour and nicely fades to this new background by getting the current background and gradually blending the new image over this. :-)
Code:
// compile with: gcc -Wall -ansi -pedantic getbg.c -o getbg -lX11 -ljpeg
// usage: ./getbg mybg.jpg
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <jpeglib.h>
int write_jpeg(XImage *img, const char* filename)
{
* outfile;
FILElong pixel;
unsigned , y;
int x* buffer;
char;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer
= fopen(filename, "wb");
outfile (!outfile) {
if (stderr, "Failed to open output file %s", filename);
fprintf1;
return }
/* collect separate RGB values to a buffer */
= malloc(sizeof(char)*3*img->width*img->height);
buffer (y = 0; y < img->height; y++) {
for (x = 0; x < img->width; x++) {
for = XGetPixel(img,x,y);
pixel [y*img->width*3+x*3+0] = (char)(pixel>>16);
buffer[y*img->width*3+x*3+1] = (char)((pixel&0x00ff00)>>8);
buffer[y*img->width*3+x*3+2] = (char)(pixel&0x0000ff);
buffer}
}
.err = jpeg_std_error(&jerr);
cinfo(&cinfo);
jpeg_create_compress(&cinfo, outfile);
jpeg_stdio_dest
.image_width = img->width;
cinfo.image_height = img->height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
cinfo
(&cinfo);
jpeg_set_defaults(&cinfo, 85, TRUE);
jpeg_set_quality(&cinfo, TRUE);
jpeg_start_compress
(cinfo.next_scanline < cinfo.image_height) {
while = (JSAMPROW) &buffer[cinfo.next_scanline
row_pointer *(img->depth>>3)*img->width];
(&cinfo, &row_pointer, 1);
jpeg_write_scanlines}
(buffer);
free(&cinfo);
jpeg_finish_compress(outfile);
fclose
0;
return }
(Display* display, Window *root)
Pixmap GetRootPixmap{
;
Pixmap currentRootPixmapact_type;
Atom ;
int act_formatlong nitems, bytes_after;
unsigned char *data = NULL;
unsigned ;
Atom _XROOTPMAP_ID
= XInternAtom(display, "_XROOTPMAP_ID", False);
_XROOTPMAP_ID
(XGetWindowProperty(display, *root, _XROOTPMAP_ID, 0, 1, False,
if , &act_type, &act_format, &nitems, &bytes_after,
XA_PIXMAP&data) == Success) {
(data) {
if = *((Pixmap *) data);
currentRootPixmap (data);
XFree}
}
;
return currentRootPixmap}
int main(int argc, const char *argv[])
{
;
int screen;
Window root* display;
Display;
XWindowAttributes attrs;
Pixmap bg* img;
XImage
(argc < 2) {
if (stderr, "Usage: %s [filename]\n", argv[0]);
fprintf1;
return }
= XOpenDisplay(getenv("DISPLAY"));
display
(display == NULL) {
if (stderr, "cannot connect to X server %s\n",
fprintf("DISPLAY") ? getenv("DISPLAY") : "(default)");
getenv1;
return }
= DefaultScreen(display);
screen
= RootWindow(display, DefaultScreen(display));
root (display, root, &attrs);
XGetWindowAttributes
= GetRootPixmap(display, &root);
bg = XGetImage(display, bg, 0, 0, attrs.width, attrs.height, ~0, ZPixmap);
img (display, bg);
XFreePixmap
(!img) {
if (stderr, "Can't create ximage\n");
fprintf1;
return }
(img->depth != 24) {
if (stderr, "Image depth is %d. Must be 24 bits.\n", img->depth);
fprintf1;
return }
(write_jpeg(img, argv[1])) {
if ("JPEG file successfully written to %s\n", argv[1]);
printf}
(img);
XDestroyImage
0;
return }