Compare commits

...

4 Commits

Author SHA1 Message Date
Emmanuel Hansen
7539e26144 Avalonia - Fixes updater (#3670)
* update avalonia

* fix updater

* fix spacing

* addressed review

* convert permission value to octal

* Add missing comma

* revert package updates
2022-10-03 11:25:25 -03:00
Luna
1c3697b6a4 Update AboutWindow.axaml (#3724) 2022-10-02 22:02:11 +00:00
gdkchan
81f848e54f Allow Surface Flinger frame enqueue after process has exited (#3733) 2022-10-02 21:50:03 +00:00
MutantAura
358a781639 Volume Hotkeys (#3500)
* Initial GTK implementation

* Less messy and Avalonia imp

* Move clamping to HLE and streamline imps

* Make viewmodel update consistent

* Fix rebase and add an english locale.

Co-authored-by: Mary-nyan <mary@mary.zone>
2022-10-02 09:38:37 +00:00
15 changed files with 222 additions and 206 deletions

View File

@@ -58,6 +58,8 @@ namespace Ryujinx.Ava
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
private const int TargetFps = 60; private const int TargetFps = 60;
private const float VolumeDelta = 0.05f;
private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None);
private readonly long _ticksPerFrame; private readonly long _ticksPerFrame;
@@ -73,6 +75,7 @@ namespace Ryujinx.Ava
private bool _isStopped; private bool _isStopped;
private bool _isActive; private bool _isActive;
private long _lastCursorMoveTime; private long _lastCursorMoveTime;
private float _newVolume;
private long _ticks = 0; private long _ticks = 0;
private KeyboardHotkeyState _prevHotkeyState; private KeyboardHotkeyState _prevHotkeyState;
@@ -1003,6 +1006,18 @@ namespace Ryujinx.Ava
GraphicsConfig.ResScale = GraphicsConfig.ResScale =
(MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1;
break; break;
case KeyboardHotkeyState.VolumeUp:
_newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2);
Device.SetVolume(_newVolume);
_parent.ViewModel.Volume = Device.GetVolume();
break;
case KeyboardHotkeyState.VolumeDown:
_newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2);
Device.SetVolume(_newVolume);
_parent.ViewModel.Volume = Device.GetVolume();
break;
case KeyboardHotkeyState.None: case KeyboardHotkeyState.None:
(_keyboardInterface as AvaloniaKeyboard).Clear(); (_keyboardInterface as AvaloniaKeyboard).Clear();
break; break;
@@ -1068,6 +1083,14 @@ namespace Ryujinx.Ava
{ {
state = KeyboardHotkeyState.ResScaleDown; state = KeyboardHotkeyState.ResScaleDown;
} }
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp))
{
state = KeyboardHotkeyState.VolumeUp;
}
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown))
{
state = KeyboardHotkeyState.VolumeDown;
}
return state; return state;
} }

View File

@@ -574,12 +574,12 @@
"Discard": "Discard", "Discard": "Discard",
"UserProfilesSetProfileImage": "Set Profile Image", "UserProfilesSetProfileImage": "Set Profile Image",
"UserProfileEmptyNameError": "Name is required", "UserProfileEmptyNameError": "Name is required",
"UserProfileNoImageError": "Profile image must be set", "UserProfileNoImageError": "Profile image must be set",
"GameUpdateWindowHeading": "Updates Available for {0} [{1}]", "GameUpdateWindowHeading": "Updates Available for {0} [{1}]",
"SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:", "SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:",
"SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:", "SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:",
"UserProfilesName": "Name:", "UserProfilesName": "Name:",
"UserProfilesUserId" : "User Id:", "UserProfilesUserId": "User Id:",
"SettingsTabGraphicsBackend": "Graphics Backend", "SettingsTabGraphicsBackend": "Graphics Backend",
"SettingsTabGraphicsBackendTooltip": "Graphics Backend to use", "SettingsTabGraphicsBackendTooltip": "Graphics Backend to use",
"SettingsEnableTextureRecompression": "Enable Texture Recompression", "SettingsEnableTextureRecompression": "Enable Texture Recompression",
@@ -589,5 +589,8 @@
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
"SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied", "SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied",
"SettingsGpuBackendRestartSubMessage": "Do you want to restart now?", "SettingsGpuBackendRestartSubMessage": "Do you want to restart now?",
"RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?",
"SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:",
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
"VolumeShort": "Vol" "VolumeShort": "Vol"
} }

View File

@@ -9,6 +9,8 @@
Pause, Pause,
ToggleMute, ToggleMute,
ResScaleUp, ResScaleUp,
ResScaleDown ResScaleDown,
VolumeUp,
VolumeDown
} }
} }

View File

@@ -1,4 +1,6 @@
using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Zip;
@@ -11,11 +13,13 @@ using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -40,6 +44,8 @@ namespace Ryujinx.Modules
private static readonly string[] WindowsDependencyDirs = Array.Empty<string>(); private static readonly string[] WindowsDependencyDirs = Array.Empty<string>();
public static bool UpdateSuccessful { get; private set; }
public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate)
{ {
if (Running) if (Running)
@@ -198,11 +204,18 @@ namespace Ryujinx.Modules
_buildSize = -1; _buildSize = -1;
} }
} }
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
// Show a message asking the user if they want to update // Show a message asking the user if they want to update
UpdaterWindow updateDialog = new(mainWindow, newVersion, _buildUrl); var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"],
await updateDialog.ShowDialog(mainWindow); LocaleManager.Instance["RyujinxUpdaterMessage"],
$"{Program.Version} -> {newVersion}");
if (shouldUpdate)
{
UpdateRyujinx(mainWindow, _buildUrl);
}
}); });
} }
@@ -216,8 +229,10 @@ namespace Ryujinx.Modules
return result; return result;
} }
public static void UpdateRyujinx(UpdaterWindow updateDialog, string downloadUrl) public static async void UpdateRyujinx(Window parent, string downloadUrl)
{ {
UpdateSuccessful = false;
// Empty update dir, although it shouldn't ever have anything inside it // Empty update dir, although it shouldn't ever have anything inside it
if (Directory.Exists(UpdateDir)) if (Directory.Exists(UpdateDir))
{ {
@@ -228,25 +243,56 @@ namespace Ryujinx.Modules
string updateFile = Path.Combine(UpdateDir, "update.bin"); string updateFile = Path.Combine(UpdateDir, "update.bin");
// Download the update .zip var taskDialog = new TaskDialog()
updateDialog.MainText.Text = LocaleManager.Instance["UpdaterDownloading"]; {
updateDialog.ProgressBar.Value = 0; Header = LocaleManager.Instance["RyujinxUpdater"],
updateDialog.ProgressBar.Maximum = 100; SubHeader = LocaleManager.Instance["UpdaterDownloading"],
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
Buttons = { },
ShowProgressBar = true
};
Task.Run(() => taskDialog.XamlRoot = parent;
taskDialog.Opened += (s, e) =>
{ {
if (_buildSize >= 0) if (_buildSize >= 0)
{ {
DoUpdateWithMultipleThreads(updateDialog, downloadUrl, updateFile); DoUpdateWithMultipleThreads(taskDialog, downloadUrl, updateFile);
} }
else else
{ {
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
} }
}); };
await taskDialog.ShowAsync(true);
if (UpdateSuccessful)
{
var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"],
LocaleManager.Instance["DialogUpdaterCompleteMessage"],
LocaleManager.Instance["DialogUpdaterRestartMessage"]);
if (shouldRestart)
{
string ryuName = Path.GetFileName(Environment.ProcessPath);
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray());
if (!OperatingSystem.IsWindows())
{
chmod(ryuExe, Convert.ToUInt32("0777", 8));
}
Process.Start(ryuExe, ryuArg);
Environment.Exit(0);
}
}
} }
private static void DoUpdateWithMultipleThreads(UpdaterWindow updateDialog, string downloadUrl, string updateFile) private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile)
{ {
// Multi-Threaded Updater // Multi-Threaded Updater
long chunkSize = _buildSize / ConnectionCount; long chunkSize = _buildSize / ConnectionCount;
@@ -290,7 +336,7 @@ namespace Ryujinx.Modules
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount; taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal);
}; };
client.DownloadDataCompleted += (_, args) => client.DownloadDataCompleted += (_, args) =>
@@ -301,6 +347,8 @@ namespace Ryujinx.Modules
{ {
webClients[index].Dispose(); webClients[index].Dispose();
taskDialog.Hide();
return; return;
} }
@@ -320,14 +368,14 @@ namespace Ryujinx.Modules
try try
{ {
InstallUpdate(updateDialog, updateFile); InstallUpdate(taskDialog, updateFile);
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warning?.Print(LogClass.Application, e.Message); Logger.Warning?.Print(LogClass.Application, e.Message);
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
return; return;
} }
@@ -348,7 +396,7 @@ namespace Ryujinx.Modules
webClients[j].CancelAsync(); webClients[j].CancelAsync();
} }
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
return; return;
} }
@@ -356,7 +404,7 @@ namespace Ryujinx.Modules
} }
} }
private static void DoUpdateWithSingleThreadWorker(UpdaterWindow updateDialog, string downloadUrl, string updateFile) private static void DoUpdateWithSingleThreadWorker(TaskDialog taskDialog, string downloadUrl, string updateFile)
{ {
using (HttpClient client = new HttpClient()) using (HttpClient client = new HttpClient())
{ {
@@ -384,19 +432,26 @@ namespace Ryujinx.Modules
byteWritten += readSize; byteWritten += readSize;
updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal);
updateFileStream.Write(buffer, 0, readSize); updateFileStream.Write(buffer, 0, readSize);
} }
} }
} }
InstallUpdate(updateDialog, updateFile); InstallUpdate(taskDialog, updateFile);
} }
} }
private static void DoUpdateWithSingleThread(UpdaterWindow updateDialog, string downloadUrl, string updateFile) [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double GetPercentage(double value, double max)
{ {
Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)); return max == 0 ? 0 : value / max * 100;
}
private static void DoUpdateWithSingleThread(TaskDialog taskDialog, string downloadUrl, string updateFile)
{
Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile));
worker.Name = "Updater.SingleThreadWorker"; worker.Name = "Updater.SingleThreadWorker";
worker.Start(); worker.Start();
} }
@@ -414,11 +469,11 @@ namespace Ryujinx.Modules
} }
} }
private static async void InstallUpdate(UpdaterWindow updateDialog, string updateFile) private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
{ {
// Extract Update // Extract Update
updateDialog.MainText.Text = LocaleManager.Instance["UpdaterExtracting"]; taskDialog.SubHeader = LocaleManager.Instance["UpdaterExtracting"];
updateDialog.ProgressBar.Value = 0; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
if (OperatingSystem.IsLinux()) if (OperatingSystem.IsLinux())
{ {
@@ -426,8 +481,6 @@ namespace Ryujinx.Modules
using (Stream gzipStream = new GZipInputStream(inStream)) using (Stream gzipStream = new GZipInputStream(inStream))
using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII)) using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII))
{ {
updateDialog.ProgressBar.Maximum = inStream.Length;
await Task.Run(() => await Task.Run(() =>
{ {
TarEntry tarEntry; TarEntry tarEntry;
@@ -450,12 +503,12 @@ namespace Ryujinx.Modules
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
updateDialog.ProgressBar.Value += entry.Size; taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal);
}); });
} }
}); });
updateDialog.ProgressBar.Value = inStream.Length; taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
} }
} }
else else
@@ -463,12 +516,12 @@ namespace Ryujinx.Modules
using (Stream inStream = File.OpenRead(updateFile)) using (Stream inStream = File.OpenRead(updateFile))
using (ZipFile zipFile = new ZipFile(inStream)) using (ZipFile zipFile = new ZipFile(inStream))
{ {
updateDialog.ProgressBar.Maximum = zipFile.Count;
await Task.Run(() => await Task.Run(() =>
{ {
double count = 0;
foreach (ZipEntry zipEntry in zipFile) foreach (ZipEntry zipEntry in zipFile)
{ {
count++;
if (zipEntry.IsDirectory) continue; if (zipEntry.IsDirectory) continue;
string outPath = Path.Combine(UpdateDir, zipEntry.Name); string outPath = Path.Combine(UpdateDir, zipEntry.Name);
@@ -485,7 +538,7 @@ namespace Ryujinx.Modules
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
updateDialog.ProgressBar.Value++; taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal);
}); });
} }
}); });
@@ -497,22 +550,23 @@ namespace Ryujinx.Modules
List<string> allFiles = EnumerateFilesToDelete().ToList(); List<string> allFiles = EnumerateFilesToDelete().ToList();
updateDialog.MainText.Text = LocaleManager.Instance["UpdaterRenaming"]; taskDialog.SubHeader = LocaleManager.Instance["UpdaterRenaming"];
updateDialog.ProgressBar.Value = 0; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
updateDialog.ProgressBar.Maximum = allFiles.Count;
// Replace old files // Replace old files
await Task.Run(() => await Task.Run(() =>
{ {
double count = 0;
foreach (string file in allFiles) foreach (string file in allFiles)
{ {
count++;
try try
{ {
File.Move(file, file + ".ryuold"); File.Move(file, file + ".ryuold");
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
updateDialog.ProgressBar.Value++; taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
}); });
} }
catch catch
@@ -523,23 +577,20 @@ namespace Ryujinx.Modules
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
updateDialog.MainText.Text = LocaleManager.Instance["UpdaterAddingFiles"]; taskDialog.SubHeader = LocaleManager.Instance["UpdaterAddingFiles"];
updateDialog.ProgressBar.Value = 0; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
updateDialog.ProgressBar.Maximum = Directory.GetFiles(UpdatePublishDir, "*", SearchOption.AllDirectories).Length;
}); });
MoveAllFilesOver(UpdatePublishDir, HomeDir, updateDialog); MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog);
}); });
Directory.Delete(UpdateDir, true); Directory.Delete(UpdateDir, true);
SetUnixPermissions(); SetUnixPermissions();
updateDialog.MainText.Text = LocaleManager.Instance["DialogUpdaterCompleteMessage"]; UpdateSuccessful = true;
updateDialog.SecondaryText.Text = LocaleManager.Instance["DialogUpdaterRestartMessage"];
updateDialog.ProgressBar.IsVisible = false; taskDialog.Hide();
updateDialog.ButtonBox.IsVisible = true;
} }
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
@@ -618,8 +669,9 @@ namespace Ryujinx.Modules
return files; return files;
} }
private static void MoveAllFilesOver(string root, string dest, UpdaterWindow dialog) private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog)
{ {
var total = Directory.GetFiles(root, "*", SearchOption.AllDirectories).Length;
foreach (string directory in Directory.GetDirectories(root)) foreach (string directory in Directory.GetDirectories(root))
{ {
string dirName = Path.GetFileName(directory); string dirName = Path.GetFileName(directory);
@@ -629,16 +681,18 @@ namespace Ryujinx.Modules
Directory.CreateDirectory(Path.Combine(dest, dirName)); Directory.CreateDirectory(Path.Combine(dest, dirName));
} }
MoveAllFilesOver(directory, Path.Combine(dest, dirName), dialog); MoveAllFilesOver(directory, Path.Combine(dest, dirName), taskDialog);
} }
double count = 0;
foreach (string file in Directory.GetFiles(root)) foreach (string file in Directory.GetFiles(root))
{ {
count++;
File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true); File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true);
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
dialog.ProgressBar.Value++; taskDialog.SetProgressBarState(GetPercentage(count, total), TaskDialogProgressState.Normal);
}); });
} }
} }

View File

@@ -6,7 +6,6 @@
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows" xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="Ryujinx - About"
Width="850" Width="850"
Height="550" Height="550"
MinWidth="500" MinWidth="500"

View File

@@ -257,6 +257,22 @@
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.VolumeUp, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.VolumeDown, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
</ScrollViewer> </ScrollViewer>

View File

@@ -1,66 +0,0 @@
<window:StyleableWindow
x:Class="Ryujinx.Ava.Ui.Windows.UpdaterWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="Ryujinx Updater"
Width="500"
Height="500"
MinWidth="500"
MinHeight="500"
d:DesignHeight="350"
d:DesignWidth="400"
CanResize="False"
SizeToContent="Height"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d">
<Grid
Margin="20"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Name="MainText"
Grid.Row="1"
Height="20"
HorizontalAlignment="Stretch"
TextAlignment="Center" />
<TextBlock
Name="SecondaryText"
Grid.Row="2"
Height="20"
HorizontalAlignment="Stretch"
TextAlignment="Center" />
<ProgressBar
Name="ProgressBar"
Grid.Row="3"
Margin="20"
HorizontalAlignment="Stretch"
IsVisible="False"
Maximum="100"
Minimum="0" />
<StackPanel
Name="ButtonBox"
Grid.Row="4"
HorizontalAlignment="Right"
IsVisible="False"
Orientation="Horizontal"
Spacing="20">
<Button MinWidth="50" Command="{Binding YesPressed}">
<TextBlock Text="{locale:Locale InputDialogYes}" TextAlignment="Center" />
</Button>
<Button MinWidth="50" Command="{Binding NoPressed}">
<TextBlock Text="{locale:Locale InputDialogNo}" TextAlignment="Center" />
</Button>
</StackPanel>
</Grid>
</window:StyleableWindow>

View File

@@ -1,73 +0,0 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Modules;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace Ryujinx.Ava.Ui.Windows
{
public partial class UpdaterWindow : StyleableWindow
{
private readonly string _buildUrl;
private readonly MainWindow _mainWindow;
private readonly Version _newVersion;
private bool _restartQuery;
public UpdaterWindow()
{
DataContext = this;
InitializeComponent();
Title = LocaleManager.Instance["RyujinxUpdater"];
}
public UpdaterWindow(MainWindow mainWindow, Version newVersion, string buildUrl) : this()
{
_mainWindow = mainWindow;
_newVersion = newVersion;
_buildUrl = buildUrl;
}
[DllImport("libc", SetLastError = true)]
private static extern int chmod(string path, uint mode);
public void YesPressed()
{
if (_restartQuery)
{
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.Ava.exe" : "Ryujinx.Ava";
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray());
if (!OperatingSystem.IsWindows())
{
chmod(ryuExe, 0777);
}
Process.Start(ryuExe, ryuArg);
Environment.Exit(0);
}
else
{
ButtonBox.IsVisible = false;
ProgressBar.IsVisible = true;
SecondaryText.Text = "";
_restartQuery = true;
Updater.UpdateRyujinx(this, _buildUrl);
}
}
public void NoPressed()
{
_mainWindow.UpdateMenuItem.IsEnabled = true;
Close();
}
}
}

View File

@@ -9,5 +9,7 @@
public Key ToggleMute { get; set; } public Key ToggleMute { get; set; }
public Key ResScaleUp { get; set; } public Key ResScaleUp { get; set; }
public Key ResScaleDown { get; set; } public Key ResScaleDown { get; set; }
public Key VolumeUp { get; set; }
public Key VolumeDown { get; set; }
} }
} }

View File

@@ -123,7 +123,8 @@ namespace Ryujinx.Graphics.Gpu
/// <param name="releaseCallback">Texture release callback</param> /// <param name="releaseCallback">Texture release callback</param>
/// <param name="userObj">User defined object passed to the release callback</param> /// <param name="userObj">User defined object passed to the release callback</param>
/// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception> /// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception>
public void EnqueueFrameThreadSafe( /// <returns>True if the frame was added to the queue, false otherwise</returns>
public bool EnqueueFrameThreadSafe(
ulong pid, ulong pid,
ulong address, ulong address,
int width, int width,
@@ -140,7 +141,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
if (!_context.PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory)) if (!_context.PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory))
{ {
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid)); return false;
} }
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4); FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4);
@@ -184,6 +185,8 @@ namespace Ryujinx.Graphics.Gpu
acquireCallback, acquireCallback,
releaseCallback, releaseCallback,
userObj)); userObj));
return true;
} }
/// <summary> /// <summary>

View File

@@ -453,7 +453,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
Item = item Item = item
}; };
_device.Gpu.Window.EnqueueFrameThreadSafe( if (_device.Gpu.Window.EnqueueFrameThreadSafe(
layer.Owner, layer.Owner,
frameBufferAddress, frameBufferAddress,
frameBufferWidth, frameBufferWidth,
@@ -466,20 +466,25 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
crop, crop,
AcquireBuffer, AcquireBuffer,
ReleaseBuffer, ReleaseBuffer,
textureCallbackInformation); textureCallbackInformation))
if (item.Fence.FenceCount == 0)
{ {
_device.Gpu.Window.SignalFrameReady(); if (item.Fence.FenceCount == 0)
_device.Gpu.GPFifo.Interrupt();
}
else
{
item.Fence.RegisterCallback(_device.Gpu, (x) =>
{ {
_device.Gpu.Window.SignalFrameReady(); _device.Gpu.Window.SignalFrameReady();
_device.Gpu.GPFifo.Interrupt(); _device.Gpu.GPFifo.Interrupt();
}); }
else
{
item.Fence.RegisterCallback(_device.Gpu, (x) =>
{
_device.Gpu.Window.SignalFrameReady();
_device.Gpu.GPFifo.Interrupt();
});
}
}
else
{
ReleaseBuffer(textureCallbackInformation);
} }
} }

View File

@@ -124,7 +124,7 @@ namespace Ryujinx.HLE
public void SetVolume(float volume) public void SetVolume(float volume)
{ {
System.SetVolume(volume); System.SetVolume(Math.Clamp(volume, 0, 1));
} }
public float GetVolume() public float GetVolume()

View File

@@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 40; public const int CurrentVersion = 41;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format

View File

@@ -677,7 +677,9 @@ namespace Ryujinx.Ui.Common.Configuration
ShowUi = Key.F4, ShowUi = Key.F4,
Pause = Key.F5, Pause = Key.F5,
ResScaleUp = Key.Unbound, ResScaleUp = Key.Unbound,
ResScaleDown = Key.Unbound ResScaleDown = Key.Unbound,
VolumeUp = Key.Unbound,
VolumeDown = Key.Unbound
}; };
Hid.InputConfig.Value = new List<InputConfig> Hid.InputConfig.Value = new List<InputConfig>
{ {
@@ -1156,6 +1158,24 @@ namespace Ryujinx.Ui.Common.Configuration
configurationFileUpdated = true; configurationFileUpdated = true;
} }
if (configurationFileFormat.Version < 41)
{
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 41.");
configurationFileFormat.Hotkeys = new KeyboardHotkeys
{
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
ShowUi = configurationFileFormat.Hotkeys.ShowUi,
Pause = configurationFileFormat.Hotkeys.Pause,
ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp,
ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown,
VolumeUp = Key.Unbound,
VolumeDown = Key.Unbound
};
}
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;

View File

@@ -35,6 +35,7 @@ namespace Ryujinx.Ui
private const int SwitchPanelHeight = 720; private const int SwitchPanelHeight = 720;
private const int TargetFps = 60; private const int TargetFps = 60;
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
private const float VolumeDelta = 0.05f;
public ManualResetEvent WaitEvent { get; set; } public ManualResetEvent WaitEvent { get; set; }
public NpadManager NpadManager { get; } public NpadManager NpadManager { get; }
@@ -57,6 +58,7 @@ namespace Ryujinx.Ui
private readonly long _ticksPerFrame; private readonly long _ticksPerFrame;
private long _ticks = 0; private long _ticks = 0;
private float _newVolume;
private readonly Stopwatch _chrono; private readonly Stopwatch _chrono;
@@ -643,6 +645,20 @@ namespace Ryujinx.Ui
(MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1;
} }
if (currentHotkeyState.HasFlag(KeyboardHotkeyState.VolumeUp) &&
!_prevHotkeyState.HasFlag(KeyboardHotkeyState.VolumeUp))
{
_newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2);
Device.SetVolume(_newVolume);
}
if (currentHotkeyState.HasFlag(KeyboardHotkeyState.VolumeDown) &&
!_prevHotkeyState.HasFlag(KeyboardHotkeyState.VolumeDown))
{
_newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2);
Device.SetVolume(_newVolume);
}
_prevHotkeyState = currentHotkeyState; _prevHotkeyState = currentHotkeyState;
} }
@@ -675,7 +691,9 @@ namespace Ryujinx.Ui
Pause = 1 << 3, Pause = 1 << 3,
ToggleMute = 1 << 4, ToggleMute = 1 << 4,
ResScaleUp = 1 << 5, ResScaleUp = 1 << 5,
ResScaleDown = 1 << 6 ResScaleDown = 1 << 6,
VolumeUp = 1 << 7,
VolumeDown = 1 << 8
} }
private KeyboardHotkeyState GetHotkeyState() private KeyboardHotkeyState GetHotkeyState()
@@ -717,6 +735,16 @@ namespace Ryujinx.Ui
state |= KeyboardHotkeyState.ResScaleDown; state |= KeyboardHotkeyState.ResScaleDown;
} }
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp))
{
state |= KeyboardHotkeyState.VolumeUp;
}
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown))
{
state |= KeyboardHotkeyState.VolumeDown;
}
return state; return state;
} }
} }