Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8c720783f5 | ||
|
8734ea9dd4 | ||
|
c586e6d2b7 | ||
|
3a4eeb77fe | ||
|
51b3953cfc | ||
|
610eecc1c1 | ||
|
492056abf6 |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -69,12 +69,6 @@ jobs:
|
||||
- name: Publish Ryujinx.Ava
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Rename Avalonia (Windows)
|
||||
run: mv ./publish_ava/Ryujinx.Ava.exe ./publish_ava/Ryujinx.exe
|
||||
if: runner.os == 'Windows' && github.event_name == 'pull_request'
|
||||
- name: Rename Avalonia (Unix)
|
||||
run: mv ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx
|
||||
if: runner.os != 'Windows' && github.event_name == 'pull_request'
|
||||
- name: Upload Ryujinx artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
@@ -46,13 +46,11 @@ jobs:
|
||||
shell: bash
|
||||
- name: Create output dir
|
||||
run: "mkdir release_output"
|
||||
|
||||
- name: Publish Windows
|
||||
run: |
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
|
||||
mv ./publish_windows_ava/publish/Ryujinx.Ava.exe ./publish_windows_ava/publish/Ryujinx.exe
|
||||
- name: Packing Windows builds
|
||||
run: |
|
||||
pushd publish_windows
|
||||
@@ -73,26 +71,29 @@ jobs:
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
|
||||
mv ./publish_linux_ava/publish/Ryujinx.Ava ./publish_linux_ava/publish/Ryujinx
|
||||
|
||||
- name: Packing Linux builds
|
||||
run: |
|
||||
pushd publish_linux
|
||||
tar --exclude "publish/Ryujinx" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
tar --exclude "publish/Ryujinx" --exclude "publish/Ryujinx.sh" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
popd
|
||||
|
||||
pushd publish_linux_sdl2_headless
|
||||
tar --exclude "publish/Ryujinx.Headless.SDL2" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
tar --exclude "publish/Ryujinx.Headless.SDL2" --exclude "publish/Ryujinx.sh" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
popd
|
||||
|
||||
pushd publish_linux_ava
|
||||
tar --exclude "publish/Ryujinx" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
|
||||
tar --exclude "publish/Ryujinx.Ava" --exclude "publish/Ryujinx.sh" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Ava" "publish/Ryujinx.Ava"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
popd
|
||||
|
111
Ryujinx.Ava/Helper/LoggerAdapter.cs
Normal file
111
Ryujinx.Ava/Helper/LoggerAdapter.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using Avalonia.Utilities;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helper
|
||||
{
|
||||
using AvaLogger = Avalonia.Logging.Logger;
|
||||
using AvaLogLevel = Avalonia.Logging.LogEventLevel;
|
||||
using RyuLogger = Ryujinx.Common.Logging.Logger;
|
||||
using RyuLogClass = Ryujinx.Common.Logging.LogClass;
|
||||
|
||||
internal class LoggerAdapter : Avalonia.Logging.ILogSink
|
||||
{
|
||||
public static void Register()
|
||||
{
|
||||
AvaLogger.Sink = new LoggerAdapter();
|
||||
}
|
||||
|
||||
private static RyuLogger.Log? GetLog(AvaLogLevel level)
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
AvaLogLevel.Verbose => RyuLogger.Trace,
|
||||
AvaLogLevel.Debug => RyuLogger.Debug,
|
||||
AvaLogLevel.Information => RyuLogger.Info,
|
||||
AvaLogLevel.Warning => RyuLogger.Warning,
|
||||
AvaLogLevel.Error => RyuLogger.Error,
|
||||
AvaLogLevel.Fatal => RyuLogger.Notice,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsEnabled(AvaLogLevel level, string area)
|
||||
{
|
||||
return GetLog(level) != null;
|
||||
}
|
||||
|
||||
public void Log(AvaLogLevel level, string area, object source, string messageTemplate)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, null));
|
||||
}
|
||||
|
||||
public void Log<T0>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, new object[] { propertyValue0 }));
|
||||
}
|
||||
|
||||
public void Log<T0, T1>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, new object[] { propertyValue0, propertyValue1 }));
|
||||
}
|
||||
|
||||
public void Log<T0, T1, T2>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, new object[] { propertyValue0, propertyValue1, propertyValue2 }));
|
||||
}
|
||||
|
||||
public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, propertyValues));
|
||||
}
|
||||
|
||||
private static string Format(string area, string template, object source, object[] v)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
var r = new CharacterReader(template.AsSpan());
|
||||
var i = 0;
|
||||
|
||||
result.Append('[');
|
||||
result.Append(area);
|
||||
result.Append("] ");
|
||||
|
||||
while (!r.End)
|
||||
{
|
||||
var c = r.Take();
|
||||
|
||||
if (c != '{')
|
||||
{
|
||||
result.Append(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r.Peek != '{')
|
||||
{
|
||||
result.Append('\'');
|
||||
result.Append(i < v.Length ? v[i++] : null);
|
||||
result.Append('\'');
|
||||
r.TakeUntil('}');
|
||||
r.Take();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append('{');
|
||||
r.Take();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (source != null)
|
||||
{
|
||||
result.Append(" (");
|
||||
result.Append(source.GetType().Name);
|
||||
result.Append(" #");
|
||||
result.Append(source.GetHashCode());
|
||||
result.Append(')');
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Ava.UI.Helper;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
@@ -89,6 +90,8 @@ namespace Ryujinx.Ava
|
||||
|
||||
Initialize(args);
|
||||
|
||||
LoggerAdapter.Register();
|
||||
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
@@ -110,8 +113,7 @@ namespace Ryujinx.Ava
|
||||
AllowEglInitialization = false,
|
||||
CompositionBackdropCornerRadius = 8.0f,
|
||||
})
|
||||
.UseSkia()
|
||||
.LogToTrace();
|
||||
.UseSkia();
|
||||
}
|
||||
|
||||
private static void Initialize(string[] args)
|
||||
|
@@ -38,9 +38,9 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
var selected = listBox.SelectedItem as ApplicationData;
|
||||
_selectedApplication = listBox.SelectedItem as ApplicationData;
|
||||
|
||||
_selectedApplication = selected;
|
||||
(DataContext as MainWindowViewModel).GridSelectedApplication = _selectedApplication;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -38,9 +38,9 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
var selected = listBox.SelectedItem as ApplicationData;
|
||||
_selectedApplication = listBox.SelectedItem as ApplicationData;
|
||||
|
||||
_selectedApplication = selected;
|
||||
(DataContext as MainWindowViewModel).ListSelectedApplication = _selectedApplication;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,56 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
private static bool _isChoiceDialogOpen;
|
||||
|
||||
private async static Task<UserResult> ShowContentDialog(
|
||||
public async static Task<UserResult> ShowContentDialog(
|
||||
string title,
|
||||
object content,
|
||||
string primaryButton,
|
||||
string secondaryButton,
|
||||
string closeButton,
|
||||
UserResult primaryButtonResult = UserResult.Ok,
|
||||
ManualResetEvent deferResetEvent = null,
|
||||
Func<Window, Task> doWhileDeferred = null,
|
||||
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
||||
{
|
||||
UserResult result = UserResult.None;
|
||||
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
Title = title,
|
||||
PrimaryButtonText = primaryButton,
|
||||
SecondaryButtonText = secondaryButton,
|
||||
CloseButtonText = closeButton,
|
||||
Content = content
|
||||
};
|
||||
|
||||
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = primaryButtonResult;
|
||||
});
|
||||
|
||||
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.No;
|
||||
contentDialog.PrimaryButtonClick -= deferCloseAction;
|
||||
});
|
||||
|
||||
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.Cancel;
|
||||
contentDialog.PrimaryButtonClick -= deferCloseAction;
|
||||
});
|
||||
|
||||
if (deferResetEvent != null)
|
||||
{
|
||||
contentDialog.PrimaryButtonClick += deferCloseAction;
|
||||
}
|
||||
|
||||
await ShowAsync(contentDialog);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async static Task<UserResult> ShowTextDialog(
|
||||
string title,
|
||||
string primaryText,
|
||||
string secondaryText,
|
||||
@@ -32,119 +81,9 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
Func<Window, Task> doWhileDeferred = null,
|
||||
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
||||
{
|
||||
UserResult result = UserResult.None;
|
||||
Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol);
|
||||
|
||||
bool useOverlay = false;
|
||||
Window mainWindow = null;
|
||||
|
||||
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
|
||||
{
|
||||
foreach (var item in al.Windows)
|
||||
{
|
||||
if (item.IsActive && item is MainWindow window && window.ViewModel.IsGameRunning)
|
||||
{
|
||||
mainWindow = window;
|
||||
useOverlay = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentDialog contentDialog = null;
|
||||
ContentDialogOverlayWindow overlay = null;
|
||||
|
||||
if (useOverlay)
|
||||
{
|
||||
overlay = new ContentDialogOverlayWindow()
|
||||
{
|
||||
Height = mainWindow.Bounds.Height,
|
||||
Width = mainWindow.Bounds.Width,
|
||||
Position = mainWindow.PointToScreen(new Point())
|
||||
};
|
||||
|
||||
mainWindow.PositionChanged += OverlayOnPositionChanged;
|
||||
|
||||
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
|
||||
{
|
||||
overlay.Position = mainWindow.PointToScreen(new Point());
|
||||
}
|
||||
|
||||
contentDialog = overlay.ContentDialog;
|
||||
|
||||
bool opened = false;
|
||||
|
||||
overlay.Opened += OverlayOnActivated;
|
||||
|
||||
async void OverlayOnActivated(object sender, EventArgs e)
|
||||
{
|
||||
if (opened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
opened = true;
|
||||
|
||||
overlay.Position = mainWindow.PointToScreen(new Point());
|
||||
|
||||
await ShowDialog();
|
||||
}
|
||||
|
||||
await overlay.ShowDialog(mainWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
contentDialog = new ContentDialog();
|
||||
|
||||
await ShowDialog();
|
||||
}
|
||||
|
||||
async Task ShowDialog()
|
||||
{
|
||||
contentDialog.Title = title;
|
||||
contentDialog.PrimaryButtonText = primaryButton;
|
||||
contentDialog.SecondaryButtonText = secondaryButton;
|
||||
contentDialog.CloseButtonText = closeButton;
|
||||
contentDialog.Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol);
|
||||
|
||||
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = primaryButtonResult;
|
||||
});
|
||||
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.No;
|
||||
contentDialog.PrimaryButtonClick -= deferCloseAction;
|
||||
});
|
||||
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.Cancel;
|
||||
contentDialog.PrimaryButtonClick -= deferCloseAction;
|
||||
});
|
||||
|
||||
if (deferResetEvent != null)
|
||||
{
|
||||
contentDialog.PrimaryButtonClick += deferCloseAction;
|
||||
}
|
||||
|
||||
if (useOverlay)
|
||||
{
|
||||
await contentDialog.ShowAsync(overlay, ContentDialogPlacement.Popup);
|
||||
|
||||
overlay!.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
|
||||
}
|
||||
}
|
||||
|
||||
if (useOverlay)
|
||||
{
|
||||
overlay.Content = null;
|
||||
overlay.Close();
|
||||
}
|
||||
|
||||
return result;
|
||||
return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, doWhileDeferred, deferCloseAction);
|
||||
}
|
||||
|
||||
public async static Task<UserResult> ShowDeferredContentDialog(
|
||||
@@ -162,7 +101,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
bool startedDeferring = false;
|
||||
UserResult result = UserResult.None;
|
||||
|
||||
return await ShowContentDialog(
|
||||
return await ShowTextDialog(
|
||||
title,
|
||||
primaryText,
|
||||
secondaryText,
|
||||
@@ -192,8 +131,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
sender.PrimaryButtonClick -= DeferClose;
|
||||
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Run(() =>
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
deferResetEvent.WaitOne();
|
||||
|
||||
@@ -202,7 +140,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
deferral.Complete();
|
||||
});
|
||||
});
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
|
||||
if (doWhileDeferred != null)
|
||||
{
|
||||
@@ -213,34 +150,42 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
private static Grid CreateDialogTextContent(string primaryText, string secondaryText, int symbol)
|
||||
private static Grid CreateTextDialogContent(string primaryText, string secondaryText, int symbol)
|
||||
{
|
||||
Grid content = new Grid();
|
||||
content.RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() };
|
||||
content.ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() };
|
||||
Grid content = new()
|
||||
{
|
||||
RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() },
|
||||
ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() },
|
||||
|
||||
content.MinHeight = 80;
|
||||
MinHeight = 80
|
||||
};
|
||||
|
||||
SymbolIcon icon = new()
|
||||
{
|
||||
Symbol = (Symbol)symbol,
|
||||
Margin = new Thickness(10),
|
||||
FontSize = 40,
|
||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||
};
|
||||
|
||||
SymbolIcon icon = new SymbolIcon { Symbol = (Symbol)symbol, Margin = new Thickness(10) };
|
||||
icon.FontSize = 40;
|
||||
icon.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center;
|
||||
Grid.SetColumn(icon, 0);
|
||||
Grid.SetRowSpan(icon, 2);
|
||||
Grid.SetRow(icon, 0);
|
||||
|
||||
TextBlock primaryLabel = new TextBlock()
|
||||
TextBlock primaryLabel = new()
|
||||
{
|
||||
Text = primaryText,
|
||||
Margin = new Thickness(5),
|
||||
Text = primaryText,
|
||||
Margin = new Thickness(5),
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
MaxWidth = 450
|
||||
MaxWidth = 450
|
||||
};
|
||||
TextBlock secondaryLabel = new TextBlock()
|
||||
|
||||
TextBlock secondaryLabel = new()
|
||||
{
|
||||
Text = secondaryText,
|
||||
Margin = new Thickness(5),
|
||||
Text = secondaryText,
|
||||
Margin = new Thickness(5),
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
MaxWidth = 450
|
||||
MaxWidth = 450
|
||||
};
|
||||
|
||||
Grid.SetColumn(primaryLabel, 1);
|
||||
@@ -262,7 +207,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
string closeButton,
|
||||
string title)
|
||||
{
|
||||
return await ShowContentDialog(
|
||||
return await ShowTextDialog(
|
||||
title,
|
||||
primary,
|
||||
secondaryText,
|
||||
@@ -280,7 +225,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
string title,
|
||||
UserResult primaryButtonResult = UserResult.Yes)
|
||||
{
|
||||
return await ShowContentDialog(
|
||||
return await ShowTextDialog(
|
||||
string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle] : title,
|
||||
primaryText,
|
||||
secondaryText,
|
||||
@@ -298,7 +243,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
|
||||
{
|
||||
await ShowContentDialog(
|
||||
await ShowTextDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle],
|
||||
primary,
|
||||
secondaryText,
|
||||
@@ -310,7 +255,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
internal static async Task CreateWarningDialog(string primary, string secondaryText)
|
||||
{
|
||||
await ShowContentDialog(
|
||||
await ShowTextDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogWarningTitle],
|
||||
primary,
|
||||
secondaryText,
|
||||
@@ -324,7 +269,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||
|
||||
await ShowContentDialog(
|
||||
await ShowTextDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogErrorTitle],
|
||||
LocaleManager.Instance[LocaleKeys.DialogErrorMessage],
|
||||
errorMessage,
|
||||
@@ -343,16 +288,15 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
_isChoiceDialogOpen = true;
|
||||
|
||||
UserResult response =
|
||||
await ShowContentDialog(
|
||||
title,
|
||||
primary,
|
||||
secondaryText,
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
(int)Symbol.Help,
|
||||
UserResult.Yes);
|
||||
UserResult response = await ShowTextDialog(
|
||||
title,
|
||||
primary,
|
||||
secondaryText,
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
(int)Symbol.Help,
|
||||
UserResult.Yes);
|
||||
|
||||
_isChoiceDialogOpen = false;
|
||||
|
||||
@@ -396,5 +340,98 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
|
||||
{
|
||||
ContentDialogResult result;
|
||||
|
||||
ContentDialogOverlayWindow contentDialogOverlayWindow = null;
|
||||
|
||||
Window parent = GetMainWindow();
|
||||
|
||||
if (parent.IsActive && parent is MainWindow window && window.ViewModel.IsGameRunning)
|
||||
{
|
||||
contentDialogOverlayWindow = new()
|
||||
{
|
||||
Height = parent.Bounds.Height,
|
||||
Width = parent.Bounds.Width,
|
||||
Position = parent.PointToScreen(new Point()),
|
||||
ShowInTaskbar = false
|
||||
};
|
||||
|
||||
parent.PositionChanged += OverlayOnPositionChanged;
|
||||
|
||||
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
|
||||
{
|
||||
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
|
||||
}
|
||||
|
||||
contentDialogOverlayWindow.ContentDialog = contentDialog;
|
||||
|
||||
bool opened = false;
|
||||
|
||||
contentDialogOverlayWindow.Opened += OverlayOnActivated;
|
||||
|
||||
async void OverlayOnActivated(object sender, EventArgs e)
|
||||
{
|
||||
if (opened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
opened = true;
|
||||
|
||||
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
|
||||
|
||||
result = await ShowDialog();
|
||||
}
|
||||
|
||||
result = await contentDialogOverlayWindow.ShowDialog<ContentDialogResult>(parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await ShowDialog();
|
||||
}
|
||||
|
||||
async Task<ContentDialogResult> ShowDialog()
|
||||
{
|
||||
if (contentDialogOverlayWindow is not null)
|
||||
{
|
||||
result = await contentDialog.ShowAsync(contentDialogOverlayWindow);
|
||||
|
||||
contentDialogOverlayWindow!.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await contentDialog.ShowAsync();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (contentDialogOverlayWindow is not null)
|
||||
{
|
||||
contentDialogOverlayWindow.Content = null;
|
||||
contentDialogOverlayWindow.Close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Window GetMainWindow()
|
||||
{
|
||||
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
|
||||
{
|
||||
foreach (Window item in al.Windows)
|
||||
{
|
||||
if (item.IsActive && item is MainWindow window)
|
||||
{
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -87,8 +87,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private float _volume;
|
||||
private string _backendText;
|
||||
|
||||
public ApplicationData ListSelectedApplication;
|
||||
public ApplicationData GridSelectedApplication;
|
||||
private bool _canUpdate;
|
||||
private Cursor _cursor;
|
||||
private string _title;
|
||||
@@ -97,6 +95,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private WindowState _windowState;
|
||||
private bool _isActive;
|
||||
|
||||
public ApplicationData ListSelectedApplication;
|
||||
public ApplicationData GridSelectedApplication;
|
||||
|
||||
public event Action ReloadGameList;
|
||||
|
||||
private string TitleName { get; set; }
|
||||
|
@@ -36,10 +36,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
|
||||
private async void StopEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Window.ViewModel.AppHost?.ShowExitPrompt();
|
||||
});
|
||||
await Window.ViewModel.AppHost?.ShowExitPrompt();
|
||||
}
|
||||
|
||||
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
if (this.VisualRoot is MainWindow window)
|
||||
if (VisualRoot is MainWindow window)
|
||||
{
|
||||
Window = window;
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
if (this.VisualRoot is MainWindow window)
|
||||
if (VisualRoot is MainWindow window)
|
||||
{
|
||||
ViewModel = window.ViewModel;
|
||||
}
|
||||
|
@@ -1,253 +1,247 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Windows.AboutWindow"
|
||||
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:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:viewModel="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Width="550"
|
||||
Height="260"
|
||||
Margin="0,-12,0,0"
|
||||
d:DesignHeight="260"
|
||||
d:DesignWidth="550"
|
||||
Height="260"
|
||||
Width="550"
|
||||
x:Class="Ryujinx.Ava.UI.Windows.AboutWindow"
|
||||
x:DataType="viewModel:AboutWindowViewModel"
|
||||
x:CompileBindings="True"
|
||||
Margin="0 -12 0 0"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
<viewModel:AboutWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
x:DataType="viewModel:AboutWindowViewModel"
|
||||
Focusable="True"
|
||||
mc:Ignorable="d">
|
||||
<Design.DataContext>
|
||||
<viewModel:AboutWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Spacing="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image
|
||||
Grid.Column="0"
|
||||
Height="80"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||
<flex:FlexPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Direction="Column"
|
||||
JustifyContent="SpaceAround"
|
||||
RowSpacing="2">
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="Ryujinx"
|
||||
TextAlignment="Left" />
|
||||
<TextBlock
|
||||
Text="(REE-YOU-JINX)"
|
||||
TextAlignment="Left" />
|
||||
</flex:FlexPanel>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Center"
|
||||
FontSize="10"
|
||||
LineHeight="12" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Spacing="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Width="200"
|
||||
Text="{locale:Locale AboutDisclaimerMessage}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
FontSize="10"
|
||||
LineHeight="12" />
|
||||
<TextBlock
|
||||
Name="AmiiboLabel"
|
||||
HorizontalAlignment="Center"
|
||||
Width="200"
|
||||
PointerPressed="AmiiboLabel_OnPointerPressed"
|
||||
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
FontSize="10"
|
||||
LineHeight="12" />
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="10">
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://www.patreon.com/ryujinx"
|
||||
ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
|
||||
<Image Source="{Binding PatreonLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx"
|
||||
ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
|
||||
<Image Source="{Binding GithubLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://discordapp.com/invite/N2FmfVc"
|
||||
ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
|
||||
<Image Source="{Binding DiscordLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://twitter.com/RyujinxEmu"
|
||||
ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
|
||||
<Image Source="{Binding TwitterLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://www.ryujinx.org"
|
||||
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Link"
|
||||
Foreground="{DynamicResource ThemeForegroundColor}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Spacing="10">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image
|
||||
Grid.Column="0"
|
||||
Height="80"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||
<flex:FlexPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Direction="Column"
|
||||
JustifyContent="SpaceAround"
|
||||
RowSpacing="2">
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="Ryujinx"
|
||||
TextAlignment="Left" />
|
||||
<TextBlock Text="(REE-YOU-JINX)" TextAlignment="Left" />
|
||||
</flex:FlexPanel>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="10"
|
||||
LineHeight="12"
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Spacing="10">
|
||||
<TextBlock
|
||||
Width="200"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="10"
|
||||
LineHeight="12"
|
||||
Text="{locale:Locale AboutDisclaimerMessage}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
Name="AmiiboLabel"
|
||||
Width="200"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="10"
|
||||
LineHeight="12"
|
||||
PointerPressed="AmiiboLabel_OnPointerPressed"
|
||||
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="10">
|
||||
<Button
|
||||
MinWidth="30"
|
||||
MinHeight="30"
|
||||
MaxWidth="30"
|
||||
MaxHeight="30"
|
||||
Padding="8"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
CornerRadius="15"
|
||||
Tag="https://www.patreon.com/ryujinx"
|
||||
ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
|
||||
<Image Source="{Binding PatreonLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MinWidth="30"
|
||||
MinHeight="30"
|
||||
MaxWidth="30"
|
||||
MaxHeight="30"
|
||||
Padding="8"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
CornerRadius="15"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx"
|
||||
ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
|
||||
<Image Source="{Binding GithubLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MinWidth="30"
|
||||
MinHeight="30"
|
||||
MaxWidth="30"
|
||||
MaxHeight="30"
|
||||
Padding="8"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
CornerRadius="15"
|
||||
Tag="https://discordapp.com/invite/N2FmfVc"
|
||||
ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
|
||||
<Image Source="{Binding DiscordLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MinWidth="30"
|
||||
MinHeight="30"
|
||||
MaxWidth="30"
|
||||
MaxHeight="30"
|
||||
Padding="8"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
CornerRadius="15"
|
||||
Tag="https://twitter.com/RyujinxEmu"
|
||||
ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
|
||||
<Image Source="{Binding TwitterLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MinWidth="30"
|
||||
MinHeight="30"
|
||||
MaxWidth="30"
|
||||
MaxHeight="30"
|
||||
Padding="8"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
CornerRadius="15"
|
||||
Tag="https://www.ryujinx.org"
|
||||
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}">
|
||||
<ui:SymbolIcon Foreground="{DynamicResource ThemeForegroundColor}" Symbol="Link" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Width="1"
|
||||
Margin="20,0"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1,0,0,0"
|
||||
Margin="20 0"/>
|
||||
<Grid
|
||||
BorderThickness="1,0,0,0" />
|
||||
<Grid
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Margin="0 10 0 0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
FontSize="15"
|
||||
Text="{locale:Locale AboutRyujinxAboutTitle}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
TextWrapping="Wrap"
|
||||
Text="{locale:Locale AboutRyujinxAboutContent}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0 10 0 0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
FontSize="15"
|
||||
Text="{locale:Locale AboutRyujinxMaintainersTitle}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Developers}" />
|
||||
<Button
|
||||
HorizontalAlignment="Left"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Padding="5"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a">
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
|
||||
TextAlignment="Right"
|
||||
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Margin="0 10 0 0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
FontSize="15"
|
||||
Text="{locale:Locale AboutRyujinxSupprtersTitle}" />
|
||||
<ScrollViewer
|
||||
VerticalScrollBarVisibility="Visible"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
Height="70">
|
||||
<TextBlock
|
||||
Name="SupportersTextBlock"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="10"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Supporters}" />
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Margin="0,10,0,0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontSize="15"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale AboutRyujinxAboutTitle}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Text="{locale:Locale AboutRyujinxAboutContent}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontSize="15"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale AboutRyujinxMaintainersTitle}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Text="{Binding Developers}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
Padding="5"
|
||||
HorizontalAlignment="Left"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a">
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
|
||||
TextAlignment="Right"
|
||||
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Margin="0,10,0,0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontSize="15"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale AboutRyujinxSupprtersTitle}" />
|
||||
<ScrollViewer
|
||||
Height="70"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Visible">
|
||||
<TextBlock
|
||||
Name="SupportersTextBlock"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="10"
|
||||
Text="{Binding Supporters}"
|
||||
TextWrapping="Wrap" />
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -4,6 +4,7 @@ using Avalonia.Interactivity;
|
||||
using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using System.Threading.Tasks;
|
||||
@@ -22,14 +23,12 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public static async Task Show()
|
||||
{
|
||||
var content = new AboutWindow();
|
||||
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
PrimaryButtonText = "",
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
|
||||
Content = content
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
|
||||
Content = new AboutWindow()
|
||||
};
|
||||
|
||||
Style closeButton = new(x => x.Name("CloseButton"));
|
||||
@@ -41,7 +40,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
contentDialog.Styles.Add(closeButton);
|
||||
contentDialog.Styles.Add(closeButtonParent);
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||
}
|
||||
|
||||
private void Button_OnClick(object sender, RoutedEventArgs e)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<UserControl
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@@ -17,9 +17,9 @@
|
||||
<Design.DataContext>
|
||||
<viewModels:AvatarProfileViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
|
@@ -7,9 +7,9 @@ namespace Ryujinx.Common.Logging
|
||||
AudioRenderer,
|
||||
Configuration,
|
||||
Cpu,
|
||||
Font,
|
||||
Emulation,
|
||||
FFmpeg,
|
||||
Font,
|
||||
Gpu,
|
||||
Hid,
|
||||
Host1x,
|
||||
@@ -66,6 +66,7 @@ namespace Ryujinx.Common.Logging
|
||||
ServiceVi,
|
||||
SurfaceFlinger,
|
||||
TamperMachine,
|
||||
Ui,
|
||||
Vic
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
9
Ryujinx.Headless.SDL2/HideCursor.cs
Normal file
9
Ryujinx.Headless.SDL2/HideCursor.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
public enum HideCursor
|
||||
{
|
||||
Never,
|
||||
OnIdle,
|
||||
Always
|
||||
}
|
||||
}
|
@@ -5,7 +5,6 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
using Ryujinx.Input.HLE;
|
||||
using System;
|
||||
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Headless.SDL2.OpenGL
|
||||
@@ -103,7 +102,13 @@ namespace Ryujinx.Headless.SDL2.OpenGL
|
||||
private GraphicsDebugLevel _glLogLevel;
|
||||
private SDL2OpenGLContext _openGLContext;
|
||||
|
||||
public OpenGLWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse)
|
||||
public OpenGLWindow(
|
||||
InputManager inputManager,
|
||||
GraphicsDebugLevel glLogLevel,
|
||||
AspectRatio aspectRatio,
|
||||
bool enableMouse,
|
||||
HideCursor hideCursor)
|
||||
: base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor)
|
||||
{
|
||||
_glLogLevel = glLogLevel;
|
||||
}
|
||||
@@ -161,4 +166,4 @@ namespace Ryujinx.Headless.SDL2.OpenGL
|
||||
SDL_GL_SwapWindow(WindowHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,6 +6,14 @@ namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
public class Options
|
||||
{
|
||||
// General
|
||||
|
||||
[Option("root-data-dir", Required = false, HelpText = "Set the custom folder path for Ryujinx data.")]
|
||||
public string BaseDataDir { get; set; }
|
||||
|
||||
[Option("profile", Required = false, HelpText = "Set the user profile to launch the game with.")]
|
||||
public string UserProfile { get; set; }
|
||||
|
||||
// Input
|
||||
|
||||
[Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")]
|
||||
@@ -23,7 +31,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
[Option("input-profile-5", Required = false, HelpText = "Set the input profile in use for Player 5.")]
|
||||
public string InputProfile5Name { get; set; }
|
||||
|
||||
[Option("input-profile-6", Required = false, HelpText = "Set the input profile in use for Player 5.")]
|
||||
[Option("input-profile-6", Required = false, HelpText = "Set the input profile in use for Player 6.")]
|
||||
public string InputProfile6Name { get; set; }
|
||||
|
||||
[Option("input-profile-7", Required = false, HelpText = "Set the input profile in use for Player 7.")]
|
||||
@@ -63,42 +71,45 @@ namespace Ryujinx.Headless.SDL2
|
||||
public string InputIdHandheld { get; set; }
|
||||
|
||||
[Option("enable-keyboard", Required = false, Default = false, HelpText = "Enable or disable keyboard support (Independent from controllers binding).")]
|
||||
public bool? EnableKeyboard { get; set; }
|
||||
public bool EnableKeyboard { get; set; }
|
||||
|
||||
[Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")]
|
||||
public bool? EnableMouse { get; set; }
|
||||
public bool EnableMouse { get; set; }
|
||||
|
||||
[Option("hide-cursor", Required = false, Default = HideCursor.OnIdle, HelpText = "Change when the cursor gets hidden.")]
|
||||
public HideCursor HideCursor { get; set; }
|
||||
|
||||
[Option("list-input-profiles", Required = false, HelpText = "List inputs profiles.")]
|
||||
public bool? ListInputProfiles { get; set; }
|
||||
public bool ListInputProfiles { get; set; }
|
||||
|
||||
[Option("list-inputs-ids", Required = false, HelpText = "List inputs ids.")]
|
||||
public bool ListInputIds { get; set; }
|
||||
|
||||
// System
|
||||
|
||||
[Option("enable-ptc", Required = false, Default = true, HelpText = "Enables profiled translation cache persistency.")]
|
||||
public bool? EnablePtc { get; set; }
|
||||
[Option("disable-ptc", Required = false, HelpText = "Disables profiled persistent translation cache.")]
|
||||
public bool DisablePtc { get; set; }
|
||||
|
||||
[Option("enable-internet-connection", Required = false, Default = false, HelpText = "Enables guest Internet connection.")]
|
||||
public bool? EnableInternetAccess { get; set; }
|
||||
public bool EnableInternetAccess { get; set; }
|
||||
|
||||
[Option("enable-fs-integrity-checks", Required = false, Default = true, HelpText = "Enables integrity checks on Game content files.")]
|
||||
public bool? EnableFsIntegrityChecks { get; set; }
|
||||
[Option("disable-fs-integrity-checks", Required = false, HelpText = "Disables integrity checks on Game content files.")]
|
||||
public bool DisableFsIntegrityChecks { get; set; }
|
||||
|
||||
[Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")]
|
||||
public int FsGlobalAccessLogMode { get; set; }
|
||||
|
||||
[Option("enable-vsync", Required = false, Default = true, HelpText = "Enables Vertical Sync.")]
|
||||
public bool? EnableVsync { get; set; }
|
||||
[Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync.")]
|
||||
public bool DisableVsync { get; set; }
|
||||
|
||||
[Option("enable-shader-cache", Required = false, Default = true, HelpText = "Enables Shader cache.")]
|
||||
public bool? EnableShaderCache { get; set; }
|
||||
[Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")]
|
||||
public bool DisableShaderCache { get; set; }
|
||||
|
||||
[Option("enable-texture-recompression", Required = false, Default = false, HelpText = "Enables Texture recompression.")]
|
||||
public bool? EnableTextureRecompression { get; set; }
|
||||
public bool EnableTextureRecompression { get; set; }
|
||||
|
||||
[Option("enable-docked-mode", Required = false, Default = true, HelpText = "Enables Docked Mode.")]
|
||||
public bool? EnableDockedMode { get; set; }
|
||||
[Option("disable-docked-mode", Required = false, HelpText = "Disables Docked Mode.")]
|
||||
public bool DisableDockedMode { get; set; }
|
||||
|
||||
[Option("system-language", Required = false, Default = SystemLanguage.AmericanEnglish, HelpText = "Change System Language.")]
|
||||
public SystemLanguage SystemLanguage { get; set; }
|
||||
@@ -120,32 +131,32 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
// Logging
|
||||
|
||||
[Option("enable-file-logging", Required = false, Default = false, HelpText = "Enables logging to a file on disk.")]
|
||||
public bool? EnableFileLog { get; set; }
|
||||
[Option("disable-file-logging", Required = false, Default = false, HelpText = "Disables logging to a file on disk.")]
|
||||
public bool DisableFileLog { get; set; }
|
||||
|
||||
[Option("enable-debug-logs", Required = false, Default = false, HelpText = "Enables printing debug log messages.")]
|
||||
public bool? LoggingEnableDebug { get; set; }
|
||||
public bool LoggingEnableDebug { get; set; }
|
||||
|
||||
[Option("enable-stub-logs", Required = false, Default = true, HelpText = "Enables printing stub log messages.")]
|
||||
public bool? LoggingEnableStub { get; set; }
|
||||
[Option("disable-stub-logs", Required = false, HelpText = "Disables printing stub log messages.")]
|
||||
public bool LoggingDisableStub { get; set; }
|
||||
|
||||
[Option("enable-info-logs", Required = false, Default = true, HelpText = "Enables printing info log messages.")]
|
||||
public bool? LoggingEnableInfo { get; set; }
|
||||
[Option("disable-info-logs", Required = false, HelpText = "Disables printing info log messages.")]
|
||||
public bool LoggingDisableInfo { get; set; }
|
||||
|
||||
[Option("enable-warning-logs", Required = false, Default = true, HelpText = "Enables printing warning log messages.")]
|
||||
public bool? LoggingEnableWarning { get; set; }
|
||||
[Option("disable-warning-logs", Required = false, HelpText = "Disables printing warning log messages.")]
|
||||
public bool LoggingDisableWarning { get; set; }
|
||||
|
||||
[Option("enable-error-logs", Required = false, Default = true, HelpText = "Enables printing error log messages.")]
|
||||
public bool? LoggingEnableError { get; set; }
|
||||
[Option("disable-error-logs", Required = false, HelpText = "Disables printing error log messages.")]
|
||||
public bool LoggingEnableError { get; set; }
|
||||
|
||||
[Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")]
|
||||
public bool? LoggingEnableTrace { get; set; }
|
||||
public bool LoggingEnableTrace { get; set; }
|
||||
|
||||
[Option("enable-guest-logs", Required = false, Default = true, HelpText = "Enables printing guest log messages.")]
|
||||
public bool? LoggingEnableGuest { get; set; }
|
||||
[Option("disable-guest-logs", Required = false, HelpText = "Disables printing guest log messages.")]
|
||||
public bool LoggingDisableGuest { get; set; }
|
||||
|
||||
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")]
|
||||
public bool? LoggingEnableFsAccessLog { get; set; }
|
||||
public bool LoggingEnableFsAccessLog { get; set; }
|
||||
|
||||
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")]
|
||||
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
|
||||
@@ -164,6 +175,9 @@ namespace Ryujinx.Headless.SDL2
|
||||
[Option("backend-threading", Required = false, Default = BackendThreading.Auto, HelpText = "Whether or not backend threading is enabled. The \"Auto\" setting will determine whether threading should be enabled at runtime.")]
|
||||
public BackendThreading BackendThreading { get; set; }
|
||||
|
||||
[Option("disable-macro-hle", Required= false, HelpText = "Disables high-level emulation of Macro code. Leaving this enabled improves performance but may cause graphical glitches in some games.")]
|
||||
public bool DisableMacroHLE { get; set; }
|
||||
|
||||
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")]
|
||||
public string GraphicsShadersDumpPath { get; set; }
|
||||
|
||||
@@ -176,14 +190,14 @@ namespace Ryujinx.Headless.SDL2
|
||||
// Hacks
|
||||
|
||||
[Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")]
|
||||
public bool? ExpandRam { get; set; }
|
||||
public bool ExpandRam { get; set; }
|
||||
|
||||
[Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")]
|
||||
public bool? IgnoreMissingServices { get; set; }
|
||||
public bool IgnoreMissingServices { get; set; }
|
||||
|
||||
// Values
|
||||
|
||||
[Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)]
|
||||
public string InputPath { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -33,7 +33,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
|
||||
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
|
||||
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
|
||||
using Key = Ryujinx.Common.Configuration.Hid.Key;
|
||||
@@ -63,20 +62,6 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";
|
||||
|
||||
AppDataManager.Initialize(null);
|
||||
|
||||
_virtualFileSystem = VirtualFileSystem.CreateInstance();
|
||||
_libHacHorizonManager = new LibHacHorizonManager();
|
||||
|
||||
_libHacHorizonManager.InitializeFsServer(_virtualFileSystem);
|
||||
_libHacHorizonManager.InitializeArpServer();
|
||||
_libHacHorizonManager.InitializeBcatServer();
|
||||
_libHacHorizonManager.InitializeSystemClients();
|
||||
|
||||
_contentManager = new ContentManager(_virtualFileSystem);
|
||||
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient);
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
|
||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
|
||||
{
|
||||
AutoResetEvent invoked = new AutoResetEvent(false);
|
||||
@@ -97,15 +82,9 @@ namespace Ryujinx.Headless.SDL2
|
||||
};
|
||||
}
|
||||
|
||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||
|
||||
GraphicsConfig.EnableShaderCache = true;
|
||||
|
||||
Parser.Default.ParseArguments<Options>(args)
|
||||
.WithParsed(options => Load(options))
|
||||
.WithParsed(Load)
|
||||
.WithNotParsed(errors => errors.Output());
|
||||
|
||||
_inputManager.Dispose();
|
||||
}
|
||||
|
||||
private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
||||
@@ -343,6 +322,24 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
static void Load(Options option)
|
||||
{
|
||||
AppDataManager.Initialize(option.BaseDataDir);
|
||||
|
||||
_virtualFileSystem = VirtualFileSystem.CreateInstance();
|
||||
_libHacHorizonManager = new LibHacHorizonManager();
|
||||
|
||||
_libHacHorizonManager.InitializeFsServer(_virtualFileSystem);
|
||||
_libHacHorizonManager.InitializeArpServer();
|
||||
_libHacHorizonManager.InitializeBcatServer();
|
||||
_libHacHorizonManager.InitializeSystemClients();
|
||||
|
||||
_contentManager = new ContentManager(_virtualFileSystem);
|
||||
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile);
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
|
||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||
|
||||
GraphicsConfig.EnableShaderCache = true;
|
||||
|
||||
IGamepad gamepad;
|
||||
|
||||
if (option.ListInputIds)
|
||||
@@ -378,8 +375,8 @@ namespace Ryujinx.Headless.SDL2
|
||||
}
|
||||
|
||||
_inputConfiguration = new List<InputConfig>();
|
||||
_enableKeyboard = (bool)option.EnableKeyboard;
|
||||
_enableMouse = (bool)option.EnableMouse;
|
||||
_enableKeyboard = option.EnableKeyboard;
|
||||
_enableMouse = option.EnableMouse;
|
||||
|
||||
void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
||||
{
|
||||
@@ -407,16 +404,16 @@ namespace Ryujinx.Headless.SDL2
|
||||
}
|
||||
|
||||
// Setup logging level
|
||||
Logger.SetEnable(LogLevel.Debug, (bool)option.LoggingEnableDebug);
|
||||
Logger.SetEnable(LogLevel.Stub, (bool)option.LoggingEnableStub);
|
||||
Logger.SetEnable(LogLevel.Info, (bool)option.LoggingEnableInfo);
|
||||
Logger.SetEnable(LogLevel.Warning, (bool)option.LoggingEnableWarning);
|
||||
Logger.SetEnable(LogLevel.Error, (bool)option.LoggingEnableError);
|
||||
Logger.SetEnable(LogLevel.Trace, (bool)option.LoggingEnableTrace);
|
||||
Logger.SetEnable(LogLevel.Guest, (bool)option.LoggingEnableGuest);
|
||||
Logger.SetEnable(LogLevel.AccessLog, (bool)option.LoggingEnableFsAccessLog);
|
||||
Logger.SetEnable(LogLevel.Debug, option.LoggingEnableDebug);
|
||||
Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub);
|
||||
Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo);
|
||||
Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning);
|
||||
Logger.SetEnable(LogLevel.Error, option.LoggingEnableError);
|
||||
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
|
||||
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
|
||||
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
||||
|
||||
if ((bool)option.EnableFileLog)
|
||||
if (!option.DisableFileLog)
|
||||
{
|
||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||
new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"),
|
||||
@@ -426,11 +423,12 @@ namespace Ryujinx.Headless.SDL2
|
||||
}
|
||||
|
||||
// Setup graphics configuration
|
||||
GraphicsConfig.EnableShaderCache = (bool)option.EnableShaderCache;
|
||||
GraphicsConfig.EnableTextureRecompression = (bool)option.EnableTextureRecompression;
|
||||
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
||||
GraphicsConfig.EnableTextureRecompression = option.EnableTextureRecompression;
|
||||
GraphicsConfig.ResScale = option.ResScale;
|
||||
GraphicsConfig.MaxAnisotropy = option.MaxAnisotropy;
|
||||
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
|
||||
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -443,6 +441,8 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
_userChannelPersistence.ShouldRestart = false;
|
||||
}
|
||||
|
||||
_inputManager.Dispose();
|
||||
}
|
||||
|
||||
private static void SetupProgressHandler()
|
||||
@@ -479,8 +479,8 @@ namespace Ryujinx.Headless.SDL2
|
||||
private static WindowBase CreateWindow(Options options)
|
||||
{
|
||||
return options.GraphicsBackend == GraphicsBackend.Vulkan
|
||||
? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse)
|
||||
: new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse);
|
||||
? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor)
|
||||
: new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor);
|
||||
}
|
||||
|
||||
private static IRenderer CreateRenderer(Options options, WindowBase window)
|
||||
@@ -533,20 +533,20 @@ namespace Ryujinx.Headless.SDL2
|
||||
_userChannelPersistence,
|
||||
renderer,
|
||||
new SDL2HardwareDeviceDriver(),
|
||||
(bool)options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB,
|
||||
options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB,
|
||||
window,
|
||||
options.SystemLanguage,
|
||||
options.SystemRegion,
|
||||
(bool)options.EnableVsync,
|
||||
(bool)options.EnableDockedMode,
|
||||
(bool)options.EnablePtc,
|
||||
(bool)options.EnableInternetAccess,
|
||||
(bool)options.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||
!options.DisableVsync,
|
||||
!options.DisableDockedMode,
|
||||
!options.DisablePtc,
|
||||
options.EnableInternetAccess,
|
||||
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||
options.FsGlobalAccessLogMode,
|
||||
options.SystemTimeOffset,
|
||||
options.SystemTimeZone,
|
||||
options.MemoryManagerMode,
|
||||
(bool)options.IgnoreMissingServices,
|
||||
options.IgnoreMissingServices,
|
||||
options.AspectRatio,
|
||||
options.AudioVolume);
|
||||
|
||||
@@ -649,7 +649,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
||||
Logger.Warning?.Print(LogClass.Application, $"Couldn't load '{options.InputPath}'. Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
||||
|
||||
_emulationContext.Dispose();
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
@@ -31,16 +31,26 @@
|
||||
<PackageReference Include="CommandLineParser" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\distribution\legal\THIRDPARTY.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>THIRDPARTY.md</TargetPath>
|
||||
</Content>
|
||||
<Content Include="..\LICENSE.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>LICENSE.txt</TargetPath>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="..\distribution\legal\THIRDPARTY.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>THIRDPARTY.md</TargetPath>
|
||||
</Content>
|
||||
<Content Include="..\LICENSE.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>LICENSE.txt</TargetPath>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
|
||||
<Content Include="..\distribution\linux\Ryujinx.sh">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Ryujinx.bmp" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Due to .net core 3.1 embedded resource loading -->
|
||||
<PropertyGroup>
|
||||
|
BIN
Ryujinx.Headless.SDL2/Ryujinx.bmp
Normal file
BIN
Ryujinx.Headless.SDL2/Ryujinx.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.1 KiB |
@@ -10,7 +10,12 @@ namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
class SDL2MouseDriver : IGamepadDriver
|
||||
{
|
||||
private const int CursorHideIdleTime = 8; // seconds
|
||||
|
||||
private bool _isDisposed;
|
||||
private HideCursor _hideCursor;
|
||||
private bool _isHidden;
|
||||
private long _lastCursorMoveTime;
|
||||
|
||||
public bool[] PressedButtons { get; }
|
||||
|
||||
@@ -18,9 +23,16 @@ namespace Ryujinx.Headless.SDL2
|
||||
public Vector2 Scroll { get; private set; }
|
||||
public Size _clientSize;
|
||||
|
||||
public SDL2MouseDriver()
|
||||
public SDL2MouseDriver(HideCursor hideCursor)
|
||||
{
|
||||
PressedButtons = new bool[(int)MouseButton.Count];
|
||||
_hideCursor = hideCursor;
|
||||
|
||||
if (_hideCursor == HideCursor.Always)
|
||||
{
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
_isHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -31,26 +43,75 @@ namespace Ryujinx.Headless.SDL2
|
||||
return (MouseButton)(rawButton - 1);
|
||||
}
|
||||
|
||||
public void Update(SDL_Event evnt)
|
||||
public void UpdatePosition()
|
||||
{
|
||||
if (evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN || evnt.type == SDL_EventType.SDL_MOUSEBUTTONUP)
|
||||
SDL_GetMouseState(out int posX, out int posY);
|
||||
Vector2 position = new(posX, posY);
|
||||
|
||||
if (CurrentPosition != position)
|
||||
{
|
||||
uint rawButton = evnt.button.button;
|
||||
CurrentPosition = position;
|
||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||
}
|
||||
|
||||
if (rawButton > 0 && rawButton <= (int)MouseButton.Count)
|
||||
CheckIdle();
|
||||
}
|
||||
|
||||
private void CheckIdle()
|
||||
{
|
||||
if (_hideCursor != HideCursor.OnIdle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime;
|
||||
|
||||
if (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency)
|
||||
{
|
||||
if (!_isHidden)
|
||||
{
|
||||
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;
|
||||
|
||||
CurrentPosition = new Vector2(evnt.button.x, evnt.button.y);
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
_isHidden = true;
|
||||
}
|
||||
}
|
||||
else if (evnt.type == SDL_EventType.SDL_MOUSEMOTION)
|
||||
else
|
||||
{
|
||||
CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y);
|
||||
if (_isHidden)
|
||||
{
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
_isHidden = false;
|
||||
}
|
||||
}
|
||||
else if (evnt.type == SDL_EventType.SDL_MOUSEWHEEL)
|
||||
}
|
||||
|
||||
public void Update(SDL_Event evnt)
|
||||
{
|
||||
switch (evnt.type)
|
||||
{
|
||||
Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y);
|
||||
case SDL_EventType.SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_EventType.SDL_MOUSEBUTTONUP:
|
||||
uint rawButton = evnt.button.button;
|
||||
|
||||
if (rawButton > 0 && rawButton <= (int)MouseButton.Count)
|
||||
{
|
||||
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;
|
||||
|
||||
CurrentPosition = new Vector2(evnt.button.x, evnt.button.y);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// NOTE: On Linux using Wayland mouse motion events won't be received at all.
|
||||
case SDL_EventType.SDL_MOUSEMOTION:
|
||||
CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y);
|
||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||
|
||||
break;
|
||||
|
||||
case SDL_EventType.SDL_MOUSEWHEEL:
|
||||
Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,4 +161,4 @@ namespace Ryujinx.Headless.SDL2
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -12,7 +12,13 @@ namespace Ryujinx.Headless.SDL2.Vulkan
|
||||
{
|
||||
private GraphicsDebugLevel _glLogLevel;
|
||||
|
||||
public VulkanWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse)
|
||||
public VulkanWindow(
|
||||
InputManager inputManager,
|
||||
GraphicsDebugLevel glLogLevel,
|
||||
AspectRatio aspectRatio,
|
||||
bool enableMouse,
|
||||
HideCursor hideCursor)
|
||||
: base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor)
|
||||
{
|
||||
_glLogLevel = glLogLevel;
|
||||
}
|
||||
@@ -95,4 +101,4 @@ namespace Ryujinx.Headless.SDL2.Vulkan
|
||||
|
||||
protected override void SwapBuffers() { }
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,13 +14,16 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using Switch = Ryujinx.HLE.Switch;
|
||||
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
abstract class WindowBase : IHostUiHandler, IDisposable
|
||||
abstract partial class WindowBase : IHostUiHandler, IDisposable
|
||||
{
|
||||
protected const int DefaultWidth = 1280;
|
||||
protected const int DefaultHeight = 720;
|
||||
@@ -29,6 +32,10 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
private static ConcurrentQueue<Action> MainThreadActions = new ConcurrentQueue<Action>();
|
||||
|
||||
[LibraryImport("SDL2")]
|
||||
// TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly
|
||||
private static partial IntPtr SDL_LoadBMP_RW(IntPtr src, int freesrc);
|
||||
|
||||
public static void QueueMainThreadAction(Action action)
|
||||
{
|
||||
MainThreadActions.Enqueue(action);
|
||||
@@ -66,9 +73,14 @@ namespace Ryujinx.Headless.SDL2
|
||||
private AspectRatio _aspectRatio;
|
||||
private bool _enableMouse;
|
||||
|
||||
public WindowBase(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse)
|
||||
public WindowBase(
|
||||
InputManager inputManager,
|
||||
GraphicsDebugLevel glLogLevel,
|
||||
AspectRatio aspectRatio,
|
||||
bool enableMouse,
|
||||
HideCursor hideCursor)
|
||||
{
|
||||
MouseDriver = new SDL2MouseDriver();
|
||||
MouseDriver = new SDL2MouseDriver(hideCursor);
|
||||
_inputManager = inputManager;
|
||||
_inputManager.SetMouseDriver(MouseDriver);
|
||||
NpadManager = _inputManager.CreateNpadManager();
|
||||
@@ -103,6 +115,34 @@ namespace Ryujinx.Headless.SDL2
|
||||
TouchScreenManager.Initialize(device);
|
||||
}
|
||||
|
||||
private void SetWindowIcon()
|
||||
{
|
||||
Stream iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.Headless.SDL2.Ryujinx.bmp");
|
||||
byte[] iconBytes = new byte[iconStream!.Length];
|
||||
|
||||
if (iconStream.Read(iconBytes, 0, iconBytes.Length) != iconBytes.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to read icon to byte array.");
|
||||
iconStream.Close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
iconStream.Close();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* iconPtr = iconBytes)
|
||||
{
|
||||
IntPtr rwOpsStruct = SDL_RWFromConstMem((IntPtr)iconPtr, iconBytes.Length);
|
||||
IntPtr iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1);
|
||||
|
||||
SDL_SetWindowIcon(WindowHandle, iconHandle);
|
||||
SDL_FreeSurface(iconHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeWindow()
|
||||
{
|
||||
string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty
|
||||
@@ -127,6 +167,8 @@ namespace Ryujinx.Headless.SDL2
|
||||
throw new Exception(errorMessage);
|
||||
}
|
||||
|
||||
SetWindowIcon();
|
||||
|
||||
_windowId = SDL_GetWindowID(WindowHandle);
|
||||
SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
|
||||
|
||||
@@ -146,9 +188,11 @@ namespace Ryujinx.Headless.SDL2
|
||||
Renderer?.Window.SetSize(Width, Height);
|
||||
MouseDriver.SetClientSize(Width, Height);
|
||||
break;
|
||||
|
||||
case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE:
|
||||
Exit();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -331,6 +375,9 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
Device.Hid.DebugPad.Update();
|
||||
|
||||
// TODO: Replace this with MouseDriver.CheckIdle() when mouse motion events are received on every supported platform.
|
||||
MouseDriver.UpdatePosition();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -451,4 +498,4 @@ namespace Ryujinx.Headless.SDL2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -163,7 +163,7 @@ namespace Ryujinx.Input.HLE
|
||||
ReloadConfiguration(inputConfig, enableKeyboard, enableMouse);
|
||||
}
|
||||
|
||||
public void Update(float aspectRatio = 0)
|
||||
public void Update(float aspectRatio = 1)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
|
@@ -1,5 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
SCRIPT_DIR=$(dirname $(realpath $0))
|
||||
RYUJINX_BIN="Ryujinx"
|
||||
|
||||
env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/Ryujinx" "$@"
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Ava"
|
||||
fi
|
||||
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||
fi
|
||||
|
||||
env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
||||
|
Reference in New Issue
Block a user