use config::{Config, File}; use log::error; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use wlx_common::{ astr_containers::AStrMap, config::{GeneralConfig, SerializedWindowSet, SerializedWindowStates}, config_io, overlays::BackendAttribValue, }; const FALLBACKS: [&str; 2] = [ include_str!("res/keyboard.yaml"), include_str!("res/wayvr.yaml"), ]; const FILES: [&str; 2] = ["keyboard.yaml", "wayvr.yaml"]; #[derive(Clone, Copy)] #[repr(usize)] pub enum ConfigType { Keyboard, Watch, Settings, Anchor, #[allow(dead_code)] WayVR, } pub fn load_known_yaml(config_type: ConfigType) -> T where T: for<'de> Deserialize<'de>, { let fallback = FALLBACKS[config_type as usize]; let file_name = FILES[config_type as usize]; let maybe_override = config_io::load(file_name); for yaml in [maybe_override.as_deref(), Some(fallback)].iter().flatten() { match serde_yaml::from_str::(yaml) { Ok(d) => return d, Err(e) => { error!("Failed to parse {file_name}, falling back to defaults."); error!("{e}"); } } } // can only get here if internal fallback is broken panic!("No usable config found."); } pub fn load_config_with_conf_d( root_config_filename: &str, ctype: config_io::ConfigRoot, ) -> ConfigData where ConfigData: for<'de> Deserialize<'de>, { let mut settings_builder = Config::builder(); // Add files from conf.d directory let path_conf_d = ctype.get_conf_d_path(); for mut base_conf in [config_io::get_config_root(), path_conf_d.clone()] { base_conf.push(root_config_filename); 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) { let mut paths: Vec<_> = paths_unsorted .filter_map(|r| match r { Ok(entry) => Some(entry), Err(e) => { error!("Failed to read conf.d directory: {e}"); None } }) .collect(); // Sort paths alphabetically paths.sort_by_key(std::fs::DirEntry::path); for path in paths { log::info!("Loading config file: {}", path.path().to_string_lossy()); settings_builder = settings_builder.add_source(File::from(path.path())); } } match settings_builder.build() { Ok(settings) => match settings.try_deserialize::() { Ok(config) => config, Err(e) => { panic!("Failed to deserialize settings: {e}"); } }, Err(e) => { panic!("Failed to build settings: {e}"); } } } pub fn load_general_config() -> GeneralConfig { load_config_with_conf_d::("config.yaml", config_io::ConfigRoot::Generic) } #[derive(Serialize)] pub struct AutoSettings { pub animation_speed: f32, pub round_multiplier: f32, pub click_freeze_time_ms: i32, pub invert_scroll_direction_x: bool, pub invert_scroll_direction_y: bool, pub scroll_speed: f32, pub long_press_duration: f32, pub notifications_enabled: bool, pub notifications_sound_enabled: bool, pub keyboard_sound_enabled: bool, pub upright_screen_fix: bool, pub double_cursor_fix: bool, pub sets_on_watch: bool, pub hide_grab_help: bool, pub xr_click_sensitivity: f32, pub xr_click_sensitivity_release: f32, pub allow_sliding: bool, pub focus_follows_mouse_mode: bool, pub left_handed_mouse: bool, pub block_game_input: bool, pub block_game_input_ignore_watch: bool, pub space_drag_multiplier: f32, pub use_skybox: bool, pub use_passthrough: bool, pub screen_render_down: bool, pub pointer_lerp_factor: f32, pub space_drag_unlocked: bool, pub space_rotate_unlocked: bool, pub clock_12h: bool, pub hide_username: bool, pub opaque_background: bool, pub xwayland_by_default: bool, pub context_menu_hold_and_release: bool, } fn get_settings_path() -> PathBuf { config_io::ConfigRoot::Generic .get_conf_d_path() .join("zz-saved-config.json5") } pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> { let conf = AutoSettings { animation_speed: config.animation_speed, round_multiplier: config.round_multiplier, click_freeze_time_ms: config.click_freeze_time_ms, invert_scroll_direction_x: config.invert_scroll_direction_x, invert_scroll_direction_y: config.invert_scroll_direction_y, scroll_speed: config.scroll_speed, long_press_duration: config.long_press_duration, notifications_enabled: config.notifications_enabled, notifications_sound_enabled: config.notifications_sound_enabled, keyboard_sound_enabled: config.keyboard_sound_enabled, upright_screen_fix: config.upright_screen_fix, double_cursor_fix: config.double_cursor_fix, sets_on_watch: config.sets_on_watch, hide_grab_help: config.hide_grab_help, xr_click_sensitivity: config.xr_click_sensitivity, xr_click_sensitivity_release: config.xr_click_sensitivity_release, allow_sliding: config.allow_sliding, focus_follows_mouse_mode: config.focus_follows_mouse_mode, left_handed_mouse: config.left_handed_mouse, block_game_input: config.block_game_input, block_game_input_ignore_watch: config.block_game_input_ignore_watch, space_drag_multiplier: config.space_drag_multiplier, use_skybox: config.use_skybox, use_passthrough: config.use_passthrough, screen_render_down: config.screen_render_down, pointer_lerp_factor: config.pointer_lerp_factor, space_drag_unlocked: config.space_drag_unlocked, space_rotate_unlocked: config.space_rotate_unlocked, clock_12h: config.clock_12h, hide_username: config.hide_username, opaque_background: config.opaque_background, xwayland_by_default: config.xwayland_by_default, context_menu_hold_and_release: config.context_menu_hold_and_release, }; let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic std::fs::write(get_settings_path(), json)?; log::info!("Saved settings."); Ok(()) } // Config that is saved after manipulating overlays #[derive(Serialize)] pub struct AutoState { pub sets: Vec, pub global_set: SerializedWindowStates, pub last_set: u32, pub attribs: AStrMap>, } fn get_state_path() -> PathBuf { config_io::ConfigRoot::Generic .get_conf_d_path() .join("zz-saved-state.json5") } pub fn save_state(config: &GeneralConfig) -> anyhow::Result<()> { let conf = AutoState { sets: config.sets.clone(), last_set: config.last_set, global_set: config.global_set.clone(), attribs: config.attribs.clone(), }; let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic std::fs::write(get_state_path(), json)?; log::info!("State was saved successfully."); Ok(()) }