diff --git a/Cargo.toml b/Cargo.toml index 0ccdad7..56755fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ opt-level = 3 glib-build-tools = "0.16" [dependencies] -discord-rich-presence = "0.2.3" gtk = { package = "gtk4", version = "0.5", features = ["v4_8"] } adw = { package = "libadwaita", version = "0.2", features = ["v1_2"] } rfd = { version = "0.10", features = ["xdg-portal"], default-features = false } @@ -33,3 +32,4 @@ lazy_static = "1.4.0" anyhow = "1.0" md5 = "0.7" cached = { version = "0.42", features = ["proc_macro"] } +discord-rich-presence = "0.2.3" diff --git a/assets/ui/preferences/enhancements.blp b/assets/ui/preferences/enhancements.blp index 7f026a3..41cd7c2 100644 --- a/assets/ui/preferences/enhancements.blp +++ b/assets/ui/preferences/enhancements.blp @@ -55,35 +55,6 @@ Adw.PreferencesPage page { ] }; } - Adw.ActionRow discord_rpc_row - { - title: "Discord RPC"; - subtitle: "Discord RPC allows you to provide Discord the info that you are currently playing the game to let your friends know."; - Gtk.Switch discord_rpc_switch { - valign: center; - } - } - - Adw.ActionRow discord_rpc_desc_row - { - title: "Discord RPC Heading"; - subtitle: "Set a custom heading for the activity status!\n(Requires launcher restart, or disable and re-enable the RPC)"; - Gtk.Entry discord_rpc_desc - { - valign:center; - } - } - - Adw.ActionRow discord_rpc_state_row - { - title: "Discord RPC State"; - subtitle: "Set a custom description for the activity status!\n(Requires launcher restart, or disable and re-enable the RPC)"; - Gtk.Entry discord_rpc_state - { - valign:center; - } - } - Adw.ComboRow fsr_combo { title: "FSR"; @@ -103,8 +74,6 @@ Adw.PreferencesPage page { } } - - Adw.ActionRow gamemode_row { title: "Gamemode"; subtitle: "This prioritizes the game over the rest of the processes"; @@ -131,6 +100,27 @@ Adw.PreferencesPage page { } } + Adw.PreferencesGroup { + title: "Discord RPC"; + + Adw.ActionRow discord_rpc_row { + title: "Enabled"; + subtitle: "Discord RPC allows you to provide Discord the info that you are currently playing the game to let your friends know"; + + Gtk.Switch discord_rpc_switch { + valign: center; + } + } + + Adw.EntryRow discord_rpc_title { + title: "Title"; + } + + Adw.EntryRow discord_rpc_subtitle { + title: "Description"; + } + } + Adw.PreferencesGroup { title: "FPS Unlocker"; diff --git a/src/lib/config/game/enhancements/discordrpc.rs b/src/lib/config/game/enhancements/discordrpc.rs deleted file mode 100644 index fe6f72c..0000000 --- a/src/lib/config/game/enhancements/discordrpc.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::thread::JoinHandle; - -use serde::{Deserialize, Serialize}; -use serde_json::Value as JsonValue; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DiscordRpc { - pub enabled: bool, - pub large_image_key: String, - pub app_id: String, - pub description: String, - pub state: String, -} - -impl Default for DiscordRpc { - fn default() -> Self { - Self { - enabled: true, - large_image_key: "gi-icon".to_string(), - app_id: "901534333360304168".to_string(), - description: "Bullying Paimon".to_string(), - state: "In the weeb game".to_string(), - } - } -} - -impl From<&JsonValue> for DiscordRpc { - fn from(value: &JsonValue) -> Self { - let default = Self::default(); - Self { - enabled: match value.get("enabled") { - Some(value) => value.as_bool().unwrap_or(default.enabled), - None => default.enabled, - }, - - description: match value.get("description") { - Some(value) => value.as_str().unwrap_or(&default.description).to_string(), - None => default.description, - }, - - state: match value.get("state") { - Some(value) => value.as_str().unwrap_or(&default.state).to_string(), - None => default.state, - }, - - large_image_key: match value.get("large_image_key") { - Some(value) => value - .as_str() - .unwrap_or(&default.large_image_key) - .to_string(), - None => default.large_image_key, - }, - app_id: "901534333360304168".to_string(), - } - } -} diff --git a/src/lib/config/game/enhancements/mod.rs b/src/lib/config/game/enhancements/mod.rs index 7fa70fa..1f85c97 100644 --- a/src/lib/config/game/enhancements/mod.rs +++ b/src/lib/config/game/enhancements/mod.rs @@ -5,7 +5,7 @@ pub mod fsr; pub mod hud; pub mod fps_unlocker; pub mod gamescope; -pub mod discordrpc; + pub mod prelude { pub use super::gamescope::prelude::*; pub use super::fps_unlocker::prelude::*; @@ -14,21 +14,17 @@ pub mod prelude { pub use super::fsr::Fsr; pub use super::hud::HUD; pub use super::fps_unlocker::FpsUnlocker; - pub use super::discordrpc::DiscordRpc; } use prelude::*; -use crate::lib::config; - #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Enhancements { pub fsr: Fsr, pub gamemode: bool, pub hud: HUD, pub fps_unlocker: FpsUnlocker, - pub gamescope: Gamescope, - pub discord_rpc: DiscordRpc, + pub gamescope: Gamescope } impl From<&JsonValue> for Enhancements { @@ -59,11 +55,7 @@ impl From<&JsonValue> for Enhancements { gamescope: match value.get("gamescope") { Some(value) => Gamescope::from(value), None => default.gamescope - }, - discord_rpc: match value.get("discord_rpc") { - Some(value) => DiscordRpc::from(value), - None => default.discord_rpc - }, + } } } } diff --git a/src/lib/config/launcher/discord_rpc.rs b/src/lib/config/launcher/discord_rpc.rs new file mode 100644 index 0000000..4c96e95 --- /dev/null +++ b/src/lib/config/launcher/discord_rpc.rs @@ -0,0 +1,58 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value as JsonValue; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DiscordRpc { + pub app_id: u64, + pub enabled: bool, + + pub title: String, + pub subtitle: String, + pub image: String +} + +impl Default for DiscordRpc { + fn default() -> Self { + Self { + app_id: 901534333360304168, + enabled: false, + + title: String::from("of Teyvat"), + subtitle: String::from("Researching the world"), + image: String::from("gi-icon") + } + } +} + +impl From<&JsonValue> for DiscordRpc { + fn from(value: &JsonValue) -> Self { + let default = Self::default(); + + Self { + app_id: match value.get("app_id") { + Some(value) => value.as_u64().unwrap_or(default.app_id), + None => default.app_id + }, + + enabled: match value.get("enabled") { + Some(value) => value.as_bool().unwrap_or(default.enabled), + None => default.enabled + }, + + title: match value.get("title") { + Some(value) => value.as_str().unwrap_or(&default.title).to_string(), + None => default.title + }, + + subtitle: match value.get("subtitle") { + Some(value) => value.as_str().unwrap_or(&default.subtitle).to_string(), + None => default.subtitle + }, + + image: match value.get("image") { + Some(value) => value.as_str().unwrap_or(&default.image).to_string(), + None => default.image + } + } + } +} diff --git a/src/lib/config/launcher/mod.rs b/src/lib/config/launcher/mod.rs index f3d5635..07bb20e 100644 --- a/src/lib/config/launcher/mod.rs +++ b/src/lib/config/launcher/mod.rs @@ -8,10 +8,12 @@ use anime_game_core::genshin::consts::GameEdition as CoreGameEdition; use crate::lib::consts::launcher_dir; pub mod repairer; +pub mod discord_rpc; pub mod prelude { pub use super::Launcher; pub use super::repairer::Repairer; + pub use super::discord_rpc::DiscordRpc; } use prelude::*; @@ -67,7 +69,8 @@ pub struct Launcher { pub temp: Option, pub speed_limit: u64, pub repairer: Repairer, - pub edition: GameEdition + pub edition: GameEdition, + pub discord_rpc: DiscordRpc } impl Default for Launcher { @@ -77,7 +80,8 @@ impl Default for Launcher { temp: launcher_dir(), speed_limit: 0, repairer: Repairer::default(), - edition: GameEdition::default() + edition: GameEdition::default(), + discord_rpc: DiscordRpc::default() } } } @@ -119,6 +123,11 @@ impl From<&JsonValue> for Launcher { edition: match value.get("edition") { Some(value) => serde_json::from_value(value.clone()).unwrap_or(default.edition), None => default.edition + }, + + discord_rpc: match value.get("discord_rpc") { + Some(value) => DiscordRpc::from(value), + None => default.discord_rpc } } } diff --git a/src/lib/discord_rpc.rs b/src/lib/discord_rpc.rs new file mode 100644 index 0000000..d45b432 --- /dev/null +++ b/src/lib/discord_rpc.rs @@ -0,0 +1,92 @@ +use std::thread::JoinHandle; +use std::sync::mpsc::{self, Sender, SendError}; + +use discord_rich_presence::{ + activity::*, + DiscordIpc, + DiscordIpcClient +}; + +use super::config::prelude::DiscordRpc as DiscordRpcConfig; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RpcUpdates { + /// Establish RPC connection + Connect, + + /// Terminate RPC connection. Panics if not connected + Disconnect, + + /// Update RPC activity + UpdateActivity { + title: String, + subtitle: String, + image: String + }, + + /// Clear RPC activity + ClearActivity +} + +pub struct DiscordRpc { + _thread: JoinHandle<()>, + sender: Sender +} + +impl DiscordRpc { + pub fn new(mut config: DiscordRpcConfig) -> Self { + let (sender, receiver) = mpsc::channel(); + + Self { + _thread: std::thread::spawn(move || { + let mut client = DiscordIpcClient::new(&config.app_id.to_string()) + .expect("Failed to register discord ipc client"); + + while let Ok(update) = receiver.recv() { + match update { + RpcUpdates::Connect => { + if !config.enabled { + config.enabled = true; + + client.connect().expect("Failed to connect to discord"); + } + } + + RpcUpdates::Disconnect => { + if config.enabled { + config.enabled = false; + + client.close().expect("Failed to disconnect from discord"); + } + } + + RpcUpdates::UpdateActivity { title, subtitle, image } => { + config.title = title; + config.subtitle = subtitle; + config.image = image; + + client.set_activity(Self::get_activity(&config)) + .expect("Failed to update discord rpc activity"); + } + + RpcUpdates::ClearActivity => { + client.clear_activity().expect("Failed to clear discord rpc activity"); + } + } + } + }), + sender + } + } + + pub fn get_activity(config: &DiscordRpcConfig) -> Activity { + Activity::new() + .state(&config.title) + .details(&config.subtitle) + .assets(Assets::new().large_image(&config.image)) + } + + pub fn update(&self, update: RpcUpdates) -> Result<(), SendError> { + self.sender.send(update) + } +} diff --git a/src/lib/dxvk.rs b/src/lib/dxvk.rs index 41239bb..2d46ad8 100644 --- a/src/lib/dxvk.rs +++ b/src/lib/dxvk.rs @@ -1,6 +1,5 @@ use serde::{Serialize, Deserialize}; -use std::process::Output; use std::path::PathBuf; use lazy_static::lazy_static; diff --git a/src/lib/game.rs b/src/lib/game.rs index f96fdc6..66cc488 100644 --- a/src/lib/game.rs +++ b/src/lib/game.rs @@ -2,8 +2,8 @@ use std::process::Command; use anime_game_core::genshin::telemetry; -use super::config; use super::consts; +use super::config; use super::fps_unlocker::FpsUnlocker; /*#[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -61,25 +61,24 @@ pub fn try_get_terminal() -> Option { }*/ /// Try to run the game -/// +/// /// If `debug = true`, then the game will be run in the new terminal window pub fn run() -> anyhow::Result<()> { let config = config::get()?; + if !config.game.path.exists() { return Err(anyhow::anyhow!("Game is not installed")); } let wine_executable = match config.try_get_wine_executable() { Some(path) => path, - None => return Err(anyhow::anyhow!("Couldn't find wine executable")), + None => return Err(anyhow::anyhow!("Couldn't find wine executable")) }; // Check telemetry servers if let Some(server) = telemetry::is_disabled(consts::TELEMETRY_CHECK_TIMEOUT) { - return Err(anyhow::anyhow!( - "Telemetry server is not disabled: {server}" - )); + return Err(anyhow::anyhow!("Telemetry server is not disabled: {server}")); } // Prepare fps unlocker @@ -95,51 +94,28 @@ pub fn run() -> anyhow::Result<()> { // Ok(None) means unknown version, so we should delete it before downloading newer one // because otherwise downloader will try to continue downloading "partially downloaded" file if let Ok(None) = other { - std::fs::remove_file(FpsUnlocker::get_binary_in( - &config.game.enhancements.fps_unlocker.path, - ))?; + std::fs::remove_file(FpsUnlocker::get_binary_in(&config.game.enhancements.fps_unlocker.path))?; } match FpsUnlocker::download(&config.game.enhancements.fps_unlocker.path) { Ok(unlocker) => unlocker, - Err(err) => { - return Err(anyhow::anyhow!("Failed to download FPS unlocker: {err}")) - } + Err(err) => return Err(anyhow::anyhow!("Failed to download FPS unlocker: {err}")) } } }; // Generate FPS unlocker config file - if let Err(err) = - unlocker.update_config(config.game.enhancements.fps_unlocker.config.clone()) - { - return Err(anyhow::anyhow!( - "Failed to update FPS unlocker config: {err}" - )); + if let Err(err) = unlocker.update_config(config.game.enhancements.fps_unlocker.config.clone()) { + return Err(anyhow::anyhow!("Failed to update FPS unlocker config: {err}")); } let bat_path = config.game.path.join("fpsunlocker.bat"); let original_bat_path = config.game.path.join("launcher.bat"); // Generate fpsunlocker.bat from launcher.bat - std::fs::write( - bat_path, - std::fs::read_to_string(original_bat_path)? - .replace( - "start GenshinImpact.exe %*", - &format!( - "start GenshinImpact.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", - unlocker.dir().to_string_lossy() - ), - ) - .replace( - "start YuanShen.exe %*", - &format!( - "start YuanShen.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", - unlocker.dir().to_string_lossy() - ), - ), - )?; + std::fs::write(bat_path, std::fs::read_to_string(original_bat_path)? + .replace("start GenshinImpact.exe %*", &format!("start GenshinImpact.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", unlocker.dir().to_string_lossy())) + .replace("start YuanShen.exe %*", &format!("start YuanShen.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", unlocker.dir().to_string_lossy())))?; } // Prepare bash -c '' @@ -156,11 +132,7 @@ pub fn run() -> anyhow::Result<()> { bash_chain += &format!("{virtual_desktop} "); } - bash_chain += if config.game.enhancements.fps_unlocker.enabled { - "fpsunlocker.bat " - } else { - "launcher.bat " - }; + bash_chain += if config.game.enhancements.fps_unlocker.enabled { "fpsunlocker.bat " } else { "launcher.bat " }; if config.game.wine.borderless { bash_chain += "-screen-fullscreen 0 -popupwindow "; @@ -178,7 +150,7 @@ pub fn run() -> anyhow::Result<()> { let bash_chain = match &config.game.command { Some(command) => command.replace("%command%", &bash_chain), - None => bash_chain, + None => bash_chain }; let mut command = Command::new("bash"); @@ -202,6 +174,7 @@ pub fn run() -> anyhow::Result<()> { command.envs(config.game.enhancements.hud.get_env_vars(&config)); command.envs(config.game.enhancements.fsr.get_env_vars()); command.envs(config.game.wine.language.get_env_vars()); + command.envs(config.game.environment); // Run command diff --git a/src/lib/mod.rs b/src/lib/mod.rs index 15b66fd..f2c82ba 100644 --- a/src/lib/mod.rs +++ b/src/lib/mod.rs @@ -6,6 +6,7 @@ pub mod wine; pub mod launcher; pub mod prettify_bytes; pub mod fps_unlocker; +pub mod discord_rpc; use std::process::{Command, Stdio}; diff --git a/src/main.rs b/src/main.rs index a9d66af..0dbf7e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,3 @@ -use discord_rich_presence::{ - activity::{self, Activity, Party, Secrets}, - DiscordIpc, DiscordIpcClient, -}; use gtk::gdk::Display; use gtk::glib; use gtk::glib::clone; @@ -40,7 +36,7 @@ fn main() { glib::OptionFlags::empty(), glib::OptionArg::None, "Run the game", - None, + None ); application.add_main_option( @@ -49,7 +45,7 @@ fn main() { glib::OptionFlags::empty(), glib::OptionArg::None, "Run the game whenever it possible, ignoring updates predownloads", - None, + None ); let run_game = std::rc::Rc::new(std::cell::Cell::new(false)); @@ -73,62 +69,6 @@ fn main() { application.connect_activate(move |app| { let config = lib::config::get().expect("Failed to load config"); - let mut client = - DiscordIpcClient::new(config.game.enhancements.discord_rpc.app_id.as_str()) - .expect("Failed to create client"); - - - let mut activity_set:bool = false; - let mut connected: bool = false; - let _thread = std::thread::spawn(move || loop { - let conf = lib::config::get().expect("Failed to load config"); - // println!("activity_set: {:?} connected: {:?}",activity_set,connected); - if conf.game.enhancements.discord_rpc.enabled { - if !connected{ - match client.connect() { - Ok(_) => { - println!("Client connected to Discord successfully.");connected=true; - } - Err(_) => { - println!( - "Client failed to connect to Discord, Please try again or relaunch Discord." - ); - } - }; - } - let act = activity::Activity::new() - .state(config.game.enhancements.discord_rpc.state.as_str()) - .details(config.game.enhancements.discord_rpc.description.as_str()) - .assets(activity::Assets::new() - .large_image(config.game.enhancements.discord_rpc.large_image_key.as_str() - )); - - if !activity_set{ - match client.set_activity(act) { - Ok(_) => {println!("Client set activity successfully."); activity_set=true;} - Err(_) => {println!("Client failed to set activity, Please try again or relaunch Discord.");} - }; - } - - std::thread::sleep(std::time::Duration::from_millis(1000)); - } else { - if activity_set{ - match client.clear_activity(){ - Ok(_) => {println!("Client activity cleared successfully.");connected=false;activity_set=false} - Err(_) => {println!("Failed to clear.");} - } - } - - if connected{ - match client.close(){ - Ok(_) => {println!("Client connection closed.");connected=false;} - Err(_) => {println!("Failed to clear.");} - } - } - - } - std::thread::sleep(std::time::Duration::from_millis(1000)); - }); // Apply CSS styles to the application let provider = CssProvider::new(); @@ -137,7 +77,7 @@ fn main() { StyleContext::add_provider_for_display( &Display::default().expect("Could not connect to a display"), &provider, - STYLE_PROVIDER_PRIORITY_APPLICATION, + STYLE_PROVIDER_PRIORITY_APPLICATION ); // Create default launcher folder if needed @@ -145,13 +85,14 @@ fn main() { if !launcher_dir.exists() || launcher_dir.join(".first-run").exists() { fs::create_dir_all(&launcher_dir).expect("Failed to create default launcher dir"); - fs::write(launcher_dir.join(".first-run"), "") - .expect("Failed to create .first-run file"); + fs::write(launcher_dir.join(".first-run"), "").expect("Failed to create .first-run file"); let first_run = FirstRunApp::new(app).expect("Failed to init FirstRunApp"); first_run.show(); - } else { + } + + else { // Create wine builds folder if !Path::new(&config.game.wine.builds).exists() { fs::create_dir_all(config.game.wine.builds) @@ -175,7 +116,9 @@ fn main() { if !run_game.get() && !just_run_game.get() { main.show(); - } else { + } + + else { use lib::launcher::states::LauncherState; let just_run_game = just_run_game.get(); @@ -187,22 +130,20 @@ fn main() { if let LauncherState::PredownloadAvailable { game, voices } = state { if just_run_game { state = &LauncherState::Launch; - } else if let Ok(config) = lib::config::get() { + } + + else if let Ok(config) = lib::config::get() { let mut predownloaded = true; let temp = config.launcher.temp.unwrap_or("/tmp".into()); - if !temp - .join(game.file_name().unwrap_or(String::from("\0"))) - .exists() - { + if !temp.join(game.file_name().unwrap_or(String::from("\0"))).exists() { predownloaded = false; - } else { + } + + else { for voice in voices { - if !temp - .join(voice.file_name().unwrap_or(String::from("\0"))) - .exists() - { + if !temp.join(voice.file_name().unwrap_or(String::from("\0"))).exists() { predownloaded = false; break; @@ -224,7 +165,7 @@ fn main() { std::process::exit(0); } - _ => main.show(), + _ => main.show() } }); } diff --git a/src/ui/main.rs b/src/ui/main.rs index 6e29ab5..3bb3693 100644 --- a/src/ui/main.rs +++ b/src/ui/main.rs @@ -31,6 +31,7 @@ use crate::lib::wine::{ List as WineList }; use crate::lib::prettify_bytes::prettify_bytes; +use crate::lib::discord_rpc::RpcUpdates; /// This structure is used to describe widgets used in application /// @@ -275,7 +276,6 @@ impl App { /// /// Changes will happen in the main thread so you can call `update` method from separate thread pub fn init_actions(self) -> Self { - let (sender, receiver) = glib::MainContext::channel::(glib::PRIORITY_DEFAULT); // I prefer to avoid using clone! here because it breaks my code autocompletion @@ -309,6 +309,23 @@ impl App { this.widgets.leaflet.navigate(adw::NavigationDirection::Back); config::flush().expect("Failed to save config file"); + + // Update Discord RPC state + let config = config::get().expect("Failed to load config"); + + let result = this.widgets.preferences_stack.enhancements_page.discord_rpc.update(RpcUpdates::UpdateActivity { + title: config.launcher.discord_rpc.title, + subtitle: config.launcher.discord_rpc.subtitle, + image: config.launcher.discord_rpc.image + }); + + let this = this.clone(); + + if let Err(err) = result { + glib::MainContext::default().invoke(move || { + this.toast("Failed to update Discord RPC", err); + }); + } } Actions::PerformButtonEvent => { @@ -327,6 +344,7 @@ impl App { this.widgets.window.hide(); + #[allow(unused_must_use)] std::thread::spawn(move || { // Display toast message if the game is failed to run if let Err(err) = game::run() { @@ -340,6 +358,8 @@ impl App { else { std::thread::sleep(std::time::Duration::from_secs(2)); + this.widgets.preferences_stack.enhancements_page.discord_rpc.update(RpcUpdates::Connect); + loop { std::thread::sleep(std::time::Duration::from_secs(3)); @@ -355,6 +375,8 @@ impl App { } } + this.widgets.preferences_stack.enhancements_page.discord_rpc.update(RpcUpdates::Disconnect); + this.widgets.window.show(); } }); @@ -879,6 +901,7 @@ impl App { }; self.actions.set(actions); + result } diff --git a/src/ui/preferences/enhancements.rs b/src/ui/preferences/enhancements.rs index 384b28e..b056208 100644 --- a/src/ui/preferences/enhancements.rs +++ b/src/ui/preferences/enhancements.rs @@ -1,14 +1,15 @@ -use gtk::ffi::gtk_text_view_set_overwrite; use gtk::prelude::*; use adw::prelude::*; use gtk::glib; use gtk::glib::clone; +use std::rc::Rc; + use crate::lib; use crate::lib::config; -use crate::lib::config::game::enhancements::discordrpc; use crate::lib::config::prelude::*; +use crate::lib::discord_rpc::DiscordRpc; use crate::ui::*; @@ -51,12 +52,8 @@ pub struct AppWidgets { pub discord_rpc_row: adw::ActionRow, pub discord_rpc: gtk::Switch, - - pub discord_rpc_state_row: adw::ActionRow, - pub discord_rpc_state: gtk::Entry, - - pub discord_rpc_desc_row: adw::ActionRow, - pub discord_rpc_desc: gtk::Entry, + pub discord_rpc_title: adw::EntryRow, + pub discord_rpc_subtitle: adw::EntryRow } impl AppWidgets { @@ -91,13 +88,11 @@ impl AppWidgets { fps_unlocker_monitor_num: get_object(&builder, "fps_unlocker_monitor_num")?, fps_unlocker_window_mode_combo: get_object(&builder, "fps_unlocker_window_mode_combo")?, fps_unlocker_priority_combo: get_object(&builder, "fps_unlocker_priority_combo")?, - discord_rpc: get_object(&builder,"discord_rpc_switch")?, - discord_rpc_row: get_object(&builder, "discord_rpc_row")?, - discord_rpc_state: get_object(&builder, "discord_rpc_state")?, - discord_rpc_state_row: get_object(&builder, "discord_rpc_state_row")?, - discord_rpc_desc: get_object(&builder, "discord_rpc_desc")?, - discord_rpc_desc_row: get_object(&builder, "discord_rpc_desc_row")?, + discord_rpc_row: get_object(&builder, "discord_rpc_row")?, + discord_rpc: get_object(&builder,"discord_rpc_switch")?, + discord_rpc_title: get_object(&builder, "discord_rpc_title")?, + discord_rpc_subtitle: get_object(&builder, "discord_rpc_subtitle")?, }; // Set availale wine languages @@ -120,11 +115,6 @@ impl AppWidgets { result.gamescope_row.set_sensitive(false); result.gamescope_row.set_tooltip_text(Some("Gamescope is not installed")); } - // result.discord_rpc_desc_row.set_sensitive(true); - // result.discord_rpc_state_row.set_sensitive(true); - result.discord_rpc_row.set_sensitive(true); - result.discord_rpc.set_sensitive(true); - Ok(result) } @@ -143,14 +133,19 @@ impl AppWidgets { /// That's what we need and what we use in `App::update` method #[derive(Clone, glib::Downgrade)] pub struct App { - widgets: AppWidgets + widgets: AppWidgets, + + pub discord_rpc: Rc } impl App { /// Create new application pub fn new(window: &adw::ApplicationWindow) -> anyhow::Result { + let config = config::get()?; + let result = Self { - widgets: AppWidgets::try_get(window)? + widgets: AppWidgets::try_get(window)?, + discord_rpc: Rc::new(DiscordRpc::new(config.launcher.discord_rpc)) }.init_events(); Ok(result) @@ -243,39 +238,35 @@ impl App { } }); - self.widgets.discord_rpc.connect_state_notify(move |switch|{ - if let Ok(mut config) = config::get() - { - config.game.enhancements.discord_rpc.enabled = switch.state(); - // config.game.enhancements.discord_rpc.toggle(); + // TODO: update RPC here or after preferences window's closing + + // Discord RPC switching + self.widgets.discord_rpc.connect_state_notify(move |switch| { + if let Ok(mut config) = config::get() { + config.launcher.discord_rpc.enabled = switch.state(); + config::update(config); } }); - self.widgets.discord_rpc_state.connect_changed(move |state| - { - if let Ok(mut config) = config::get() - { - let string = state.text().as_str().to_string(); - std::thread::sleep(std::time::Duration::from_millis(10)); - config.game.enhancements.discord_rpc.state = string; - // println!("[Debug] Updated string: {}",config.game.enhancements.discord_rpc.state); + // Discord RPC title + self.widgets.discord_rpc_title.connect_changed(move |state| { + if let Ok(mut config) = config::get() { + config.launcher.discord_rpc.title = state.text().as_str().to_string(); + config::update(config); } }); + // Discord RPC subtitle + self.widgets.discord_rpc_subtitle.connect_changed(move |state| { + if let Ok(mut config) = config::get() { + config.launcher.discord_rpc.subtitle = state.text().as_str().to_string(); - self.widgets.discord_rpc_desc.connect_changed(move |state| - { - if let Ok(mut config) = config::get() - { - let string = state.text().as_str().to_string(); - std::thread::sleep(std::time::Duration::from_millis(10)); - config.game.enhancements.discord_rpc.description = string; - // println!("[Debug] Updated string: {}",config.game.enhancements.discord_rpc.description); - config::update(config); - } + config::update(config); + } }); + // Gamemode switching self.widgets.gamemode_switcher.connect_state_notify(move |switch| { if let Ok(mut config) = config::get() { @@ -318,8 +309,6 @@ impl App { } }); - - // FPS unlocker -> power saving swithing self.widgets.fps_unlocker_power_saving_switcher.connect_state_notify(move |switch| { if let Ok(mut config) = config::get() { @@ -353,8 +342,6 @@ impl App { } }); - - self } @@ -411,9 +398,9 @@ impl App { self.widgets.fsr_switcher.set_state(config.game.enhancements.fsr.enabled); // Discord RPC - self.widgets.discord_rpc.set_state(config.game.enhancements.discord_rpc.enabled); - self.widgets.discord_rpc_state.set_placeholder_text(Some(config.game.enhancements.discord_rpc.state.as_str())); - self.widgets.discord_rpc_desc.set_placeholder_text(Some(config.game.enhancements.discord_rpc.description.as_str())); + self.widgets.discord_rpc.set_state(config.launcher.discord_rpc.enabled); + self.widgets.discord_rpc_title.set_text(&config.launcher.discord_rpc.title); + self.widgets.discord_rpc_subtitle.set_text(&config.launcher.discord_rpc.subtitle); // Gamemode switching self.widgets.gamemode_switcher.set_state(config.game.enhancements.gamemode);