diff --git a/CLAUDE.md b/CLAUDE.md index 99ae28b..395fc30 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,7 +19,7 @@ Teil des Moonarch-Ökosystems. ## Projektstruktur - `src/` — Rust-Quellcode (main.rs, lockscreen.rs, auth.rs, fingerprint.rs, config.rs, i18n.rs, users.rs, power.rs) -- `resources/` — GResource-Assets (style.css, wallpaper.jpg, default-avatar.svg) +- `resources/` — GResource-Assets (style.css, default-avatar.svg) - `config/` — PAM-Konfiguration und Beispiel-Config ## Kommandos diff --git a/Cargo.lock b/Cargo.lock index 2b7e2ea..fd2c74a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "moonlock" -version = "0.6.0" +version = "0.6.2" dependencies = [ "gdk-pixbuf", "gdk4", diff --git a/Cargo.toml b/Cargo.toml index 52638fe..c057e46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "moonlock" -version = "0.6.1" +version = "0.6.2" edition = "2024" description = "A secure Wayland lockscreen with GTK4, PAM and fingerprint support" license = "MIT" diff --git a/DECISIONS.md b/DECISIONS.md index cc156ce..722b862 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -2,6 +2,13 @@ Architectural and design decisions for Moonlock, in reverse chronological order. +## 2026-03-28 – Remove embedded wallpaper from binary + +- **Who**: Nyx, Dom +- **Why**: Wallpaper is installed by moonarch to /usr/share/moonarch/wallpaper.jpg. Embedding a 374K JPEG in the binary is redundant. GTK background color (Catppuccin Mocha base) is a clean fallback. +- **Tradeoffs**: Without moonarch installed AND without config, lockscreen shows plain dark background instead of wallpaper. Acceptable — that's the expected minimal state. +- **How**: Remove wallpaper.jpg from GResources, return None from resolve_background_path when no file found, skip background picture creation when no texture available. + ## 2026-03-28 – Audit-driven security and lifecycle fixes (v0.6.0) - **Who**: Nyx, Dom diff --git a/resources/resources.gresource.xml b/resources/resources.gresource.xml index 84f8548..af39301 100644 --- a/resources/resources.gresource.xml +++ b/resources/resources.gresource.xml @@ -2,7 +2,6 @@ style.css - wallpaper.jpg default-avatar.svg diff --git a/resources/wallpaper.jpg b/resources/wallpaper.jpg deleted file mode 100644 index 86371cd..0000000 Binary files a/resources/wallpaper.jpg and /dev/null differ diff --git a/src/config.rs b/src/config.rs index 022b9e8..c190b1e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,7 +6,6 @@ use std::fs; use std::path::{Path, PathBuf}; const MOONARCH_WALLPAPER: &str = "/usr/share/moonarch/wallpaper.jpg"; -const GRESOURCE_PREFIX: &str = "/dev/moonarch/moonlock"; fn default_config_paths() -> Vec { let mut paths = vec![PathBuf::from("/etc/moonlock/moonlock.toml")]; @@ -64,17 +63,17 @@ pub fn load_config(config_paths: Option<&[PathBuf]>) -> Config { merged } -pub fn resolve_background_path(config: &Config) -> PathBuf { +pub fn resolve_background_path(config: &Config) -> Option { resolve_background_path_with(config, Path::new(MOONARCH_WALLPAPER)) } -pub fn resolve_background_path_with(config: &Config, moonarch_wallpaper: &Path) -> PathBuf { +pub fn resolve_background_path_with(config: &Config, moonarch_wallpaper: &Path) -> Option { if let Some(ref bg) = config.background_path { let path = PathBuf::from(bg); - if path.is_file() && !path.is_symlink() { return path; } + if path.is_file() && !path.is_symlink() { return Some(path); } } - if moonarch_wallpaper.is_file() { return moonarch_wallpaper.to_path_buf(); } - PathBuf::from(format!("{GRESOURCE_PREFIX}/wallpaper.jpg")) + if moonarch_wallpaper.is_file() { return Some(moonarch_wallpaper.to_path_buf()); } + None } #[cfg(test)] @@ -109,7 +108,7 @@ mod tests { let dir = tempfile::tempdir().unwrap(); let wp = dir.path().join("bg.jpg"); fs::write(&wp, "fake").unwrap(); let c = Config { background_path: Some(wp.to_str().unwrap().to_string()), ..Config::default() }; - assert_eq!(resolve_background_path_with(&c, Path::new("/nonexistent")), wp); + assert_eq!(resolve_background_path_with(&c, Path::new("/nonexistent")), Some(wp)); } #[test] fn empty_user_config_preserves_system_fingerprint() { let dir = tempfile::tempdir().unwrap(); @@ -120,10 +119,10 @@ mod tests { let c = load_config(Some(&[sys_conf, usr_conf])); assert!(!c.fingerprint_enabled); } - #[test] fn resolve_gresource_fallback() { + #[test] fn resolve_no_wallpaper_returns_none() { let c = Config::default(); let r = resolve_background_path_with(&c, Path::new("/nonexistent")); - assert!(r.to_str().unwrap().contains("moonlock")); + assert!(r.is_none()); } #[test] fn toml_parse_error_returns_default() { let dir = tempfile::tempdir().unwrap(); @@ -141,8 +140,8 @@ mod tests { fs::write(&real, "fake").unwrap(); std::os::unix::fs::symlink(&real, &link).unwrap(); let c = Config { background_path: Some(link.to_str().unwrap().to_string()), ..Config::default() }; - // Symlink should be rejected — falls through to moonarch wallpaper or gresource + // Symlink should be rejected — falls through to None let r = resolve_background_path_with(&c, Path::new("/nonexistent")); - assert_ne!(r, link); + assert!(r.is_none()); } } diff --git a/src/lockscreen.rs b/src/lockscreen.rs index 3c06086..30a4ab3 100644 --- a/src/lockscreen.rs +++ b/src/lockscreen.rs @@ -44,7 +44,7 @@ struct LockscreenState { /// The `blur_cache` and `avatar_cache` are shared across monitors for multi-monitor /// setups, avoiding redundant GPU renders and SVG rasterizations. pub fn create_lockscreen_window( - bg_texture: &gdk::Texture, + bg_texture: Option<&gdk::Texture>, config: &Config, app: >k::Application, unlock_callback: Rc, @@ -86,9 +86,11 @@ pub fn create_lockscreen_window( let overlay = gtk::Overlay::new(); window.set_child(Some(&overlay)); - // Background wallpaper - let background = create_background_picture(bg_texture, config.background_blur, blur_cache); - overlay.set_child(Some(&background)); + // Background wallpaper (if available — otherwise GTK background color shows through) + if let Some(texture) = bg_texture { + let background = create_background_picture(texture, config.background_blur, blur_cache); + overlay.set_child(Some(&background)); + } // Centered vertical box let main_box = gtk::Box::new(gtk::Orientation::Vertical, 0); @@ -430,18 +432,16 @@ pub fn start_fingerprint( } /// Load the wallpaper as a texture once, for sharing across all windows. +/// Returns None if no wallpaper path is provided or the file cannot be loaded. /// Blur is applied at render time via GPU (GskBlurNode), not here. -pub fn load_background_texture(bg_path: &Path) -> gdk::Texture { - let fallback = "/dev/moonarch/moonlock/wallpaper.jpg"; - - if bg_path.starts_with("/dev/moonarch/moonlock") { - let resource_path = bg_path.to_str().unwrap_or(fallback); - gdk::Texture::from_resource(resource_path) - } else { - let file = gio::File::for_path(bg_path); - gdk::Texture::from_file(&file).unwrap_or_else(|_| { - gdk::Texture::from_resource(fallback) - }) +pub fn load_background_texture(bg_path: &Path) -> Option { + let file = gio::File::for_path(bg_path); + match gdk::Texture::from_file(&file) { + Ok(texture) => Some(texture), + Err(e) => { + log::warn!("Failed to load wallpaper {}: {e}", bg_path.display()); + None + } } } diff --git a/src/main.rs b/src/main.rs index 5d9d458..892f440 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,16 +40,16 @@ fn activate(app: >k::Application) { load_css(&display); let config = config::load_config(None); - let bg_path = config::resolve_background_path(&config); - let bg_texture = lockscreen::load_background_texture(&bg_path); + let bg_texture = config::resolve_background_path(&config) + .and_then(|path| lockscreen::load_background_texture(&path)); if gtk4_session_lock::is_supported() { - activate_with_session_lock(app, &display, &bg_texture, &config); + activate_with_session_lock(app, &display, bg_texture.as_ref(), &config); } else { #[cfg(debug_assertions)] { log::warn!("ext-session-lock-v1 not supported — running in development mode"); - activate_without_lock(app, &bg_texture, &config); + activate_without_lock(app, bg_texture.as_ref(), &config); } #[cfg(not(debug_assertions))] { @@ -62,7 +62,7 @@ fn activate(app: >k::Application) { fn activate_with_session_lock( app: >k::Application, display: &gdk::Display, - bg_texture: &gdk::Texture, + bg_texture: Option<&gdk::Texture>, config: &config::Config, ) { let lock = gtk4_session_lock::Instance::new(); @@ -158,7 +158,7 @@ fn init_fingerprint_async(all_handles: Vec) { #[cfg(debug_assertions)] fn activate_without_lock( app: >k::Application, - bg_texture: &gdk::Texture, + bg_texture: Option<&gdk::Texture>, config: &config::Config, ) { let app_clone = app.clone();