feat: switch to systemd-journal-logger, add debug logging (v0.6.0)
Replace env_logger with systemd-journal-logger for consistent logging across moonset/moonlock/moongreet. Add MOONSET_DEBUG env var and debug statements across all modules. Also includes shared blur cache for multi-monitor and detached moonlock spawn for lock action.
This commit is contained in:
parent
14affb1533
commit
71670eb263
13
CHANGELOG.md
13
CHANGELOG.md
@ -3,6 +3,19 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
Format based on [Keep a Changelog](https://keepachangelog.com/).
|
||||
|
||||
## [0.6.0] - 2026-03-28
|
||||
|
||||
### Added
|
||||
|
||||
- Systemd journal logging (`journalctl -t moonset`) replacing env_logger stderr output
|
||||
- `MOONSET_DEBUG` env var to enable debug-level journal output
|
||||
- Debug logging across all modules (config resolution, wallpaper source, avatar loading, power actions, locale detection, blur cache)
|
||||
- Shared blur cache for multi-monitor — GPU blur computed once, reused by all windows
|
||||
|
||||
### Changed
|
||||
|
||||
- Lock action spawns moonlock as detached process instead of blocking via run_command — moonset can quit immediately while moonlock runs independently
|
||||
|
||||
## [0.4.1] - 2026-03-28
|
||||
|
||||
### Added
|
||||
|
||||
@ -35,7 +35,7 @@ LD_PRELOAD=/usr/lib/libgtk4-layer-shell.so ./target/release/moonset
|
||||
|
||||
## Architektur
|
||||
|
||||
- `main.rs` — Entry Point, GTK App, Layer Shell Setup, Multi-Monitor, zentrale `GRESOURCE_PREFIX`-Konstante
|
||||
- `main.rs` — Entry Point, GTK App, Layer Shell Setup, Multi-Monitor, systemd-Journal-Logging, Debug-Level per `MOONSET_DEBUG` Env-Var, zentrale `GRESOURCE_PREFIX`-Konstante
|
||||
- `power.rs` — 5 Power-Action-Wrapper mit absoluten Pfaden und 30s Timeout (lock, logout, hibernate, reboot, shutdown)
|
||||
- `i18n.rs` — Locale-Erkennung und String-Tabellen (DE/EN)
|
||||
- `config.rs` — TOML-Config + Wallpaper-Fallback
|
||||
@ -56,3 +56,4 @@ Kurzfassung der wichtigsten Entscheidungen:
|
||||
- **Absolute Pfade für Binaries**: `/usr/bin/systemctl` etc. statt relativer Pfade (Security)
|
||||
- **GResource-Bundle**: CSS, Wallpaper (komprimiert) und Default-Avatar sind in die Binary kompiliert
|
||||
- **Async Power Actions**: `glib::spawn_future_local` + `gio::spawn_blocking` mit 30s Timeout
|
||||
- **Journal-Logging**: `systemd-journal-logger` statt File-Logging — `journalctl -t moonset`, Debug-Level per `MOONSET_DEBUG` Env-Var
|
||||
|
||||
186
Cargo.lock
generated
186
Cargo.lock
generated
@ -2,65 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.102"
|
||||
@ -124,12 +65,6 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "6.0.0"
|
||||
@ -151,29 +86,6 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"jiff",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@ -642,42 +554,12 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "khronos_api"
|
||||
version = "3.1.0"
|
||||
@ -737,7 +619,6 @@ name = "moonset"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"env_logger",
|
||||
"gdk-pixbuf",
|
||||
"gdk4",
|
||||
"glib",
|
||||
@ -748,6 +629,7 @@ dependencies = [
|
||||
"log",
|
||||
"nix",
|
||||
"serde",
|
||||
"systemd-journal-logger",
|
||||
"tempfile",
|
||||
"toml 0.8.23",
|
||||
]
|
||||
@ -770,12 +652,6 @@ version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
@ -818,21 +694,6 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic-util"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
@ -887,35 +748,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
@ -1041,6 +873,16 @@ dependencies = [
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "systemd-journal-logger"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7266304d24ca5a4b230545fc558c80e18bd3e1d2eb1be149b6bcd04398d3e79c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.13.3"
|
||||
@ -1193,12 +1035,6 @@ version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.2.1"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "moonset"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
edition = "2024"
|
||||
description = "Wayland session power menu with GTK4 and Layer Shell"
|
||||
license = "MIT"
|
||||
@ -17,7 +17,7 @@ serde = { version = "1", features = ["derive"] }
|
||||
nix = { version = "0.29", features = ["user"] }
|
||||
graphene-rs = { version = "0.22", package = "graphene-rs" }
|
||||
log = "0.4"
|
||||
env_logger = "0.11"
|
||||
systemd-journal-logger = "2.2"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
||||
@ -2,6 +2,13 @@
|
||||
|
||||
Architectural and design decisions for Moonset, in reverse chronological order.
|
||||
|
||||
## 2026-03-28 – Switch from env_logger to systemd-journal-logger
|
||||
|
||||
- **Who**: Ragnar, Dom
|
||||
- **Why**: moonlock and moongreet already use systemd-journal-logger. moonset used env_logger which writes to stderr — not useful for a GUI app launched via keybind. Journal integration enables `journalctl -t moonset` and consistent troubleshooting across all three Moon projects.
|
||||
- **Tradeoffs**: Requires systemd at runtime. Graceful fallback to eprintln if journal logger fails. Acceptable since Moonarch targets systemd-based Arch Linux.
|
||||
- **How**: Replace `env_logger` dep with `systemd-journal-logger`, add `setup_logging()` with `MOONSET_DEBUG` env var for debug-level output. Same pattern as moonlock/moongreet.
|
||||
|
||||
## 2026-03-28 – Replace action name dispatch with `quit_after` field
|
||||
|
||||
- **Who**: Hekate, Dom
|
||||
|
||||
@ -33,6 +33,7 @@ pub fn load_config(config_paths: Option<&[PathBuf]>) -> Config {
|
||||
if let Ok(content) = fs::read_to_string(path) {
|
||||
match toml::from_str::<Config>(&content) {
|
||||
Ok(parsed) => {
|
||||
log::debug!("Config loaded: {}", path.display());
|
||||
if parsed.background_path.is_some() {
|
||||
merged.background_path = parsed.background_path;
|
||||
}
|
||||
@ -41,7 +42,7 @@ pub fn load_config(config_paths: Option<&[PathBuf]>) -> Config {
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Warning: failed to parse {}: {e}", path.display());
|
||||
log::warn!("Failed to parse {}: {e}", path.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,17 +64,20 @@ pub fn resolve_background_path_with(config: &Config, moonarch_wallpaper: &Path)
|
||||
if let Some(ref bg) = config.background_path {
|
||||
let path = PathBuf::from(bg);
|
||||
if path.is_file() {
|
||||
log::debug!("Wallpaper source: config ({})", path.display());
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
// Moonarch ecosystem default
|
||||
if moonarch_wallpaper.is_file() {
|
||||
log::debug!("Wallpaper source: moonarch default ({})", moonarch_wallpaper.display());
|
||||
return moonarch_wallpaper.to_path_buf();
|
||||
}
|
||||
|
||||
// GResource fallback path (loaded from compiled resources at runtime)
|
||||
let prefix = crate::GRESOURCE_PREFIX;
|
||||
log::debug!("Wallpaper source: GResource fallback");
|
||||
PathBuf::from(format!("{prefix}/wallpaper.jpg"))
|
||||
}
|
||||
|
||||
|
||||
17
src/i18n.rs
17
src/i18n.rs
@ -112,15 +112,20 @@ fn read_lang_from_conf(path: &Path) -> Option<String> {
|
||||
|
||||
/// Determine the system language from LANG env var or /etc/locale.conf.
|
||||
pub fn detect_locale() -> String {
|
||||
let lang = env::var("LANG")
|
||||
.ok()
|
||||
.filter(|s| !s.is_empty())
|
||||
.or_else(|| read_lang_from_conf(Path::new(DEFAULT_LOCALE_CONF)));
|
||||
let (raw, source) = if let Some(val) = env::var("LANG").ok().filter(|s| !s.is_empty()) {
|
||||
(Some(val), "LANG env")
|
||||
} else if let Some(val) = read_lang_from_conf(Path::new(DEFAULT_LOCALE_CONF)) {
|
||||
(Some(val), "locale.conf")
|
||||
} else {
|
||||
(None, "default")
|
||||
};
|
||||
|
||||
match lang {
|
||||
let result = match raw {
|
||||
Some(l) => parse_lang_prefix(&l),
|
||||
None => "en".to_string(),
|
||||
}
|
||||
};
|
||||
log::debug!("Detected locale: {result} (source: {source})");
|
||||
result
|
||||
}
|
||||
|
||||
/// Return the string table for the given locale, defaulting to English.
|
||||
|
||||
33
src/main.rs
33
src/main.rs
@ -53,14 +53,16 @@ fn activate(app: >k::Application) {
|
||||
|
||||
load_css(&display);
|
||||
|
||||
// Resolve wallpaper once, decode texture once, share across all windows
|
||||
// Blur is applied on the GPU via GskBlurNode at widget realization time.
|
||||
// Resolve wallpaper once, decode texture once, share across all windows.
|
||||
// Blur is applied on the GPU via GskBlurNode at first widget realization,
|
||||
// then cached and reused by all subsequent windows.
|
||||
let config = config::load_config(None);
|
||||
let bg_path = config::resolve_background_path(&config);
|
||||
let texture = panel::load_background_texture(&bg_path);
|
||||
let blur_cache = panel::new_blur_cache();
|
||||
|
||||
// Panel on focused output (no set_monitor → compositor picks focused)
|
||||
let panel = panel::create_panel_window(&texture, config.background_blur, app);
|
||||
let panel = panel::create_panel_window(&texture, config.background_blur, &blur_cache, app);
|
||||
setup_layer_shell(&panel, true, gtk4_layer_shell::Layer::Overlay);
|
||||
panel.present();
|
||||
|
||||
@ -68,7 +70,7 @@ fn activate(app: >k::Application) {
|
||||
let monitors = display.monitors();
|
||||
for i in 0..monitors.n_items() {
|
||||
if let Some(monitor) = monitors.item(i).and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) {
|
||||
let wallpaper = panel::create_wallpaper_window(&texture, config.background_blur, app);
|
||||
let wallpaper = panel::create_wallpaper_window(&texture, config.background_blur, &blur_cache, app);
|
||||
setup_layer_shell(&wallpaper, false, gtk4_layer_shell::Layer::Top);
|
||||
wallpaper.set_monitor(Some(&monitor));
|
||||
wallpaper.present();
|
||||
@ -76,10 +78,27 @@ fn activate(app: >k::Application) {
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_logging() {
|
||||
match systemd_journal_logger::JournalLog::new() {
|
||||
Ok(logger) => {
|
||||
if let Err(e) = logger.install() {
|
||||
eprintln!("Failed to install journal logger: {e}");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to create journal logger: {e}");
|
||||
}
|
||||
}
|
||||
let level = if std::env::var("MOONSET_DEBUG").is_ok() {
|
||||
log::LevelFilter::Debug
|
||||
} else {
|
||||
log::LevelFilter::Info
|
||||
};
|
||||
log::set_max_level(level);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::Builder::from_default_env()
|
||||
.filter_level(log::LevelFilter::Info)
|
||||
.init();
|
||||
setup_logging();
|
||||
log::info!("Moonset starting");
|
||||
|
||||
// Register compiled GResources
|
||||
|
||||
40
src/panel.rs
40
src/panel.rs
@ -91,6 +91,7 @@ pub fn action_definitions() -> Vec<ActionDef> {
|
||||
pub fn load_background_texture(bg_path: &Path) -> gdk::Texture {
|
||||
let fallback = format!("{}/wallpaper.jpg", crate::GRESOURCE_PREFIX);
|
||||
|
||||
log::debug!("Background: {}", bg_path.display());
|
||||
if bg_path.starts_with(crate::GRESOURCE_PREFIX) {
|
||||
let resource_path = bg_path.to_str().unwrap_or(&fallback);
|
||||
gdk::Texture::from_resource(resource_path)
|
||||
@ -134,14 +135,19 @@ fn fade_out_and_quit(app: >k::Application) {
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a new shared blur cache for GPU-blurred wallpaper textures.
|
||||
pub fn new_blur_cache() -> BlurCache {
|
||||
Rc::new(RefCell::new(None))
|
||||
}
|
||||
|
||||
/// Create a wallpaper-only window for secondary monitors.
|
||||
pub fn create_wallpaper_window(texture: &gdk::Texture, blur_radius: Option<f32>, app: >k::Application) -> gtk::ApplicationWindow {
|
||||
pub fn create_wallpaper_window(texture: &gdk::Texture, blur_radius: Option<f32>, blur_cache: &BlurCache, app: >k::Application) -> gtk::ApplicationWindow {
|
||||
let window = gtk::ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.build();
|
||||
window.add_css_class("wallpaper");
|
||||
|
||||
let background = create_background_picture(texture, blur_radius);
|
||||
let background = create_background_picture(texture, blur_radius, blur_cache);
|
||||
window.set_child(Some(&background));
|
||||
|
||||
// Fade-in on map
|
||||
@ -159,7 +165,7 @@ pub fn create_wallpaper_window(texture: &gdk::Texture, blur_radius: Option<f32>,
|
||||
}
|
||||
|
||||
/// Create the main panel window with action buttons and confirm flow.
|
||||
pub fn create_panel_window(texture: &gdk::Texture, blur_radius: Option<f32>, app: >k::Application) -> gtk::ApplicationWindow {
|
||||
pub fn create_panel_window(texture: &gdk::Texture, blur_radius: Option<f32>, blur_cache: &BlurCache, app: >k::Application) -> gtk::ApplicationWindow {
|
||||
let window = gtk::ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.build();
|
||||
@ -172,6 +178,7 @@ pub fn create_panel_window(texture: &gdk::Texture, blur_radius: Option<f32>, app
|
||||
home: dirs::home_dir().unwrap_or_default(),
|
||||
uid: u32::MAX,
|
||||
});
|
||||
log::debug!("User: {} ({})", user.display_name, user.username);
|
||||
|
||||
// State for confirm box
|
||||
let confirm_box: Rc<RefCell<Option<gtk::Box>>> = Rc::new(RefCell::new(None));
|
||||
@ -181,7 +188,7 @@ pub fn create_panel_window(texture: &gdk::Texture, blur_radius: Option<f32>, app
|
||||
window.set_child(Some(&overlay));
|
||||
|
||||
// Background wallpaper
|
||||
let background = create_background_picture(texture, blur_radius);
|
||||
let background = create_background_picture(texture, blur_radius, blur_cache);
|
||||
overlay.set_child(Some(&background));
|
||||
|
||||
// Click on background dismisses the menu
|
||||
@ -285,8 +292,17 @@ pub fn create_panel_window(texture: &gdk::Texture, blur_radius: Option<f32>, app
|
||||
window
|
||||
}
|
||||
|
||||
/// Shared cache for the GPU-blurred wallpaper texture.
|
||||
/// Computed once on first window realize, reused by all subsequent windows.
|
||||
type BlurCache = Rc<RefCell<Option<gdk::Texture>>>;
|
||||
|
||||
/// Create a Picture widget for the wallpaper background, optionally with GPU blur.
|
||||
fn create_background_picture(texture: &gdk::Texture, blur_radius: Option<f32>) -> gtk::Picture {
|
||||
/// When a blur_cache is provided, the blurred texture is computed once and shared.
|
||||
fn create_background_picture(
|
||||
texture: &gdk::Texture,
|
||||
blur_radius: Option<f32>,
|
||||
blur_cache: &BlurCache,
|
||||
) -> gtk::Picture {
|
||||
let background = gtk::Picture::for_paintable(texture);
|
||||
background.set_content_fit(gtk::ContentFit::Cover);
|
||||
background.set_hexpand(true);
|
||||
@ -294,9 +310,18 @@ fn create_background_picture(texture: &gdk::Texture, blur_radius: Option<f32>) -
|
||||
|
||||
if let Some(sigma) = blur_radius.filter(|s| *s > 0.0) {
|
||||
let texture = texture.clone();
|
||||
let cache = blur_cache.clone();
|
||||
background.connect_realize(move |picture| {
|
||||
// Use cached blur if available, otherwise compute and cache
|
||||
if let Some(ref cached) = *cache.borrow() {
|
||||
log::debug!("Blur cache hit");
|
||||
picture.set_paintable(Some(cached));
|
||||
return;
|
||||
}
|
||||
log::debug!("Blur cache miss, rendering GPU blur");
|
||||
if let Some(blurred) = render_blurred_texture(picture, &texture, sigma) {
|
||||
picture.set_paintable(Some(&blurred));
|
||||
*cache.borrow_mut() = Some(blurred);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -470,6 +495,7 @@ fn execute_action(
|
||||
error_label: >k::Label,
|
||||
) {
|
||||
dismiss_confirm(confirm_area, confirm_box);
|
||||
log::debug!("Executing power action: {}", action_def.name);
|
||||
|
||||
let action_fn = action_def.action_fn;
|
||||
let action_name = action_def.name;
|
||||
@ -514,6 +540,7 @@ fn load_avatar_async(image: >k::Image, window: >k::ApplicationWindow, user:
|
||||
|
||||
match avatar_path {
|
||||
Some(path) => {
|
||||
log::debug!("Avatar source: file {}", path.display());
|
||||
// File-based avatar: load and scale in background thread
|
||||
glib::spawn_future_local(clone!(
|
||||
#[weak]
|
||||
@ -521,7 +548,7 @@ fn load_avatar_async(image: >k::Image, window: >k::ApplicationWindow, user:
|
||||
async move {
|
||||
let result = gio::spawn_blocking(move || {
|
||||
Pixbuf::from_file_at_scale(
|
||||
path.to_str().unwrap_or(""),
|
||||
&path,
|
||||
AVATAR_SIZE,
|
||||
AVATAR_SIZE,
|
||||
true,
|
||||
@ -539,6 +566,7 @@ fn load_avatar_async(image: >k::Image, window: >k::ApplicationWindow, user:
|
||||
));
|
||||
}
|
||||
None => {
|
||||
log::debug!("Avatar source: default SVG");
|
||||
// Default SVG avatar: needs widget color, keep synchronous
|
||||
set_default_avatar(image, window);
|
||||
}
|
||||
|
||||
19
src/power.rs
19
src/power.rs
@ -31,6 +31,7 @@ impl std::error::Error for PowerError {}
|
||||
|
||||
/// Run a command with timeout and return a PowerError on failure.
|
||||
fn run_command(action: &'static str, program: &str, args: &[&str]) -> Result<(), PowerError> {
|
||||
log::debug!("Power action: {action} ({program} {args:?})");
|
||||
let mut child = Command::new(program)
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
@ -45,6 +46,9 @@ fn run_command(action: &'static str, program: &str, args: &[&str]) -> Result<(),
|
||||
loop {
|
||||
match child.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
if status.success() {
|
||||
log::debug!("Power action {action} completed");
|
||||
}
|
||||
if !status.success() {
|
||||
let mut stderr_buf = String::new();
|
||||
if let Some(mut stderr) = child.stderr.take() {
|
||||
@ -76,8 +80,21 @@ fn run_command(action: &'static str, program: &str, args: &[&str]) -> Result<(),
|
||||
}
|
||||
|
||||
/// Lock the current session by launching moonlock.
|
||||
/// Spawns moonlock as a detached process and returns immediately —
|
||||
/// moonlock runs independently until the user unlocks.
|
||||
pub fn lock() -> Result<(), PowerError> {
|
||||
run_command("lock", "/usr/bin/moonlock", &[])
|
||||
log::debug!("Power action: lock (spawning moonlock)");
|
||||
Command::new("/usr/bin/moonlock")
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.map_err(|e| PowerError::CommandFailed {
|
||||
action: "lock",
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
// Child handle is dropped here — moonlock continues running independently.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Quit the Niri compositor (logout).
|
||||
|
||||
@ -55,6 +55,7 @@ pub fn get_avatar_path_with(
|
||||
// ~/.face takes priority
|
||||
let face = home.join(".face");
|
||||
if face.exists() {
|
||||
log::debug!("Avatar: using ~/.face");
|
||||
return Some(face);
|
||||
}
|
||||
|
||||
@ -63,6 +64,7 @@ pub fn get_avatar_path_with(
|
||||
if accountsservice_dir.exists() {
|
||||
let icon = accountsservice_dir.join(name);
|
||||
if icon.exists() {
|
||||
log::debug!("Avatar: using AccountsService icon");
|
||||
return Some(icon);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user