From d02d9d8c01a192e27260f5021f37c712450ee4ab Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Sun, 18 Feb 2024 14:38:54 +0100 Subject: [PATCH 1/3] Add config file format and config file parsing --- Cargo.lock | 45 ++++++++++++++++++++++++++++++++---- Cargo.toml | 4 ++++ src/config.rs | 5 ++++ src/config/backend.rs | 37 ++++++++++++++++++++++++++++++ src/config/user.rs | 53 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 src/config/backend.rs create mode 100644 src/config/user.rs diff --git a/Cargo.lock b/Cargo.lock index 490977a..ef934e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1201,7 +1201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.12", ] [[package]] @@ -1505,9 +1505,12 @@ dependencies = [ "libpulse-binding", "nix", "pulsectl-rs", + "serde", + "serde_derive", "shrinkwraprs", "substring", "thiserror", + "toml 0.8.2", "zbus", ] @@ -1542,7 +1545,7 @@ dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml", + "toml 0.7.6", "version-compare", ] @@ -1595,7 +1598,19 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.19.12", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", ] [[package]] @@ -1617,7 +1632,20 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.4.9", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", ] [[package]] @@ -1888,6 +1916,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "xdg-home" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 75284f3..4a6f014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,10 @@ path = "src/input-backend/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Config dependencies +toml = "0.8" +serde = "1" +serde_derive = "1" # GUI Dependencies gtk = "0.17.1" gtk-layer-shell = "0.6.1" diff --git a/src/config.rs b/src/config.rs index 57fd246..8c34454 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,10 @@ #![allow(dead_code)] +#[path = "config/backend.rs"] +pub mod backend; +#[path = "config/user.rs"] +pub mod user; + pub const DBUS_PATH: &str = "/org/erikreider/swayosd"; pub const DBUS_BACKEND_NAME: &str = "org.erikreider.swayosd"; pub const DBUS_SERVER_NAME: &str = "org.erikreider.swayosd-server"; diff --git a/src/config/backend.rs b/src/config/backend.rs new file mode 100644 index 0000000..e2c9bbf --- /dev/null +++ b/src/config/backend.rs @@ -0,0 +1,37 @@ +use gtk::glib::system_config_dirs; +use serde_derive::Deserialize; +use std::error::Error; +use std::path::PathBuf; + +#[derive(Deserialize, Default, Debug)] +#[serde(deny_unknown_fields)] +pub struct InputBackendConfig {} + +#[derive(Deserialize, Default, Debug)] +#[serde(deny_unknown_fields)] +pub struct BackendConfig { + #[serde(default)] + pub input: InputBackendConfig, +} + +fn find_backend_config() -> Option { + for path in system_config_dirs() { + let path = path.join("swayosd").join("backend.toml"); + if path.exists() { + return Some(path); + } + } + + None +} + +pub fn read_backend_config() -> Result> { + let path = match find_backend_config() { + Some(path) => path, + None => return Ok(Default::default()), + }; + + let config_file = std::fs::read_to_string(path)?; + let config: BackendConfig = toml::from_str(&config_file)?; + Ok(config) +} diff --git a/src/config/user.rs b/src/config/user.rs new file mode 100644 index 0000000..179ba89 --- /dev/null +++ b/src/config/user.rs @@ -0,0 +1,53 @@ +use gtk::glib::system_config_dirs; +use gtk::glib::user_config_dir; +use serde_derive::Deserialize; +use std::error::Error; +use std::path::PathBuf; + +#[derive(Deserialize, Default, Debug)] +#[serde(deny_unknown_fields)] +pub struct ClientConfig {} + +#[derive(Deserialize, Default, Debug)] +#[serde(deny_unknown_fields)] +pub struct ServerConfig { + pub style: Option, + pub top_margin: Option, + pub max_volume: Option, +} + +#[derive(Deserialize, Default, Debug)] +#[serde(deny_unknown_fields)] +pub struct UserConfig { + #[serde(default)] + pub server: ServerConfig, + #[serde(default)] + pub client: ClientConfig, +} + +fn find_user_config() -> Option { + let path = user_config_dir().join("swayosd").join("config.toml"); + if path.exists() { + return Some(path); + } + + for path in system_config_dirs() { + let path = path.join("swayosd").join("config.toml"); + if path.exists() { + return Some(path); + } + } + + None +} + +pub fn read_user_config() -> Result> { + let path = match find_user_config() { + Some(path) => path, + None => return Ok(Default::default()), + }; + + let config_file = std::fs::read_to_string(path)?; + let config: UserConfig = toml::from_str(&config_file)?; + Ok(config) +} From 78d9910737310d73a15e364855340abcf1a48486 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Sun, 18 Feb 2024 14:40:28 +0100 Subject: [PATCH 2/3] Parse config file in server, client, and input-backend --- src/client/main.rs | 5 +++++ src/input-backend/main.rs | 5 +++++ src/server/application.rs | 43 +++++++++++++++++++++++---------------- src/server/main.rs | 9 ++++++-- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/client/main.rs b/src/client/main.rs index fee2279..702e3f3 100644 --- a/src/client/main.rs +++ b/src/client/main.rs @@ -30,6 +30,11 @@ pub fn get_proxy() -> zbus::Result> { } fn main() -> Result<(), glib::Error> { + // Parse Config + let _client_config = config::user::read_user_config() + .expect("Failed to parse config file") + .client; + // Make sure that the server is running let proxy = match get_proxy() { Ok(proxy) => match proxy.introspect() { diff --git a/src/input-backend/main.rs b/src/input-backend/main.rs index 889d9d5..9186b80 100644 --- a/src/input-backend/main.rs +++ b/src/input-backend/main.rs @@ -42,6 +42,11 @@ impl LibinputInterface for Interface { } fn main() -> Result<(), zbus::Error> { + // Parse Config + let _input_config = config::backend::read_backend_config() + .expect("Failed to parse config file") + .input; + // Create DBUS server let connection = task::block_on(DbusServer.init()); let object_server = connection.object_server(); diff --git a/src/server/application.rs b/src/server/application.rs index 13c6c3c..77eb444 100644 --- a/src/server/application.rs +++ b/src/server/application.rs @@ -13,6 +13,8 @@ use std::cell::RefCell; use std::rc::Rc; use std::sync::{Arc, Mutex}; +use super::config::user::ServerConfig; + #[derive(Clone, Shrinkwrap)] pub struct SwayOSDApplication { #[shrinkwrap(main_field)] @@ -21,7 +23,7 @@ pub struct SwayOSDApplication { } impl SwayOSDApplication { - pub fn new(action_receiver: Receiver<(ArgTypes, String)>) -> Self { + pub fn new(server_config: ServerConfig, action_receiver: Receiver<(ArgTypes, String)>) -> Self { let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); app.add_main_option( @@ -50,6 +52,16 @@ impl SwayOSDApplication { windows: Rc::new(RefCell::new(Vec::new())), }; + // Apply Server Config + if let Some(margin) = server_config.top_margin { + if (0_f32..1_f32).contains(&margin) { + set_top_margin(margin); + } + } + if let Some(max_volume) = server_config.max_volume { + set_default_max_volume(max_volume); + } + // Parse args app.connect_handle_local_options(clone!(@strong osd_app => move |_app, args| { let actions = match handle_application_args(args.to_variant()) { @@ -59,24 +71,21 @@ impl SwayOSDApplication { for (arg_type, data) in actions { match (arg_type, data) { (ArgTypes::TopMargin, margin) => { - let margin: Option = match margin { - Some(margin) => match margin.parse::() { - Ok(margin) => (0_f32..1_f32).contains(&margin).then_some(margin), - _ => None, - }, - _ => None, - }; - set_top_margin(margin.unwrap_or(*TOP_MARGIN_DEFAULT)) + let margin: Option = margin + .and_then(|margin| margin.parse().ok()) + .and_then(|margin| (0_f32..1_f32).contains(&margin).then_some(margin)); + + if let Some(margin) = margin { + set_top_margin(margin) + } }, (ArgTypes::MaxVolume, max) => { - let volume: u8 = match max { - Some(max) => match max.parse() { - Ok(max) => max, - _ => get_default_max_volume(), - } - _ => get_default_max_volume(), - }; - set_default_max_volume(volume); + let max: Option = max + .and_then(|max| max.parse().ok()); + + if let Some(max) = max { + set_default_max_volume(max); + } }, (arg_type, data) => Self::action_activated(&osd_app, arg_type, data), } diff --git a/src/server/main.rs b/src/server/main.rs index fb571d9..21cbf0d 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -79,6 +79,11 @@ fn main() { std::process::exit(1); } + // Parse Config + let server_config = config::user::read_user_config() + .expect("Failed to parse config file") + .server; + // Load the compiled resource bundle let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); let resource_data = Bytes::from(&resources_bytes[..]); @@ -107,7 +112,7 @@ fn main() { } // Try loading the users CSS theme - let mut custom_user_css: Option = None; + let mut custom_user_css: Option = server_config.style.clone(); let mut args = args_os().into_iter(); while let Some(arg) = args.next() { match arg.to_str() { @@ -135,5 +140,5 @@ fn main() { // Start the DBus Server async_std::task::spawn(DbusServer::new(sender)); // Start the GTK Application - std::process::exit(SwayOSDApplication::new(receiver).start()); + std::process::exit(SwayOSDApplication::new(server_config, receiver).start()); } From e62e9509ef80fbba92cf1426a3bd0b850ca880e2 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Mon, 6 May 2024 18:30:54 +0200 Subject: [PATCH 3/3] Add --config option for server and client --- src/client/main.rs | 28 +++++++++++++++++++++++++++- src/config/user.rs | 5 +++-- src/global_utils.rs | 1 + src/server/application.rs | 9 +++++++++ src/server/main.rs | 36 +++++++++++++++++++++++++----------- 5 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/client/main.rs b/src/client/main.rs index 702e3f3..0ca837e 100644 --- a/src/client/main.rs +++ b/src/client/main.rs @@ -13,6 +13,8 @@ use global_utils::{handle_application_args, HandleLocalStatus}; use gtk::glib::{OptionArg, OptionFlags}; use gtk::{gio::ApplicationFlags, Application}; use gtk::{glib, prelude::*}; +use std::env::args_os; +use std::path::PathBuf; use zbus::{blocking::Connection, dbus_proxy}; #[dbus_proxy( @@ -30,8 +32,22 @@ pub fn get_proxy() -> zbus::Result> { } fn main() -> Result<(), glib::Error> { + // Get config path from command line + let mut config_path: Option = None; + let mut args = args_os().into_iter(); + while let Some(arg) = args.next() { + match arg.to_str() { + Some("--config") => { + if let Some(path) = args.next() { + config_path = Some(path.into()); + } + } + _ => (), + } + } + // Parse Config - let _client_config = config::user::read_user_config() + let _client_config = config::user::read_user_config(config_path.as_deref()) .expect("Failed to parse config file") .client; @@ -52,6 +68,16 @@ fn main() -> Result<(), glib::Error> { let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); + // Config cmdline arg for documentation + app.add_main_option( + "config", + glib::Char::from(0), + OptionFlags::NONE, + OptionArg::String, + "Use a custom config file instead of looking for one.", + Some(""), + ); + // Capslock cmdline arg app.add_main_option( "caps-lock", diff --git a/src/config/user.rs b/src/config/user.rs index 179ba89..bcd98fc 100644 --- a/src/config/user.rs +++ b/src/config/user.rs @@ -2,6 +2,7 @@ use gtk::glib::system_config_dirs; use gtk::glib::user_config_dir; use serde_derive::Deserialize; use std::error::Error; +use std::path::Path; use std::path::PathBuf; #[derive(Deserialize, Default, Debug)] @@ -41,8 +42,8 @@ fn find_user_config() -> Option { None } -pub fn read_user_config() -> Result> { - let path = match find_user_config() { +pub fn read_user_config(path: Option<&Path>) -> Result> { + let path = match path.map(Path::to_owned).or_else(find_user_config) { Some(path) => path, None => return Ok(Default::default()), }; diff --git a/src/global_utils.rs b/src/global_utils.rs index fa64428..d786474 100644 --- a/src/global_utils.rs +++ b/src/global_utils.rs @@ -118,6 +118,7 @@ pub(crate) fn handle_application_args( } } "style" => continue, + "config" => continue, e => { eprintln!("Unknown Variant Key: \"{}\"!...", e); return (HandleLocalStatus::FAILURE, actions); diff --git a/src/server/application.rs b/src/server/application.rs index 77eb444..8a85fe0 100644 --- a/src/server/application.rs +++ b/src/server/application.rs @@ -26,6 +26,15 @@ impl SwayOSDApplication { pub fn new(server_config: ServerConfig, action_receiver: Receiver<(ArgTypes, String)>) -> Self { let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); + app.add_main_option( + "config", + glib::Char::from(0), + OptionFlags::NONE, + OptionArg::String, + "Use a custom config file instead of looking for one.", + Some(""), + ); + app.add_main_option( "style", glib::Char::from('s' as u8), diff --git a/src/server/main.rs b/src/server/main.rs index 21cbf0d..d2f1a97 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -79,11 +79,6 @@ fn main() { std::process::exit(1); } - // Parse Config - let server_config = config::user::read_user_config() - .expect("Failed to parse config file") - .server; - // Load the compiled resource bundle let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); let resource_data = Bytes::from(&resources_bytes[..]); @@ -111,18 +106,37 @@ fn main() { None => eprintln!("Could not find the system CSS file..."), } - // Try loading the users CSS theme - let mut custom_user_css: Option = server_config.style.clone(); + // Get config path and CSS theme path from command line + let mut config_path: Option = None; + let mut custom_user_css: Option = None; let mut args = args_os().into_iter(); while let Some(arg) = args.next() { match arg.to_str() { - Some("-s") | Some("--style") => match args.next() { - Some(path) => custom_user_css = path.to_str().map(|s| PathBuf::from(s)), - _ => (), - }, + Some("--config") => { + if let Some(path) = args.next() { + config_path = Some(path.into()); + } + } + Some("-s") | Some("--style") => { + if let Some(path) = args.next() { + custom_user_css = Some(path.into()); + } + } _ => (), } } + + // Parse Config + let server_config = config::user::read_user_config(config_path.as_deref()) + .expect("Failed to parse config file") + .server; + + // Load style path from config if none is given on CLI + if custom_user_css.is_none() { + custom_user_css = server_config.style.clone(); + } + + // Try loading the users CSS theme if let Some(user_config_path) = user_style_path(custom_user_css) { let user_provider = CssProvider::new(); user_provider