Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config file and implement config file parsing #79

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some config dir information in the client, server, and backends --help print information

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good idea.

Though, I can't find a way to add text to --help without adding another option with gtk-rs. 😅

I think one way to workaround that would be to add a --config option that allows specifying a custom config path, and explain the config stuff in the help text for that option.

I'll look at this at some point in the next few days, when I have a bit more time.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good idea.

Though, I can't find a way to add text to --help without adding another option with gtk-rs. 😅

I think one way to workaround that would be to add a --config option that allows specifying a custom config path, and explain the config stuff in the help text for that option.

I'll look at this at some point in the next few days, when I have a bit more time.

Oh yeah, true. Sounds like a good idea 👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented the --config option today. Sorry it took so long. The libinput backend doesn't have any command line parsing at the moment, so I left it out there, but I can add similar parsing to it as with client/server.

The help message isn't final yet, I still need to think about how to best communicate the location without hardcoding paths or making the message too long.

Let me know if you like the basic implementation outline or if you'd like it done differently.

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