retrom_plugin_steam/
lib.rs1use std::collections::HashSet;
2
3use notify::{EventKind, Watcher};
4use tauri::{
5 plugin::{Builder, TauriPlugin},
6 Emitter, Manager, Runtime,
7};
8
9mod commands;
10mod desktop;
11mod error;
12
13pub use error::{Error, Result};
14
15use desktop::Steam;
16use tracing::instrument;
17
18pub trait SteamExt<R: Runtime> {
20 fn steam(&self) -> Option<&Steam<R>>;
21}
22
23impl<R: Runtime, T: Manager<R>> crate::SteamExt<R> for T {
24 fn steam(&self) -> Option<&Steam<R>> {
25 self.try_state::<Steam<R>>().map(|s| s.inner())
26 }
27}
28
29#[instrument(skip_all)]
31pub fn init<R: Runtime>() -> TauriPlugin<R> {
32 Builder::new("steam")
33 .invoke_handler(tauri::generate_handler![])
34 .setup(|app, api| {
35 let steam = match desktop::init(app, api) {
36 Ok(steam) => steam,
37 Err(e) => {
38 tracing::warn!(
39 "Failed to initialize steam plugin, is steam installed: {:?}",
40 e
41 );
42 return Ok(());
43 }
44 };
45
46 let handle = app.app_handle().clone();
47 std::thread::spawn(move || {
48 let steam = match steamlocate::SteamDir::locate() {
49 Ok(steam) => steam,
50 Err(e) => {
51 tracing::debug!("Could not locate steam directory: {:?}", e);
52 return;
53 }
54 };
55
56 let libraries = steam.library_paths().unwrap_or_default();
57
58 let mut apps_installed = match steam.libraries() {
59 Ok(libraries) => libraries
60 .filter_map(std::result::Result::ok)
61 .flat_map(|lib| lib.app_ids().to_vec())
62 .collect::<HashSet<_>>(),
63
64 Err(e) => {
65 tracing::debug!("Could not get steam libraries: {:?}", e);
66 return;
67 }
68 };
69
70 let (tx, rx) = std::sync::mpsc::channel::<notify::Result<notify::Event>>();
71
72 let mut watchers = vec![];
73 for lib in libraries.iter() {
74 let tx = tx.clone();
75 let mut watcher = match notify::recommended_watcher(tx) {
76 Ok(watcher) => watcher,
77 Err(e) => {
78 tracing::warn!("Error creating watcher: {:?}", e);
79 continue;
80 }
81 };
82
83 if let Err(why) = watcher.watch(lib.as_path(), notify::RecursiveMode::Recursive)
84 {
85 tracing::warn!("Error watching directory: {:?}", why);
86 continue;
87 }
88
89 watchers.push(watcher);
90 }
91
92 for res in rx {
93 match res {
94 Ok(event) => match event.kind {
95 EventKind::Create(_) | EventKind::Remove(_) => {
96 let libraries = match steam.libraries() {
97 Ok(libraries) => libraries,
98 Err(e) => {
99 tracing::warn!("Could not get steam libraries: {:?}", e);
100 continue;
101 }
102 };
103
104 let app_ids = libraries
105 .filter_map(std::result::Result::ok)
106 .flat_map(|lib| lib.app_ids().to_vec())
107 .collect::<HashSet<_>>();
108
109 let newly_installed = app_ids.difference(&apps_installed);
110 let newly_uninstalled = apps_installed.difference(&app_ids);
111
112 if newly_installed.count() > 0 {
113 if let Err(why) = handle.emit("steam-game-installed", ()) {
114 tracing::error!("Error emitting event: {:?}", why);
115 }
116 }
117
118 if newly_uninstalled.count() > 0 {
119 if let Err(why) = handle.emit("steam-game-uninstalled", ()) {
120 tracing::error!("Error emitting event: {:?}", why);
121 }
122 }
123
124 apps_installed = app_ids;
125 }
126 _ => {}
127 },
128 Err(e) => {
129 tracing::error!("watch error: {:?}", e);
130 }
131 }
132 }
133 });
134
135 app.manage(steam);
136 Ok(())
137 })
138 .build()
139}