diff --git a/ui/MainWindow.xaml b/ui/MainWindow.xaml
index 11825393..9434e9a2 100644
--- a/ui/MainWindow.xaml
+++ b/ui/MainWindow.xaml
@@ -116,7 +116,18 @@
+ Click="SteamNav_Click"
+ Tag="restart" />
+
+
diff --git a/ui/MainWindow.xaml.cs b/ui/MainWindow.xaml.cs
index 3ccc4934..52314728 100644
--- a/ui/MainWindow.xaml.cs
+++ b/ui/MainWindow.xaml.cs
@@ -4,6 +4,7 @@
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Threading;
using Wpf.Ui.Appearance;
using Wpf.Ui.Controls;
using TextBlock = System.Windows.Controls.TextBlock;
@@ -14,6 +15,7 @@ public partial class MainWindow : FluentWindow
{
private Services.AppUpdater.CheckResult? _pendingUpdate;
public bool AppUpdateAvailable { get; private set; }
+ private readonly DispatcherTimer _steamStateTimer = new() { Interval = TimeSpan.FromSeconds(5) };
public MainWindow()
{
@@ -40,6 +42,10 @@ public MainWindow()
RootNavigation.Navigate(typeof(Pages.DashboardPage));
}
catch { }
+
+ UpdateSteamNavItem();
+ _steamStateTimer.Tick += (_, _) => UpdateSteamNavItem();
+ _steamStateTimer.Start();
};
}
@@ -179,18 +185,42 @@ private void UpdateSkip_Click(object sender, RoutedEventArgs e)
public void ShowRestartSteam()
{
- // Button is always visible now; kept for callers.
+ UpdateSteamNavItem();
+ }
+
+ private void UpdateSteamNavItem()
+ {
+ var running = Services.SteamDetector.IsSteamRunning();
+ RestartSteamItem.Visibility = running ? Visibility.Visible : Visibility.Collapsed;
+ CloseSteamItem.Visibility = running ? Visibility.Visible : Visibility.Collapsed;
+ StartSteamItem.Visibility = running ? Visibility.Collapsed : Visibility.Visible;
}
- private async void RestartSteamItem_Click(object sender, RoutedEventArgs e)
+ private async void SteamNav_Click(object sender, RoutedEventArgs e)
{
+ var tag = (sender as FrameworkElement)?.Tag as string;
+
var steamPath = Services.SteamDetector.FindSteamPath();
if (steamPath == null) return;
var steamExe = Path.Combine(steamPath, "steam.exe");
if (!File.Exists(steamExe)) return;
- // Graceful shutdown first
+ if (tag == "start")
+ {
+ try
+ {
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = steamExe,
+ UseShellExecute = true
+ })?.Dispose();
+ }
+ catch { }
+ return;
+ }
+
+ // restart or close — shut down Steam first
var procs = Process.GetProcessesByName("steam");
bool wasRunning = procs.Length > 0;
foreach (var p in procs) p.Dispose();
@@ -204,7 +234,6 @@ private async void RestartSteamItem_Click(object sender, RoutedEventArgs e)
UseShellExecute = true
})?.Dispose();
- // Wait up to 15s for Steam to close
for (int i = 0; i < 30; i++)
{
await Task.Delay(500);
@@ -215,16 +244,19 @@ private async void RestartSteamItem_Click(object sender, RoutedEventArgs e)
}
}
- try
+ if (tag == "restart")
{
- Process.Start(new ProcessStartInfo
+ try
{
- FileName = steamExe,
- UseShellExecute = true
- })?.Dispose();
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = steamExe,
+ UseShellExecute = true
+ })?.Dispose();
+ }
+ catch { }
}
- catch { }
- RestartSteamItem.Visibility = Visibility.Collapsed;
+ UpdateSteamNavItem();
}
}
diff --git a/ui/Pages/DashboardPage.xaml b/ui/Pages/DashboardPage.xaml
index 45e7411e..4282ecd8 100644
--- a/ui/Pages/DashboardPage.xaml
+++ b/ui/Pages/DashboardPage.xaml
@@ -99,10 +99,21 @@
Icon="{ui:SymbolIcon Document24}"
Margin="0,0,8,8"
Click="OpenLog_Click" />
-
+
+
diff --git a/ui/Pages/DashboardPage.xaml.cs b/ui/Pages/DashboardPage.xaml.cs
index 1420a17b..b2d4541e 100644
--- a/ui/Pages/DashboardPage.xaml.cs
+++ b/ui/Pages/DashboardPage.xaml.cs
@@ -4,6 +4,7 @@
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Threading;
using CloudRedirect.Resources;
namespace CloudRedirect.Pages;
@@ -11,6 +12,7 @@ namespace CloudRedirect.Pages;
public partial class DashboardPage : Page
{
private string? _steamPath;
+ private readonly DispatcherTimer _steamStateTimer = new() { Interval = TimeSpan.FromSeconds(5) };
public void HideDllUpdateBanner()
{
@@ -25,6 +27,10 @@ public DashboardPage()
try { await LoadStatusAsync(); }
catch { }
+ UpdateSteamButtons();
+ _steamStateTimer.Tick += (_, _) => UpdateSteamButtons();
+ _steamStateTimer.Start();
+
// Give the app-level update check time to finish, then hide the
// DLL banner if a full app update is available (avoids two banners).
await Task.Delay(3000);
@@ -268,6 +274,96 @@ await Task.Run(() =>
}
}
+ private void UpdateSteamButtons()
+ {
+ var running = Services.SteamDetector.IsSteamRunning();
+ RestartSteamBtn.Visibility = running ? Visibility.Visible : Visibility.Collapsed;
+ CloseSteamBtn.Visibility = running ? Visibility.Visible : Visibility.Collapsed;
+ StartSteamBtn.Visibility = running ? Visibility.Collapsed : Visibility.Visible;
+ }
+
+ private async void CloseSteam_Click(object sender, RoutedEventArgs e)
+ {
+ var steamPath = Services.SteamDetector.FindSteamPath();
+ if (steamPath == null) return;
+
+ var steamExe = Path.Combine(steamPath, "steam.exe");
+ if (!File.Exists(steamExe)) return;
+
+ var button = (Wpf.Ui.Controls.Button)sender;
+ button.IsEnabled = false;
+ var originalContent = button.Content;
+ button.Content = S.Get("Dashboard_ShuttingDownSteam");
+
+ try
+ {
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = steamExe,
+ Arguments = "-shutdown",
+ UseShellExecute = true
+ })?.Dispose();
+
+ bool exited = await Task.Run(async () =>
+ {
+ for (int i = 0; i < 30; i++)
+ {
+ await Task.Delay(500);
+ var procs = Process.GetProcessesByName("steam");
+ bool any = procs.Length > 0;
+ foreach (var p in procs) p.Dispose();
+ if (!any) return true;
+ }
+ return false;
+ });
+
+ if (!exited)
+ {
+ button.Content = S.Get("Dashboard_ForceKilling");
+ await Task.Run(() =>
+ {
+ foreach (var proc in Process.GetProcessesByName("steam"))
+ {
+ try { proc.Kill(); }
+ catch { }
+ finally { proc.Dispose(); }
+ }
+ });
+ await Task.Delay(1000);
+ }
+ }
+ catch { }
+
+ button.Content = originalContent;
+ button.IsEnabled = true;
+ UpdateSteamButtons();
+ }
+
+ private async void StartSteam_Click(object sender, RoutedEventArgs e)
+ {
+ var steamPath = Services.SteamDetector.FindSteamPath();
+ if (steamPath == null) return;
+
+ var steamExe = Path.Combine(steamPath, "steam.exe");
+ if (!File.Exists(steamExe)) return;
+
+ var button = (Wpf.Ui.Controls.Button)sender;
+ button.IsEnabled = false;
+
+ try
+ {
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = steamExe,
+ UseShellExecute = true
+ })?.Dispose();
+ }
+ catch { }
+
+ button.IsEnabled = true;
+ UpdateSteamButtons();
+ }
+
private async void UpdateDll_Click(object sender, RoutedEventArgs e)
{
if (_steamPath == null) return;
diff --git a/ui/Resources/Strings.resx b/ui/Resources/Strings.resx
index c695380d..ca246422 100644
--- a/ui/Resources/Strings.resx
+++ b/ui/Resources/Strings.resx
@@ -73,6 +73,12 @@
Restart Steam
+
+ Close Steam
+
+
+ Start Steam
+
Mode
@@ -518,6 +524,12 @@ If you skip this, saves will be stored locally in your Steam folder.
Restart Steam
+
+ Close Steam
+
+
+ Start Steam
+