use crate::protocol::Parameter; use anyhow::{Context, Result, ensure}; use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, fs, path::{Path, PathBuf}, }; #[derive(Debug, Clone, Deserialize, Serialize)] #[serde(default, deny_unknown_fields)] pub struct Config { pub monitor_serial: String, pub socket: PathBuf, pub state_file: PathBuf, pub transport: String, pub serial_device: Option, pub usb_path: Option, pub require_companion: bool, pub require_display: bool, pub keepalive_ms: u64, pub reconnect_ms: u64, pub watchdog_ms: u64, pub display: Display, pub startup: BTreeMap, } #[derive(Debug, Clone, Deserialize, Serialize)] #[serde(default, deny_unknown_fields)] pub struct Display { pub enabled: bool, pub edid: PathBuf, // AMD's virtual hotplug API was verified on the actual machine. Other GPUs // should use the portable firmware EDID mechanism or their KMS client. pub hotplug: String, } impl Default for Display { fn default() -> Self { Self { enabled: false, edid: "/usr/lib/firmware/edid/dasung-paperlike13k-37hz.bin".into(), hotplug: "none".into(), } } } impl Default for Config { fn default() -> Self { Self { monitor_serial: "L56051794302".into(), socket: "/run/dasungd/control.sock".into(), state_file: "/run/dasungd/settings.json".into(), transport: "usb".into(), serial_device: None, usb_path: None, require_companion: true, require_display: true, keepalive_ms: 2000, reconnect_ms: 2000, watchdog_ms: 30000, display: Display::default(), startup: BTreeMap::new(), } } } impl Config { pub fn load(path: &Path) -> Result { let config: Self = toml::from_str( &fs::read_to_string(path).with_context(|| format!("read {}", path.display()))?, )?; ensure!( !config.monitor_serial.is_empty(), "monitor_serial must identify the intended monitor" ); ensure!( (500..=5000).contains(&config.keepalive_ms), "keepalive_ms must be 500..5000" ); ensure!( (500..=30000).contains(&config.reconnect_ms), "reconnect_ms must be 500..30000" ); ensure!( config.watchdog_ms >= 20000, "watchdog_ms must be at least 20000" ); ensure!( matches!(config.transport.as_str(), "usb" | "serial"), "transport must be usb or serial" ); ensure!( config.transport != "serial" || config.serial_device.is_some(), "serial transport needs serial_device" ); ensure!( matches!(config.display.hotplug.as_str(), "none" | "amdgpu"), "display.hotplug must be none or amdgpu" ); for (¶meter, &value) in &config.startup { parameter.validate(value)?; } Ok(config) } }