Minimal working solid background setter
- First step, to understand the concepts - No background image yet - only hard-coded solid color - Comments for explanation and readability
This commit is contained in:
parent
26dba7d111
commit
d1e3a3606e
165
src/main.rs
165
src/main.rs
@ -1,3 +1,164 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use anyhow::Result;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use x11rb::wrapper::ConnectionExt as _;
|
||||
use x11rb::{
|
||||
connection::Connection,
|
||||
protocol::xproto::{
|
||||
AtomEnum, ChangeWindowAttributesAux, CloseDown, ConnectionExt, CreateGCAux, EventMask,
|
||||
PropMode, Rectangle,
|
||||
},
|
||||
rust_connection::RustConnection,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Connect to the running graphical session, the X11 server.
|
||||
let (conn, screen_num) = RustConnection::connect(None)?;
|
||||
// Select the current screen.
|
||||
let screen = &conn.setup().roots[screen_num];
|
||||
// Find the "root window," i.e., the desktop where you set the background.
|
||||
let root = screen.root;
|
||||
|
||||
// Short delay to avoid race conditions that can cause the WM to overwrite the wallpaper.
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
|
||||
// Ensure X11 resources created by this process remain valid after exit.
|
||||
// To keep the wallpaper persistent after `icing` exits.
|
||||
conn.set_close_down_mode(CloseDown::RETAIN_PERMANENT)?;
|
||||
|
||||
// Atom (property) used to track ownership of the current wallpaper pixmap.
|
||||
// This is read to find and clean up the previous wallpaper owner.
|
||||
let atom_eset = conn.intern_atom(false, b"ESETROOT_PMAP_ID")?.reply()?.atom;
|
||||
|
||||
// Remove any previously registered wallpaper pixmap.
|
||||
// This prevents resource leaks and forces consumers to refresh.
|
||||
if let Ok(reply) = conn
|
||||
// Look for previous background image pixmap.
|
||||
.get_property(false, root, atom_eset, AtomEnum::PIXMAP, 0, 1)?
|
||||
.reply()
|
||||
// Get any existing old one, if it exists.
|
||||
&& let Some(old) = reply.value32().and_then(|mut v| v.next())
|
||||
{
|
||||
// Kill the client that owns the old wallpaper pixmap.
|
||||
// Allows X server to free old pixmap and forces clients to refresh if cached.
|
||||
conn.kill_client(old)?;
|
||||
}
|
||||
|
||||
// Get screen dimensions and color depth
|
||||
let width = screen.width_in_pixels;
|
||||
let height = screen.height_in_pixels;
|
||||
let depth = screen.root_depth;
|
||||
|
||||
// Create a pixmap (image) matching the screen size and depth.
|
||||
// This pixmap will become the desktop background.
|
||||
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;
|
||||
|
||||
// Create a Graphics Context (GC), which defines how drawing is done.
|
||||
// It stores drawing parameters such as color, fill style, etc.
|
||||
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))?;
|
||||
|
||||
// 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
|
||||
}],
|
||||
)?;
|
||||
|
||||
// Grab and lock the X server to prevent other clients from observing or reacting to intermediate state.
|
||||
// Prevents the WM or other apps from acting (repainting, caching, etc.) while the background is being updated.
|
||||
conn.grab_server()?;
|
||||
|
||||
// Temporarily suppress root window event notifications to the WM until the process is done.
|
||||
// Prevent the WM from reacting mid-update, reading outdated data, repainting the background, or ignoring the final background.
|
||||
conn.change_window_attributes(
|
||||
root,
|
||||
&ChangeWindowAttributesAux::new().event_mask(EventMask::NO_EVENT),
|
||||
)?;
|
||||
|
||||
// Core operation: assign the pixmap as the root window's background. Aka set the wallpaper.
|
||||
conn.change_window_attributes(
|
||||
root,
|
||||
&ChangeWindowAttributesAux::new().background_pixmap(pixmap),
|
||||
)?;
|
||||
|
||||
// Request the root window to redraw itself using its current background pixmap.
|
||||
// "Clear area": Historical X11 terminology stuck without name change.
|
||||
// Original meaning: "Erase drawn content on top, restore the window's (historically plain) background."
|
||||
// Current meaning, practically: "Fill the region using the window's current background (color or pixmap)."
|
||||
conn.clear_area(
|
||||
false, // Exposures: false. Do not generate expose events (redraw requests) for clients; avoids waking other clients during the update.
|
||||
root, // Window: The desktop window (root).
|
||||
0, 0, // x,y: Start at the top-left corner.
|
||||
0, 0, // Width, height: 0 means "the entire window."
|
||||
)?;
|
||||
|
||||
// Atoms (properties) used by WMs, compositors, and client apps (e.g., for transparency effects) to locate and reuse the wallpaper pixmap.
|
||||
// There's no standard. Different apps rely on different properties: `_XROOTPMAP_ID`, `ESETROOT_PMAP_ID`, & `_XSETROOT_ID`. Better use all 3.
|
||||
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.
|
||||
conn.change_property32(
|
||||
PropMode::REPLACE,
|
||||
root,
|
||||
atom_root, // _XROOTPMAP_ID: For discovery.
|
||||
AtomEnum::PIXMAP,
|
||||
&[pixmap],
|
||||
)?;
|
||||
conn.change_property32(
|
||||
PropMode::REPLACE,
|
||||
root,
|
||||
atom_eset, // ESETROOT_PMAP_ID: For discovery and ownership / lifecycle.
|
||||
AtomEnum::PIXMAP,
|
||||
&[pixmap],
|
||||
)?;
|
||||
conn.change_property32(
|
||||
PropMode::REPLACE,
|
||||
root,
|
||||
atom_setroot, // _XSETROOT_ID: For discovery, but legacy.
|
||||
AtomEnum::PIXMAP,
|
||||
&[pixmap],
|
||||
)?;
|
||||
|
||||
// Restore root window event notifications required by the window manager.
|
||||
// Without these, the WM cannot track window creation, layout changes, or property updates, which breaks normal window management.
|
||||
conn.change_window_attributes(
|
||||
root,
|
||||
&ChangeWindowAttributesAux::new().event_mask(
|
||||
// Allows the WM to control and manage child windows (positioning, tiling, etc.).
|
||||
EventMask::SUBSTRUCTURE_REDIRECT
|
||||
// Notifies the WM about changes to managed windows (open, close, move, etc.).
|
||||
| EventMask::SUBSTRUCTURE_NOTIFY
|
||||
// Notifies listeners when root window properties change (like wallpaper). This is mainly what was disabled earlier.
|
||||
| EventMask::PROPERTY_CHANGE,
|
||||
),
|
||||
)?;
|
||||
|
||||
// Release the server lock and allow normal processing to resume, since the background is set.
|
||||
conn.ungrab_server()?;
|
||||
|
||||
// Send all required updates in queue to X11, refresh immediately.
|
||||
// Ensure the requests are sent and everything's done.
|
||||
conn.flush()?;
|
||||
|
||||
// Short delay to ensure the server processes all requests before `icing` exits.
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user