From 29ce185886e6ddfb8fa5fef6abbd0f4a457ee77d Mon Sep 17 00:00:00 2001 From: nevaforget Date: Fri, 24 Apr 2026 08:56:41 +0200 Subject: [PATCH] feat: apply cursor theme via GtkSettings instead of XCURSOR_THEME env (v0.9.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GTK4 under greetd does not honour XCURSOR_THEME reliably — the env-prefix hack in /etc/greetd/config.toml only reached the wlroots pointer in cage, while GTK widgets kept using the default fallback cursor. Mirror the existing gtk-theme handling: new cursor-theme + cursor-size fields in the [appearance] section, applied via gtk::Settings::set_gtk_cursor_theme_*. Keeps the fix scoped to the greeter, no system-wide GTK config changes. --- CLAUDE.md | 3 ++- Cargo.lock | 2 +- Cargo.toml | 2 +- DECISIONS.md | 7 +++++ config/moongreet.toml | 5 ++++ src/config.rs | 62 ++++++++++++++++++++++++++++++++++++++++++- src/greeter.rs | 17 ++++++++++++ 7 files changed, 94 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 1d709e4..117977a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -43,7 +43,7 @@ cd pkg && makepkg -sf && sudo pacman -U moongreet-git--x86_64.pkg.tar.z - `power.rs` — Reboot/Shutdown via loginctl - `i18n.rs` — Locale-Erkennung (LANG / /etc/locale.conf) und String-Tabellen (DE/EN), alle UI- und Login-Fehlermeldungen - `fingerprint.rs` — fprintd D-Bus Probe (gio::DBusProxy) — Geräteerkennung und Enrollment-Check für UI-Feedback -- `config.rs` — TOML-Config ([appearance] background, gtk-theme, fingerprint-enabled) + Wallpaper-Fallback + Blur-Validierung (finite, clamp 0–200) +- `config.rs` — TOML-Config ([appearance] background, gtk-theme, cursor-theme, cursor-size, fingerprint-enabled) + Wallpaper-Fallback + Blur-Validierung (finite, clamp 0–200) + Cursor-Size-Validierung (range 1–256) - `greeter.rs` — GTK4 UI (Overlay-Layout), Login-Flow via greetd IPC (Multi-Stage-Auth für fprintd), Faillock-Warnung, Avatar-Cache, Last-User/Last-Session Persistence (0o700 Dirs, 0o600 Files). Zweite Funktion `create_wallpaper_window()` für Sekundär-Monitore (Background only, keine Widgets) - `main.rs` — Entry Point, GTK App, Layer Shell Setup, Multi-Monitor mit Hotplug via `items-changed` auf Monitor-ListModel. Built-in display (via `pick_primary_monitor_index`) zeigt das Login-Widget mit `KeyboardMode::Exclusive`, alle anderen Monitore (inkl. Hotplug) zeigen nur Wallpaper mit `KeyboardMode::None`. Systemd-journal-logger. - `resources/style.css` — Catppuccin-inspiriertes Theme @@ -60,6 +60,7 @@ cd pkg && makepkg -sf && sudo pacman -U moongreet-git--x86_64.pkg.tar.z - **Symmetrie mit moonlock/moonset**: Gleiche Patterns (i18n, config, users, power, GResource, GPU-Blur) - **Session-Validierung**: Relative Pfade erlaubt (greetd löst PATH auf), nur `..`/Null-Bytes werden abgelehnt - **GTK-Theme-Validierung**: Nur alphanumerisch + `_-+.` erlaubt, verhindert Path-Traversal über Config +- **Cursor-Theme via GtkSettings**: GTK4 unter greetd liest `XCURSOR_THEME` env nicht zuverlässig — Cursor wird via `gtk::Settings::set_gtk_cursor_theme_name()` gesetzt, analog zu `gtk-theme`. Gleiche Validierung (`is_valid_gtk_theme`) gegen Path-Traversal. - **Journal-Logging**: `systemd-journal-logger` statt File-Logging — `journalctl -t moongreet`, Debug-Level per `MOONGREET_DEBUG` Env-Var - **File Permissions**: Cache-Verzeichnisse 0o700 via `DirBuilder::mode()`, Cache-Dateien 0o600 - **Testbare Persistence**: `save_*_to`/`load_*_from` Varianten mit konfigurierbarem Pfad für Unit-Tests diff --git a/Cargo.lock b/Cargo.lock index fcd3931..d5a22f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "moongreet" -version = "0.8.5" +version = "0.9.0" dependencies = [ "gdk-pixbuf", "gdk4", diff --git a/Cargo.toml b/Cargo.toml index 05ab797..ae1556a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "moongreet" -version = "0.8.5" +version = "0.9.0" edition = "2024" description = "A greetd greeter for Wayland with GTK4 and Layer Shell" license = "MIT" diff --git a/DECISIONS.md b/DECISIONS.md index 5e1162a..4e9ba26 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1,5 +1,12 @@ # Decisions +## 2026-04-24 – Cursor theme via config instead of env (v0.9.0) + +- **Who**: ClaudeCode, Dom +- **Why**: Cursor theme in the greeter was the default fallback even with `XCURSOR_THEME=Sweet-cursors` in `/etc/greetd/config.toml`'s `env` prefix. Cage forwards the env, but GTK4 does not honour `XCURSOR_THEME` reliably under greetd — it picks up the theme from `gtk-cursor-theme-name` on `GtkSettings`, and without a session-level settings.ini or GSettings override in the greeter user's home, that property stays at the GTK default. Adding an env-var hack worked for the wlroots pointer rendered by cage, but GTK widgets (button hover, text input) used their own wrong cursor. +- **Tradeoffs**: Adds two config fields (`cursor-theme`, `cursor-size`) — symmetric with the existing `gtk-theme` field and justified by the same cause (GTK4 under greetd ignores the usual discovery paths). Alternative would have been a system-wide `/etc/gtk-4.0/settings.ini` with `gtk-cursor-theme-name=`, but that couples moongreet's appearance to the host system's GTK config and affects every GTK4 app running as any user. +- **How**: `config.rs` gains `cursor_theme: Option` and `cursor_size: Option` (range-validated 1–256). `greeter.rs::create_greeter_window` applies them via `gtk::Settings::set_gtk_cursor_theme_name()` and `set_gtk_cursor_theme_size()` directly after the existing `gtk-theme` handling, reusing `is_valid_gtk_theme()` for name validation. Moonarch's deployed config gains `cursor-theme = "Sweet-cursors"` + `cursor-size = 24`. The env-prefix hack in `/etc/greetd/config.toml` is now redundant. + ## 2026-04-23 – Wallpaper-only windows on secondary monitors (v0.8.5) - **Who**: ClaudeCode, Dom diff --git a/config/moongreet.toml b/config/moongreet.toml index cccaf19..eca14f9 100644 --- a/config/moongreet.toml +++ b/config/moongreet.toml @@ -8,3 +8,8 @@ background = "/usr/share/backgrounds/wallpaper.jpg" # GTK theme name — must match a directory in /usr/share/themes/ # Required because GTK4 under greetd does not reliably read settings.ini gtk-theme = "Colloid-Grey-Dark-Catppuccin" + +# Cursor theme name — must match a directory in /usr/share/icons/ +# GTK4 under greetd does not honour XCURSOR_THEME, so set it here. +cursor-theme = "Sweet-cursors" +cursor-size = 24 diff --git a/src/config.rs b/src/config.rs index f86a736..c4f5576 100644 --- a/src/config.rs +++ b/src/config.rs @@ -25,6 +25,10 @@ struct Appearance { background_blur: Option, #[serde(rename = "gtk-theme")] gtk_theme: Option, + #[serde(rename = "cursor-theme")] + cursor_theme: Option, + #[serde(rename = "cursor-size")] + cursor_size: Option, #[serde(rename = "fingerprint-enabled")] fingerprint_enabled: Option, } @@ -35,6 +39,8 @@ pub struct Config { pub background_path: Option, pub background_blur: Option, pub gtk_theme: Option, + pub cursor_theme: Option, + pub cursor_size: Option, pub fingerprint_enabled: bool, } @@ -44,6 +50,8 @@ impl Default for Config { background_path: None, background_blur: None, gtk_theme: None, + cursor_theme: None, + cursor_size: None, fingerprint_enabled: true, } } @@ -82,6 +90,16 @@ pub fn load_config(config_paths: Option<&[PathBuf]>) -> Config { if appearance.gtk_theme.is_some() { merged.gtk_theme = appearance.gtk_theme; } + if appearance.cursor_theme.is_some() { + merged.cursor_theme = appearance.cursor_theme; + } + if let Some(size) = appearance.cursor_size { + if (1..=256).contains(&size) { + merged.cursor_size = Some(size); + } else { + log::warn!("Ignoring cursor-size out of range (1–256): {size}"); + } + } if let Some(fp) = appearance.fingerprint_enabled { merged.fingerprint_enabled = fp; } @@ -98,7 +116,15 @@ pub fn load_config(config_paths: Option<&[PathBuf]>) -> Config { } } - log::debug!("Config result: background={:?}, blur={:?}, gtk_theme={:?}, fingerprint={}", merged.background_path, merged.background_blur, merged.gtk_theme, merged.fingerprint_enabled); + log::debug!( + "Config result: background={:?}, blur={:?}, gtk_theme={:?}, cursor_theme={:?}, cursor_size={:?}, fingerprint={}", + merged.background_path, + merged.background_blur, + merged.gtk_theme, + merged.cursor_theme, + merged.cursor_size, + merged.fingerprint_enabled + ); merged } @@ -321,6 +347,40 @@ mod tests { assert!(config.background_blur.is_none()); } + // -- Cursor theme tests -- + + #[test] + fn load_config_cursor_theme_and_size() { + let dir = tempfile::tempdir().unwrap(); + let conf = dir.path().join("moongreet.toml"); + fs::write( + &conf, + "[appearance]\ncursor-theme = \"Sweet-cursors\"\ncursor-size = 32\n", + ) + .unwrap(); + let config = load_config(Some(&[conf])); + assert_eq!(config.cursor_theme.as_deref(), Some("Sweet-cursors")); + assert_eq!(config.cursor_size, Some(32)); + } + + #[test] + fn load_config_cursor_size_out_of_range_rejected() { + let dir = tempfile::tempdir().unwrap(); + let conf = dir.path().join("moongreet.toml"); + fs::write(&conf, "[appearance]\ncursor-size = 9999\n").unwrap(); + let config = load_config(Some(&[conf])); + assert!(config.cursor_size.is_none()); + } + + #[test] + fn load_config_cursor_size_zero_rejected() { + let dir = tempfile::tempdir().unwrap(); + let conf = dir.path().join("moongreet.toml"); + fs::write(&conf, "[appearance]\ncursor-size = 0\n").unwrap(); + let config = load_config(Some(&[conf])); + assert!(config.cursor_size.is_none()); + } + #[test] fn load_config_blur_inf_rejected() { let dir = tempfile::tempdir().unwrap(); diff --git a/src/greeter.rs b/src/greeter.rs index 5fc5ed4..872de9a 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -284,6 +284,23 @@ pub fn create_greeter_window( } } + // Apply cursor theme from config — GTK4 under greetd does not read XCURSOR_THEME + // reliably, so set the gtk-cursor-theme-name property directly. + if let Some(ref cursor) = config.cursor_theme { + if is_valid_gtk_theme(cursor) { + if let Some(settings) = gtk::Settings::default() { + settings.set_gtk_cursor_theme_name(Some(cursor)); + } + } else { + log::warn!("Ignoring invalid cursor theme name: {cursor}"); + } + } + if let Some(size) = config.cursor_size { + if let Some(settings) = gtk::Settings::default() { + settings.set_gtk_cursor_theme_size(size); + } + } + let strings = load_strings(None); let fingerprint_enabled = config.fingerprint_enabled; let all_users = users::get_users(None);