Several changes

- renamed `ToastError` trait to `Toast`;
  renamed its `toast_error` method to `toast`;
  now `toast` method will not display button if toast's message is empty
- updated `game::run` function, now it prints running command
  and supports `gamemoderun`
This commit is contained in:
Observer KRypt0n_ 2022-08-02 09:38:27 +02:00
parent 85b2e6741e
commit 114d388a8c
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
7 changed files with 105 additions and 82 deletions

View file

@ -61,7 +61,7 @@ pub fn try_get_terminal() -> Option<Terminal> {
/// Try to run the game /// Try to run the game
/// ///
/// If `debug = true`, then the game will be run in the new terminal window /// If `debug = true`, then the game will be run in the new terminal window
pub fn run(debug: bool) -> Result<(), Error> { pub fn run(debug: bool) -> std::io::Result<()> {
let config = config::get()?; let config = config::get()?;
if !Path::new(&config.game.path).exists() { if !Path::new(&config.game.path).exists() {
@ -73,7 +73,15 @@ pub fn run(debug: bool) -> Result<(), Error> {
None => return Err(Error::new(ErrorKind::Other, "Couldn't find wine executable")) None => return Err(Error::new(ErrorKind::Other, "Couldn't find wine executable"))
}; };
let mut command = Command::new(wine_executable); // Prepare bash -c '<command>'
let mut bash_chain = String::new();
if config.game.enhancements.gamemode {
bash_chain += "gamemoderun ";
}
bash_chain += &format!("'{}' ", wine_executable);
if debug { if debug {
// Is not supported now because new spawned terminal needs // Is not supported now because new spawned terminal needs
@ -92,9 +100,16 @@ pub fn run(debug: bool) -> Result<(), Error> {
} }
else { else {
command.arg("launcher.bat"); bash_chain += "launcher.bat";
} }
let mut command = Command::new("bash");
command.arg("-c");
command.arg(&bash_chain);
// Setup environment
command.env("WINEPREFIX", &config.game.wine.prefix); command.env("WINEPREFIX", &config.game.wine.prefix);
// Add DXVK_ASYNC=1 for dxvk-async builds automatically // Add DXVK_ASYNC=1 for dxvk-async builds automatically
@ -109,9 +124,13 @@ pub fn run(debug: bool) -> Result<(), Error> {
command.envs(config.game.enhancements.fsr.get_env_vars()); command.envs(config.game.enhancements.fsr.get_env_vars());
command.envs(config.game.wine.language.get_env_vars()); command.envs(config.game.wine.language.get_env_vars());
command.envs(config.game.environment) command.envs(config.game.environment);
.current_dir(config.game.path)
.spawn()?; // Run command
println!("Running command: bash -c \"{}\"", bash_chain);
command.current_dir(config.game.path).spawn()?;
Ok(()) Ok(())
} }

View file

@ -15,7 +15,7 @@ use wait_not_await::Await;
use crate::ui::*; use crate::ui::*;
use super::preferences::PreferencesStack; use super::preferences::PreferencesStack;
use super::traits::toast_error::ToastError; use super::traits::toast::Toast;
use super::components::progress_bar::*; use super::components::progress_bar::*;
use crate::lib::config; use crate::lib::config;
@ -135,7 +135,7 @@ pub enum Actions {
ShowProgressBar, ShowProgressBar,
UpdateProgress { fraction: Rc<f64>, title: Rc<String> }, UpdateProgress { fraction: Rc<f64>, title: Rc<String> },
HideProgressBar, HideProgressBar,
ToastError(Rc<(String, Error)>) Toast(Rc<(String, Error)>)
} }
impl Actions { impl Actions {
@ -239,7 +239,7 @@ impl App {
glib::MainContext::default().invoke(move || { glib::MainContext::default().invoke(move || {
this.update(Actions::PreferencesGoBack).unwrap(); this.update(Actions::PreferencesGoBack).unwrap();
this.toast_error("Failed to update preferences", err); this.toast("Failed to update preferences", err);
}); });
} }
})); }));
@ -270,7 +270,7 @@ impl App {
if let Err(err) = game::run(false) { if let Err(err) = game::run(false) {
this.widgets.window.show(); this.widgets.window.show();
this.toast_error("Failed to run game", err); this.toast("Failed to run game", err);
} }
else { else {
@ -318,20 +318,20 @@ impl App {
Ok(true) => synced = true, Ok(true) => synced = true,
Ok(false) => { Ok(false) => {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to sync patch folder"), Error::last_os_error() String::from("Failed to sync patch folder"), Error::last_os_error()
)))).unwrap(); )))).unwrap();
} }
Err(err) => { Err(err) => {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to sync patch folder"), err String::from("Failed to sync patch folder"), err
)))).unwrap(); )))).unwrap();
} }
} }
} }
Err(err) => this.update(Actions::ToastError(Rc::new(( Err(err) => this.update(Actions::Toast(Rc::new((
String::from("Failed to check patch folder state"), err String::from("Failed to check patch folder state"), err
)))).unwrap() )))).unwrap()
} }
@ -341,7 +341,7 @@ impl App {
Ok(_) => (), Ok(_) => (),
Err(err) => { Err(err) => {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to patch game"), err String::from("Failed to patch game"), err
)))).unwrap(); )))).unwrap();
} }
@ -395,7 +395,7 @@ impl App {
ProgressUpdateResult::Error(msg, err) => { ProgressUpdateResult::Error(msg, err) => {
this.widgets.progress_bar.hide(); this.widgets.progress_bar.hide();
this.toast_error(msg, err); this.toast(msg, err);
} }
ProgressUpdateResult::Finished => { ProgressUpdateResult::Finished => {
@ -419,10 +419,10 @@ impl App {
}); });
}); });
}, },
Err(err) => this.toast_error("Failed to init wine version installer", err) Err(err) => this.toast("Failed to init wine version installer", err)
} }
}, },
Err(err) => this.toast_error("Failed to get latest wine version", err) Err(err) => this.toast("Failed to get latest wine version", err)
} }
} }
@ -430,7 +430,7 @@ impl App {
this.update_state(); this.update_state();
} }
}, },
Err(err) => this.toast_error("Failed to list downloaded wine versions", err) Err(err) => this.toast("Failed to list downloaded wine versions", err)
} }
} }
@ -445,7 +445,7 @@ impl App {
this.widgets.launch_game.set_sensitive(false); this.widgets.launch_game.set_sensitive(false);
if let Err(err) = prefix.update(&config.game.wine.builds, wine) { if let Err(err) = prefix.update(&config.game.wine.builds, wine) {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to create wine prefix"), err String::from("Failed to create wine prefix"), err
)))).unwrap(); )))).unwrap();
} }
@ -455,7 +455,7 @@ impl App {
this.update_state(); this.update_state();
}); });
}, },
None => this.toast_error("Failed to get selected wine version", Error::last_os_error()) None => this.toast("Failed to get selected wine version", Error::last_os_error())
} }
} }
@ -477,7 +477,7 @@ impl App {
ProgressUpdateResult::Error(msg, err) => { ProgressUpdateResult::Error(msg, err) => {
this.widgets.progress_bar.hide(); this.widgets.progress_bar.hide();
this.toast_error(msg, err); this.toast(msg, err);
} }
ProgressUpdateResult::Finished => { ProgressUpdateResult::Finished => {
@ -516,7 +516,7 @@ impl App {
LauncherState::VoiceOutdated(_) => () LauncherState::VoiceOutdated(_) => ()
} }
}, },
Err(err) => this.toast_error("Failed to load config", err) Err(err) => this.toast("Failed to load config", err)
} }
} }
@ -648,7 +648,7 @@ impl App {
for (i, file) in broken.into_iter().enumerate() { for (i, file) in broken.into_iter().enumerate() {
if !is_patch_applied || !should_ignore(&file.path) { if !is_patch_applied || !should_ignore(&file.path) {
if let Err(err) = file.repair(&config.game.path) { if let Err(err) = file.repair(&config.game.path) {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to repair game file"), err String::from("Failed to repair game file"), err
)))).unwrap(); )))).unwrap();
} }
@ -666,7 +666,7 @@ impl App {
this.update(Actions::HideProgressBar).unwrap(); this.update(Actions::HideProgressBar).unwrap();
}, },
Err(err) => { Err(err) => {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to get integrity files"), err String::from("Failed to get integrity files"), err
)))).unwrap(); )))).unwrap();
@ -675,7 +675,7 @@ impl App {
} }
}); });
}, },
Err(err) => this.toast_error("Failed to load config", err) Err(err) => this.toast("Failed to load config", err)
} }
} }
@ -691,10 +691,10 @@ impl App {
this.widgets.progress_bar.hide(); this.widgets.progress_bar.hide();
} }
Actions::ToastError(toast) => { Actions::Toast(toast) => {
let (msg, err) = (toast.0.clone(), toast.1.to_string()); let (msg, err) = (toast.0.clone(), toast.1.to_string());
this.toast_error(msg, err); this.toast(msg, err);
} }
} }
@ -829,7 +829,7 @@ impl App {
send.send(Err(err.to_string())).unwrap(); send.send(Err(err.to_string())).unwrap();
glib::MainContext::default().invoke(move || { glib::MainContext::default().invoke(move || {
this.toast_error("Failed to get initial launcher state", err); this.toast("Failed to get initial launcher state", err);
}); });
} }
} }
@ -841,7 +841,7 @@ impl App {
} }
} }
impl ToastError for App { impl Toast for App {
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) { fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
(self.widgets.window.clone(), self.widgets.toast_overlay.clone()) (self.widgets.window.clone(), self.widgets.toast_overlay.clone())
} }

View file

@ -171,7 +171,7 @@ pub enum Actions {
SelectDxvkVersion(Rc<usize>), SelectDxvkVersion(Rc<usize>),
UpdateWineComboRow, UpdateWineComboRow,
SelectWineVersion(Rc<usize>), SelectWineVersion(Rc<usize>),
ToastError(Rc<(String, Error)>) Toast(Rc<(String, Error)>)
} }
impl Actions { impl Actions {
@ -304,7 +304,7 @@ impl App {
match component.apply(&config.game.dxvk.builds, &config.game.wine.prefix) { match component.apply(&config.game.dxvk.builds, &config.game.wine.prefix) {
Ok(output) => println!("{}", output), Ok(output) => println!("{}", output),
Err(err) => { Err(err) => {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to apply DXVK"), err String::from("Failed to apply DXVK"), err
)))).unwrap(); )))).unwrap();
} }
@ -352,7 +352,7 @@ impl App {
std::thread::spawn(move || { std::thread::spawn(move || {
if let Err(err) = component.package.delete_in(&config.game.path) { if let Err(err) = component.package.delete_in(&config.game.path) {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to delete voiceover"), err String::from("Failed to delete voiceover"), err
)))).unwrap(); )))).unwrap();
} }
@ -389,7 +389,7 @@ impl App {
if component.is_downloaded(&config.game.dxvk.builds) { if component.is_downloaded(&config.game.dxvk.builds) {
if let Err(err) = component.delete(&config.game.dxvk.builds) { if let Err(err) = component.delete(&config.game.dxvk.builds) {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to delete DXVK"), err String::from("Failed to delete DXVK"), err
)))).unwrap(); )))).unwrap();
} }
@ -405,7 +405,7 @@ impl App {
match component.apply(&config.game.dxvk.builds, &config.game.wine.prefix) { match component.apply(&config.game.dxvk.builds, &config.game.wine.prefix) {
Ok(output) => println!("{}", output), Ok(output) => println!("{}", output),
Err(err) => { Err(err) => {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to apply DXVK"), err String::from("Failed to apply DXVK"), err
)))).unwrap(); )))).unwrap();
} }
@ -426,7 +426,7 @@ impl App {
if component.is_downloaded(&config.game.wine.builds) { if component.is_downloaded(&config.game.wine.builds) {
if let Err(err) = component.delete(&config.game.wine.builds) { if let Err(err) = component.delete(&config.game.wine.builds) {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to delete wine"), err String::from("Failed to delete wine"), err
)))).unwrap(); )))).unwrap();
} }
@ -501,7 +501,7 @@ impl App {
match version.apply(&config.game.dxvk.builds, &config.game.wine.prefix) { match version.apply(&config.game.dxvk.builds, &config.game.wine.prefix) {
Ok(output) => println!("{}", output), Ok(output) => println!("{}", output),
Err(err) => { Err(err) => {
this.update(Actions::ToastError(Rc::new(( this.update(Actions::Toast(Rc::new((
String::from("Failed to apply DXVK"), err String::from("Failed to apply DXVK"), err
)))).unwrap(); )))).unwrap();
} }
@ -566,10 +566,10 @@ impl App {
config::update(config); config::update(config);
} }
Actions::ToastError(toast) => { Actions::Toast(toast) => {
let (msg, err) = (toast.0.clone(), toast.1.to_string()); let (msg, err) = (toast.0.clone(), toast.1.to_string());
this.toast_error(msg, err); this.toast(msg, err);
} }
} }
@ -697,7 +697,7 @@ impl App {
} }
} }
impl ToastError for App { impl Toast for App {
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) { fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
let app = (&*self.app).take(); let app = (&*self.app).take();
self.app.set(app.clone()); self.app.set(app.clone());

View file

@ -89,7 +89,7 @@ impl PreferencesStack {
} }
} }
impl ToastError for PreferencesStack { impl Toast for PreferencesStack {
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) { fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
let app = (&*self.app).take(); let app = (&*self.app).take();
self.app.set(app.clone()); self.app.set(app.clone());

View file

@ -1,7 +1,7 @@
pub mod toast_error; pub mod toast;
pub mod download_component; pub mod download_component;
pub mod prelude { pub mod prelude {
pub use super::toast_error::*; pub use super::toast::*;
pub use super::download_component::*; pub use super::download_component::*;
} }

44
src/ui/traits/toast.rs Normal file
View file

@ -0,0 +1,44 @@
use gtk4::{self as gtk, prelude::*};
use libadwaita as adw;
use crate::ui::add_action;
pub trait Toast {
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay);
/// Show toast with `toast` title and `See message` button
///
/// This button will show message dialog with some message
fn toast<T: ToString, F: std::fmt::Display + 'static>(&self, toast: T, message: F) {
let toast = adw::Toast::new(toast.to_string().as_str());
let (window, toast_overlay) = self.get_toast_widgets();
toast.set_timeout(0);
let message = format!("{}", message);
if message.len() > 0 {
toast.set_button_label(Some("See message"));
toast.set_action_name(Some("see-message.see-message"));
// Show message in a dialog window
add_action(&toast_overlay, "see-message", move || {
let dialog = gtk::MessageDialog::new(
Some(&window),
gtk::DialogFlags::all(),
gtk::MessageType::Info,
gtk::ButtonsType::Close,
&message
);
dialog.connect_response(move |dialog, _| {
dialog.close();
});
dialog.show();
});
}
toast_overlay.add_toast(&toast);
}
}

View file

@ -1,40 +0,0 @@
use gtk4::{self as gtk, prelude::*};
use libadwaita as adw;
use crate::ui::add_action;
pub trait ToastError {
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay);
/// Show toast with `toast` title and `See message` button
///
/// This button will show message dialog with error message
fn toast_error<T: ToString, F: std::fmt::Display + 'static>(&self, toast: T, err: F) {
let toast = adw::Toast::new(toast.to_string().as_str());
toast.set_button_label(Some("See message"));
toast.set_action_name(Some("see-message.see-message"));
toast.set_timeout(0);
let (window, toast_overlay) = self.get_toast_widgets();
// Show error message in a dialog window
add_action(&toast_overlay, "see-message", move || {
let dialog = gtk::MessageDialog::new(
Some(&window),
gtk::DialogFlags::all(),
gtk::MessageType::Info,
gtk::ButtonsType::Close,
&format!("{}", err)
);
dialog.connect_response(move |dialog, _| {
dialog.close();
});
dialog.show();
});
toast_overlay.add_toast(&toast);
}
}