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();