GUI: add cross-platform release updater (#243)

This commit is contained in:
mskomek
2026-07-16 14:01:42 +03:00
committed by GitHub
parent b4b95014f1
commit 53da00fa89
7 changed files with 397 additions and 3 deletions
+85 -1
View File
@@ -57,6 +57,9 @@ public partial class MainWindow : Window
private bool _isRunning;
private int _autoScrollTicks;
private int _activePageIndex;
private Updater.UpdateInfo? _availableUpdate;
private string _updateStatusKey = "Updater.Status.Ready";
private object?[] _updateStatusArgs = [BuildInfo.CommitSha ?? "dev"];
// Discord Rich Presence state.
private readonly long _launcherStartUnixSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
@@ -131,6 +134,9 @@ public partial class MainWindow : Window
_settings.DiscordRichPresence = DiscordToggle.IsChecked == true;
UpdateDiscordPresence();
};
AutoUpdateToggle.IsCheckedChanged += (_, _) =>
_settings.CheckForUpdatesOnStartup = AutoUpdateToggle.IsChecked == true;
UpdateButton.Click += async (_, _) => await OnUpdateButtonAsync();
SelectLogFilePathButton.Click += async (_, _) => await SelectLogFilePathAsync();
EnvBthidToggle.IsCheckedChanged += (_, _) =>
SetEnvironmentToggle("SHARPEMU_BTHID_UNAVAILABLE", EnvBthidToggle.IsChecked == true);
@@ -375,6 +381,10 @@ public partial class MainWindow : Window
ApplySettingsToControls();
LocateEmulator();
UpdateDiscordPresence();
if (_settings.CheckForUpdatesOnStartup)
{
_ = CheckForUpdatesAsync();
}
await RescanLibraryAsync();
}
@@ -478,8 +488,10 @@ public partial class MainWindow : Window
DiscordLabel.Text = loc.Get("Options.Discord.Label");
DiscordDesc.Text = loc.Get("Options.Discord.Desc");
AutoUpdateLabel.Text = loc.Get("Updater.Auto.Label");
AutoUpdateDesc.Text = loc.Get("Updater.Auto.Desc");
foreach (var toggle in new[] { StrictToggle, LogToFileToggle, OverrideLogFileToggle, TitleMusicToggle, DiscordToggle })
foreach (var toggle in new[] { StrictToggle, LogToFileToggle, OverrideLogFileToggle, TitleMusicToggle, DiscordToggle, AutoUpdateToggle })
{
toggle.OnContent = loc.Get("Common.On");
toggle.OffContent = loc.Get("Common.Off");
@@ -503,6 +515,8 @@ public partial class MainWindow : Window
DiscordServerDesc.Text = loc.Get("About.Discord.Desc");
GithubButton.Content = loc.Get("About.GithubButton");
DiscordButton.Content = loc.Get("About.DiscordButton");
UpdateLabel.Text = loc.Get("Updater.Label");
RefreshUpdateText();
UpdateEmptyStateTexts();
UpdateSelectedGameTexts();
@@ -618,6 +632,7 @@ public partial class MainWindow : Window
OverrideLogFileToggle.IsChecked = _settings.OverrideLogFile;
TitleMusicToggle.IsChecked = _settings.PlayTitleMusic;
DiscordToggle.IsChecked = _settings.DiscordRichPresence;
AutoUpdateToggle.IsChecked = _settings.CheckForUpdatesOnStartup;
EnvBthidToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_BTHID_UNAVAILABLE");
EnvLoopGuardToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_DISABLE_IMPORT_LOOP_GUARD");
EnvWritableApp0Toggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_WRITABLE_APP0");
@@ -629,6 +644,75 @@ public partial class MainWindow : Window
UpdateLogFilePathText();
}
private async Task OnUpdateButtonAsync()
{
if (_availableUpdate is null)
{
await CheckForUpdatesAsync();
return;
}
UpdateButton.IsEnabled = false;
try
{
var progress = new Progress<int>(value =>
SetUpdateStatus("Updater.Status.Downloading", value));
await Updater.DownloadAndRestartAsync(_availableUpdate, progress);
SetUpdateStatus("Updater.Status.Installing");
Close();
}
catch
{
SetUpdateStatus("Updater.Status.Failed");
UpdateButton.IsEnabled = true;
}
}
private async Task CheckForUpdatesAsync()
{
_availableUpdate = null;
UpdateButton.IsEnabled = false;
SetUpdateStatus("Updater.Status.Checking");
try
{
_availableUpdate = await Updater.CheckAsync(BuildInfo.CommitSha);
SetUpdateStatus(
_availableUpdate is null ? "Updater.Status.Current" : "Updater.Status.Available",
_availableUpdate?.Sha ?? BuildInfo.CommitSha ?? "dev");
}
catch (OperationCanceledException)
{
SetUpdateStatus("Updater.Status.Timeout");
}
catch (PlatformNotSupportedException)
{
SetUpdateStatus("Updater.Status.Unsupported");
}
catch
{
SetUpdateStatus("Updater.Status.Failed");
}
finally
{
UpdateButton.IsEnabled = true;
RefreshUpdateText();
}
}
private void SetUpdateStatus(string key, params object?[] args)
{
_updateStatusKey = key;
_updateStatusArgs = args;
RefreshUpdateText();
}
private void RefreshUpdateText()
{
UpdateStatusText.Text = Localization.Instance.Format(_updateStatusKey, _updateStatusArgs);
UpdateButton.Content = Localization.Instance.Get(
_availableUpdate is null ? "Updater.Check" : "Updater.DownloadRestart");
}
// Environment variables set on this process at the previous launch; children
// inherit the process environment, so stale names must be cleared explicitly.
private readonly HashSet<string> _appliedEnvironmentVariables = new(StringComparer.OrdinalIgnoreCase);