Skip to content

Commit

Permalink
Add config file and implement config file parsing (#79)
Browse files Browse the repository at this point in the history
* Add config file format and config file parsing

* Parse config file in server, client, and input-backend

* Add --config option for server and client
  • Loading branch information
Ferdi265 authored Aug 25, 2024
1 parent 1127176 commit d304967
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 27 deletions.
45 changes: 41 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
31 changes: 31 additions & 0 deletions src/client/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -30,6 +32,25 @@ pub fn get_proxy() -> zbus::Result<ServerProxyBlocking<'static>> {
}

fn main() -> Result<(), glib::Error> {
// Get config path from command line
let mut config_path: Option<PathBuf> = 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(config_path.as_deref())
.expect("Failed to parse config file")
.client;

// Make sure that the server is running
let proxy = match get_proxy() {
Ok(proxy) => match proxy.introspect() {
Expand All @@ -47,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("<CONFIG FILE PATH>"),
);

// Capslock cmdline arg
app.add_main_option(
"caps-lock",
Expand Down
5 changes: 5 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
37 changes: 37 additions & 0 deletions src/config/backend.rs
Original file line number Diff line number Diff line change
@@ -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<PathBuf> {
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<BackendConfig, Box<dyn Error>> {
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)
}
54 changes: 54 additions & 0 deletions src/config/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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)]
#[serde(deny_unknown_fields)]
pub struct ClientConfig {}

#[derive(Deserialize, Default, Debug)]
#[serde(deny_unknown_fields)]
pub struct ServerConfig {
pub style: Option<PathBuf>,
pub top_margin: Option<f32>,
pub max_volume: Option<u8>,
}

#[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<PathBuf> {
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(path: Option<&Path>) -> Result<UserConfig, Box<dyn Error>> {
let path = match path.map(Path::to_owned).or_else(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)
}
1 change: 1 addition & 0 deletions src/global_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pub(crate) fn handle_application_args(
}
}
"style" => continue,
"config" => continue,
e => {
eprintln!("Unknown Variant Key: \"{}\"!...", e);
return (HandleLocalStatus::FAILURE, actions);
Expand Down
5 changes: 5 additions & 0 deletions src/input-backend/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
52 changes: 35 additions & 17 deletions src/server/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -21,9 +23,18 @@ 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(
"config",
glib::Char::from(0),
OptionFlags::NONE,
OptionArg::String,
"Use a custom config file instead of looking for one.",
Some("<CONFIG FILE PATH>"),
);

app.add_main_option(
"style",
glib::Char::from('s' as u8),
Expand All @@ -50,6 +61,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()) {
Expand All @@ -59,24 +80,21 @@ impl SwayOSDApplication {
for (arg_type, data) in actions {
match (arg_type, data) {
(ArgTypes::TopMargin, margin) => {
let margin: Option<f32> = match margin {
Some(margin) => match margin.parse::<f32>() {
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<f32> = 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<u8> = 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),
}
Expand Down
Loading

0 comments on commit d304967

Please sign in to comment.