@@ -6,9 +6,14 @@ use crate::steam::ProtonVersion; |
| 6 | 6 | use std::collections::HashMap; |
| 7 | 7 | use std::path::Path; |
| 8 | 8 | use std::process::Stdio; |
| 9 | +use std::time::Duration; |
| 9 | 10 | use tokio::process::Command; |
| 11 | +use tokio::time::timeout; |
| 10 | 12 | use tracing::{debug, error, info, warn}; |
| 11 | 13 | |
| 14 | +/// Timeout for WeMod installer (2 minutes should be plenty) |
| 15 | +const INSTALLER_TIMEOUT: Duration = Duration::from_secs(120); |
| 16 | + |
| 12 | 17 | /// Handles WeMod installation into Wine prefixes |
| 13 | 18 | pub struct WemodInstaller<'a> { |
| 14 | 19 | /// The prefix to install into |
@@ -108,43 +113,50 @@ impl<'a> WemodInstaller<'a> { |
| 108 | 113 | info!("Using wine: {}", wine); |
| 109 | 114 | info!("Running: {} {}", wine, installer_path.display()); |
| 110 | 115 | |
| 111 | | - // WeMod uses Squirrel installer which runs silently by default |
| 112 | | - let output = Command::new(&wine) |
| 116 | + // WeMod uses Squirrel installer - run with silent flag to avoid GUI interaction |
| 117 | + // Use inherited stdout/stderr so we can see installer output |
| 118 | + // Add timeout to prevent indefinite hangs |
| 119 | + info!("Running installer (timeout: {:?})...", INSTALLER_TIMEOUT); |
| 120 | + |
| 121 | + let install_future = Command::new(&wine) |
| 113 | 122 | .arg(installer_path) |
| 123 | + .arg("--silent") // Squirrel silent install flag |
| 114 | 124 | .envs(&env) |
| 115 | | - .stdout(Stdio::piped()) |
| 116 | | - .stderr(Stdio::piped()) |
| 117 | | - .output() |
| 118 | | - .await |
| 119 | | - .map_err(|e| WandaError::WemodInstallFailed { |
| 120 | | - reason: format!("Failed to run installer: {}", e), |
| 121 | | - })?; |
| 122 | | - |
| 123 | | - let stdout = String::from_utf8_lossy(&output.stdout); |
| 124 | | - let stderr = String::from_utf8_lossy(&output.stderr); |
| 125 | | - |
| 126 | | - if !stdout.is_empty() { |
| 127 | | - debug!("Installer stdout: {}", stdout); |
| 128 | | - } |
| 129 | | - |
| 130 | | - if !output.status.success() { |
| 131 | | - error!("WeMod installer exited with status: {:?}", output.status.code()); |
| 132 | | - if !stderr.is_empty() { |
| 133 | | - error!("Installer stderr: {}", stderr); |
| 125 | + .stdout(Stdio::inherit()) |
| 126 | + .stderr(Stdio::inherit()) |
| 127 | + .status(); |
| 128 | + |
| 129 | + match timeout(INSTALLER_TIMEOUT, install_future).await { |
| 130 | + Ok(Ok(status)) => { |
| 131 | + if !status.success() { |
| 132 | + warn!("WeMod installer exited with status: {:?}", status.code()); |
| 133 | + warn!("Installation may still have succeeded - checking..."); |
| 134 | + } else { |
| 135 | + debug!("Installer completed with success status"); |
| 136 | + } |
| 137 | + } |
| 138 | + Ok(Err(e)) => { |
| 139 | + return Err(WandaError::WemodInstallFailed { |
| 140 | + reason: format!("Failed to run installer: {}", e), |
| 141 | + }); |
| 142 | + } |
| 143 | + Err(_) => { |
| 144 | + warn!("Installer timed out after {:?}", INSTALLER_TIMEOUT); |
| 145 | + warn!("Checking if installation succeeded anyway..."); |
| 134 | 146 | } |
| 135 | | - // Don't fail immediately - installer might still have worked |
| 136 | | - } else { |
| 137 | | - debug!("Installer completed with success status"); |
| 138 | 147 | } |
| 139 | 148 | |
| 140 | | - // Wait for wineserver using Proton's wineserver |
| 149 | + // Wait for wineserver using Proton's wineserver (with timeout) |
| 141 | 150 | let wineserver = self.wineserver_exe(); |
| 142 | 151 | debug!("Waiting for wineserver: {}", wineserver); |
| 143 | | - let _ = Command::new(&wineserver) |
| 152 | + let wineserver_wait = Command::new(&wineserver) |
| 144 | 153 | .arg("-w") |
| 145 | 154 | .envs(&env) |
| 146 | | - .status() |
| 147 | | - .await; |
| 155 | + .status(); |
| 156 | + |
| 157 | + if timeout(Duration::from_secs(30), wineserver_wait).await.is_err() { |
| 158 | + warn!("Wineserver wait timed out - continuing anyway"); |
| 159 | + } |
| 148 | 160 | |
| 149 | 161 | // Verify installation |
| 150 | 162 | info!("Verifying WeMod installation..."); |
@@ -309,10 +321,14 @@ impl<'a> WemodInstaller<'a> { |
| 309 | 321 | info!("Using wine: {}", wine); |
| 310 | 322 | |
| 311 | 323 | // Run WeMod with required flags for Wine/Proton compatibility |
| 324 | + // These Electron flags are necessary for proper rendering under Wine |
| 312 | 325 | let child = Command::new(&wine) |
| 313 | 326 | .arg(&wemod_exe) |
| 314 | | - .arg("--no-sandbox") // Required for Wine |
| 315 | | - .arg("--disable-gpu") // Avoid GPU compositor issues |
| 327 | + .arg("--no-sandbox") // Required for Wine |
| 328 | + .arg("--disable-gpu") // Disable GPU acceleration |
| 329 | + .arg("--disable-gpu-compositing") // Disable GPU compositing |
| 330 | + .arg("--disable-software-rasterizer") // Force hardware path (paradoxically helps) |
| 331 | + .arg("--in-process-gpu") // Run GPU in main process |
| 316 | 332 | .envs(&env) |
| 317 | 333 | .stdout(Stdio::inherit()) |
| 318 | 334 | .stderr(Stdio::inherit()) |