rework config system

This commit is contained in:
galister
2024-04-04 22:46:39 +09:00
parent 2d0f733c67
commit e3ae9a9890
7 changed files with 431 additions and 263 deletions

467
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@ ash = "^0.37.2"
chrono = "0.4.29" chrono = "0.4.29"
chrono-tz = "0.8.5" chrono-tz = "0.8.5"
clap = { version = "4.5.1", features = ["derive"] } clap = { version = "4.5.1", features = ["derive"] }
config = "0.14.0"
cstr = "0.2.11" cstr = "0.2.11"
ctrlc = { version = "3.4.2", features = ["termination"] } ctrlc = { version = "3.4.2", features = ["termination"] }
dbus = { version = "0.9.7" } dbus = { version = "0.9.7" }
@@ -27,6 +28,7 @@ idmap = { version = "0.2.21", features = ["serde"] }
idmap-derive = "0.1.2" idmap-derive = "0.1.2"
input-linux = "0.6.0" input-linux = "0.6.0"
json = { version = "0.12.4", optional = true } json = { version = "0.12.4", optional = true }
json5 = "0.4.1"
libc = "0.2.153" libc = "0.2.153"
log = "0.4.21" log = "0.4.21"
once_cell = "1.19.0" once_cell = "1.19.0"
@@ -37,7 +39,7 @@ rodio = { version = "0.17.1", default-features = false, features = ["wav", "houn
rosc = { version = "0.10.1", optional = true } rosc = { version = "0.10.1", optional = true }
serde = { version = "1.0.188", features = ["derive", "rc"] } serde = { version = "1.0.188", features = ["derive", "rc"] }
serde_json = "1.0.113" serde_json = "1.0.113"
serde_yaml = "0.9.25" serde_yaml = "0.9.34"
smallvec = "1.11.0" smallvec = "1.11.0"
strum = { version = "0.25.0", features = ["derive"] } strum = { version = "0.25.0", features = ["derive"] }
thiserror = "1.0.56" thiserror = "1.0.56"

View File

@@ -1,7 +1,6 @@
use dbus::{blocking::Connection, channel::MatchingReceiver, message::MatchRule}; use dbus::{blocking::Connection, channel::MatchingReceiver, message::MatchRule};
use serde::{Deserialize, Serialize}; use serde::Deserialize;
use std::{ use std::{
path::PathBuf,
sync::{ sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
mpsc, Arc, mpsc, Arc,
@@ -10,8 +9,6 @@ use std::{
}; };
use crate::{ use crate::{
config::def_true,
config_io,
overlays::toast::{Toast, ToastTopic}, overlays::toast::{Toast, ToastTopic},
state::AppState, state::AppState,
}; };
@@ -238,29 +235,3 @@ struct XsoMessage {
sourceApp: Option<Arc<str>>, sourceApp: Option<Arc<str>>,
alwaysShow: Option<bool>, alwaysShow: Option<bool>,
} }
#[derive(Deserialize, Serialize)]
pub struct NotifiConf {
#[serde(default = "def_true")]
pub notifications_enabled: bool,
#[serde(default = "def_true")]
pub notifications_sound_enabled: bool,
}
fn get_config_path() -> PathBuf {
let mut path = config_io::get_conf_d_path();
path.push("notifications.yaml");
path
}
pub fn save_notifications(app: &mut AppState) -> anyhow::Result<()> {
let conf = NotifiConf {
notifications_enabled: app.session.config.notifications_enabled,
notifications_sound_enabled: app.session.config.notifications_sound_enabled,
};
let yaml = serde_yaml::to_string(&conf)?;
std::fs::write(get_config_path(), yaml)?;
Ok(())
}

View File

@@ -2,12 +2,14 @@ use std::sync::Arc;
use crate::config_io; use crate::config_io;
use crate::config_io::get_conf_d_path; use crate::config_io::get_conf_d_path;
use crate::config_io::CONFIG_ROOT_PATH;
use crate::gui::modular::ModularUiConfig; use crate::gui::modular::ModularUiConfig;
use crate::load_with_fallback;
use crate::overlays::toast::DisplayMethod; use crate::overlays::toast::DisplayMethod;
use crate::overlays::toast::ToastTopic; use crate::overlays::toast::ToastTopic;
use crate::state::LeftRight; use crate::state::LeftRight;
use anyhow::bail; use anyhow::bail;
use config::Config;
use config::File;
use idmap::IdMap; use idmap::IdMap;
use log::error; use log::error;
use serde::Deserialize; use serde::Deserialize;
@@ -221,10 +223,19 @@ pub fn load_custom_ui(name: &str) -> anyhow::Result<ModularUiConfig> {
} }
pub fn load_general() -> GeneralConfig { pub fn load_general() -> GeneralConfig {
let mut yaml_data = String::new(); let mut settings_builder = Config::builder();
// Add files from conf.d directory // Add files from conf.d directory
let path_conf_d = get_conf_d_path(); let path_conf_d = get_conf_d_path();
for mut base_conf in [CONFIG_ROOT_PATH.clone(), path_conf_d.clone()] {
base_conf.push("config.yaml");
if base_conf.exists() {
log::info!("Loading config file: {}", base_conf.to_string_lossy());
settings_builder = settings_builder.add_source(File::from(base_conf));
}
}
if let Ok(paths_unsorted) = std::fs::read_dir(path_conf_d) { if let Ok(paths_unsorted) = std::fs::read_dir(path_conf_d) {
let mut paths: Vec<_> = paths_unsorted let mut paths: Vec<_> = paths_unsorted
.filter_map(|r| match r { .filter_map(|r| match r {
@@ -238,37 +249,24 @@ pub fn load_general() -> GeneralConfig {
// Sort paths alphabetically // Sort paths alphabetically
paths.sort_by_key(|dir| dir.path()); paths.sort_by_key(|dir| dir.path());
for path in paths { for path in paths {
let file_type = match path.file_type() { log::info!("Loading config file: {}", path.path().to_string_lossy());
Ok(file_type) => file_type, settings_builder = settings_builder.add_source(File::from(path.path()));
Err(e) => {
error!(
"Failed to get file type of {}: {}",
path.path().to_string_lossy(),
e
);
continue;
}
};
if !file_type.is_file() {
continue;
}
log::info!("Loading config file {}", path.path().to_string_lossy());
if let Ok(data) = std::fs::read_to_string(path.path()) {
yaml_data.push('\n'); // Just in case, if end of the config file was not newline
yaml_data.push_str(data.as_str());
} else {
// Shouldn't happen anyways
error!("Failed to load {}", path.path().to_string_lossy());
}
} }
} }
if yaml_data.is_empty() { match settings_builder.build() {
yaml_data.push_str(load_with_fallback!("config.yaml", "res/config.yaml").as_str()); Ok(settings) => {
} match settings.try_deserialize::<GeneralConfig>() {
Ok(config) => {
serde_yaml::from_str(&yaml_data).expect("Failed to parse config.yaml") return config;
}
Err(e) => {
panic!("Failed to deserialize settings: {}", e);
}
};
}
Err(e) => {
panic!("Failed to build settings: {}", e);
}
};
} }

View File

@@ -53,19 +53,3 @@ pub fn load(filename: &str) -> Option<String> {
None None
} }
} }
#[macro_export]
macro_rules! load_with_fallback {
($filename: expr, $fallback: expr) => {
if let Some(data) = config_io::load($filename) {
data
} else {
log::info!(
"Config {}/{} does not exist, using internal fallback",
config_io::CONFIG_ROOT_PATH.to_string_lossy(),
$filename
);
include_str!($fallback).to_string()
}
};
}

View File

@@ -1,26 +1,28 @@
use std::{ use std::{
f32::consts::PI, f32::consts::PI,
ops::Add, ops::Add,
path::PathBuf,
process::{self, Child}, process::{self, Child},
sync::Arc, sync::Arc,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use glam::{Quat, Vec3A, Vec4}; use glam::{Quat, Vec3A, Vec4};
use serde::Deserialize; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
backend::{ backend::{
common::{ColorChannel, OverlaySelector, SystemTask, TaskType}, common::{ColorChannel, OverlaySelector, SystemTask, TaskType},
input::PointerMode, input::PointerMode,
notifications::save_notifications,
overlay::RelativeTo, overlay::RelativeTo,
}, },
config::{def_half, def_left, def_point7, def_true, def_watch_pos, def_watch_rot},
config_io,
overlays::{ overlays::{
toast::{Toast, ToastTopic}, toast::{Toast, ToastTopic},
watch::{save_watch, WATCH_NAME}, watch::WATCH_NAME,
}, },
state::AppState, state::{AppState, LeftRight},
}; };
use super::{ExecArgs, ModularControl, ModularData}; use super::{ExecArgs, ModularControl, ModularData};
@@ -407,11 +409,8 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
.submit(app); .submit(app);
} }
SystemAction::PersistConfig => { SystemAction::PersistConfig => {
if let Err(e) = save_watch(app) { if let Err(e) = save_settings(app) {
log::error!("Failed to save watch config: {:?}", e); log::error!("Failed to save config: {:?}", e);
};
if let Err(e) = save_notifications(app) {
log::error!("Failed to save notifications config: {:?}", e);
} }
} }
} }
@@ -699,3 +698,53 @@ const THUMP_AUDIO_WAV: &[u8] = include_bytes!("../../res/380885.wav");
fn audio_thump(app: &mut AppState) { fn audio_thump(app: &mut AppState) {
app.audio.play(THUMP_AUDIO_WAV); app.audio.play(THUMP_AUDIO_WAV);
} }
#[derive(Deserialize, Serialize)]
pub struct AutoSettings {
#[serde(default = "def_watch_pos")]
pub watch_pos: [f32; 3],
#[serde(default = "def_watch_rot")]
pub watch_rot: [f32; 4],
#[serde(default = "def_left")]
pub watch_hand: LeftRight,
#[serde(default = "def_half")]
pub watch_view_angle_min: f32,
#[serde(default = "def_point7")]
pub watch_view_angle_max: f32,
#[serde(default = "def_true")]
pub notifications_enabled: bool,
#[serde(default = "def_true")]
pub notifications_sound_enabled: bool,
#[serde(default = "def_true")]
pub realign_on_showhide: bool,
}
fn get_config_path() -> PathBuf {
let mut path = config_io::get_conf_d_path();
path.push("zz-saved-config.json5");
path
}
pub fn save_settings(app: &mut AppState) -> anyhow::Result<()> {
let conf = AutoSettings {
watch_pos: app.session.config.watch_pos,
watch_rot: app.session.config.watch_rot,
watch_hand: app.session.config.watch_hand,
watch_view_angle_min: app.session.config.watch_view_angle_min,
watch_view_angle_max: app.session.config.watch_view_angle_max,
notifications_enabled: app.session.config.notifications_enabled,
notifications_sound_enabled: app.session.config.notifications_sound_enabled,
realign_on_showhide: app.session.config.realign_on_showhide,
};
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic
std::fs::write(get_config_path(), json)?;
Ok(())
}

View File

@@ -1,18 +1,13 @@
use glam::{Quat, Vec3A}; use glam::{Quat, Vec3A};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use crate::{ use crate::{
backend::overlay::{ui_transform, OverlayData, OverlayState, RelativeTo}, backend::overlay::{ui_transform, OverlayData, OverlayState, RelativeTo},
config::{ config::{load_known_yaml, ConfigType},
def_half, def_left, def_point7, def_watch_pos, def_watch_rot, load_known_yaml, ConfigType,
},
config_io,
gui::{ gui::{
modular::{modular_canvas, ModularData, ModularUiConfig}, modular::{modular_canvas, ModularData, ModularUiConfig},
Canvas, Canvas,
}, },
state::{AppState, LeftRight}, state::AppState,
}; };
pub const WATCH_NAME: &str = "watch"; pub const WATCH_NAME: &str = "watch";
@@ -79,41 +74,3 @@ where
watch.state.alpha = watch.state.alpha.clamp(0., 1.); watch.state.alpha = watch.state.alpha.clamp(0., 1.);
} }
} }
#[derive(Deserialize, Serialize)]
pub struct WatchConf {
#[serde(default = "def_watch_pos")]
pub watch_pos: [f32; 3],
#[serde(default = "def_watch_rot")]
pub watch_rot: [f32; 4],
#[serde(default = "def_left")]
pub watch_hand: LeftRight,
#[serde(default = "def_half")]
pub watch_view_angle_min: f32,
#[serde(default = "def_point7")]
pub watch_view_angle_max: f32,
}
fn get_config_path() -> PathBuf {
let mut path = config_io::get_conf_d_path();
path.push("watch_state.yaml");
path
}
pub fn save_watch(app: &mut AppState) -> anyhow::Result<()> {
let conf = WatchConf {
watch_pos: app.session.config.watch_pos,
watch_rot: app.session.config.watch_rot,
watch_hand: app.session.config.watch_hand,
watch_view_angle_min: app.session.config.watch_view_angle_min,
watch_view_angle_max: app.session.config.watch_view_angle_max,
};
let yaml = serde_yaml::to_string(&conf)?;
std::fs::write(get_config_path(), yaml)?;
Ok(())
}