Compare commits

...

3 Commits

Author SHA1 Message Date
nevaforget 77b94a560d fix: prevent edge darkening on GPU-blurred wallpaper (v0.5.3)
GskBlurNode samples pixels outside texture bounds as transparent,
causing visible darkening at wallpaper edges. Fix renders the texture
with 3x-sigma padding before blur, then clips back to original size.
Symmetric fix with moonset v0.7.1.
2026-03-28 23:28:39 +01:00
nevaforget b06b02faac refactor: remove embedded wallpaper from binary (v0.5.2)
Wallpaper is installed by moonarch to /usr/share/moonarch/wallpaper.jpg.
Embedding a 374K JPEG in the binary was redundant. Without a wallpaper
file, GTK background color (Catppuccin Mocha base) shows through and
wallpaper-only windows on secondary monitors are skipped.
2026-03-28 23:26:33 +01:00
nevaforget 9a89da8b13 docs: update for wallpaper removal from binary
Sync documentation with greetd-moongreet wallpaper removal.
2026-03-28 23:23:10 +01:00
9 changed files with 62 additions and 68 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ Teil des Moonarch-Ökosystems.
## Projektstruktur ## Projektstruktur
- `src/` — Rust-Quellcode (main.rs, greeter.rs, ipc.rs, config.rs, users.rs, sessions.rs, i18n.rs, power.rs) - `src/` — Rust-Quellcode (main.rs, greeter.rs, ipc.rs, config.rs, users.rs, sessions.rs, i18n.rs, power.rs)
- `resources/` — GResource-Assets (style.css, wallpaper.jpg, default-avatar.svg) - `resources/` — GResource-Assets (style.css, default-avatar.svg)
- `config/` — Beispiel-Konfigurationsdateien für `/etc/moongreet/` und `/etc/greetd/` - `config/` — Beispiel-Konfigurationsdateien für `/etc/moongreet/` und `/etc/greetd/`
- `pkg/` — PKGBUILD für Arch-Linux-Paketierung (`makepkg -sf`) - `pkg/` — PKGBUILD für Arch-Linux-Paketierung (`makepkg -sf`)
Generated
+1 -1
View File
@@ -569,7 +569,7 @@ dependencies = [
[[package]] [[package]]
name = "moongreet" name = "moongreet"
version = "0.5.0" version = "0.5.3"
dependencies = [ dependencies = [
"gdk-pixbuf", "gdk-pixbuf",
"gdk4", "gdk4",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "moongreet" name = "moongreet"
version = "0.5.1" version = "0.5.3"
edition = "2024" edition = "2024"
description = "A greetd greeter for Wayland with GTK4 and Layer Shell" description = "A greetd greeter for Wayland with GTK4 and Layer Shell"
license = "MIT" license = "MIT"
+7
View File
@@ -1,5 +1,12 @@
# Decisions # Decisions
## 2026-03-28 Remove embedded wallpaper from binary
- **Who**: Selene, 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, greeter 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 wallpaper window creation and background picture when no path available.
## 2026-03-28 GPU blur via GskBlurNode replaces CPU blur ## 2026-03-28 GPU blur via GskBlurNode replaces CPU blur
- **Who**: Ragnar, Dom - **Who**: Ragnar, Dom
-1
View File
@@ -2,7 +2,6 @@
<gresources> <gresources>
<gresource prefix="/dev/moonarch/moongreet"> <gresource prefix="/dev/moonarch/moongreet">
<file>style.css</file> <file>style.css</file>
<file>wallpaper.jpg</file>
<file>default-avatar.svg</file> <file>default-avatar.svg</file>
</gresource> </gresource>
</gresources> </gresources>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 KiB

+12 -14
View File
@@ -6,7 +6,6 @@ use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
const MOONARCH_WALLPAPER: &str = "/usr/share/moonarch/wallpaper.jpg"; const MOONARCH_WALLPAPER: &str = "/usr/share/moonarch/wallpaper.jpg";
const GRESOURCE_PREFIX: &str = "/dev/moonarch/moongreet";
/// Default config search path: system-wide config. /// Default config search path: system-wide config.
fn default_config_paths() -> Vec<PathBuf> { fn default_config_paths() -> Vec<PathBuf> {
@@ -84,19 +83,19 @@ pub fn load_config(config_paths: Option<&[PathBuf]>) -> Config {
/// Resolve the wallpaper path using the fallback hierarchy. /// Resolve the wallpaper path using the fallback hierarchy.
/// ///
/// Priority: config background_path > Moonarch system default > gresource fallback. /// Priority: config background_path > Moonarch system default > None (GTK background color).
pub fn resolve_background_path(config: &Config) -> PathBuf { pub fn resolve_background_path(config: &Config) -> Option<PathBuf> {
resolve_background_path_with(config, Path::new(MOONARCH_WALLPAPER)) resolve_background_path_with(config, Path::new(MOONARCH_WALLPAPER))
} }
/// Resolve with configurable moonarch wallpaper path (for testing). /// Resolve with configurable moonarch wallpaper path (for testing).
pub fn resolve_background_path_with(config: &Config, moonarch_wallpaper: &Path) -> PathBuf { pub fn resolve_background_path_with(config: &Config, moonarch_wallpaper: &Path) -> Option<PathBuf> {
// User-configured path // User-configured path
if let Some(ref bg) = config.background_path { if let Some(ref bg) = config.background_path {
let path = PathBuf::from(bg); let path = PathBuf::from(bg);
if path.is_file() { if path.is_file() {
log::debug!("Wallpaper: using config path {}", path.display()); log::debug!("Wallpaper: using config path {}", path.display());
return path; return Some(path);
} }
log::debug!("Wallpaper: config path {} not found, trying fallbacks", path.display()); log::debug!("Wallpaper: config path {} not found, trying fallbacks", path.display());
} }
@@ -104,12 +103,11 @@ pub fn resolve_background_path_with(config: &Config, moonarch_wallpaper: &Path)
// Moonarch ecosystem default // Moonarch ecosystem default
if moonarch_wallpaper.is_file() { if moonarch_wallpaper.is_file() {
log::debug!("Wallpaper: using moonarch default {}", moonarch_wallpaper.display()); log::debug!("Wallpaper: using moonarch default {}", moonarch_wallpaper.display());
return moonarch_wallpaper.to_path_buf(); return Some(moonarch_wallpaper.to_path_buf());
} }
// GResource fallback path (loaded from compiled resources at runtime) log::debug!("Wallpaper: no wallpaper found, using GTK background color");
log::debug!("Wallpaper: using GResource fallback"); None
PathBuf::from(format!("{GRESOURCE_PREFIX}/wallpaper.jpg"))
} }
#[cfg(test)] #[cfg(test)]
@@ -218,7 +216,7 @@ mod tests {
}; };
assert_eq!( assert_eq!(
resolve_background_path_with(&config, Path::new("/nonexistent")), resolve_background_path_with(&config, Path::new("/nonexistent")),
wallpaper Some(wallpaper)
); );
} }
@@ -229,7 +227,7 @@ mod tests {
..Config::default() ..Config::default()
}; };
let result = resolve_background_path_with(&config, Path::new("/nonexistent")); let result = resolve_background_path_with(&config, Path::new("/nonexistent"));
assert!(result.to_str().unwrap().contains("moongreet")); assert!(result.is_none());
} }
#[test] #[test]
@@ -240,14 +238,14 @@ mod tests {
let config = Config::default(); let config = Config::default();
assert_eq!( assert_eq!(
resolve_background_path_with(&config, &moonarch_wp), resolve_background_path_with(&config, &moonarch_wp),
moonarch_wp Some(moonarch_wp)
); );
} }
#[test] #[test]
fn resolve_uses_gresource_fallback_as_last_resort() { fn resolve_returns_none_when_no_wallpaper_found() {
let config = Config::default(); let config = Config::default();
let result = resolve_background_path_with(&config, Path::new("/nonexistent")); let result = resolve_background_path_with(&config, Path::new("/nonexistent"));
assert!(result.to_str().unwrap().contains("wallpaper.jpg")); assert!(result.is_none());
} }
} }
+35 -41
View File
@@ -95,42 +95,23 @@ fn is_valid_username(name: &str) -> bool {
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '@') .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '@')
} }
/// Load background texture from GResource or filesystem. /// Load background texture from filesystem.
pub fn load_background_texture(bg_path: &Path) -> Option<gdk::Texture> { pub fn load_background_texture(bg_path: &Path) -> Option<gdk::Texture> {
let path_str = bg_path.to_str()?; if let Ok(meta) = std::fs::metadata(bg_path)
if bg_path.starts_with("/dev/moonarch/moongreet") { && meta.len() > MAX_WALLPAPER_FILE_SIZE
match gio::resources_lookup_data(path_str, gio::ResourceLookupFlags::NONE) { {
Ok(bytes) => match gdk::Texture::from_bytes(&bytes) { log::warn!(
Ok(texture) => Some(texture), "Wallpaper file too large ({} bytes), skipping: {}",
Err(e) => { meta.len(), bg_path.display()
log::debug!("GResource texture decode error: {e}"); );
log::warn!("Failed to decode background texture from GResource {path_str}"); return None;
None }
} match gdk::Texture::from_filename(bg_path) {
}, Ok(texture) => Some(texture),
Err(e) => { Err(e) => {
log::debug!("GResource lookup error: {e}"); log::debug!("Wallpaper load error: {e}");
log::warn!("Failed to load background texture from GResource {path_str}"); log::warn!("Failed to load background texture from {}", bg_path.display());
None None
}
}
} else {
if let Ok(meta) = std::fs::metadata(bg_path)
&& meta.len() > MAX_WALLPAPER_FILE_SIZE
{
log::warn!(
"Wallpaper file too large ({} bytes), skipping: {}",
meta.len(), bg_path.display()
);
return None;
}
match gdk::Texture::from_filename(bg_path) {
Ok(texture) => Some(texture),
Err(e) => {
log::debug!("Wallpaper load error: {e}");
log::warn!("Failed to load background texture from {}", bg_path.display());
None
}
} }
} }
} }
@@ -138,6 +119,10 @@ pub fn load_background_texture(bg_path: &Path) -> Option<gdk::Texture> {
// -- GPU blur via GskBlurNode ------------------------------------------------- // -- GPU blur via GskBlurNode -------------------------------------------------
/// Render a blurred texture using the GPU via GskBlurNode. /// Render a blurred texture using the GPU via GskBlurNode.
///
/// To avoid edge darkening (blur samples transparent pixels outside bounds),
/// the texture is rendered with padding equal to 3x the blur sigma. The blur
/// is applied to the padded area, then cropped back to the original size.
fn render_blurred_texture( fn render_blurred_texture(
widget: &impl IsA<gtk::Widget>, widget: &impl IsA<gtk::Widget>,
texture: &gdk::Texture, texture: &gdk::Texture,
@@ -145,15 +130,24 @@ fn render_blurred_texture(
) -> Option<gdk::Texture> { ) -> Option<gdk::Texture> {
let native = widget.native()?; let native = widget.native()?;
let renderer = native.renderer()?; let renderer = native.renderer()?;
let w = texture.width() as f32;
let h = texture.height() as f32;
// Padding must cover the blur kernel radius (typically ~3x sigma)
let pad = (sigma * 3.0).ceil();
let snapshot = gtk::Snapshot::new(); let snapshot = gtk::Snapshot::new();
let bounds = graphene_rs::Rect::new( // Clip output to original texture size
0.0, 0.0, texture.width() as f32, texture.height() as f32, snapshot.push_clip(&graphene_rs::Rect::new(pad, pad, w, h));
);
snapshot.push_blur(sigma as f64); snapshot.push_blur(sigma as f64);
snapshot.append_texture(texture, &bounds); // Render texture with padding on all sides (edges repeat via oversized bounds)
snapshot.pop(); snapshot.append_texture(texture, &graphene_rs::Rect::new(0.0, 0.0, w + 2.0 * pad, h + 2.0 * pad));
snapshot.pop(); // blur
snapshot.pop(); // clip
let node = snapshot.to_node()?; let node = snapshot.to_node()?;
Some(renderer.render_texture(&node, None)) let viewport = graphene_rs::Rect::new(pad, pad, w, h);
Some(renderer.render_texture(&node, Some(&viewport)))
} }
/// Create a wallpaper-only window for secondary monitors. /// Create a wallpaper-only window for secondary monitors.
+5 -9
View File
@@ -51,15 +51,11 @@ fn activate(app: &gtk::Application) {
// Load config and resolve wallpaper // Load config and resolve wallpaper
let config = config::load_config(None); let config = config::load_config(None);
let bg_path = config::resolve_background_path(&config); let bg_texture = config::resolve_background_path(&config)
log::debug!("Background path: {}", bg_path.display()); .and_then(|path| {
log::debug!("Background path: {}", path.display());
// Load background texture once — shared across all windows greeter::load_background_texture(&path)
// Blur is applied on the GPU via GskBlurNode at widget realization time. });
let bg_texture = greeter::load_background_texture(&bg_path);
if bg_texture.is_none() {
log::error!("Failed to load background texture — greeter will start without wallpaper");
}
let use_layer_shell = std::env::var("MOONGREET_NO_LAYER_SHELL").is_err(); let use_layer_shell = std::env::var("MOONGREET_NO_LAYER_SHELL").is_err();
log::debug!("Layer shell: {use_layer_shell}"); log::debug!("Layer shell: {use_layer_shell}");