Added hot config system, fixed gamemode option

This commit is contained in:
Observer KRypt0n_ 2022-07-24 15:01:59 +02:00
parent a6667f73d4
commit 737aa7e8bc
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
6 changed files with 69 additions and 16 deletions

View file

@ -14,7 +14,7 @@ gtk4 = "0.4"
gtk4 = "0.4" gtk4 = "0.4"
libadwaita = "0.1" libadwaita = "0.1"
anime-game-core = { path = "anime-game-core", features = ["all"] } anime-game-core = { path = "anime-game-core", features = ["all", "static"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"

View file

@ -19,7 +19,26 @@ pub use hud::HUD;
pub use wine_sync::WineSync; pub use wine_sync::WineSync;
pub use wine_lang::WineLang; pub use wine_lang::WineLang;
static mut CONFIG: Option<Config> = None;
/// Get config data
///
/// This method will load config from file once and store it into the memory.
/// If you know that the config file was updated - you should run `get_raw` method
/// that always loads config directly from the file. This will also update in-memory config
pub fn get() -> Result<Config, Error> { pub fn get() -> Result<Config, Error> {
unsafe {
match &CONFIG {
Some(config) => Ok(config.clone()),
None => get_raw()
}
}
}
/// Get config data
///
/// This method will always load data directly from the file and update in-memory config
pub fn get_raw() -> Result<Config, Error> {
match config_file() { match config_file() {
Some(path) => { Some(path) => {
// Try to read config if the file exists // Try to read config if the file exists
@ -30,14 +49,20 @@ pub fn get() -> Result<Config, Error> {
file.read_to_string(&mut json)?; file.read_to_string(&mut json)?;
match serde_json::from_str::<Config>(&json) { match serde_json::from_str::<Config>(&json) {
Ok(json) => Ok(json), Ok(config) => {
unsafe {
CONFIG = Some(config.clone());
}
Ok(config)
},
Err(err) => Err(Error::new(ErrorKind::InvalidData, format!("Failed to decode data from json format: {}", err.to_string()))) Err(err) => Err(Error::new(ErrorKind::InvalidData, format!("Failed to decode data from json format: {}", err.to_string())))
} }
} }
// Otherwise create default config file // Otherwise create default config file
else { else {
update(Config::default())?; update_raw(Config::default())?;
Ok(Config::default()) Ok(Config::default())
} }
@ -46,7 +71,21 @@ pub fn get() -> Result<Config, Error> {
} }
} }
pub fn update(config: Config) -> Result<(), Error> { /// Update in-memory config data
///
/// Use `update_raw` if you want to update config file itself
pub fn update(config: Config) {
unsafe {
CONFIG = Some(config);
}
}
/// Update config file
///
/// This method will also update in-memory config data
pub fn update_raw(config: Config) -> Result<(), Error> {
update(config.clone());
match config_file() { match config_file() {
Some(path) => { Some(path) => {
let mut file = File::create(&path)?; let mut file = File::create(&path)?;
@ -64,6 +103,16 @@ pub fn update(config: Config) -> Result<(), Error> {
} }
} }
/// Update config file from the in-memory saved config
pub fn flush() -> Result<(), Error> {
unsafe {
match &CONFIG {
Some(config) => update_raw(config.clone()),
None => Err(Error::new(ErrorKind::Other, "Config wasn't loaded into the memory"))
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Config { pub struct Config {
pub launcher: Launcher, pub launcher: Launcher,

View file

@ -42,6 +42,11 @@ async fn main() {
main.show(); main.show();
}); });
// Flush config from the memory to the file before closing the app
application.connect_shutdown(|_| {
lib::config::flush().expect("Failed to save config data");
});
// Run app // Run app
application.run(); application.run();
} }

View file

@ -91,7 +91,7 @@ pub enum Actions {
impl Actions { impl Actions {
pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> { pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> {
Box::new(clone!(@strong self as action, @strong app => move |_| { Box::new(clone!(@strong self as action, @weak app => move |_| {
app.update(action.clone()).expect(&format!("Failed to execute action {:?}", &action)); app.update(action.clone()).expect(&format!("Failed to execute action {:?}", &action));
})) }))
} }

View file

@ -154,8 +154,7 @@ impl App {
match action { match action {
Actions::OptionSelection(update, value) => { Actions::OptionSelection(update, value) => {
if let Ok(config) = config::get() { if let Ok(config) = config::get() {
// TODO: show toast config::update((update)(config, value));
config::update((update)(config, value)).unwrap();
} }
} }
} }
@ -193,7 +192,7 @@ impl App {
self.widgets.fsr_switcher.set_state(config.game.enhancements.fsr.enabled); self.widgets.fsr_switcher.set_state(config.game.enhancements.fsr.enabled);
// Gamemode switching // Gamemode switching
self.widgets.fsr_switcher.set_state(config.game.enhancements.gamemode); self.widgets.gamemode_switcher.set_state(config.game.enhancements.gamemode);
Ok(()) Ok(())
} }

View file

@ -150,7 +150,7 @@ pub enum Actions {
impl Actions { impl Actions {
pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> { pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> {
Box::new(clone!(@strong self as action, @strong app => move |_| { Box::new(clone!(@strong self as action, @weak app => move |_| {
app.update(action.clone()).expect(&format!("Failed to execute action {:?}", &action)); app.update(action.clone()).expect(&format!("Failed to execute action {:?}", &action));
})) }))
} }
@ -199,7 +199,7 @@ impl App {
/// Add default events and values to the widgets /// Add default events and values to the widgets
fn init_events(self) -> Self { fn init_events(self) -> Self {
self.widgets.wine_selected.connect_selected_notify(clone!(@strong self as this => move |combo_row| { self.widgets.wine_selected.connect_selected_notify(clone!(@weak self as this => move |combo_row| {
if let Some(model) = combo_row.model() { if let Some(model) = combo_row.model() {
if model.n_items() > 0 { if model.n_items() > 0 {
this.update(Actions::SelectWineVersion(Rc::new(combo_row.selected() as usize))).unwrap(); this.update(Actions::SelectWineVersion(Rc::new(combo_row.selected() as usize))).unwrap();
@ -207,7 +207,7 @@ impl App {
} }
})); }));
self.widgets.dxvk_selected.connect_selected_notify(clone!(@strong self as this => move |combo_row| { self.widgets.dxvk_selected.connect_selected_notify(clone!(@weak self as this => move |combo_row| {
if let Some(model) = combo_row.model() { if let Some(model) = combo_row.model() {
if model.n_items() > 0 { if model.n_items() > 0 {
this.update(Actions::SelectDxvkVersion(Rc::new(combo_row.selected() as usize))).unwrap(); this.update(Actions::SelectDxvkVersion(Rc::new(combo_row.selected() as usize))).unwrap();
@ -216,7 +216,7 @@ impl App {
})); }));
// Set wine recommended only switcher event // Set wine recommended only switcher event
self.widgets.wine_recommended_only.connect_state_notify(clone!(@strong self as this => move |switcher| { self.widgets.wine_recommended_only.connect_state_notify(clone!(@weak self as this => move |switcher| {
for group in &*this.widgets.wine_components { for group in &*this.widgets.wine_components {
for component in &group.version_components { for component in &group.version_components {
component.row.set_visible(if switcher.state() { component.row.set_visible(if switcher.state() {
@ -238,7 +238,7 @@ impl App {
} }
// Set DXVK recommended only switcher event // Set DXVK recommended only switcher event
self.widgets.dxvk_recommended_only.connect_state_notify(clone!(@strong self as this => move |switcher| { self.widgets.dxvk_recommended_only.connect_state_notify(clone!(@weak self as this => move |switcher| {
for component in &*this.widgets.dxvk_components { for component in &*this.widgets.dxvk_components {
component.row.set_visible(if switcher.state() { component.row.set_visible(if switcher.state() {
component.version.recommended component.version.recommended
@ -254,7 +254,7 @@ impl App {
for (i, component) in components.into_iter().enumerate() { for (i, component) in components.into_iter().enumerate() {
component.button.connect_clicked(Actions::DxvkPerformAction(Rc::new(i)).into_fn(&self)); component.button.connect_clicked(Actions::DxvkPerformAction(Rc::new(i)).into_fn(&self));
component.apply_button.connect_clicked(clone!(@strong component, @strong self as this => move |_| { component.apply_button.connect_clicked(clone!(@strong component, @weak self as this => move |_| {
std::thread::spawn(clone!(@strong component, @strong this => move || { std::thread::spawn(clone!(@strong component, @strong this => move || {
let config = config::get().expect("Failed to load config"); let config = config::get().expect("Failed to load config");
@ -400,7 +400,7 @@ impl App {
this.values.set(values); this.values.set(values);
config::update(config).expect("Failed to update config"); config::update(config);
} }
Actions::UpdateWineComboRow => { Actions::UpdateWineComboRow => {
@ -446,7 +446,7 @@ impl App {
this.values.set(values); this.values.set(values);
config::update(config).expect("Failed to update config"); config::update(config);
} }
Actions::ToastError(toast) => { Actions::ToastError(toast) => {