diff --git a/src/main.rs b/src/main.rs index 5772596..5eb0564 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use std::thread; use std::time::Duration; use x11rb::wrapper::ConnectionExt as _; @@ -6,12 +6,16 @@ use x11rb::{ connection::Connection, protocol::xproto::{ AtomEnum, ChangeWindowAttributesAux, CloseDown, ConnectionExt, CreateGCAux, EventMask, - PropMode, Rectangle, + ImageFormat, PropMode, }, rust_connection::RustConnection, }; fn main() -> Result<()> { + // Path to wallpaper image. + // This is hardcoded for now and will later be replaced by config loading. + let image_path = "/path/to/image"; + // Connect to the running graphical session, the X11 server. let (conn, screen_num) = RustConnection::connect(None)?; // Select the current screen. @@ -54,30 +58,54 @@ fn main() -> Result<()> { let pixmap = conn.generate_id()?; conn.create_pixmap(depth, pixmap, root, width, height)?; - // Fill the pixmap with the pixels to be set as the background. - // For now, allocate a solid color (RGB 26,132,74 scaled to X11 16-bit format). - let color = conn - .alloc_color(screen.default_colormap, 26 * 257, 132 * 257, 74 * 257)? - .reply()? - .pixel; + // Load the wallpaper image from disk. + let img = + image::open(image_path).with_context(|| format!("Failed to open image: {image_path}"))?; - // Create a Graphics Context (GC), which defines how drawing is done. - // It stores drawing parameters such as color, fill style, etc. + // Convert the image into RGBA8 (4 bytes per pixel). + // Predictable, CPU-friendly format to work on. + let img = img.to_rgba8(); + + // Resize the image to exactly match the screen size. + // This uses a simple "fill" strategy (no aspect ratio preservation). + let img = image::imageops::resize( + &img, + width.into(), + height.into(), + image::imageops::FilterType::Lanczos3, + ); + + // Extract raw pixel bytes from the image buffer. + let mut pixel_data = img.into_raw(); + + // Image libraries produce RGBA, but X11 TrueColor visuals typically use + // BGRX/BGRA byte order on little-endian systems. + // Swap red and blue channels to match the server’s native pixel layout. + // Without this conversion, colors appear incorrect. + for pixel in pixel_data.chunks_exact_mut(4) { + pixel.swap(0, 2); // Swap Red and Blue channels + } + + // Create a Graphics Context (GC). + // A GC is required by X11 for image uploads, even though most of its + // drawing settings are unused when using put_image. let gc = conn.generate_id()?; - // Associate the GC with the pixmap and configure its foreground color. - conn.create_gc(gc, pixmap, &CreateGCAux::new().foreground(color))?; + conn.create_gc(gc, pixmap, &CreateGCAux::new())?; - // Paint a solid-color rectangle onto the pixmap using the GC. - // This is only for the solid-color case and will be replaced when using real images. - conn.poly_fill_rectangle( - pixmap, // BG Image (pixmap) - gc, // The Graphics Context to paint on - &[Rectangle { - x: 0, - y: 0, // Start from top-left corner - width, - height, // Set rectangle dimensions = screen width, height - }], + // Upload the image pixels into the pixmap. + // This writes the final wallpaper image into server memory. + // No scaling or color conversion happens here; the data must already be correct. + conn.put_image( + ImageFormat::Z_PIXMAP, + pixmap, // drawable: Destination pixmap (wallpaper image) + gc, // Graphics Context (required by X11) + width, + height, // width/height: size of the uploaded image region in pixels + 0, + 0, // dst_x/dst_y: Destination offset inside the pixmap (top-left corner) + 0, // left_pad: legacy bitmap padding, always 0 for modern images + depth, // depth: must match the root window’s depth (usually 24 or 32) + &pixel_data, // data: raw BGRA/BGRX pixel buffer )?; // Grab and lock the X server to prevent other clients from observing or reacting to intermediate state. @@ -113,7 +141,7 @@ fn main() -> Result<()> { let atom_root = conn.intern_atom(false, b"_XROOTPMAP_ID")?.reply()?.atom; let atom_setroot = conn.intern_atom(false, b"_XSETROOT_ID")?.reply()?.atom; - // Set root window properties that publish the wallpaper pixmap location to consumers. + // Set root window properties that publish the wallpaper pixmap location to other clients. conn.change_property32( PropMode::REPLACE, root,