rework config system
This commit is contained in:
467
Cargo.lock
generated
467
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||||
|
|||||||
@@ -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(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match settings_builder.build() {
|
||||||
|
Ok(settings) => {
|
||||||
|
match settings.try_deserialize::<GeneralConfig>() {
|
||||||
|
Ok(config) => {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
panic!("Failed to deserialize settings: {}", e);
|
||||||
"Failed to get file type of {}: {}",
|
|
||||||
path.path().to_string_lossy(),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !file_type.is_file() {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
log::info!("Loading config file {}", path.path().to_string_lossy());
|
panic!("Failed to build settings: {}", e);
|
||||||
|
|
||||||
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() {
|
|
||||||
yaml_data.push_str(load_with_fallback!("config.yaml", "res/config.yaml").as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
serde_yaml::from_str(&yaml_data).expect("Failed to parse config.yaml")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -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(())
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user