bscreensaver/util/src/settings.rs
Brian J. Tarricone dda1a53856 Make the new login button stuff more automatic
By default it'll look at your environment to try to figure out which
display manager is used in order to start a new session.  We first try
the org.freedesktop.DisplayManager dbus interface, and if that fails,
inspect XDG_SESSION_DESKTOP to try to figure out which display manager
is running.

The user can also still specify the correct display manager, or a custom
command.
2022-05-15 20:50:45 -07:00

139 lines
4.8 KiB
Rust

use anyhow::anyhow;
use std::{fmt, fs::{self, File}, io::{Read, Write}, path::PathBuf, time::Duration};
use toml::Value;
use crate::desktop::NewLoginCommand;
const CONFIG_FILE_RELATIVE_PATH: &str = "bscreensaver/bscreensaver.toml";
const LOCK_TIMEOUT: &str = "lock-timeout";
const BLANK_BEFORE_LOCKING: &str = "blank-before-locking";
const DIALOG_BACKEND: &str = "dialog-backend";
const NEW_LOGIN_COMMAND: &str = "new-login-command";
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DialogBackend {
Gtk3,
}
impl DialogBackend {
pub fn binary_name(&self) -> &str {
match self {
Self::Gtk3 => "bscreensaver-dialog-gtk3",
}
}
pub fn display_name(&self) -> &str {
match self {
Self::Gtk3 => "GTK3",
}
}
}
impl fmt::Display for DialogBackend {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Gtk3 => write!(f, "gtk3"),
}
}
}
impl TryFrom<&str> for DialogBackend {
type Error = anyhow::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"gtk3" => Ok(Self::Gtk3),
other => Err(anyhow!("'{}' is not a valid dialog backend (valid: 'gtk3')", other)),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Configuration {
pub lock_timeout: Duration,
pub blank_before_locking: Duration,
pub dialog_backend: DialogBackend,
pub new_login_command: NewLoginCommand,
}
impl Configuration {
pub fn load() -> anyhow::Result<Configuration> {
use humantime::parse_duration;
let config_tomls = xdg::BaseDirectories::new()?
.find_config_files(CONFIG_FILE_RELATIVE_PATH)
.map(|config_path| parse_config_toml(&config_path))
.collect::<Result<Vec<Value>, _>>()?;
let mut config = Configuration::default();
for config_toml in config_tomls {
config.lock_timeout = match config_toml.get("lock-timeout") {
None => config.lock_timeout,
Some(val) => parse_duration(val.as_str().ok_or(anyhow!("'lock-timeout' must be a duration string like '10m' or '90s'"))?)?,
};
config.blank_before_locking = match config_toml.get("blank-before-locking") {
None => config.blank_before_locking,
Some(val) => parse_duration(val.as_str().ok_or(anyhow!("'blank-before-locking' must be a duration string like '10m' or '90s'"))?)?,
};
config.dialog_backend = match config_toml.get("dialog-backend") {
None => config.dialog_backend,
Some(val) => DialogBackend::try_from(val.as_str().ok_or(anyhow!("'dialog-backend' must be a string"))?)?,
};
config.new_login_command = match config_toml.get("new-login-command") {
None => config.new_login_command,
Some(val) => val.try_into().unwrap_or(NewLoginCommand::Auto),
};
}
if config.blank_before_locking >= config.lock_timeout {
Err(anyhow!("'blank-before-locking' cannot be greater than 'lock-timeout'"))
} else {
Ok(config)
}
}
pub fn save(&self) -> anyhow::Result<()> {
use humantime::format_duration;
use toml::map::Map;
let mut config_map = Map::new();
config_map.insert(LOCK_TIMEOUT.to_string(), Value::String(format_duration(self.lock_timeout).to_string()));
config_map.insert(BLANK_BEFORE_LOCKING.to_string(), Value::String(format_duration(self.blank_before_locking).to_string()));
config_map.insert(DIALOG_BACKEND.to_string(), Value::String(self.dialog_backend.to_string()));
config_map.insert(NEW_LOGIN_COMMAND.to_string(), self.new_login_command.clone().try_into()?);
let config_path = xdg::BaseDirectories::new()?.place_config_file(CONFIG_FILE_RELATIVE_PATH)?;
let mut tmp_filename = config_path.file_name().unwrap().to_os_string();
tmp_filename.push(".new");
let tmp_path = config_path.with_file_name(tmp_filename);
let mut f = File::create(&tmp_path)?;
f.write_all(Value::Table(config_map).to_string().as_bytes())?;
drop(f);
fs::rename(tmp_path, config_path)?;
Ok(())
}
}
impl Default for Configuration {
fn default() -> Self {
Self {
lock_timeout: Duration::from_secs(60 * 10),
blank_before_locking: Duration::ZERO,
dialog_backend: DialogBackend::Gtk3,
new_login_command: NewLoginCommand::Auto,
}
}
}
fn parse_config_toml(config_path: &PathBuf) -> anyhow::Result<Value> {
let mut f = File::open(config_path)?;
let mut config = String::new();
f.read_to_string(&mut config)?;
drop(f);
let config_toml = config.parse::<Value>()?;
Ok(config_toml)
}