| 1 | //! Greeter configuration |
| 2 | //! |
| 3 | //! Loads visual settings, theming, and accessibility options. |
| 4 | |
| 5 | use crate::theme::{AccessibilityConfig, Theme, ThemeConfig}; |
| 6 | use anyhow::{Context, Result}; |
| 7 | use serde::Deserialize; |
| 8 | use std::path::PathBuf; |
| 9 | |
| 10 | /// Main greeter configuration |
| 11 | #[derive(Debug, Deserialize, Default)] |
| 12 | pub struct GreeterConfig { |
| 13 | #[serde(default)] |
| 14 | pub visual: VisualConfig, |
| 15 | #[serde(default)] |
| 16 | pub garbg: GarbgIntegration, |
| 17 | #[serde(default)] |
| 18 | pub theme: ThemeConfig, |
| 19 | #[serde(default)] |
| 20 | pub accessibility: AccessibilityConfig, |
| 21 | } |
| 22 | |
| 23 | /// Visual appearance settings |
| 24 | #[derive(Debug, Deserialize)] |
| 25 | pub struct VisualConfig { |
| 26 | /// Blur radius for background |
| 27 | #[serde(default = "default_blur_radius")] |
| 28 | pub blur_radius: f32, |
| 29 | |
| 30 | /// Background brightness (0.0-1.0, lower = darker) |
| 31 | #[serde(default = "default_brightness")] |
| 32 | pub brightness: f32, |
| 33 | |
| 34 | /// Corner radius for UI elements |
| 35 | #[serde(default = "default_corner_radius")] |
| 36 | pub corner_radius: f64, |
| 37 | |
| 38 | /// Fade-out duration in milliseconds when starting session |
| 39 | #[serde(default = "default_fade_duration")] |
| 40 | pub fade_duration_ms: u64, |
| 41 | } |
| 42 | |
| 43 | impl Default for VisualConfig { |
| 44 | fn default() -> Self { |
| 45 | Self { |
| 46 | blur_radius: default_blur_radius(), |
| 47 | brightness: default_brightness(), |
| 48 | corner_radius: default_corner_radius(), |
| 49 | fade_duration_ms: default_fade_duration(), |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | /// garbg integration settings |
| 55 | #[derive(Debug, Deserialize)] |
| 56 | pub struct GarbgIntegration { |
| 57 | /// Whether to use garbg wallpaper |
| 58 | #[serde(default = "default_true")] |
| 59 | pub enabled: bool, |
| 60 | |
| 61 | /// Fallback wallpaper if garbg not configured |
| 62 | #[serde(default = "default_fallback")] |
| 63 | pub fallback: String, |
| 64 | } |
| 65 | |
| 66 | impl Default for GarbgIntegration { |
| 67 | fn default() -> Self { |
| 68 | Self { |
| 69 | enabled: default_true(), |
| 70 | fallback: default_fallback(), |
| 71 | } |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | fn default_blur_radius() -> f32 { |
| 76 | 25.0 |
| 77 | } |
| 78 | fn default_brightness() -> f32 { |
| 79 | 0.6 |
| 80 | } |
| 81 | fn default_corner_radius() -> f64 { |
| 82 | 16.0 |
| 83 | } |
| 84 | fn default_fade_duration() -> u64 { |
| 85 | 200 |
| 86 | } |
| 87 | fn default_true() -> bool { |
| 88 | true |
| 89 | } |
| 90 | fn default_fallback() -> String { |
| 91 | // Try common locations for a default wallpaper |
| 92 | let candidates = [ |
| 93 | "/home/mfwolffe/Pictures/background/cold/a_snowy_mountain_with_clouds_above.jpg", |
| 94 | "/usr/share/backgrounds/default.png", |
| 95 | "/usr/share/gardm/backgrounds/default.jpg", |
| 96 | ]; |
| 97 | for path in candidates { |
| 98 | if std::path::Path::new(path).exists() { |
| 99 | return path.to_string(); |
| 100 | } |
| 101 | } |
| 102 | "/usr/share/gardm/backgrounds/default.jpg".to_string() |
| 103 | } |
| 104 | |
| 105 | impl GreeterConfig { |
| 106 | /// Load configuration from file or use defaults |
| 107 | pub fn load() -> Result<Self> { |
| 108 | // Try system config location |
| 109 | let config_paths = [ |
| 110 | PathBuf::from("/etc/gardm/greeter.toml"), |
| 111 | PathBuf::from("/usr/share/gardm/greeter.toml"), |
| 112 | ]; |
| 113 | |
| 114 | for path in &config_paths { |
| 115 | if path.exists() { |
| 116 | let content = std::fs::read_to_string(path) |
| 117 | .with_context(|| format!("Failed to read config from {:?}", path))?; |
| 118 | let config: GreeterConfig = toml::from_str(&content) |
| 119 | .with_context(|| format!("Failed to parse config from {:?}", path))?; |
| 120 | tracing::info!("Loaded greeter config from {:?}", path); |
| 121 | return Ok(config); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | // Use defaults |
| 126 | tracing::debug!("No config file found, using defaults"); |
| 127 | Ok(Self::default()) |
| 128 | } |
| 129 | |
| 130 | /// Build the theme from config, applying accessibility options |
| 131 | pub fn build_theme(&self) -> Theme { |
| 132 | self.theme.clone().into_theme(&self.accessibility) |
| 133 | } |
| 134 | |
| 135 | /// Get effective fade duration (0 if reduce_motion is enabled) |
| 136 | pub fn effective_fade_duration(&self) -> u64 { |
| 137 | if self.accessibility.reduce_motion { |
| 138 | 0 |
| 139 | } else { |
| 140 | self.visual.fade_duration_ms |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 |