Skip to content

Instantly share code, notes, and snippets.

@AlecsFerra
Last active August 29, 2024 16:18
Show Gist options
  • Save AlecsFerra/ef1cc008990319f3b676eb2d8aa89903 to your computer and use it in GitHub Desktop.
Save AlecsFerra/ef1cc008990319f3b676eb2d8aa89903 to your computer and use it in GitHub Desktop.
POC for simple animated wallpapers in Xorg
#define _POSIX_C_SOURCE 199309L
//#define DEBUG
#include <Imlib2.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct {
Window root;
Pixmap pixmap;
Imlib_Context *render_context;
int width, height;
} Monitor;
void setRootAtoms(Display *display, Monitor *monitor) {
Atom atom_root, atom_eroot, type;
unsigned char *data_root, *data_eroot;
int format;
unsigned long length, after;
atom_root = XInternAtom(display, "_XROOTMAP_ID", True);
atom_eroot = XInternAtom(display, "ESETROOT_PMAP_ID", True);
// doing this to clean up after old background
if (atom_root != None && atom_eroot != None) {
XGetWindowProperty(display, monitor->root, atom_root, 0L, 1L, False,
AnyPropertyType, &type, &format, &length, &after,
&data_root);
if (type == XA_PIXMAP) {
XGetWindowProperty(display, monitor->root, atom_eroot, 0L, 1L, False,
AnyPropertyType, &type, &format, &length, &after,
&data_eroot);
if (data_root && data_eroot && type == XA_PIXMAP &&
*((Pixmap *)data_root) == *((Pixmap *)data_eroot))
XKillClient(display, *((Pixmap *)data_root));
}
}
atom_root = XInternAtom(display, "_XROOTPMAP_ID", False);
atom_eroot = XInternAtom(display, "ESETROOT_PMAP_ID", False);
// setting new background atoms
XChangeProperty(display, monitor->root, atom_root, XA_PIXMAP, 32,
PropModeReplace, (unsigned char *)&monitor->pixmap, 1);
XChangeProperty(display, monitor->root, atom_eroot, XA_PIXMAP, 32,
PropModeReplace, (unsigned char *)&monitor->pixmap, 1);
}
int main(int argc, char *argv[]) {
#ifdef DEBUG
fprintf(stdout, "Loading images");
#endif
Imlib_Image images[] = {
imlib_load_image("/home/alecs/Repos/paperview/scenes/castle/out-0.bmp"),
imlib_load_image("/home/alecs/Repos/paperview/scenes/castle/out-1.bmp"),
imlib_load_image("/home/alecs/Repos/paperview/scenes/castle/out-2.bmp"),
imlib_load_image("/home/alecs/Repos/paperview/scenes/castle/out-3.bmp"),
imlib_load_image("/home/alecs/Repos/paperview/scenes/castle/out-4.bmp"),
imlib_load_image("/home/alecs/Repos/paperview/scenes/castle/out-5.bmp"),
imlib_load_image("/home/alecs/Repos/paperview/scenes/castle/out-6.bmp"),
imlib_load_image("/home/alecs/Repos/paperview/scenes/castle/out-7.bmp"),
};
int images_count = 8;
#ifdef DEBUG
fprintf(stdout, "Loading monitors\n");
#endif
Display *display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "Could not open XDisplay\n");
exit(42);
}
const int screen_count = ScreenCount(display);
#ifdef DEBUG
fprintf(stdout, "Found %d screens\n", screen_count);
#endif
Monitor *monitors = malloc(sizeof(Monitor) * screen_count);
for (int current_screen = 0; current_screen < screen_count;
++current_screen) {
#ifdef DEBUG
fprintf(stdout, "Running screen %d\n", current_screen);
#endif
const int width = DisplayWidth(display, current_screen);
const int height = DisplayHeight(display, current_screen);
const int depth = DefaultDepth(display, current_screen);
Visual *vis = DefaultVisual(display, current_screen);
const int cm = DefaultColormap(display, current_screen);
#ifdef DEBUG
fprintf(stdout, "Screen %d: width: %d, height: %d, depth: %d\n",
current_screen, width, height, depth);
#endif
Window root = RootWindow(display, current_screen);
Pixmap pixmap = XCreatePixmap(display, root, width, height, depth);
monitors[current_screen].width = width;
monitors[current_screen].height = height;
monitors[current_screen].root = root;
monitors[current_screen].pixmap = pixmap;
monitors[current_screen].render_context = imlib_context_new();
imlib_context_push(monitors[current_screen].render_context);
imlib_context_set_display(display);
imlib_context_set_visual(vis);
imlib_context_set_colormap(cm);
imlib_context_set_drawable(pixmap);
imlib_context_set_color_range(imlib_create_color_range());
imlib_context_pop();
}
#ifdef DEBUG
fprintf(stdout, "Loaded %d screens\n", screen_count);
#endif
#ifdef DEBUG
fprintf(stdout, "Starting render loop");
#endif
struct timespec timeout;
timeout.tv_sec = 0;
timeout.tv_nsec = 33000000;
for (int cycle = 0; cycle < 10; ++cycle) {
Imlib_Image current = images[cycle % images_count];
for (int monitor = 0; monitor < screen_count; ++monitor) {
Monitor *c_monitor = &monitors[monitor];
imlib_context_push(c_monitor->render_context);
imlib_context_set_dither(1);
imlib_context_set_blend(1);
imlib_context_set_image(current);
imlib_render_image_on_drawable(0, 0);
setRootAtoms(display, c_monitor);
XKillClient(display, AllTemporary);
XSetCloseDownMode(display, RetainTemporary);
XSetWindowBackgroundPixmap(display, c_monitor->root, c_monitor->pixmap);
XClearWindow(display, c_monitor->root);
XFlush(display);
XSync(display, False);
imlib_context_pop();
}
nanosleep(&timeout, NULL);
}
}
@AlecsFerra
Copy link
Author

To enable logging you should uncomment line 2
To slow down the program try setting tv.sec to some higher value
I didn't understand the comment about the external monitor

@Unkn0wable
Copy link

Unkn0wable commented Apr 22, 2022

ACK! Wow, whatta month. Sorry about the delay in answering, Alecs. When I wrote this I had like 5 browser windows open, my machine crashed, and when it came back up it only opened one, so I lost like 40 tabs of important stuff I was doing. Took some doing, but I'm back in the game. Here's my answer:

Yes, I hadn't enabled debugging before, but that is super useful, you're right. I'll get to the rest of this when I address your last comment.

I'm betting you meant the "timeout.tv_sec = " line, which after the mods in my file is line 125 (I only have two wallpapers so I removed a few lines.) I currently have it as high as I can get it to compile:

timeout.tv_sec = 1000000000000000000;
// timeout.tv_nsec = 330000000000000;

I've tried messing with the other line (timeout.tv_nsec), but no real effect. As far as increasing the first line listed, I've got it up as far as I can and it's still blinking so fast I can barely see it. Obviously I removed the // (yes, I do know enough to understand that that comments the line out.) I tried it a couple of different ways, with one line commented out, then the other, and so on. No changes. I also tried pasting the same lines over and over where it lists the wallpapers to see if I could slow it down just a hair, still no effect:


Imlib_Image images[] = {
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/on.bmp"),
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/on.bmp"),
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/on.bmp"),
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/on.bmp"),
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/on.bmp"),
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/off.bmp"),
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/off.bmp"),
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/off.bmp"),
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/off.bmp"),
imlib_load_image("/home/unknowable/!everything_else/Pictures/CPM/off.bmp"),
};
int images_count = 10;


It was still super fast, no real change there either.

As far as your last comment on the external monitor, what I was referring to is that I have a second monitor. It's not a desktop display though, it's a projector that shows the display on the wall almost 3 meters across. I have the desktop extended onto the projector though, not mirrored. The computer just sees it as another monitor though, so anything I can show on my desktop monitor will slide over to the projector, no big deal.

However, that brings up the debug info. Since you told me to do that, I get some pretty useful info:


unknowable@ANDROID:/animated_wallpaper$ gcc animated_wallpaper.c -lX11 -lImlib2
unknowable@ANDROID:
/animated_wallpaper$ ./a.out
Loading imagesLoading monitors
Found 1 screens
Running screen 0
Screen 0: width: 3840, height: 1080, depth: 24
Loaded 1 screens
^C
unknowable@ANDROID:~/animated_wallpaper$


Sorry for the ugly text, couldn't get the "Add code" button to work right. It seems to be removing all the line breaks and putting everything on one line.

The output above tells me that it only sees one giant screen. Both my displays are set at 1920x1080, but the script is seeing them together as only one screen showing 3840x1080. That must be why the desktop LCD shows the wallpapers while the projector's picture looks all weird and messed up. I guess if I wanted to, I could just double the width of both wallpapers - just copy and paste the image twice onto a new image same height, double the width. Hm. Think I'll try that right now. Should just take a minute...

Anyway, that's where I am, and why I disappeared. So I am really, REALLY sorry about ditching you. I think I know where I am set now, here in GitHub. :) So I'll just double the width and see if I can at least get it on both displays. In the meantime, let me know if you have any more thoughts on slowing it down.

Thanks again! This script rules and so does the guy who wrote it! :D Thanks Alecs!


EDIT 1: I've actually been awake for a couple of days and I have something important coming up tonight, so I'll actually do the double sizing (and whatever else you suggest) much later after sleeping. Night Alecs. Thanks again for dealing with us end users. I'm trying to learn I promise. :)


EDIT 2: Right after I wrote the above edit, I realized I hadn't eaten in like 5 hours. I went to go eat, and now I'm all supercharged again. :D

Made the change I described, basically copy/pasting both wallpapers from their original 1920x1080 into a double-wide 3840x1080 image. Both the ON and OFF pics appear twice in each image, one on the left side and one on the right. That makes it stretch across both displays so I have CP/M running on both monitors. grin Also, those pics are now just over 12MB in size each so it takes just a hair longer to load them up so and does look just a tiny bit slower.

Not a huge resolution on speed, but I've had wallpaper on only one screen for like a month, and now I have CP/M flashing on both my desktop LCD and the projector, so I'm going bananas. :D This script keeps getting better the more I learn how to work it.

Thanks again for authoring, then supporting your script, Alecs. You simply rule. No two ways about that one.

@CardealRusso
Copy link

CardealRusso commented Aug 21, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment