diff --git a/.gitignore b/.gitignore index 1d184ac..1c25476 100644 --- a/.gitignore +++ b/.gitignore @@ -24,5 +24,5 @@ dist-ssr *.sw? libsteamium/target - -temp +libsteamium/cli/target +temp \ No newline at end of file diff --git a/libsteamium/Cargo.lock b/libsteamium/Cargo.lock index 457b0b5..5da5f46 100644 --- a/libsteamium/Cargo.lock +++ b/libsteamium/Cargo.lock @@ -8,6 +8,48 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc95de49ad098572c02d3fbf368c9a020bfff5ae78483685b77f51d8a7e9486d" +dependencies = [ + "num_threads", +] + [[package]] name = "itoa" version = "1.0.15" @@ -19,15 +61,24 @@ name = "keyvalues-parser" version = "0.2.0" source = "git+https://github.com/CosmicHorrorDev/vdf-rs.git?rev=2aff7147c5ba2bf2551566269dc167099fe7d527#2aff7147c5ba2bf2551566269dc167099fe7d527" +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + [[package]] name = "libsteamium" version = "0.1.0" dependencies = [ "anyhow", + "base64", + "env", "keyvalues-parser", "log", "serde", "serde_json", + "steam_shortcuts_util", ] [[package]] @@ -42,6 +93,42 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom_locate" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "proc-macro2" version = "1.0.94" @@ -98,6 +185,18 @@ dependencies = [ "serde", ] +[[package]] +name = "steam_shortcuts_util" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0543ebdb23a93b196aceebc53f70cc5a573bb74248a974b3f5fa3883e6a89b6" +dependencies = [ + "ascii", + "crc32fast", + "nom", + "nom_locate", +] + [[package]] name = "syn" version = "2.0.100" diff --git a/libsteamium/Cargo.toml b/libsteamium/Cargo.toml index 07d3d37..4613422 100644 --- a/libsteamium/Cargo.toml +++ b/libsteamium/Cargo.toml @@ -9,3 +9,6 @@ keyvalues-parser = { git = "https://github.com/CosmicHorrorDev/vdf-rs.git", rev log = "0.4.27" serde = { version = "1.0.218", features = ["derive"] } serde_json = "1.0.140" +steam_shortcuts_util = "1.1.7" +base64 = "0.22.1" +env = "1.0.1" \ No newline at end of file diff --git a/libsteamium/cli/Cargo.lock b/libsteamium/cli/Cargo.lock index 6b59465..3719786 100644 --- a/libsteamium/cli/Cargo.lock +++ b/libsteamium/cli/Cargo.lock @@ -67,6 +67,30 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "4.5.35" @@ -113,6 +137,24 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc95de49ad098572c02d3fbf368c9a020bfff5ae78483685b77f51d8a7e9486d" +dependencies = [ + "num_threads", +] + [[package]] name = "env_filter" version = "0.1.3" @@ -183,15 +225,24 @@ name = "keyvalues-parser" version = "0.2.0" source = "git+https://github.com/CosmicHorrorDev/vdf-rs.git?rev=2aff7147c5ba2bf2551566269dc167099fe7d527#2aff7147c5ba2bf2551566269dc167099fe7d527" +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + [[package]] name = "libsteamium" version = "0.1.0" dependencies = [ "anyhow", + "base64", + "env", "keyvalues-parser", "log", "serde", "serde_json", + "steam_shortcuts_util", ] [[package]] @@ -206,6 +257,42 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom_locate" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -312,6 +399,18 @@ dependencies = [ "serde", ] +[[package]] +name = "steam_shortcuts_util" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0543ebdb23a93b196aceebc53f70cc5a573bb74248a974b3f5fa3883e6a89b6" +dependencies = [ + "ascii", + "crc32fast", + "nom", + "nom_locate", +] + [[package]] name = "steamium_cli" version = "0.1.0" diff --git a/libsteamium/src/lib.rs b/libsteamium/src/lib.rs index 90e8b24..8502a7c 100644 --- a/libsteamium/src/lib.rs +++ b/libsteamium/src/lib.rs @@ -1,6 +1,11 @@ +use base64::{engine::general_purpose, Engine as _}; use keyvalues_parser::{Obj, Vdf}; use serde::{Deserialize, Serialize}; +use std::fs; +use std::io::Read; +use std::path::Path; use std::path::PathBuf; +use steam_shortcuts_util::parse_shortcuts; pub struct Steamium { steam_root: PathBuf, @@ -18,19 +23,22 @@ fn get_steam_root() -> anyhow::Result { .iter() .map(|path| home.join(path)) .filter(|p| p.exists()) - .next() else { - anyhow::bail!("Couldn't find Steam installation in search paths"); - }; + .next() + else { + anyhow::bail!("Couldn't find Steam installation in search paths"); + }; Ok(steam_path) } -pub type AppID = u64; +pub type AppID = String; #[derive(Debug, Serialize, Deserialize)] pub struct AppManifest { app_id: AppID, + run_game_id: AppID, name: String, + cover_b64: Option, raw_state_flags: u64, // documentation: https://github.com/lutris/lutris/blob/master/docs/steam.rst last_played: Option, // unix timestamp } @@ -81,7 +89,7 @@ fn vdf_parse_libraryfolders<'a>(vdf_root: &'a Vdf<'a>) -> Option> .iter() .filter_map(|item| item.0.parse::().ok()) .map(|app_id| AppEntry { - app_id, + app_id: app_id.to_string(), root_path: String::from(path), }), ); @@ -110,7 +118,9 @@ fn vdf_parse_appstate<'a>(app_id: AppID, vdf_root: &'a Vdf<'a>) -> Option anyhow::Result<()> { pub fn launch(app_id: AppID) -> anyhow::Result<()> { log::info!("Launching Steam game with AppID {}", app_id); - call_steam(&format!("steam://run/{}", app_id))?; + call_steam(&format!("steam://rungameid/{}", app_id))?; Ok(()) } @@ -152,6 +162,15 @@ pub struct RunningGame { pub pid: i32, } +#[derive(Serialize)] +struct Shortcut { + name: String, + exe: String, + run_game_id: u64, + app_id: u64, + cover_b64: Option, +} + pub fn list_running_games() -> anyhow::Result> { let mut res = Vec::::new(); @@ -212,8 +231,8 @@ pub fn list_running_games() -> anyhow::Result> { // AppID found. Add it to the list res.push(RunningGame { - app_id: app_id_num as AppID, - pid, + app_id: app_id_num.to_string(), + pid: pid, }); break; @@ -233,7 +252,90 @@ fn call_steam(arg: &str) -> anyhow::Result<()> { } } +fn shortcut_to_fake_manifest(shortcut: &Shortcut) -> AppManifest { + AppManifest { + app_id: shortcut.app_id.to_string(), + run_game_id: shortcut.run_game_id.to_string(), + name: shortcut.name.clone(), + cover_b64: shortcut.cover_b64.clone(), + raw_state_flags: 0, // Not applicable for shortcuts, 0 by default + last_played: None, // Steam does not use this for shortcuts + } +} + +fn compute_rungameid(app_id: u32) -> u64 { + (app_id as u64) << 32 | 0x02000000 +} + impl Steamium { + fn convert_cover_to_base64(app_id: &u32,original_path: &Path,) -> std::io::Result> { + // List of supported extensions with their MIME types + let extensions = [ + ("png", "image/png"), + ("jpg", "image/jpeg"), + ("jpeg", "image/jpeg"), + ("webp", "image/webp"), + ("bmp", "image/bmp"), + ("gif", "image/gif"), + ]; + + for (ext, mime) in extensions.iter() { + let filepath = original_path + .join("grid") + .join(format!("{}p.{}", app_id, ext)); + if filepath.exists() { + let mut file = fs::File::open(&filepath)?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + + let base64_string = general_purpose::STANDARD.encode(&buffer); + let data_uri = format!("data:{};base64,{}", mime, base64_string); + return Ok(Some(data_uri)); + } + } + + Ok(None) + } + + fn list_shortcuts(&self) -> Result, Box> { + let userdata_dir = self.steam_root.join("userdata"); + let user_dirs = fs::read_dir(userdata_dir)?; + + let mut shortcuts: Vec = Vec::new(); + + for user in user_dirs.flatten() { + let config_path = user.path().join("config"); + let shortcut_path = config_path.join("shortcuts.vdf"); + + if !shortcut_path.exists() { + continue; + } + + let content = std::fs::read(&shortcut_path)?; + let shortcuts_data = parse_shortcuts(content.as_slice())?; + + for s in shortcuts_data { + let run_game_id = compute_rungameid(s.app_id); + let cover_base64 = match Steamium::convert_cover_to_base64(&s.app_id, &config_path) { + Ok(path) => path, // If successful, use the new path + Err(e) => { + log::error!("Error converting cover for app {}: {}", s.app_id, e); + None + } + }; + shortcuts.push(Shortcut { + name: s.app_name.to_string(), + exe: s.exe.to_string(), + run_game_id: run_game_id, + app_id: s.app_id as u64, + cover_b64: cover_base64, + }); + } + } + + Ok(shortcuts) + } + fn get_dir_steamapps(&self) -> PathBuf { self.steam_root.join("steamapps") } @@ -251,7 +353,7 @@ impl Steamium { let vdf_data = std::fs::read_to_string(manifest_path)?; let vdf_root = keyvalues_parser::Vdf::parse(&vdf_data)?; - let Some(manifest) = vdf_parse_appstate(app_entry.app_id, &vdf_root) else { + let Some(manifest) = vdf_parse_appstate(app_entry.app_id.clone(), &vdf_root) else { anyhow::bail!("Failed to parse AppState"); }; @@ -289,6 +391,16 @@ impl Steamium { }) .collect(); + if let Ok(shortcuts) = self.list_shortcuts() { + let mut fake_manifests = shortcuts + .iter() + .map(shortcut_to_fake_manifest) + .collect::>(); + games.append(&mut fake_manifests); + } else { + log::error!("Failed to read non-Steam shortcuts"); + } + match sort_method { GameSortMethod::NameAsc => { games.sort_by(|a, b| a.name.cmp(&b.name)); diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 313c085..28530cd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -62,6 +62,12 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "atk" version = "0.18.2" @@ -216,6 +222,12 @@ version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + [[package]] name = "bytemuck" version = "1.22.0" @@ -844,6 +856,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc95de49ad098572c02d3fbf368c9a020bfff5ae78483685b77f51d8a7e9486d" +dependencies = [ + "num_threads", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -2078,10 +2099,13 @@ name = "libsteamium" version = "0.1.0" dependencies = [ "anyhow", + "base64 0.22.1", + "env", "keyvalues-parser", "log", "serde", "serde_json", + "steam_shortcuts_util", ] [[package]] @@ -2280,6 +2304,17 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom_locate" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2326,6 +2361,15 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "objc-sys" version = "0.3.5" @@ -3736,6 +3780,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "steam_shortcuts_util" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0543ebdb23a93b196aceebc53f70cc5a573bb74248a974b3f5fa3883e6a89b6" +dependencies = [ + "ascii", + "crc32fast", + "nom", + "nom_locate", +] + [[package]] name = "string_cache" version = "0.8.9" @@ -4874,6 +4930,7 @@ version = "0.3.4" dependencies = [ "anyhow", "bytes", + "env", "gio 0.20.9", "glib 0.20.9", "gtk", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 31bdc2f..5540346 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -58,3 +58,4 @@ tracing = "0.1.41" gio = "0.20.9" glib = "0.20.9" gtk = "0.18.2" +env = "1.0.1" diff --git a/src-tauri/src/frontend_ipc.rs b/src-tauri/src/frontend_ipc.rs index 0a93e9f..c56dda3 100644 --- a/src-tauri/src/frontend_ipc.rs +++ b/src-tauri/src/frontend_ipc.rs @@ -9,7 +9,6 @@ use wayvr_ipc::{ }; use crate::util::{self, pactl_wrapper}; - type AppStateType = Mutex; #[derive(Debug, Serialize)] @@ -74,13 +73,13 @@ pub async fn game_list(state: tauri::State<'_, AppStateType>) -> Result Result<(), String> { - handle_result("launch a game", libsteamium::launch(app_id as u64)) +pub fn game_launch(app_id: String) -> Result<(), String> { + handle_result("launch a game", libsteamium::launch(app_id)) } #[tauri::command] -pub fn game_stop(app_id: i32, force: bool) -> Result<(), String> { - handle_result("stop a game", libsteamium::stop(app_id as u64, force)) +pub fn game_stop(app_id: String, force: bool) -> Result<(), String> { + handle_result("stop a game", libsteamium::stop(app_id, force)) } #[tauri::command] diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ebac716..e7cb275 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -21,7 +21,7 @@ } ], "security": { - "csp": "default-src 'self'; img-src 'self' asset: http://asset.localhost https://shared.cloudflare.steamstatic.com; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'", + "csp": "default-src 'self'; img-src 'self' data: asset: http://asset.localhost https://shared.cloudflare.steamstatic.com; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'", "assetProtocol": { "enable": true, "scope": { diff --git a/src/gui/gui.tsx b/src/gui/gui.tsx index 49bf576..f8e0dfe 100644 --- a/src/gui/gui.tsx +++ b/src/gui/gui.tsx @@ -257,7 +257,7 @@ export function TooltipSimple({ children, title, extend }: { children: any, titl } class FailedCovers { - covers = new Array; + covers = new Array; } export function failed_covers_clear() { @@ -277,41 +277,82 @@ function failed_covers_set(covers: FailedCovers) { localStorage.setItem("failed_covers", JSON.stringify(covers)); } -function get_alt_cover(manifest: ipc.AppManifest) { - return <> - - {manifest.name} - ; +async function get_alt_cover(manifest: ipc.AppManifest) { + console.log(manifest.cover_b64); + if(manifest.cover_b64!= undefined){ + console.log("Runnning copy png to frontend") + return <> + + ; + } + else{ + return <> + + {manifest.name} + ; + } + } export function GameCover({ manifest, big, on_click }: { manifest: ipc.AppManifest, big?: boolean, on_click?: () => void }) { const [content, setContent] = useState(<>); useEffect(() => { - const failed_covers = failed_covers_get(); - - const ret = failed_covers.covers.find((val) => { return val == manifest.app_id }); - if (ret === undefined) { - const url = "https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/" + manifest.app_id + "/library_600x900.jpg"; - setContent( { - console.log("Alt cover " + manifest.app_id + " failed to load, remembering it to prevent unnecessary requests to Steam API "); - const covers = failed_covers_get(); - covers.covers.push(manifest.app_id); - failed_covers_set(covers); - setContent(get_alt_cover(manifest)); - }} />); - } - else { - console.log("using previously failed cover"); - setContent(get_alt_cover(manifest)); - } - }, []); + const run = async () => { + try { + const failed_covers = failed_covers_get(); + const already_failed = failed_covers.covers.includes(manifest.app_id); + + if(manifest.run_game_id.length<10){ + if (!already_failed) { + const url = `https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/${manifest.app_id}/library_600x900.jpg`; + setContent( + { + try { + console.log("Alt cover " + manifest.app_id + " failed to load, remembering it to prevent unnecessary requests to Steam API "); + const updated_covers = failed_covers_get(); + updated_covers.covers.push(manifest.app_id); + failed_covers_set(updated_covers); + setContent(await get_alt_cover(manifest)); + } catch (err) { + console.error("Failed to load alt cover:", err); + } + }} + /> + ); + } else { + console.log("using previously failed cover"); + setContent(await get_alt_cover(manifest)); + } + } + else{ + console.log("handling non Steam Game"); + setContent(await get_alt_cover(manifest)); - return
- {content} -
-
+ } + } catch (err) { + console.error("Unhandled error in GameCover useEffect:", err); + } + }; + + run(); + }, [manifest]); + + return ( +
+ {content} +
+
+ ); } export function ApplicationCover({ big, application, on_click }: { big?: boolean, application: ipc.DesktopFile, on_click?: () => void }) { @@ -565,7 +606,7 @@ function ManifestView({ globals, manifest }: { globals: Globals, manifest: ipc.A
{manifest.name}
{details} { - ipc.game_launch(manifest.app_id); + ipc.game_launch(manifest.run_game_id); globals.toast_manager.push("Game launched. This might take a while"); }} /> @@ -720,4 +761,4 @@ export function Popup({ children, on_close, ref_element }: { children: any, on_c }}> {children}
-} \ No newline at end of file +} diff --git a/src/ipc.ts b/src/ipc.ts index f7f0d34..34561be 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -24,10 +24,12 @@ export namespace ipc { } export interface AppManifest { - app_id: number; + app_id: string; + run_game_id : string; name: string; raw_state_flags: number; - last_played?: number + last_played?: number; + cover_b64? : string } export interface Games { @@ -108,23 +110,24 @@ export namespace ipc { } export interface SteamiumRunningGame { - app_id: number; - pid: number; + app_id: string; + pid: string; } export async function desktop_file_list(): Promise> { return await invoke("desktop_file_list"); } + export async function game_list(): Promise { return await invoke("game_list"); } - export async function game_launch(app_id: number): Promise { + export async function game_launch(app_id: string): Promise { return await invoke("game_launch", { appId: app_id }) } - export async function game_stop(app_id: number, force: boolean): Promise { + export async function game_stop(app_id: string, force: boolean): Promise { return await invoke("game_stop", { appId: app_id, force: force }); } @@ -323,7 +326,7 @@ export namespace ipc { } export interface ProcessHandle { - idx: number; + idx: bigint; generation: number; } diff --git a/src/panel/games.tsx b/src/panel/games.tsx index 8dcf40e..c7bfe51 100644 --- a/src/panel/games.tsx +++ b/src/panel/games.tsx @@ -1,12 +1,12 @@ import { BoxDown, BoxRight, Button, Container, createWindowManifest, GameCover, Icon, Separator, Title, TooltipSimple } from "../gui/gui" import style from "../app.module.scss" -import { useEffect, useMemo, useState } from "preact/hooks"; +import { useEffect, useState } from "preact/hooks"; import { ipc } from "../ipc"; import { Globals } from "../globals"; import { get_external_url } from "@/utils"; interface GameIcon { - app_id: number; + app_id: string; icon_path?: string; } @@ -27,9 +27,10 @@ async function game_icon_list(): Promise { } out.push({ - app_id: parseInt(appid_str), + app_id: appid_str, icon_path: cell.icon }); + } return out; @@ -144,20 +145,24 @@ export function PanelGames({ globals }: { globals: Globals }) { const [games, setGames] = useState(undefined); - useMemo(async () => { - const games = await ipc.game_list(); - setGames(games); - - const arr = games.manifests.map((manifest) => { - return { - createWindowManifest(globals, manifest); - }} manifest={manifest} /> - }); - - setList(<> - {arr} - ); - }, []) + useEffect(() => { + const fetchGames = async () => { + const games = await ipc.game_list(); + setGames(games); + + const arr = games.manifests.map((manifest) => ( + createWindowManifest(globals, manifest)} + manifest={manifest} + /> + )); + + setList(<>{arr}); + }; + + fetchGames(); + }, []); return <> {games ? : undefined} diff --git a/src/utils.ts b/src/utils.ts index 7f0c6d7..aa538dc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -23,7 +23,7 @@ class AppDetailItem { data!: AppDetails; } -export async function get_app_details_json(app_id: number) { +export async function get_app_details_json(app_id: string) { const key = "app_details_" + app_id; const storage = window.localStorage; diff --git a/wayvr-dashboard.desktop b/wayvr-dashboard.desktop old mode 100644 new mode 100755 index 29d56e0..59b1df8 --- a/wayvr-dashboard.desktop +++ b/wayvr-dashboard.desktop @@ -1,3 +1,4 @@ +#!/usr/bin/env xdg-open [Desktop Entry] Type=Application Name=WayVR Dashboard