Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3a4eeb77fe | ||
|
51b3953cfc | ||
|
610eecc1c1 | ||
|
492056abf6 | ||
|
ee6e682ab4 | ||
|
6f60e102a2 | ||
|
eeb2af9953 | ||
|
550747eac6 | ||
|
3ffceab1fb | ||
|
b9f2a96595 | ||
|
cbaa845f5d | ||
|
9e2681f2d7 | ||
|
81fae0d1a6 | ||
|
38519f3b9a | ||
|
7f27aabbd0 | ||
|
e876c43ce9 | ||
|
8639245533 | ||
|
d6b86a6629 | ||
|
8f2b7b5b8e | ||
|
fc4b7cba2c | ||
|
08831eecf7 |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -69,6 +69,12 @@ 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:
|
||||
|
29
.github/workflows/release.yml
vendored
29
.github/workflows/release.yml
vendored
@@ -38,19 +38,21 @@ jobs:
|
||||
shell: bash
|
||||
- name: Configure for release
|
||||
run: |
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformations.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformations.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformations.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformations.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformations.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
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
|
||||
@@ -71,19 +73,28 @@ 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 -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
tar --exclude "publish/Ryujinx" -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"
|
||||
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 -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
tar --exclude "publish/Ryujinx.Headless.SDL2" -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"
|
||||
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 -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
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"
|
||||
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
|
||||
shell: bash
|
||||
|
||||
|
@@ -6,7 +6,6 @@ using ARMeilleure.Instructions;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
@@ -44,14 +43,13 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public IMemoryManager Memory { get; }
|
||||
|
||||
public bool HasPtc { get; }
|
||||
|
||||
public EntryTable<uint> CountTable { get; }
|
||||
public AddressTable<ulong> FunctionTable { get; }
|
||||
public TranslatorStubs Stubs { get; }
|
||||
|
||||
public ulong EntryAddress { get; }
|
||||
public bool HighCq { get; }
|
||||
public bool HasPtc { get; }
|
||||
public Aarch32Mode Mode { get; }
|
||||
|
||||
private int _ifThenBlockStateIndex = 0;
|
||||
@@ -66,15 +64,16 @@ namespace ARMeilleure.Translation
|
||||
TranslatorStubs stubs,
|
||||
ulong entryAddress,
|
||||
bool highCq,
|
||||
bool hasPtc,
|
||||
Aarch32Mode mode)
|
||||
{
|
||||
HasPtc = Ptc.State != PtcState.Disabled;
|
||||
Memory = memory;
|
||||
CountTable = countTable;
|
||||
FunctionTable = funcTable;
|
||||
Stubs = stubs;
|
||||
EntryAddress = entryAddress;
|
||||
HighCq = highCq;
|
||||
HasPtc = hasPtc;
|
||||
Mode = mode;
|
||||
|
||||
_labels = new Dictionary<ulong, Operand>();
|
||||
|
10
ARMeilleure/Translation/PTC/IPtcLoadState.cs
Normal file
10
ARMeilleure/Translation/PTC/IPtcLoadState.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
public interface IPtcLoadState
|
||||
{
|
||||
event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||
void Continue();
|
||||
}
|
||||
}
|
@@ -22,7 +22,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
public static class Ptc
|
||||
class Ptc : IPtcLoadState
|
||||
{
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
@@ -35,45 +35,49 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string TitleIdTextDefault = "0000000000000000";
|
||||
private const string DisplayVersionDefault = "0";
|
||||
|
||||
internal static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||
internal static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||
internal static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||
|
||||
private const byte FillingByte = 0x00;
|
||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||
|
||||
public PtcProfiler Profiler { get; }
|
||||
|
||||
// Carriers.
|
||||
private static MemoryStream _infosStream;
|
||||
private static List<byte[]> _codesList;
|
||||
private static MemoryStream _relocsStream;
|
||||
private static MemoryStream _unwindInfosStream;
|
||||
private MemoryStream _infosStream;
|
||||
private List<byte[]> _codesList;
|
||||
private MemoryStream _relocsStream;
|
||||
private MemoryStream _unwindInfosStream;
|
||||
|
||||
private static readonly ulong _outerHeaderMagic;
|
||||
private static readonly ulong _innerHeaderMagic;
|
||||
private readonly ulong _outerHeaderMagic;
|
||||
private readonly ulong _innerHeaderMagic;
|
||||
|
||||
private static readonly ManualResetEvent _waitEvent;
|
||||
private readonly ManualResetEvent _waitEvent;
|
||||
|
||||
private static readonly object _lock;
|
||||
private readonly object _lock;
|
||||
|
||||
private static bool _disposed;
|
||||
private bool _disposed;
|
||||
|
||||
internal static string TitleIdText { get; private set; }
|
||||
internal static string DisplayVersion { get; private set; }
|
||||
public string TitleIdText { get; private set; }
|
||||
public string DisplayVersion { get; private set; }
|
||||
|
||||
private static MemoryManagerMode _memoryMode;
|
||||
private MemoryManagerType _memoryMode;
|
||||
|
||||
internal static string CachePathActual { get; private set; }
|
||||
internal static string CachePathBackup { get; private set; }
|
||||
public string CachePathActual { get; private set; }
|
||||
public string CachePathBackup { get; private set; }
|
||||
|
||||
internal static PtcState State { get; private set; }
|
||||
public PtcState State { get; private set; }
|
||||
|
||||
// Progress reporting helpers.
|
||||
private static volatile int _translateCount;
|
||||
private static volatile int _translateTotalCount;
|
||||
public static event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||
private volatile int _translateCount;
|
||||
private volatile int _translateTotalCount;
|
||||
public event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||
|
||||
static Ptc()
|
||||
public Ptc()
|
||||
{
|
||||
Profiler = new PtcProfiler(this);
|
||||
|
||||
InitializeCarriers();
|
||||
|
||||
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
||||
@@ -94,12 +98,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
Disable();
|
||||
}
|
||||
|
||||
public static void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerMode memoryMode)
|
||||
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode)
|
||||
{
|
||||
Wait();
|
||||
|
||||
PtcProfiler.Wait();
|
||||
PtcProfiler.ClearEntries();
|
||||
Profiler.Wait();
|
||||
Profiler.ClearEntries();
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
|
||||
|
||||
@@ -137,12 +141,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
|
||||
|
||||
PreLoad();
|
||||
PtcProfiler.PreLoad();
|
||||
Profiler.PreLoad();
|
||||
|
||||
Enable();
|
||||
}
|
||||
|
||||
private static void InitializeCarriers()
|
||||
private void InitializeCarriers()
|
||||
{
|
||||
_infosStream = new MemoryStream();
|
||||
_codesList = new List<byte[]>();
|
||||
@@ -150,7 +154,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
_unwindInfosStream = new MemoryStream();
|
||||
}
|
||||
|
||||
private static void DisposeCarriers()
|
||||
private void DisposeCarriers()
|
||||
{
|
||||
_infosStream.Dispose();
|
||||
_codesList.Clear();
|
||||
@@ -158,12 +162,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
_unwindInfosStream.Dispose();
|
||||
}
|
||||
|
||||
private static bool AreCarriersEmpty()
|
||||
private bool AreCarriersEmpty()
|
||||
{
|
||||
return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L;
|
||||
}
|
||||
|
||||
private static void ResetCarriersIfNeeded()
|
||||
private void ResetCarriersIfNeeded()
|
||||
{
|
||||
if (AreCarriersEmpty())
|
||||
{
|
||||
@@ -175,7 +179,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
InitializeCarriers();
|
||||
}
|
||||
|
||||
private static void PreLoad()
|
||||
private void PreLoad()
|
||||
{
|
||||
string fileNameActual = string.Concat(CachePathActual, ".cache");
|
||||
string fileNameBackup = string.Concat(CachePathBackup, ".cache");
|
||||
@@ -199,7 +203,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe bool Load(string fileName, bool isBackup)
|
||||
private unsafe bool Load(string fileName, bool isBackup)
|
||||
{
|
||||
using (FileStream compressedStream = new(fileName, FileMode.Open))
|
||||
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
|
||||
@@ -376,12 +380,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void InvalidateCompressedStream(FileStream compressedStream)
|
||||
private void InvalidateCompressedStream(FileStream compressedStream)
|
||||
{
|
||||
compressedStream.SetLength(0L);
|
||||
}
|
||||
|
||||
private static void PreSave()
|
||||
private void PreSave()
|
||||
{
|
||||
_waitEvent.Reset();
|
||||
|
||||
@@ -409,7 +413,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
_waitEvent.Set();
|
||||
}
|
||||
|
||||
private static unsafe void Save(string fileName)
|
||||
private unsafe void Save(string fileName)
|
||||
{
|
||||
int translatedFuncsCount;
|
||||
|
||||
@@ -517,7 +521,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LoadTranslations(Translator translator)
|
||||
public void LoadTranslations(Translator translator)
|
||||
{
|
||||
if (AreCarriersEmpty())
|
||||
{
|
||||
@@ -550,7 +554,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize);
|
||||
|
||||
if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
|
||||
if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
|
||||
{
|
||||
infoEntry.Stubbed = true;
|
||||
infoEntry.CodeLength = 0;
|
||||
@@ -601,38 +605,38 @@ namespace ARMeilleure.Translation.PTC
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded");
|
||||
}
|
||||
|
||||
private static int GetEntriesCount()
|
||||
private int GetEntriesCount()
|
||||
{
|
||||
return _codesList.Count;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private static void SkipCode(int index, int codeLength)
|
||||
private void SkipCode(int index, int codeLength)
|
||||
{
|
||||
Debug.Assert(_codesList[index].Length == 0);
|
||||
Debug.Assert(codeLength == 0);
|
||||
}
|
||||
|
||||
private static void SkipReloc(int relocEntriesCount)
|
||||
private void SkipReloc(int relocEntriesCount)
|
||||
{
|
||||
_relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
private static void SkipUnwindInfo(BinaryReader unwindInfosReader)
|
||||
private void SkipUnwindInfo(BinaryReader unwindInfosReader)
|
||||
{
|
||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||
|
||||
_unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
private static byte[] ReadCode(int index, int codeLength)
|
||||
private byte[] ReadCode(int index, int codeLength)
|
||||
{
|
||||
Debug.Assert(_codesList[index].Length == codeLength);
|
||||
|
||||
return _codesList[index];
|
||||
}
|
||||
|
||||
private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount)
|
||||
private RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount)
|
||||
{
|
||||
RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount];
|
||||
|
||||
@@ -648,7 +652,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
return relocEntries;
|
||||
}
|
||||
|
||||
private static void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter)
|
||||
private void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter)
|
||||
{
|
||||
callCounter = null;
|
||||
|
||||
@@ -702,7 +706,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader)
|
||||
private UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader)
|
||||
{
|
||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||
|
||||
@@ -723,7 +727,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
return new UnwindInfo(pushEntries, prologueSize);
|
||||
}
|
||||
|
||||
private static TranslatedFunction FastTranslate(
|
||||
private TranslatedFunction FastTranslate(
|
||||
byte[] code,
|
||||
Counter<uint> callCounter,
|
||||
ulong guestSize,
|
||||
@@ -736,19 +740,19 @@ namespace ARMeilleure.Translation.PTC
|
||||
return new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
|
||||
}
|
||||
|
||||
private static void UpdateInfo(InfoEntry infoEntry)
|
||||
private void UpdateInfo(InfoEntry infoEntry)
|
||||
{
|
||||
_infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current);
|
||||
|
||||
SerializeStructure(_infosStream, infoEntry);
|
||||
}
|
||||
|
||||
private static void StubCode(int index)
|
||||
private void StubCode(int index)
|
||||
{
|
||||
_codesList[index] = Array.Empty<byte>();
|
||||
}
|
||||
|
||||
private static void StubReloc(int relocEntriesCount)
|
||||
private void StubReloc(int relocEntriesCount)
|
||||
{
|
||||
for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++)
|
||||
{
|
||||
@@ -756,7 +760,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static void StubUnwindInfo(BinaryReader unwindInfosReader)
|
||||
private void StubUnwindInfo(BinaryReader unwindInfosReader)
|
||||
{
|
||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||
|
||||
@@ -766,9 +770,9 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MakeAndSaveTranslations(Translator translator)
|
||||
public void MakeAndSaveTranslations(Translator translator)
|
||||
{
|
||||
var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||
var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||
|
||||
_translateCount = 0;
|
||||
_translateTotalCount = profiledFuncsToTranslate.Count;
|
||||
@@ -811,7 +815,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
ulong address = item.address;
|
||||
|
||||
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
|
||||
Debug.Assert(Profiler.IsAddressInStaticCodeRange(address));
|
||||
|
||||
TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
|
||||
|
||||
@@ -861,7 +865,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
preSaveThread.Start();
|
||||
}
|
||||
|
||||
private static void ReportProgress(object state)
|
||||
private void ReportProgress(object state)
|
||||
{
|
||||
const int refreshRate = 50; // ms.
|
||||
|
||||
@@ -882,12 +886,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
while (!endEvent.WaitOne(refreshRate));
|
||||
}
|
||||
|
||||
internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize)
|
||||
public static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize)
|
||||
{
|
||||
return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize))));
|
||||
}
|
||||
|
||||
internal static void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc)
|
||||
public void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -936,12 +940,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteCode(ReadOnlySpan<byte> code)
|
||||
private void WriteCode(ReadOnlySpan<byte> code)
|
||||
{
|
||||
_codesList.Add(code.ToArray());
|
||||
}
|
||||
|
||||
internal static bool GetEndianness()
|
||||
public static bool GetEndianness()
|
||||
{
|
||||
return BitConverter.IsLittleEndian;
|
||||
}
|
||||
@@ -955,7 +959,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
(uint)HardwareCapabilities.FeatureInfo7Ecx);
|
||||
}
|
||||
|
||||
private static byte GetMemoryManagerMode()
|
||||
private byte GetMemoryManagerMode()
|
||||
{
|
||||
return (byte)_memoryMode;
|
||||
}
|
||||
@@ -1050,12 +1054,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
public int RelocEntriesCount;
|
||||
}
|
||||
|
||||
private static void Enable()
|
||||
private void Enable()
|
||||
{
|
||||
State = PtcState.Enabled;
|
||||
}
|
||||
|
||||
public static void Continue()
|
||||
public void Continue()
|
||||
{
|
||||
if (State == PtcState.Enabled)
|
||||
{
|
||||
@@ -1063,7 +1067,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
public void Close()
|
||||
{
|
||||
if (State == PtcState.Enabled ||
|
||||
State == PtcState.Continuing)
|
||||
@@ -1072,17 +1076,17 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Disable()
|
||||
public void Disable()
|
||||
{
|
||||
State = PtcState.Disabled;
|
||||
}
|
||||
|
||||
private static void Wait()
|
||||
private void Wait()
|
||||
{
|
||||
_waitEvent.WaitOne();
|
||||
}
|
||||
|
||||
public static void Dispose()
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
|
@@ -16,7 +16,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
public static class PtcProfiler
|
||||
class PtcProfiler
|
||||
{
|
||||
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
||||
|
||||
@@ -26,27 +26,31 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||
|
||||
private static readonly System.Timers.Timer _timer;
|
||||
private readonly Ptc _ptc;
|
||||
|
||||
private static readonly ulong _outerHeaderMagic;
|
||||
private readonly System.Timers.Timer _timer;
|
||||
|
||||
private static readonly ManualResetEvent _waitEvent;
|
||||
private readonly ulong _outerHeaderMagic;
|
||||
|
||||
private static readonly object _lock;
|
||||
private readonly ManualResetEvent _waitEvent;
|
||||
|
||||
private static bool _disposed;
|
||||
private readonly object _lock;
|
||||
|
||||
private static Hash128 _lastHash;
|
||||
private bool _disposed;
|
||||
|
||||
internal static Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
|
||||
private Hash128 _lastHash;
|
||||
|
||||
internal static bool Enabled { get; private set; }
|
||||
public Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
|
||||
|
||||
public static ulong StaticCodeStart { internal get; set; }
|
||||
public static ulong StaticCodeSize { internal get; set; }
|
||||
public bool Enabled { get; private set; }
|
||||
|
||||
static PtcProfiler()
|
||||
public ulong StaticCodeStart { get; set; }
|
||||
public ulong StaticCodeSize { get; set; }
|
||||
|
||||
public PtcProfiler(Ptc ptc)
|
||||
{
|
||||
_ptc = ptc;
|
||||
|
||||
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
|
||||
_timer.Elapsed += PreSave;
|
||||
|
||||
@@ -63,7 +67,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
internal static void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
{
|
||||
if (IsAddressInStaticCodeRange(address))
|
||||
{
|
||||
@@ -76,7 +80,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
{
|
||||
if (IsAddressInStaticCodeRange(address))
|
||||
{
|
||||
@@ -91,12 +95,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsAddressInStaticCodeRange(ulong address)
|
||||
public bool IsAddressInStaticCodeRange(ulong address)
|
||||
{
|
||||
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
|
||||
}
|
||||
|
||||
internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
||||
public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
||||
{
|
||||
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
|
||||
|
||||
@@ -111,18 +115,18 @@ namespace ARMeilleure.Translation.PTC
|
||||
return profiledFuncsToTranslate;
|
||||
}
|
||||
|
||||
internal static void ClearEntries()
|
||||
public void ClearEntries()
|
||||
{
|
||||
ProfiledFuncs.Clear();
|
||||
ProfiledFuncs.TrimExcess();
|
||||
}
|
||||
|
||||
internal static void PreLoad()
|
||||
public void PreLoad()
|
||||
{
|
||||
_lastHash = default;
|
||||
|
||||
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
|
||||
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
|
||||
string fileNameActual = string.Concat(_ptc.CachePathActual, ".info");
|
||||
string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info");
|
||||
|
||||
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
||||
FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
|
||||
@@ -143,7 +147,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static bool Load(string fileName, bool isBackup)
|
||||
private bool Load(string fileName, bool isBackup)
|
||||
{
|
||||
using (FileStream compressedStream = new(fileName, FileMode.Open))
|
||||
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
|
||||
@@ -228,22 +232,22 @@ namespace ARMeilleure.Translation.PTC
|
||||
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
||||
private ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
||||
{
|
||||
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
|
||||
}
|
||||
|
||||
private static void InvalidateCompressedStream(FileStream compressedStream)
|
||||
private void InvalidateCompressedStream(FileStream compressedStream)
|
||||
{
|
||||
compressedStream.SetLength(0L);
|
||||
}
|
||||
|
||||
private static void PreSave(object source, System.Timers.ElapsedEventArgs e)
|
||||
private void PreSave(object source, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
_waitEvent.Reset();
|
||||
|
||||
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
|
||||
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
|
||||
string fileNameActual = string.Concat(_ptc.CachePathActual, ".info");
|
||||
string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info");
|
||||
|
||||
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
||||
|
||||
@@ -257,7 +261,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
_waitEvent.Set();
|
||||
}
|
||||
|
||||
private static void Save(string fileName)
|
||||
private void Save(string fileName)
|
||||
{
|
||||
int profiledFuncsCount;
|
||||
|
||||
@@ -329,7 +333,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
|
||||
private void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
|
||||
{
|
||||
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
|
||||
}
|
||||
@@ -361,7 +365,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
|
||||
internal struct FuncProfile
|
||||
public struct FuncProfile
|
||||
{
|
||||
public ExecutionMode Mode;
|
||||
public bool HighCq;
|
||||
@@ -373,10 +377,10 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Start()
|
||||
public void Start()
|
||||
{
|
||||
if (Ptc.State == PtcState.Enabled ||
|
||||
Ptc.State == PtcState.Continuing)
|
||||
if (_ptc.State == PtcState.Enabled ||
|
||||
_ptc.State == PtcState.Continuing)
|
||||
{
|
||||
Enabled = true;
|
||||
|
||||
@@ -384,7 +388,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
public void Stop()
|
||||
{
|
||||
Enabled = false;
|
||||
|
||||
@@ -394,12 +398,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Wait()
|
||||
public void Wait()
|
||||
{
|
||||
_waitEvent.WaitOne();
|
||||
}
|
||||
|
||||
public static void Dispose()
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
|
@@ -44,6 +44,8 @@ namespace ARMeilleure.Translation
|
||||
private readonly IJitMemoryAllocator _allocator;
|
||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||
|
||||
private readonly Ptc _ptc;
|
||||
|
||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||
internal AddressTable<ulong> FunctionTable { get; }
|
||||
internal EntryTable<uint> CountTable { get; }
|
||||
@@ -63,6 +65,8 @@ namespace ARMeilleure.Translation
|
||||
|
||||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
||||
|
||||
_ptc = new Ptc();
|
||||
|
||||
Queue = new TranslatorQueue();
|
||||
|
||||
JitCache.Initialize(allocator);
|
||||
@@ -80,22 +84,37 @@ namespace ARMeilleure.Translation
|
||||
}
|
||||
}
|
||||
|
||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
{
|
||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type);
|
||||
return _ptc;
|
||||
}
|
||||
|
||||
public void PrepareCodeRange(ulong address, ulong size)
|
||||
{
|
||||
if (_ptc.Profiler.StaticCodeSize == 0)
|
||||
{
|
||||
_ptc.Profiler.StaticCodeStart = address;
|
||||
_ptc.Profiler.StaticCodeSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(State.ExecutionContext context, ulong address)
|
||||
{
|
||||
if (Interlocked.Increment(ref _threadCount) == 1)
|
||||
{
|
||||
IsReadyForTranslation.WaitOne();
|
||||
|
||||
if (Ptc.State == PtcState.Enabled)
|
||||
if (_ptc.State == PtcState.Enabled)
|
||||
{
|
||||
Debug.Assert(Functions.Count == 0);
|
||||
Ptc.LoadTranslations(this);
|
||||
Ptc.MakeAndSaveTranslations(this);
|
||||
_ptc.LoadTranslations(this);
|
||||
_ptc.MakeAndSaveTranslations(this);
|
||||
}
|
||||
|
||||
PtcProfiler.Start();
|
||||
_ptc.Profiler.Start();
|
||||
|
||||
Ptc.Disable();
|
||||
_ptc.Disable();
|
||||
|
||||
// Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht
|
||||
// etc). All threads are normal priority except from the last, which just fills as much of the last core
|
||||
@@ -148,6 +167,12 @@ namespace ARMeilleure.Translation
|
||||
Stubs.Dispose();
|
||||
FunctionTable.Dispose();
|
||||
CountTable.Dispose();
|
||||
|
||||
_ptc.Close();
|
||||
_ptc.Profiler.Stop();
|
||||
|
||||
_ptc.Dispose();
|
||||
_ptc.Profiler.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,9 +214,9 @@ namespace ARMeilleure.Translation
|
||||
func = oldFunc;
|
||||
}
|
||||
|
||||
if (PtcProfiler.Enabled)
|
||||
if (_ptc.Profiler.Enabled)
|
||||
{
|
||||
PtcProfiler.AddEntry(address, mode, highCq: false);
|
||||
_ptc.Profiler.AddEntry(address, mode, highCq: false);
|
||||
}
|
||||
|
||||
RegisterFunction(address, func);
|
||||
@@ -217,6 +242,7 @@ namespace ARMeilleure.Translation
|
||||
Stubs,
|
||||
address,
|
||||
highCq,
|
||||
_ptc.State != PtcState.Disabled,
|
||||
mode: Aarch32Mode.User);
|
||||
|
||||
Logger.StartPass(PassName.Decoding);
|
||||
@@ -262,7 +288,7 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
|
||||
|
||||
Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||
}
|
||||
|
||||
GuestFunction func = compiledFunc.Map<GuestFunction>();
|
||||
@@ -284,9 +310,9 @@ namespace ARMeilleure.Translation
|
||||
return func;
|
||||
});
|
||||
|
||||
if (PtcProfiler.Enabled)
|
||||
if (_ptc.Profiler.Enabled)
|
||||
{
|
||||
PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true);
|
||||
_ptc.Profiler.UpdateEntry(request.Address, request.Mode, highCq: true);
|
||||
}
|
||||
|
||||
RegisterFunction(request.Address, func);
|
||||
|
@@ -34,7 +34,7 @@
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.24.2-build21" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.1-build23" />
|
||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.1" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||
@@ -50,7 +50,5 @@
|
||||
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
|
||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3"/>
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@@ -1,5 +1,6 @@
|
||||
using ARMeilleure.Translation;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Threading;
|
||||
using LibHac.Tools.FsSystem;
|
||||
@@ -14,6 +15,7 @@ using Ryujinx.Ava.Input;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
@@ -43,7 +45,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Image = SixLabors.ImageSharp.Image;
|
||||
using InputManager = Ryujinx.Input.HLE.InputManager;
|
||||
using Key = Ryujinx.Input.Key;
|
||||
using MouseButton = Ryujinx.Input.MouseButton;
|
||||
@@ -68,7 +70,7 @@ namespace Ryujinx.Ava
|
||||
private readonly AccountManager _accountManager;
|
||||
private readonly UserChannelPersistence _userChannelPersistence;
|
||||
private readonly InputManager _inputManager;
|
||||
private readonly MainWindow _parent;
|
||||
private readonly MainWindowViewModel _viewModel;
|
||||
private readonly IKeyboard _keyboardInterface;
|
||||
private readonly GraphicsDebugLevel _glLogLevel;
|
||||
|
||||
@@ -111,6 +113,7 @@ namespace Ryujinx.Ava
|
||||
public bool ScreenshotRequested { get; set; }
|
||||
|
||||
private object _lockObject = new();
|
||||
private TopLevel _topLevel;
|
||||
|
||||
public AppHost(
|
||||
RendererHost renderer,
|
||||
@@ -120,9 +123,10 @@ namespace Ryujinx.Ava
|
||||
ContentManager contentManager,
|
||||
AccountManager accountManager,
|
||||
UserChannelPersistence userChannelPersistence,
|
||||
MainWindow parent)
|
||||
MainWindowViewModel viewmodel,
|
||||
TopLevel topLevel)
|
||||
{
|
||||
_parent = parent;
|
||||
_viewModel = viewmodel;
|
||||
_inputManager = inputManager;
|
||||
_accountManager = accountManager;
|
||||
_userChannelPersistence = userChannelPersistence;
|
||||
@@ -130,7 +134,8 @@ namespace Ryujinx.Ava
|
||||
_hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle;
|
||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
|
||||
_inputManager.SetMouseDriver(new AvaloniaMouseDriver(_parent, renderer));
|
||||
_topLevel = topLevel;
|
||||
_inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer));
|
||||
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
|
||||
|
||||
NpadManager = _inputManager.CreateNpadManager();
|
||||
@@ -145,15 +150,15 @@ namespace Ryujinx.Ava
|
||||
|
||||
if (ApplicationPath.StartsWith("@SystemContent"))
|
||||
{
|
||||
ApplicationPath = _parent.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
|
||||
ApplicationPath = _viewModel.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
|
||||
|
||||
_isFirmwareTitle = true;
|
||||
}
|
||||
|
||||
ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorState_Changed;
|
||||
|
||||
_parent.PointerLeave += Parent_PointerLeft;
|
||||
_parent.PointerMoved += Parent_PointerMoved;
|
||||
_topLevel.PointerLeave += TopLevel_PointerLeave;
|
||||
_topLevel.PointerMoved += TopLevel_PointerMoved;
|
||||
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
|
||||
@@ -163,25 +168,27 @@ namespace Ryujinx.Ava
|
||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private void Parent_PointerMoved(object sender, PointerEventArgs e)
|
||||
private void TopLevel_PointerMoved(object sender, PointerEventArgs e)
|
||||
{
|
||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||
var p = e.GetCurrentPoint(_parent).Position;
|
||||
var r = _parent.InputHitTest(p);
|
||||
_isMouseInRenderer = r == Renderer;
|
||||
if (sender is Control visual)
|
||||
{
|
||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||
var point = e.GetCurrentPoint(visual).Position;
|
||||
_isMouseInRenderer = Equals(visual.InputHitTest(point), Renderer);
|
||||
}
|
||||
}
|
||||
|
||||
private void Parent_PointerLeft(object sender, PointerEventArgs e)
|
||||
private void TopLevel_PointerLeave(object sender, PointerEventArgs e)
|
||||
{
|
||||
_isMouseInRenderer = false;
|
||||
_parent.Cursor = Cursor.Default;
|
||||
_viewModel.Cursor = Cursor.Default;
|
||||
}
|
||||
|
||||
private void SetRendererWindowSize(Size size)
|
||||
{
|
||||
if (_renderer != null)
|
||||
{
|
||||
double scale = _parent.PlatformImpl.RenderScaling;
|
||||
double scale = _topLevel.PlatformImpl.RenderScaling;
|
||||
_renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale));
|
||||
}
|
||||
}
|
||||
@@ -257,7 +264,7 @@ namespace Ryujinx.Ava
|
||||
NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||
TouchScreenManager.Initialize(Device);
|
||||
|
||||
_parent.ViewModel.IsGameRunning = true;
|
||||
_viewModel.IsGameRunning = true;
|
||||
|
||||
string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName)
|
||||
? string.Empty
|
||||
@@ -277,10 +284,10 @@ namespace Ryujinx.Ava
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
_parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
|
||||
_viewModel.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
|
||||
});
|
||||
|
||||
_parent.ViewModel.HandleShaderProgress(Device);
|
||||
_viewModel.SetUIProgressHandlers(Device);
|
||||
|
||||
Renderer.SizeChanged += Window_SizeChanged;
|
||||
|
||||
@@ -288,7 +295,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
_renderingThread.Start();
|
||||
|
||||
_parent.ViewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value;
|
||||
_viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value;
|
||||
|
||||
MainLoop();
|
||||
|
||||
@@ -322,7 +329,7 @@ namespace Ryujinx.Ava
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
var value = e.NewValue;
|
||||
_parent.ViewModel.Volume = e.NewValue;
|
||||
_viewModel.Volume = e.NewValue;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -357,8 +364,6 @@ namespace Ryujinx.Ava
|
||||
|
||||
DisplaySleep.Restore();
|
||||
|
||||
Ptc.Close();
|
||||
PtcProfiler.Stop();
|
||||
NpadManager.Dispose();
|
||||
TouchScreenManager.Dispose();
|
||||
Device.Dispose();
|
||||
@@ -372,7 +377,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
if (Device.Application != null)
|
||||
{
|
||||
_parent.UpdateGameMetadata(Device.Application.TitleIdText);
|
||||
_viewModel.UpdateGameMetadata(Device.Application.TitleIdText);
|
||||
}
|
||||
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
|
||||
@@ -380,6 +385,9 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
||||
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
|
||||
|
||||
_topLevel.PointerLeave -= TopLevel_PointerLeave;
|
||||
_topLevel.PointerMoved -= TopLevel_PointerMoved;
|
||||
|
||||
_gpuCancellationTokenSource.Cancel();
|
||||
_gpuCancellationTokenSource.Dispose();
|
||||
|
||||
@@ -413,7 +421,7 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
else
|
||||
{
|
||||
_parent.Cursor = Cursor.Default;
|
||||
_viewModel.Cursor = Cursor.Default;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -425,57 +433,65 @@ namespace Ryujinx.Ava
|
||||
|
||||
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||
|
||||
if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError))
|
||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion))
|
||||
if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError))
|
||||
{
|
||||
if (userError == UserError.NoFirmware)
|
||||
{
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], firmwareVersion.VersionString),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
"");
|
||||
|
||||
if (result != UserResult.Yes)
|
||||
if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion))
|
||||
{
|
||||
await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
|
||||
if (userError == UserError.NoFirmware)
|
||||
{
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage],
|
||||
firmwareVersion.VersionString),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
"");
|
||||
|
||||
if (result != UserResult.Yes)
|
||||
{
|
||||
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
|
||||
{
|
||||
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tell the user that we installed a firmware for them.
|
||||
if (userError == UserError.NoFirmware)
|
||||
{
|
||||
firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||
|
||||
_viewModel.RefreshFirmwareStatus();
|
||||
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage],
|
||||
firmwareVersion.VersionString),
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage],
|
||||
firmwareVersion.VersionString),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
|
||||
{
|
||||
await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tell the user that we installed a firmware for them.
|
||||
if (userError == UserError.NoFirmware)
|
||||
{
|
||||
firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||
|
||||
_parent.RefreshFirmwareStatus();
|
||||
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], firmwareVersion.VersionString),
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], firmwareVersion.VersionString),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,7 +586,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
DiscordIntegrationModule.SwitchToPlayingState(Device.Application.TitleIdText, Device.Application.TitleName);
|
||||
|
||||
_parent.ApplicationLibrary.LoadAndSaveMetaData(Device.Application.TitleIdText, appMetadata =>
|
||||
_viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Application.TitleIdText, appMetadata =>
|
||||
{
|
||||
appMetadata.LastPlayed = DateTime.UtcNow.ToString();
|
||||
});
|
||||
@@ -581,13 +597,13 @@ namespace Ryujinx.Ava
|
||||
internal void Resume()
|
||||
{
|
||||
Device?.System.TogglePauseEmulation(false);
|
||||
_parent.ViewModel.IsPaused = false;
|
||||
_viewModel.IsPaused = false;
|
||||
}
|
||||
|
||||
internal void Pause()
|
||||
{
|
||||
Device?.System.TogglePauseEmulation(true);
|
||||
_parent.ViewModel.IsPaused = true;
|
||||
_viewModel.IsPaused = true;
|
||||
}
|
||||
|
||||
private void InitializeSwitchInstance()
|
||||
@@ -635,7 +651,7 @@ namespace Ryujinx.Ava
|
||||
Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration.");
|
||||
|
||||
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl;
|
||||
MainWindow.SaveConfig();
|
||||
MainWindowViewModel.SaveConfig();
|
||||
|
||||
deviceDriver = new OpenALHardwareDeviceDriver();
|
||||
}
|
||||
@@ -648,7 +664,7 @@ namespace Ryujinx.Ava
|
||||
Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
|
||||
|
||||
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
|
||||
MainWindow.SaveConfig();
|
||||
MainWindowViewModel.SaveConfig();
|
||||
|
||||
deviceDriver = new SoundIoHardwareDeviceDriver();
|
||||
}
|
||||
@@ -674,7 +690,7 @@ namespace Ryujinx.Ava
|
||||
Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration.");
|
||||
|
||||
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2;
|
||||
MainWindow.SaveConfig();
|
||||
MainWindowViewModel.SaveConfig();
|
||||
|
||||
deviceDriver = new SDL2HardwareDeviceDriver();
|
||||
}
|
||||
@@ -687,7 +703,7 @@ namespace Ryujinx.Ava
|
||||
Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration.");
|
||||
|
||||
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl;
|
||||
MainWindow.SaveConfig();
|
||||
MainWindowViewModel.SaveConfig();
|
||||
|
||||
deviceDriver = new OpenALHardwareDeviceDriver();
|
||||
}
|
||||
@@ -713,7 +729,7 @@ namespace Ryujinx.Ava
|
||||
Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration.");
|
||||
|
||||
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2;
|
||||
MainWindow.SaveConfig();
|
||||
MainWindowViewModel.SaveConfig();
|
||||
|
||||
deviceDriver = new SDL2HardwareDeviceDriver();
|
||||
}
|
||||
@@ -726,7 +742,7 @@ namespace Ryujinx.Ava
|
||||
Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
|
||||
|
||||
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
|
||||
MainWindow.SaveConfig();
|
||||
MainWindowViewModel.SaveConfig();
|
||||
|
||||
deviceDriver = new SoundIoHardwareDeviceDriver();
|
||||
}
|
||||
@@ -743,14 +759,14 @@ namespace Ryujinx.Ava
|
||||
IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None;
|
||||
|
||||
HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(VirtualFileSystem,
|
||||
_parent.LibHacHorizonManager,
|
||||
_viewModel.LibHacHorizonManager,
|
||||
ContentManager,
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer,
|
||||
deviceDriver,
|
||||
memoryConfiguration,
|
||||
_parent.UiHandler,
|
||||
_viewModel.UiHandler,
|
||||
(SystemLanguage)ConfigurationState.Instance.System.Language.Value,
|
||||
(RegionCode)ConfigurationState.Instance.System.Region.Value,
|
||||
ConfigurationState.Instance.Graphics.EnableVsync,
|
||||
@@ -791,14 +807,14 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (_parent.ViewModel.StartGamesInFullscreen)
|
||||
if (_viewModel.StartGamesInFullscreen)
|
||||
{
|
||||
_parent.WindowState = WindowState.FullScreen;
|
||||
_viewModel.WindowState = WindowState.FullScreen;
|
||||
}
|
||||
|
||||
if (_parent.WindowState == WindowState.FullScreen)
|
||||
if (_viewModel.WindowState == WindowState.FullScreen)
|
||||
{
|
||||
_parent.ViewModel.ShowMenuAndStatusBar = false;
|
||||
_viewModel.ShowMenuAndStatusBar = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -822,7 +838,7 @@ namespace Ryujinx.Ava
|
||||
Width = (int)Renderer.Bounds.Width;
|
||||
Height = (int)Renderer.Bounds.Height;
|
||||
|
||||
_renderer.Window.SetSize((int)(Width * _parent.PlatformImpl.RenderScaling), (int)(Height * _parent.PlatformImpl.RenderScaling));
|
||||
_renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling));
|
||||
|
||||
_chrono.Start();
|
||||
|
||||
@@ -850,7 +866,7 @@ namespace Ryujinx.Ava
|
||||
if (!_renderingStarted)
|
||||
{
|
||||
_renderingStarted = true;
|
||||
_parent.SwitchToGameControl();
|
||||
_viewModel.SwitchToRenderer(false);
|
||||
}
|
||||
|
||||
Device.PresentFrame(() => Renderer?.SwapBuffers());
|
||||
@@ -917,7 +933,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
_parent.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default;
|
||||
_viewModel.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default;
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -928,7 +944,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
_parent.Cursor = cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency ? InvisibleCursor : Cursor.Default;
|
||||
_viewModel.Cursor = cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency ? InvisibleCursor : Cursor.Default;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -941,22 +957,22 @@ namespace Ryujinx.Ava
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_parent.IsActive)
|
||||
if (_viewModel.IsActive)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
HandleScreenState();
|
||||
|
||||
if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _parent.WindowState != WindowState.FullScreen)
|
||||
if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen)
|
||||
{
|
||||
Ptc.Continue();
|
||||
Device.Application.DiskCacheLoadState?.Cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
|
||||
|
||||
if (_parent.IsActive)
|
||||
if (_viewModel.IsActive)
|
||||
{
|
||||
KeyboardHotkeyState currentHotkeyState = GetHotkeyState();
|
||||
|
||||
@@ -972,10 +988,10 @@ namespace Ryujinx.Ava
|
||||
ScreenshotRequested = true;
|
||||
break;
|
||||
case KeyboardHotkeyState.ShowUi:
|
||||
_parent.ViewModel.ShowMenuAndStatusBar = true;
|
||||
_viewModel.ShowMenuAndStatusBar = true;
|
||||
break;
|
||||
case KeyboardHotkeyState.Pause:
|
||||
if (_parent.ViewModel.IsPaused)
|
||||
if (_viewModel.IsPaused)
|
||||
{
|
||||
Resume();
|
||||
}
|
||||
@@ -994,7 +1010,7 @@ namespace Ryujinx.Ava
|
||||
Device.SetVolume(0);
|
||||
}
|
||||
|
||||
_parent.ViewModel.Volume = Device.GetVolume();
|
||||
_viewModel.Volume = Device.GetVolume();
|
||||
break;
|
||||
case KeyboardHotkeyState.ResScaleUp:
|
||||
GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1;
|
||||
@@ -1007,13 +1023,13 @@ namespace Ryujinx.Ava
|
||||
_newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2);
|
||||
Device.SetVolume(_newVolume);
|
||||
|
||||
_parent.ViewModel.Volume = Device.GetVolume();
|
||||
_viewModel.Volume = Device.GetVolume();
|
||||
break;
|
||||
case KeyboardHotkeyState.VolumeDown:
|
||||
_newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2);
|
||||
Device.SetVolume(_newVolume);
|
||||
|
||||
_parent.ViewModel.Volume = Device.GetVolume();
|
||||
_viewModel.Volume = Device.GetVolume();
|
||||
break;
|
||||
case KeyboardHotkeyState.None:
|
||||
(_keyboardInterface as AvaloniaKeyboard).Clear();
|
||||
@@ -1033,7 +1049,7 @@ namespace Ryujinx.Ava
|
||||
// Touchscreen
|
||||
bool hasTouch = false;
|
||||
|
||||
if (_parent.IsActive && !ConfigurationState.Instance.Hid.EnableMouse)
|
||||
if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse)
|
||||
{
|
||||
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
|
||||
}
|
||||
|
@@ -269,6 +269,8 @@
|
||||
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
|
||||
<Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
|
||||
<Color x:Key="ThemeControlBorderColor">#FF505050</Color>
|
||||
<Color x:Key="VsyncEnabled">#FF2EEAC9</Color>
|
||||
<Color x:Key="VsyncDisabled">#FFFF4554</Color>
|
||||
<x:Double x:Key="ScrollBarThickness">15</x:Double>
|
||||
<x:Double x:Key="FontSizeSmall">8</x:Double>
|
||||
<x:Double x:Key="FontSizeNormal">10</x:Double>
|
||||
|
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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,10 +11,10 @@ namespace Ryujinx.Ava.Input
|
||||
{
|
||||
internal class AvaloniaMouseDriver : IGamepadDriver
|
||||
{
|
||||
private Control _widget;
|
||||
private bool _isDisposed;
|
||||
private Size _size;
|
||||
private readonly Window _window;
|
||||
private Control _widget;
|
||||
private bool _isDisposed;
|
||||
private Size _size;
|
||||
private readonly TopLevel _window;
|
||||
|
||||
public bool[] PressedButtons { get; }
|
||||
public Vector2 CurrentPosition { get; private set; }
|
||||
@@ -23,7 +23,7 @@ namespace Ryujinx.Ava.Input
|
||||
public string DriverName => "AvaloniaMouseDriver";
|
||||
public ReadOnlySpan<string> GamepadsIds => new[] { "0" };
|
||||
|
||||
public AvaloniaMouseDriver(Window window, Control parent)
|
||||
public AvaloniaMouseDriver(TopLevel window, Control parent)
|
||||
{
|
||||
_widget = parent;
|
||||
_window = window;
|
||||
|
@@ -56,7 +56,7 @@ namespace Ryujinx.Modules
|
||||
}
|
||||
|
||||
Running = true;
|
||||
mainWindow.CanUpdate = false;
|
||||
mainWindow.ViewModel.CanUpdate = false;
|
||||
|
||||
// Detect current platform
|
||||
if (OperatingSystem.IsMacOS())
|
||||
@@ -95,7 +95,7 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
using (HttpClient jsonClient = ConstructHttpClient())
|
||||
{
|
||||
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformations.ReleaseChannelOwner}/{ReleaseInformations.ReleaseChannelRepo}/releases/latest";
|
||||
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||
|
||||
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
||||
JObject jsonRoot = JObject.Parse(fetchedJson);
|
||||
@@ -182,7 +182,7 @@ namespace Ryujinx.Modules
|
||||
}
|
||||
|
||||
Running = false;
|
||||
mainWindow.CanUpdate = true;
|
||||
mainWindow.ViewModel.CanUpdate = true;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -625,7 +625,7 @@ namespace Ryujinx.Modules
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Program.Version.Contains("dirty") || !ReleaseInformations.IsValid())
|
||||
if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid())
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
@@ -640,7 +640,7 @@ namespace Ryujinx.Modules
|
||||
#else
|
||||
if (showWarnings)
|
||||
{
|
||||
if (ReleaseInformations.IsFlatHubBuild())
|
||||
if (ReleaseInformation.IsFlatHubBuild())
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
|
||||
}
|
||||
@@ -711,4 +711,4 @@ namespace Ryujinx.Modules
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using Avalonia;
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Ava.UI.Helper;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
@@ -14,8 +14,10 @@ using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava
|
||||
@@ -33,9 +35,51 @@ namespace Ryujinx.Ava
|
||||
|
||||
private const uint MB_ICONWARNING = 0x30;
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
static void RegisterMimeTypes()
|
||||
{
|
||||
if (ReleaseInformation.IsFlatHubBuild())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime");
|
||||
|
||||
if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml")))
|
||||
{
|
||||
string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml");
|
||||
using Process mimeProcess = new();
|
||||
|
||||
mimeProcess.StartInfo.FileName = "xdg-mime";
|
||||
mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}";
|
||||
|
||||
mimeProcess.Start();
|
||||
mimeProcess.WaitForExit();
|
||||
|
||||
if (mimeProcess.ExitCode != 0)
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}");
|
||||
return;
|
||||
}
|
||||
|
||||
using Process updateMimeProcess = new();
|
||||
|
||||
updateMimeProcess.StartInfo.FileName = "update-mime-database";
|
||||
updateMimeProcess.StartInfo.Arguments = mimeDbPath;
|
||||
|
||||
updateMimeProcess.Start();
|
||||
updateMimeProcess.WaitForExit();
|
||||
|
||||
if (updateMimeProcess.ExitCode != 0)
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Version = ReleaseInformations.GetVersion();
|
||||
Version = ReleaseInformation.GetVersion();
|
||||
|
||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
||||
{
|
||||
@@ -46,6 +90,8 @@ namespace Ryujinx.Ava
|
||||
|
||||
Initialize(args);
|
||||
|
||||
LoggerAdapter.Register();
|
||||
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
@@ -67,8 +113,7 @@ namespace Ryujinx.Ava
|
||||
AllowEglInitialization = false,
|
||||
CompositionBackdropCornerRadius = 8.0f,
|
||||
})
|
||||
.UseSkia()
|
||||
.LogToTrace();
|
||||
.UseSkia();
|
||||
}
|
||||
|
||||
private static void Initialize(string[] args)
|
||||
@@ -94,6 +139,12 @@ namespace Ryujinx.Ava
|
||||
// Initialize the logger system.
|
||||
LoggerModule.Initialize();
|
||||
|
||||
// Register mime types on linux.
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
RegisterMimeTypes();
|
||||
}
|
||||
|
||||
// Initialize Discord integration.
|
||||
DiscordIntegrationModule.Initialize();
|
||||
|
||||
@@ -197,9 +248,6 @@ namespace Ryujinx.Ava
|
||||
|
||||
private static void ProcessUnhandledException(Exception ex, bool isTerminating)
|
||||
{
|
||||
Ptc.Close();
|
||||
PtcProfiler.Stop();
|
||||
|
||||
string message = $"Unhandled exception caught: {ex}";
|
||||
|
||||
Logger.Error?.PrintMsg(LogClass.Application, message);
|
||||
@@ -219,10 +267,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
DiscordIntegrationModule.Exit();
|
||||
|
||||
Ptc.Dispose();
|
||||
PtcProfiler.Dispose();
|
||||
|
||||
Logger.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -76,6 +76,16 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
|
||||
<Content Include="..\distribution\linux\Ryujinx.sh">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="..\distribution\linux\mime\Ryujinx.xml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>mime\Ryujinx.xml</TargetPath>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Ui\**\*.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
|
@@ -144,9 +144,9 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value)
|
||||
{
|
||||
device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value);
|
||||
if (_parent.AppHost != null)
|
||||
if (_parent.ViewModel.AppHost != null)
|
||||
{
|
||||
_parent.AppHost.Stop();
|
||||
_parent.ViewModel.AppHost.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -136,7 +136,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
_hiddenTextBox.Clear();
|
||||
_parent.RendererControl.Focus();
|
||||
_parent.ViewModel.RendererControl.Focus();
|
||||
|
||||
_parent = null;
|
||||
});
|
||||
|
@@ -13,8 +13,8 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
DefaultBackgroundColor = BrushToThemeColor(parent.Background);
|
||||
DefaultForegroundColor = BrushToThemeColor(parent.Foreground);
|
||||
DefaultBorderColor = BrushToThemeColor(parent.BorderBrush);
|
||||
SelectionBackgroundColor = BrushToThemeColor(parent.SearchBox.SelectionBrush);
|
||||
SelectionForegroundColor = BrushToThemeColor(parent.SearchBox.SelectionForegroundBrush);
|
||||
SelectionBackgroundColor = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionBrush);
|
||||
SelectionForegroundColor = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionForegroundBrush);
|
||||
}
|
||||
|
||||
public string FontFamily { get; }
|
||||
|
@@ -139,6 +139,9 @@
|
||||
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
|
||||
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" />
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -137,12 +137,12 @@
|
||||
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
|
||||
<Setter Property="MinHeight" Value="100" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
|
||||
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.ListItemSelectorSize}" />
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
136
Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs
Normal file
136
Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public class AboutWindowViewModel : BaseModel
|
||||
{
|
||||
private Bitmap _githubLogo;
|
||||
private Bitmap _discordLogo;
|
||||
private Bitmap _patreonLogo;
|
||||
private Bitmap _twitterLogo;
|
||||
|
||||
private string _version;
|
||||
private string _supporters;
|
||||
|
||||
public Bitmap GithubLogo
|
||||
{
|
||||
get => _githubLogo;
|
||||
set
|
||||
{
|
||||
_githubLogo = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap DiscordLogo
|
||||
{
|
||||
get => _discordLogo;
|
||||
set
|
||||
{
|
||||
_discordLogo = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap PatreonLogo
|
||||
{
|
||||
get => _patreonLogo;
|
||||
set
|
||||
{
|
||||
_patreonLogo = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap TwitterLogo
|
||||
{
|
||||
get => _twitterLogo;
|
||||
set
|
||||
{
|
||||
_twitterLogo = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Supporters
|
||||
{
|
||||
get => _supporters;
|
||||
set
|
||||
{
|
||||
_supporters = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Version
|
||||
{
|
||||
get => _version;
|
||||
set
|
||||
{
|
||||
_version = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Developers
|
||||
{
|
||||
get => string.Format(LocaleManager.Instance[LocaleKeys.AboutPageDeveloperListMore], "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz");
|
||||
}
|
||||
|
||||
public AboutWindowViewModel()
|
||||
{
|
||||
Version = Program.Version;
|
||||
|
||||
var assets = AvaloniaLocator.Current.GetService<Avalonia.Platform.IAssetLoader>();
|
||||
|
||||
if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light")
|
||||
{
|
||||
GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.Ui.Common")));
|
||||
DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.Ui.Common")));
|
||||
PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.Ui.Common")));
|
||||
TwitterLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.Ui.Common")));
|
||||
}
|
||||
else
|
||||
{
|
||||
GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.Ui.Common")));
|
||||
DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.Ui.Common")));
|
||||
PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.Ui.Common")));
|
||||
TwitterLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.Ui.Common")));
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);
|
||||
}
|
||||
|
||||
private async Task DownloadPatronsJson()
|
||||
{
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
{
|
||||
Supporters = LocaleManager.Instance[LocaleKeys.ConnectionError];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
HttpClient httpClient = new();
|
||||
|
||||
try
|
||||
{
|
||||
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
||||
|
||||
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\n\n";
|
||||
}
|
||||
catch
|
||||
{
|
||||
Supporters = LocaleManager.Instance[LocaleKeys.ApiError];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -244,9 +244,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
|
||||
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
|
||||
if (_mainWindow.AppHost != null)
|
||||
if (_mainWindow.ViewModel.AppHost != null)
|
||||
{
|
||||
_mainWindow.AppHost.NpadManager.BlockInputUpdates();
|
||||
_mainWindow.ViewModel.AppHost.NpadManager.BlockInputUpdates();
|
||||
}
|
||||
|
||||
_isLoaded = false;
|
||||
@@ -862,7 +862,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
_mainWindow.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||
_mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||
|
||||
// Atomically replace and signal input change.
|
||||
// NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
|
||||
@@ -891,7 +891,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
|
||||
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
|
||||
|
||||
_mainWindow.AppHost?.NpadManager.UnblockInputUpdates();
|
||||
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
|
||||
|
||||
SelectedGamepad?.Dispose();
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
@@ -8,8 +7,6 @@ using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SoundIo;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Input;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common.Configuration;
|
||||
@@ -19,7 +16,6 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Configuration.System;
|
||||
using System;
|
||||
@@ -30,11 +26,10 @@ using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
internal class SettingsViewModel : BaseModel
|
||||
public class SettingsViewModel : BaseModel
|
||||
{
|
||||
private readonly VirtualFileSystem _virtualFileSystem;
|
||||
private readonly ContentManager _contentManager;
|
||||
private readonly StyleableWindow _owner;
|
||||
private TimeZoneContentManager _timeZoneContentManager;
|
||||
|
||||
private readonly List<string> _validTzRegions;
|
||||
@@ -44,10 +39,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private int _graphicsBackendMultithreadingIndex;
|
||||
private float _volume;
|
||||
private bool _isVulkanAvailable = true;
|
||||
private bool _directoryChanged = false;
|
||||
private List<string> _gpuIds = new List<string>();
|
||||
private bool _directoryChanged;
|
||||
private List<string> _gpuIds = new();
|
||||
private KeyboardHotkeys _keyboardHotkeys;
|
||||
private int _graphicsBackendIndex;
|
||||
private string _customThemePath;
|
||||
|
||||
public event Action CloseWindow;
|
||||
public event Action SaveSettingsEvent;
|
||||
|
||||
public int ResolutionScale
|
||||
{
|
||||
@@ -67,19 +66,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
_graphicsBackendMultithreadingIndex = value;
|
||||
|
||||
if (_owner != null)
|
||||
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
|
||||
{
|
||||
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
|
||||
"",
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
|
||||
});
|
||||
}
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
|
||||
"",
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
|
||||
});
|
||||
}
|
||||
|
||||
OnPropertyChanged();
|
||||
@@ -120,12 +116,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool IsMacOS
|
||||
{
|
||||
get => OperatingSystem.IsMacOS();
|
||||
}
|
||||
|
||||
|
||||
public bool EnableDiscordIntegration { get; set; }
|
||||
public bool CheckUpdatesOnStart { get; set; }
|
||||
public bool ShowConfirmExit { get; set; }
|
||||
@@ -160,7 +156,20 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public string TimeZone { get; set; }
|
||||
public string ShaderDumpPath { get; set; }
|
||||
public string CustomThemePath { get; set; }
|
||||
|
||||
public string CustomThemePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _customThemePath;
|
||||
}
|
||||
set
|
||||
{
|
||||
_customThemePath = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public int Language { get; set; }
|
||||
public int Region { get; set; }
|
||||
@@ -191,7 +200,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
_volume = value;
|
||||
|
||||
ConfigurationState.Instance.System.AudioVolume.Value = (float)(_volume / 100);
|
||||
ConfigurationState.Instance.System.AudioVolume.Value = _volume / 100;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
@@ -199,7 +208,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public DateTimeOffset DateOffset { get; set; }
|
||||
public TimeSpan TimeOffset { get; set; }
|
||||
public AvaloniaList<TimeZone> TimeZones { get; set; }
|
||||
private AvaloniaList<TimeZone> TimeZones { get; set; }
|
||||
public AvaloniaList<string> GameDirectories { get; set; }
|
||||
public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
|
||||
|
||||
@@ -214,17 +223,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public IGamepadDriver AvaloniaKeyboardDriver { get; }
|
||||
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager, StyleableWindow owner) : this()
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_contentManager = contentManager;
|
||||
_owner = owner;
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
LoadTimeZones();
|
||||
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,10 +256,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
|
||||
}
|
||||
|
||||
private unsafe void LoadAvailableGpus()
|
||||
private void LoadAvailableGpus()
|
||||
{
|
||||
_gpuIds = new List<string>();
|
||||
List<string> names = new List<string>();
|
||||
List<string> names = new();
|
||||
var devices = VulkanRenderer.GetPhysicalDevices();
|
||||
|
||||
if (devices.Length == 0)
|
||||
@@ -272,7 +277,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
|
||||
AvailableGpus.Clear();
|
||||
AvailableGpus.AddRange(names.Select(x => new ComboBoxItem() { Content = x }));
|
||||
AvailableGpus.AddRange(names.Select(x => new ComboBoxItem { Content = x }));
|
||||
}
|
||||
|
||||
public void LoadTimeZones()
|
||||
@@ -302,25 +307,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public async void BrowseTheme()
|
||||
{
|
||||
var dialog = new OpenFileDialog()
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
|
||||
AllowMultiple = false
|
||||
};
|
||||
|
||||
dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
|
||||
|
||||
var file = await dialog.ShowAsync(_owner);
|
||||
|
||||
if (file != null && file.Length > 0)
|
||||
{
|
||||
CustomThemePath = file[0];
|
||||
OnPropertyChanged(nameof(CustomThemePath));
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadCurrentConfiguration()
|
||||
{
|
||||
ConfigurationState config = ConfigurationState.Instance;
|
||||
@@ -477,16 +463,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
|
||||
MainWindow.UpdateGraphicsConfig();
|
||||
|
||||
if (_owner is SettingsWindow owner)
|
||||
{
|
||||
owner.ControllerSettings?.SaveCurrentProfile();
|
||||
}
|
||||
|
||||
if (_owner.Owner is MainWindow window && _directoryChanged)
|
||||
{
|
||||
window.ViewModel.LoadApplications();
|
||||
}
|
||||
|
||||
SaveSettingsEvent?.Invoke();
|
||||
|
||||
_directoryChanged = false;
|
||||
}
|
||||
@@ -504,13 +482,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public void OkButton()
|
||||
{
|
||||
SaveSettings();
|
||||
_owner.Close();
|
||||
CloseWindow?.Invoke();
|
||||
}
|
||||
|
||||
public void CancelButton()
|
||||
{
|
||||
RevertIfNotSaved();
|
||||
_owner.Close();
|
||||
CloseWindow?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
216
Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml
Normal file
216
Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml
Normal file
@@ -0,0 +1,216 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
mc:Ignorable="d"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
x:DataType="viewModels:MainWindowViewModel"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Main.MainMenuBarView"
|
||||
x:CompileBindings="True">
|
||||
<Design.DataContext>
|
||||
<viewModels:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<DockPanel HorizontalAlignment="Stretch">
|
||||
<Menu
|
||||
Name="Menu"
|
||||
Height="35"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Left">
|
||||
<Menu.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<DockPanel Margin="0" HorizontalAlignment="Stretch" />
|
||||
</ItemsPanelTemplate>
|
||||
</Menu.ItemsPanel>
|
||||
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarFile}">
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenFile}"
|
||||
Header="{locale:Locale MenuBarFileOpenFromFile}"
|
||||
IsEnabled="{Binding EnableNonGameRunningControls}"
|
||||
ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenFolder}"
|
||||
Header="{locale:Locale MenuBarFileOpenUnpacked}"
|
||||
IsEnabled="{Binding EnableNonGameRunningControls}"
|
||||
ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
|
||||
<MenuItem Header="{locale:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}">
|
||||
<MenuItem
|
||||
Click="OpenMiiApplet"
|
||||
Header="Mii Edit Applet"
|
||||
ToolTip.Tip="{locale:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenRyujinxFolder}"
|
||||
Header="{locale:Locale MenuBarFileOpenEmuFolder}"
|
||||
ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenLogsFolder}"
|
||||
Header="{locale:Locale MenuBarFileOpenLogsFolder}"
|
||||
ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Click="CloseWindow"
|
||||
Header="{locale:Locale MenuBarFileExit}"
|
||||
ToolTip.Tip="{locale:Locale ExitTooltip}" />
|
||||
</MenuItem>
|
||||
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarOptions}">
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ToggleFullscreen}"
|
||||
Header="{locale:Locale MenuBarOptionsToggleFullscreen}"
|
||||
InputGesture="F11" />
|
||||
<MenuItem>
|
||||
<MenuItem.Icon>
|
||||
<CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}"
|
||||
MinWidth="250">
|
||||
<TextBlock Text="{locale:Locale MenuBarOptionsStartGamesInFullscreen}"/>
|
||||
</CheckBox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem IsVisible="{Binding ShowConsoleVisible}">
|
||||
<MenuItem.Icon>
|
||||
<CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}"
|
||||
MinWidth="250">
|
||||
<TextBlock Text="{locale:Locale MenuBarOptionsShowConsole}"/>
|
||||
</CheckBox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="{locale:Locale MenuBarOptionsChangeLanguage}">
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="de_DE"
|
||||
Header="Deutsch" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="en_US"
|
||||
Header="English (US)" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="es_ES"
|
||||
Header="Español (ES)" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="fr_FR"
|
||||
Header="Français" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="it_IT"
|
||||
Header="Italiano" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="pt_BR"
|
||||
Header="Português (BR)" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="tr_TR"
|
||||
Header="Türkçe" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="el_GR"
|
||||
Header="Ελληνικά" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="pl_PL"
|
||||
Header="Polski" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="ru_RU"
|
||||
Header="Русский" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="zh_CN"
|
||||
Header="简体中文" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="zh_TW"
|
||||
Header="繁體中文" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="ja_JP"
|
||||
Header="日本語" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="ko_KR"
|
||||
Header="한국어" />
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Click="OpenSettings"
|
||||
Header="{locale:Locale MenuBarOptionsSettings}"
|
||||
ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ManageProfiles}"
|
||||
Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
|
||||
IsEnabled="{Binding EnableNonGameRunningControls}"
|
||||
ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
Name="ActionsMenuItem"
|
||||
VerticalAlignment="Center"
|
||||
Header="{locale:Locale MenuBarActions}"
|
||||
IsEnabled="{Binding IsGameRunning}">
|
||||
<MenuItem
|
||||
Click="PauseEmulation_Click"
|
||||
Header="{locale:Locale MenuBarOptionsPauseEmulation}"
|
||||
InputGesture="{Binding PauseKey}"
|
||||
IsEnabled="{Binding !IsPaused}"
|
||||
IsVisible="{Binding !IsPaused}" />
|
||||
<MenuItem
|
||||
Click="ResumeEmulation_Click"
|
||||
Header="{locale:Locale MenuBarOptionsResumeEmulation}"
|
||||
InputGesture="{Binding PauseKey}"
|
||||
IsEnabled="{Binding IsPaused}"
|
||||
IsVisible="{Binding IsPaused}" />
|
||||
<MenuItem
|
||||
Click="StopEmulation_Click"
|
||||
Header="{locale:Locale MenuBarOptionsStopEmulation}"
|
||||
InputGesture="Escape"
|
||||
IsEnabled="{Binding IsGameRunning}"
|
||||
ToolTip.Tip="{locale:Locale StopEmulationTooltip}" />
|
||||
<MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Name="ScanAmiiboMenuItem"
|
||||
AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
|
||||
Click="OpenAmiiboWindow"
|
||||
Header="{locale:Locale MenuBarActionsScanAmiibo}"
|
||||
IsEnabled="{Binding IsAmiiboRequested}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding TakeScreenshot}"
|
||||
Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
|
||||
InputGesture="{Binding ScreenshotKey}"
|
||||
IsEnabled="{Binding IsGameRunning}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding HideUi}"
|
||||
Header="{locale:Locale MenuBarFileToolsHideUi}"
|
||||
InputGesture="{Binding ShowUiKey}"
|
||||
IsEnabled="{Binding IsGameRunning}" />
|
||||
<MenuItem
|
||||
Click="OpenCheatManagerForCurrentApp"
|
||||
Header="{locale:Locale GameListContextMenuManageCheat}"
|
||||
IsEnabled="{Binding IsGameRunning}" />
|
||||
</MenuItem>
|
||||
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarTools}">
|
||||
<MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" IsEnabled="{Binding EnableNonGameRunningControls}">
|
||||
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
|
||||
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
|
||||
<MenuItem
|
||||
Name="UpdateMenuItem"
|
||||
IsEnabled="{Binding CanUpdate}"
|
||||
Click="CheckForUpdates"
|
||||
Header="{locale:Locale MenuBarHelpCheckForUpdates}"
|
||||
ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Click="OpenAboutWindow"
|
||||
Header="{locale:Locale MenuBarHelpAbout}"
|
||||
ToolTip.Tip="{locale:Locale OpenAboutTooltip}" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</DockPanel>
|
||||
</UserControl>
|
143
Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
Normal file
143
Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using System.Threading.Tasks;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Ncm;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.Modules;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
public partial class MainMenuBarView : UserControl
|
||||
{
|
||||
public MainWindow Window { get; private set; }
|
||||
public MainWindowViewModel ViewModel { get; private set; }
|
||||
|
||||
public MainMenuBarView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
if (this.VisualRoot is MainWindow window)
|
||||
{
|
||||
Window = window;
|
||||
}
|
||||
|
||||
ViewModel = Window.ViewModel;
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
|
||||
private async void StopEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Window.ViewModel.AppHost?.ShowExitPrompt();
|
||||
}
|
||||
|
||||
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Window.ViewModel.AppHost?.Pause();
|
||||
});
|
||||
}
|
||||
|
||||
private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Window.ViewModel.AppHost?.Resume();
|
||||
});
|
||||
}
|
||||
|
||||
public async void OpenSettings(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
|
||||
|
||||
await Window.SettingsWindow.ShowDialog(Window);
|
||||
|
||||
ViewModel.LoadConfigurableHotKeys();
|
||||
}
|
||||
|
||||
public void OpenMiiApplet(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
if (!string.IsNullOrEmpty(contentPath))
|
||||
{
|
||||
ViewModel.LoadApplication(contentPath, false, "Mii Applet");
|
||||
}
|
||||
}
|
||||
|
||||
public async void OpenAmiiboWindow(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!ViewModel.IsAmiiboRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
|
||||
{
|
||||
string titleId = ViewModel.AppHost.Device.Application.TitleIdText.ToUpper();
|
||||
AmiiboWindow window = new(ViewModel.ShowAll, ViewModel.LastScannedAmiiboId, titleId);
|
||||
|
||||
await window.ShowDialog(Window);
|
||||
|
||||
if (window.IsScanned)
|
||||
{
|
||||
ViewModel.ShowAll = window.ViewModel.ShowAllAmiibo;
|
||||
ViewModel.LastScannedAmiiboId = window.ScannedAmiibo.GetId();
|
||||
|
||||
ViewModel.AppHost.Device.System.ScanAmiibo(deviceId, ViewModel.LastScannedAmiiboId, window.ViewModel.UseRandomUuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!ViewModel.IsGameRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationLoader application = ViewModel.AppHost.Device.Application;
|
||||
if (application != null)
|
||||
{
|
||||
await new CheatWindow(Window.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(Window);
|
||||
|
||||
ViewModel.AppHost.Device.EnableCheats();
|
||||
}
|
||||
}
|
||||
|
||||
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (sender is MenuItem)
|
||||
{
|
||||
ViewModel.IsAmiiboRequested = Window.ViewModel.AppHost.Device.System.SearchingForAmiibo(out _);
|
||||
}
|
||||
}
|
||||
|
||||
public async void CheckForUpdates(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Updater.CanUpdate(true, Window))
|
||||
{
|
||||
await Updater.BeginParse(Window, true);
|
||||
}
|
||||
}
|
||||
|
||||
public async void OpenAboutWindow(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await AboutWindow.Show();
|
||||
}
|
||||
|
||||
public void CloseWindow(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Window.Close();
|
||||
}
|
||||
}
|
||||
}
|
232
Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml
Normal file
232
Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml
Normal file
@@ -0,0 +1,232 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:MainWindowViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid
|
||||
Name="StatusBar"
|
||||
Margin="0"
|
||||
MinHeight="22"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}"
|
||||
DockPanel.Dock="Bottom"
|
||||
IsVisible="{Binding ShowMenuAndStatusBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Margin="5"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding EnableNonGameRunningControls}">
|
||||
<Grid Margin="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Width="25"
|
||||
Height="25"
|
||||
MinWidth="0"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
Command="{ReflectionBinding LoadApplications}">
|
||||
<ui:SymbolIcon
|
||||
Width="50"
|
||||
Height="100"
|
||||
Symbol="Refresh" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Name="LoadStatus"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding EnableNonGameRunningControls}"
|
||||
Text="{locale:Locale StatusBarGamesLoaded}" />
|
||||
<ProgressBar
|
||||
Name="LoadProgressBar"
|
||||
Grid.Column="2"
|
||||
Height="6"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource HighlightColor}"
|
||||
IsVisible="{Binding StatusBarVisible}"
|
||||
Maximum="{Binding StatusBarProgressMaximum}"
|
||||
Value="{Binding StatusBarProgressValue}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Margin="0,2"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsGameRunning}"
|
||||
MaxHeight="18"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Name="VsyncStatus"
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{Binding VsyncColor}"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerReleased="VsyncStatus_PointerReleased"
|
||||
Text="VSync"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Name="DockedStatus"
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerReleased="DockedStatus_PointerReleased"
|
||||
Text="{Binding DockedStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Name="AspectRatioStatus"
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerReleased="AspectRatioStatus_PointerReleased"
|
||||
Text="{Binding AspectRatioStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<ui:ToggleSplitButton
|
||||
Name="VolumeStatus"
|
||||
Padding="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}"
|
||||
BorderThickness="0"
|
||||
Content="{Binding VolumeStatusText}"
|
||||
IsChecked="{Binding VolumeMuted}"
|
||||
IsVisible="{Binding !ShowLoadProgress}">
|
||||
<ui:ToggleSplitButton.Flyout>
|
||||
<Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||
<Grid Margin="0">
|
||||
<Slider
|
||||
MaxHeight="40"
|
||||
Width="150"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
IsSnapToTickEnabled="True"
|
||||
LargeChange="0.05"
|
||||
Maximum="1"
|
||||
Minimum="0"
|
||||
SmallChange="0.01"
|
||||
TickFrequency="0.05"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Value="{Binding Volume}" />
|
||||
</Grid>
|
||||
</Flyout>
|
||||
</ui:ToggleSplitButton.Flyout>
|
||||
</ui:ToggleSplitButton>
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding GameStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding FifoStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding BackendText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding GpuNameText}"
|
||||
TextAlignment="Left" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="3"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding ShowFirmwareStatus}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Name="FirmwareStatus"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale StatusBarSystemVersion}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
52
Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs
Normal file
52
Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
public partial class MainStatusBarView : UserControl
|
||||
{
|
||||
public MainWindow Window;
|
||||
|
||||
public MainStatusBarView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
if (VisualRoot is MainWindow window)
|
||||
{
|
||||
Window = window;
|
||||
}
|
||||
|
||||
DataContext = Window.ViewModel;
|
||||
}
|
||||
|
||||
private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
Window.ViewModel.AppHost.Device.EnableDeviceVsync = !Window.ViewModel.AppHost.Device.EnableDeviceVsync;
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {Window.ViewModel.AppHost.Device.EnableDeviceVsync}");
|
||||
}
|
||||
|
||||
private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
|
||||
}
|
||||
|
||||
private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
|
||||
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
|
||||
}
|
||||
}
|
||||
}
|
176
Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml
Normal file
176
Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml
Normal file
@@ -0,0 +1,176 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Main.MainViewControls"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:MainWindowViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<DockPanel
|
||||
Margin="0,0,0,5"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Button
|
||||
Width="40"
|
||||
MinWidth="40"
|
||||
Margin="5,2,0,2"
|
||||
VerticalAlignment="Stretch"
|
||||
Command="{ReflectionBinding SetListMode}"
|
||||
IsEnabled="{Binding IsGrid}">
|
||||
<ui:FontIcon
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
|
||||
Glyph="{helpers:GlyphValueConverter List}" />
|
||||
</Button>
|
||||
<Button
|
||||
Width="40"
|
||||
MinWidth="40"
|
||||
Margin="5,2,5,2"
|
||||
VerticalAlignment="Stretch"
|
||||
Command="{ReflectionBinding SetGridMode}"
|
||||
IsEnabled="{Binding IsList}">
|
||||
<ui:FontIcon
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
|
||||
Glyph="{helpers:GlyphValueConverter Grid}" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Margin="10,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale IconSize}"
|
||||
ToolTip.Tip="{locale:Locale IconSizeTooltip}" />
|
||||
<Slider
|
||||
Width="150"
|
||||
Height="35"
|
||||
Margin="5,-10,5,0"
|
||||
VerticalAlignment="Center"
|
||||
IsSnapToTickEnabled="True"
|
||||
Maximum="4"
|
||||
Minimum="1"
|
||||
TickFrequency="1"
|
||||
ToolTip.Tip="{locale:Locale IconSizeTooltip}"
|
||||
Value="{Binding GridSizeScale}" />
|
||||
<CheckBox
|
||||
Margin="0"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding ShowNames, Mode=TwoWay}"
|
||||
IsVisible="{Binding IsGrid}">
|
||||
<TextBlock Margin="5,3,0,0" Text="{locale:Locale CommonShowNames}" />
|
||||
</CheckBox>
|
||||
<TextBox
|
||||
Name="SearchBox"
|
||||
MinWidth="200"
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
DockPanel.Dock="Right"
|
||||
KeyUp="SearchBox_OnKeyUp"
|
||||
Text="{Binding SearchText}"
|
||||
Watermark="{locale:Locale MenuSearch}" />
|
||||
<ui:DropDownButton
|
||||
Width="150"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="{Binding SortName}"
|
||||
DockPanel.Dock="Right">
|
||||
<ui:DropDownButton.Flyout>
|
||||
<Flyout Placement="Bottom">
|
||||
<StackPanel
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel>
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale CommonFavorite}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByFavorite, Mode=OneTime}"
|
||||
Tag="Favorite" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderApplication}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
|
||||
Tag="Title" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderDeveloper}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByDeveloper, Mode=OneTime}"
|
||||
Tag="Developer" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderTimePlayed}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByTimePlayed, Mode=OneTime}"
|
||||
Tag="TotalTimePlayed" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderLastPlayed}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByLastPlayed, Mode=OneTime}"
|
||||
Tag="LastPlayed" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderFileExtension}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByType, Mode=OneTime}"
|
||||
Tag="FileType" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderFileSize}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedBySize, Mode=OneTime}"
|
||||
Tag="FileSize" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderPath}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByPath, Mode=OneTime}"
|
||||
Tag="Path" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
Width="60"
|
||||
Height="2"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
BorderBrush="White"
|
||||
BorderThickness="0,1,0,0">
|
||||
<Separator Height="0" HorizontalAlignment="Stretch" />
|
||||
</Border>
|
||||
<RadioButton
|
||||
Checked="Order_Checked"
|
||||
Content="{locale:Locale OrderAscending}"
|
||||
GroupName="Order"
|
||||
IsChecked="{Binding IsAscending, Mode=OneTime}"
|
||||
Tag="Ascending" />
|
||||
<RadioButton
|
||||
Checked="Order_Checked"
|
||||
Content="{locale:Locale OrderDescending}"
|
||||
GroupName="Order"
|
||||
IsChecked="{Binding !IsAscending, Mode=OneTime}"
|
||||
Tag="Descending" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</ui:DropDownButton.Flyout>
|
||||
</ui:DropDownButton>
|
||||
<TextBlock
|
||||
Margin="10,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Right"
|
||||
Text="{locale:Locale CommonSort}" />
|
||||
</DockPanel>
|
||||
</UserControl>
|
54
Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs
Normal file
54
Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
public partial class MainViewControls : UserControl
|
||||
{
|
||||
public MainWindowViewModel ViewModel;
|
||||
|
||||
public MainViewControls()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
if (VisualRoot is MainWindow window)
|
||||
{
|
||||
ViewModel = window.ViewModel;
|
||||
}
|
||||
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
|
||||
public void Sort_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton button)
|
||||
{
|
||||
ViewModel.Sort(Enum.Parse<ApplicationSort>(button.Tag.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
public void Order_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton button)
|
||||
{
|
||||
ViewModel.Sort(button.Tag.ToString() != "Descending");
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
ViewModel.SearchText = SearchBox.Text;
|
||||
}
|
||||
}
|
||||
}
|
81
Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml
Normal file
81
Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml
Normal file
@@ -0,0 +1,81 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsAudioView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer
|
||||
Name="AudioPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabAudio}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemAudioBackend}"
|
||||
ToolTip.Tip="{locale:Locale AudioBackendTooltip}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding AudioBackend}"
|
||||
Width="350"
|
||||
HorizontalContentAlignment="Left">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendDummy}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsOpenAlEnabled}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendOpenAL}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsSoundIoEnabled}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSoundIO}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsSDL2Enabled}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSDL2}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemAudioVolume}"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Width="250" />
|
||||
<ui:NumberBox Value="{Binding Volume}"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Width="350"
|
||||
SmallChange="1"
|
||||
LargeChange="10"
|
||||
SimpleNumberFormat="F0"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Minimum="0"
|
||||
Maximum="100" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<Slider Value="{Binding Volume}"
|
||||
Margin="250,0,0,0"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
SmallChange="5"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True"
|
||||
LargeChange="10"
|
||||
Width="350" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
12
Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs
Normal file
12
Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsAudioView : UserControl
|
||||
{
|
||||
public SettingsAudioView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
72
Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml
Normal file
72
Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml
Normal file
@@ -0,0 +1,72 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsCPUView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer
|
||||
Name="CpuPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuCache}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnablePptc}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnablePptc}"
|
||||
ToolTip.Tip="{locale:Locale PptcToggleTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuMemory}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemMemoryManagerMode}"
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding MemoryMode}"
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerSoftwareTooltip}">
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemMemoryManagerModeSoftware}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerHostTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemMemoryManagerModeHost}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerUnsafeTooltip}">
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemMemoryManagerModeHostUnchecked}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
12
Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs
Normal file
12
Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsCPUView : UserControl
|
||||
{
|
||||
public SettingsCPUView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
218
Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml
Normal file
218
Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml
Normal file
@@ -0,0 +1,218 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsGraphicsView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer
|
||||
Name="GraphicsPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsAPI}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsBackend}"
|
||||
Width="250" />
|
||||
<ComboBox Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
|
||||
SelectedIndex="{Binding GraphicsBackendIndex}">
|
||||
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}">
|
||||
<TextBlock Text="Vulkan" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
|
||||
<TextBlock Text="OpenGL" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding IsVulkanSelected}">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsPreferredGpu}"
|
||||
Width="250" />
|
||||
<ComboBox Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
|
||||
SelectedIndex="{Binding PreferredGpuIndex}"
|
||||
Items="{Binding AvailableGpus}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsFeatures}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableShaderCache}"
|
||||
ToolTip.Tip="{locale:Locale ShaderCacheToggleTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsEnableShaderCache}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableTextureRecompression}"
|
||||
ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsEnableTextureRecompression}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableMacroHLE}"
|
||||
ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsResolutionScale}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding ResolutionScale}"
|
||||
Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleCustom}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleNative}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale2x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale3x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale4x}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<ui:NumberBox
|
||||
Margin="10,0,0,0"
|
||||
ToolTip.Tip="{locale:Locale ResolutionScaleEntryTooltip}"
|
||||
MinWidth="150"
|
||||
SmallChange="0.1"
|
||||
LargeChange="1"
|
||||
SimpleNumberFormat="F2"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
IsVisible="{Binding IsCustomResolutionScaleActive}"
|
||||
Maximum="100"
|
||||
Minimum="0.1"
|
||||
Value="{Binding CustomResolutionScale}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding MaxAnisotropy}"
|
||||
Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale AnisotropyTooltip}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabGraphicsAnisotropicFilteringAuto}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering2x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering4x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering8x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering16x}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale AspectRatioTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsAspectRatio}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding AspectRatio}"
|
||||
Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale AspectRatioTooltip}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio4x3}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x9}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x10}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio21x9}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio32x9}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatioStretch}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale GraphicsBackendThreadingTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsBackendMultithreading}"
|
||||
Width="250" />
|
||||
<ComboBox Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale GalThreadingTooltip}"
|
||||
SelectedIndex="{Binding GraphicsBackendMultithreadingIndex}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale CommonAuto}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale CommonOff}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale CommonOn}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsShaderDumpPath}"
|
||||
Width="250" />
|
||||
<TextBox Text="{Binding ShaderDumpPath}"
|
||||
Width="350"
|
||||
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
12
Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs
Normal file
12
Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsGraphicsView : UserControl
|
||||
{
|
||||
public SettingsGraphicsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
104
Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml
Normal file
104
Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml
Normal file
@@ -0,0 +1,104 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Resources>
|
||||
<helpers:KeyValueConverter x:Key="Key" />
|
||||
</UserControl.Resources>
|
||||
<ScrollViewer
|
||||
Name="HotkeysPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ToggleVsync, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.Screenshot, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.Pause, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ToggleMute, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ResScaleUp, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ResScaleDown, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</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>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
81
Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs
Normal file
81
Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.Input;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.Assigner;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsHotkeysView : UserControl
|
||||
{
|
||||
private ButtonKeyAssigner _currentAssigner;
|
||||
private IGamepadDriver AvaloniaKeyboardDriver;
|
||||
|
||||
public SettingsHotkeysView()
|
||||
{
|
||||
InitializeComponent();
|
||||
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
|
||||
}
|
||||
|
||||
private void MouseClick(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
|
||||
|
||||
_currentAssigner?.Cancel(shouldUnbind);
|
||||
|
||||
PointerPressed -= MouseClick;
|
||||
}
|
||||
|
||||
private void Button_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ToggleButton button)
|
||||
{
|
||||
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentAssigner == null && button.IsChecked != null && (bool)button.IsChecked)
|
||||
{
|
||||
_currentAssigner = new ButtonKeyAssigner(button);
|
||||
|
||||
FocusManager.Instance?.Focus(this, NavigationMethod.Pointer);
|
||||
|
||||
PointerPressed += MouseClick;
|
||||
|
||||
var keyboard = (IKeyboard)AvaloniaKeyboardDriver.GetGamepad(AvaloniaKeyboardDriver.GamepadsIds[0]);
|
||||
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
|
||||
|
||||
_currentAssigner.GetInputAndAssign(assigner);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentAssigner != null)
|
||||
{
|
||||
ToggleButton oldButton = _currentAssigner.ToggledButton;
|
||||
|
||||
_currentAssigner.Cancel();
|
||||
_currentAssigner = null;
|
||||
|
||||
button.IsChecked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Button_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
}
|
||||
}
|
||||
}
|
46
Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml
Normal file
46
Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml
Normal file
@@ -0,0 +1,46 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsInputView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer
|
||||
Name="InputPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel Margin="4" Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<CheckBox Margin="5,0"
|
||||
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
|
||||
IsChecked="{Binding EnableDockedMode}">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
|
||||
</CheckBox>
|
||||
<CheckBox Margin="5,0"
|
||||
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
|
||||
IsChecked="{Binding EnableKeyboard}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
|
||||
</CheckBox>
|
||||
<CheckBox Margin="5,0"
|
||||
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
|
||||
IsChecked="{Binding EnableMouse}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<window:ControllerSettingsWindow Name="ControllerSettings" Margin="0" MinHeight="600" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
17
Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs
Normal file
17
Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsInputView : UserControl
|
||||
{
|
||||
public SettingsInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ControllerSettings.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
118
Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml
Normal file
118
Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml
Normal file
@@ -0,0 +1,118 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsLoggingView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer
|
||||
Name="LoggingPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingLogging}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableFileLog}"
|
||||
ToolTip.Tip="{locale:Locale FileLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableLoggingToFile}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableStub}"
|
||||
ToolTip.Tip="{locale:Locale StubLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableStubLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableInfo}"
|
||||
ToolTip.Tip="{locale:Locale InfoLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableInfoLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableWarn}"
|
||||
ToolTip.Tip="{locale:Locale WarnLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableWarningLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableError}"
|
||||
ToolTip.Tip="{locale:Locale ErrorLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableErrorLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableTrace}"
|
||||
ToolTip.Tip="{locale:Locale TraceLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableTraceLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableGuest}"
|
||||
ToolTip.Tip="{locale:Locale GuestLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableGuestLogs}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableDebug}"
|
||||
ToolTip.Tip="{locale:Locale DebugLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableDebugLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableFsAccessLog}"
|
||||
ToolTip.Tip="{locale:Locale FileAccessLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableFsAccessLogs}" />
|
||||
</CheckBox>
|
||||
<StackPanel Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Stretch">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale FSAccessLogModeTooltip}"
|
||||
Text="{locale:Locale SettingsTabLoggingFsGlobalAccessLogMode}"
|
||||
Width="285" />
|
||||
<ui:NumberBox
|
||||
Maximum="3"
|
||||
Minimum="0"
|
||||
Width="150"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
SmallChange="1"
|
||||
LargeChange="1"
|
||||
Value="{Binding FsGlobalAccessLogMode}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,10,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevel}"
|
||||
ToolTip.Tip="{locale:Locale OpenGlLogLevel}"
|
||||
Width="285" />
|
||||
<ComboBox SelectedIndex="{Binding OpenglDebugLevel}"
|
||||
Width="150"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale OpenGlLogLevel}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelError}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelPerformance}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelAll}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
12
Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs
Normal file
12
Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsLoggingView : UserControl
|
||||
{
|
||||
public SettingsLoggingView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
35
Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml
Normal file
35
Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml
Normal file
@@ -0,0 +1,35 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsNetworkView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer
|
||||
Name="NetworkPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabNetworkConnection}" />
|
||||
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
|
||||
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
12
Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs
Normal file
12
Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsNetworkView : UserControl
|
||||
{
|
||||
public SettingsNetworkView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
195
Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml
Normal file
195
Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml
Normal file
@@ -0,0 +1,195 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsSystemView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer
|
||||
Name="SystemPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemSystemRegion}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding Region}"
|
||||
ToolTip.Tip="{locale:Locale RegionTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionUSA}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionEurope}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionAustralia}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionChina}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionKorea}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionTaiwan}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguage}"
|
||||
ToolTip.Tip="{locale:Locale LanguageTooltip}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding Language}"
|
||||
ToolTip.Tip="{locale:Locale LanguageTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageGerman}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageItalian}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageSpanish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageChinese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageKorean}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageDutch}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguagePortuguese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageRussian}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
|
||||
ToolTip.Tip="{locale:Locale TimezoneTooltip}"
|
||||
Width="250" />
|
||||
<AutoCompleteBox
|
||||
Name="TimeZoneBox"
|
||||
Width="350"
|
||||
MaxDropDownHeight="500"
|
||||
FilterMode="Contains"
|
||||
Items="{Binding TimeZones}"
|
||||
SelectionChanged="TimeZoneBox_OnSelectionChanged"
|
||||
Text="{Binding Path=TimeZone, Mode=OneWay}"
|
||||
TextChanged="TimeZoneBox_OnTextChanged"
|
||||
ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemSystemTime}"
|
||||
ToolTip.Tip="{locale:Locale TimeTooltip}"
|
||||
Width="250"/>
|
||||
<DatePicker VerticalAlignment="Center" SelectedDate="{Binding DateOffset}"
|
||||
ToolTip.Tip="{locale:Locale TimeTooltip}"
|
||||
Width="350" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="250,0,0,10" Orientation="Horizontal">
|
||||
<TimePicker
|
||||
VerticalAlignment="Center"
|
||||
ClockIdentifier="24HourClock"
|
||||
SelectedTime="{Binding TimeOffset}"
|
||||
Width="350"
|
||||
ToolTip.Tip="{locale:Locale TimeTooltip}" />
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding EnableVsync}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}"
|
||||
ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}"
|
||||
ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" />
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemHacksNote}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding ExpandDramSize}"
|
||||
ToolTip.Tip="{locale:Locale DRamTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding IgnoreMissingServices}"
|
||||
ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
52
Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs
Normal file
52
Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Converters;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsSystemView : UserControl
|
||||
{
|
||||
public SettingsViewModel ViewModel;
|
||||
|
||||
public SettingsSystemView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
|
||||
MultiBinding tzMultiBinding = new() { Converter = converter };
|
||||
|
||||
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
|
||||
tzMultiBinding.Bindings.Add(new Binding("Location"));
|
||||
tzMultiBinding.Bindings.Add(new Binding("Abbreviation"));
|
||||
|
||||
TimeZoneBox.ValueMemberBinding = tzMultiBinding;
|
||||
}
|
||||
|
||||
private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.AddedItems != null && e.AddedItems.Count > 0)
|
||||
{
|
||||
if (e.AddedItems[0] is TimeZone timeZone)
|
||||
{
|
||||
e.Handled = true;
|
||||
|
||||
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TimeZoneBox_OnTextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is AutoCompleteBox box && box.SelectedItem is TimeZone timeZone)
|
||||
{
|
||||
{
|
||||
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
156
Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml
Normal file
156
Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml
Normal file
@@ -0,0 +1,156 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUIView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer
|
||||
Name="UiPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGeneral}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableDiscordIntegration}">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ToggleDiscordTooltip}"
|
||||
Text="{locale:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding CheckUpdatesOnStart}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralCheckUpdatesOnLaunch}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding ShowConfirmExit}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding HideCursorOnIdle}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<ListBox
|
||||
Name="GameList"
|
||||
MinHeight="230"
|
||||
Items="{Binding GameDirectories}">
|
||||
<ListBox.Styles>
|
||||
<Style Selector="ListBoxItem">
|
||||
<Setter Property="Padding" Value="10" />
|
||||
<Setter Property="Background" Value="{DynamicResource ListBoxBackground}" />
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
</ListBox>
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox
|
||||
Name="PathBox"
|
||||
Margin="0"
|
||||
ToolTip.Tip="{locale:Locale AddGameDirBoxTooltip}"
|
||||
VerticalAlignment="Stretch" />
|
||||
<Button
|
||||
Name="AddButton"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="10,0,0,0"
|
||||
ToolTip.Tip="{locale:Locale AddGameDirTooltip}"
|
||||
Click="AddButton_OnClick">
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabGeneralAdd}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="RemoveButton"
|
||||
Grid.Column="2"
|
||||
MinWidth="90"
|
||||
Margin="10,0,0,0"
|
||||
ToolTip.Tip="{locale:Locale RemoveGameDirTooltip}"
|
||||
Click="RemoveButton_OnClick">
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabGeneralRemove}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralTheme}" />
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<CheckBox
|
||||
IsChecked="{Binding EnableCustomTheme}"
|
||||
ToolTip.Tip="{locale:Locale CustomThemeCheckTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeEnableCustomTheme}" />
|
||||
</CheckBox>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,10,0,0"
|
||||
Text="{locale:Locale SettingsTabGeneralThemeCustomTheme}"
|
||||
ToolTip.Tip="{locale:Locale CustomThemePathTooltip}" />
|
||||
<TextBox
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="0,10,0,0"
|
||||
Text="{Binding CustomThemePath}" />
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="10,10,0,0"
|
||||
Click="BrowseTheme"
|
||||
ToolTip.Tip="{locale:Locale CustomThemeBrowseTooltip}"
|
||||
Content="{locale:Locale ButtonBrowse}" />
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Grid.Row="2"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,10,0,0"
|
||||
Text="{locale:Locale SettingsTabGeneralThemeBaseStyle}" />
|
||||
<ComboBox
|
||||
Grid.Column="1"
|
||||
Grid.Row="2"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,10,0,0"
|
||||
MinWidth="100"
|
||||
SelectedIndex="{Binding BaseStyleIndex}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleLight}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleDark}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
82
Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs
Normal file
82
Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsUIView : UserControl
|
||||
{
|
||||
public SettingsViewModel ViewModel;
|
||||
|
||||
public SettingsUIView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void AddButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string path = PathBox.Text;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path))
|
||||
{
|
||||
ViewModel.GameDirectories.Add(path);
|
||||
ViewModel.DirectoryChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
path = await new OpenFolderDialog().ShowAsync(desktop.MainWindow);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
ViewModel.GameDirectories.Add(path);
|
||||
ViewModel.DirectoryChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
int oldIndex = GameList.SelectedIndex;
|
||||
|
||||
foreach (string path in new List<string>(GameList.SelectedItems.Cast<string>()))
|
||||
{
|
||||
ViewModel.GameDirectories.Remove(path);
|
||||
ViewModel.DirectoryChanged = true;
|
||||
}
|
||||
|
||||
if (GameList.ItemCount > 0)
|
||||
{
|
||||
GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public async void BrowseTheme(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dialog = new OpenFileDialog()
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
|
||||
AllowMultiple = false
|
||||
};
|
||||
|
||||
dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
|
||||
|
||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var file = await dialog.ShowAsync(desktop.MainWindow);
|
||||
|
||||
if (file != null && file.Length > 0)
|
||||
{
|
||||
ViewModel.CustomThemePath = file[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,282 +1,247 @@
|
||||
<window:StyleableWindow
|
||||
<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: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:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
Width="850"
|
||||
Height="550"
|
||||
MinWidth="500"
|
||||
MinHeight="550"
|
||||
d:DesignHeight="350"
|
||||
d:DesignWidth="400"
|
||||
CanResize="False"
|
||||
SizeToContent="Width"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<Grid
|
||||
Margin="15"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:viewModel="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
Width="550"
|
||||
Height="260"
|
||||
Margin="0,-12,0,0"
|
||||
d:DesignHeight="260"
|
||||
d:DesignWidth="550"
|
||||
x:CompileBindings="True"
|
||||
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"
|
||||
Margin="20"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="0"
|
||||
Height="110"
|
||||
MinWidth="50"
|
||||
Margin="5,10,20,10"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="0,20,0,0"
|
||||
FontSize="35"
|
||||
Text="Ryujinx"
|
||||
TextAlignment="Center" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,0"
|
||||
FontSize="16"
|
||||
Text="(REE-YOU-JINX)"
|
||||
TextAlignment="Center" />
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="0"
|
||||
<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" />
|
||||
<Grid
|
||||
Grid.Column="2"
|
||||
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
|
||||
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://www.ryujinx.org/">
|
||||
<TextBlock
|
||||
Text="www.ryujinx.org"
|
||||
TextAlignment="Center"
|
||||
TextDecorations="Underline"
|
||||
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Center" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Margin="20"
|
||||
HorizontalAlignment="Center"
|
||||
MaxLines="2"
|
||||
Text="{locale:Locale AboutDisclaimerMessage}"
|
||||
TextAlignment="Center" />
|
||||
<TextBlock
|
||||
Name="AmiiboLabel"
|
||||
Grid.Row="3"
|
||||
Margin="20"
|
||||
HorizontalAlignment="Center"
|
||||
MaxLines="2"
|
||||
PointerPressed="AmiiboLabel_OnPointerPressed"
|
||||
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
|
||||
TextAlignment="Center" />
|
||||
<StackPanel
|
||||
Grid.Row="4"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="10">
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
|
||||
<Button
|
||||
Height="65"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://www.patreon.com/ryujinx">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Patreon.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Patreon" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
|
||||
<Button
|
||||
Height="65"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_GitHub.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="GitHub" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
|
||||
<Button
|
||||
Height="65"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://discordapp.com/invite/N2FmfVc">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Discord.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Discord" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
|
||||
<Button
|
||||
Height="65"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://twitter.com/RyujinxEmu">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Twitter.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Twitter" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="2"
|
||||
Margin="5"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="White"
|
||||
BorderThickness="1,0,0,0">
|
||||
<Separator Width="0" />
|
||||
</Border>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="20"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale AboutRyujinxAboutTitle}"
|
||||
TextDecorations="Underline" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="20,5,5,5"
|
||||
LineHeight="20"
|
||||
Text="{locale:Locale AboutRyujinxAboutContent}" />
|
||||
<TextBlock
|
||||
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"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale AboutRyujinxMaintainersTitle}"
|
||||
TextDecorations="Underline" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Margin="20,5,5,5"
|
||||
LineHeight="20"
|
||||
Text="{Binding Developers}" />
|
||||
<Button
|
||||
Grid.Row="4"
|
||||
HorizontalAlignment="Right"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a">
|
||||
<TextBlock
|
||||
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
|
||||
TextAlignment="Right"
|
||||
TextDecorations="Underline"
|
||||
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Margin="0,0,0,0"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale AboutRyujinxSupprtersTitle}"
|
||||
TextDecorations="Underline" />
|
||||
<Border
|
||||
Grid.Row="6"
|
||||
Width="460"
|
||||
Height="200"
|
||||
Margin="20,5"
|
||||
Padding="5"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="White"
|
||||
BorderThickness="1">
|
||||
<TextBlock
|
||||
Name="SupportersTextBlock"
|
||||
VerticalAlignment="Top"
|
||||
Text="{Binding Supporters}"
|
||||
TextWrapping="Wrap" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
||||
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>
|
@@ -1,38 +1,47 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
using Button = Avalonia.Controls.Button;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public partial class AboutWindow : StyleableWindow
|
||||
public partial class AboutWindow : UserControl
|
||||
{
|
||||
public AboutWindow()
|
||||
{
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.MenuBarHelpAbout];
|
||||
}
|
||||
|
||||
Version = Program.Version;
|
||||
|
||||
DataContext = this;
|
||||
DataContext = new AboutWindowViewModel();
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
_ = DownloadPatronsJson();
|
||||
}
|
||||
|
||||
public string Supporters { get; set; }
|
||||
public string Version { get; set; }
|
||||
public static async Task Show()
|
||||
{
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
|
||||
Content = new AboutWindow()
|
||||
};
|
||||
|
||||
public string Developers => string.Format(LocaleManager.Instance[LocaleKeys.AboutPageDeveloperListMore], "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD«");
|
||||
Style closeButton = new(x => x.Name("CloseButton"));
|
||||
closeButton.Setters.Add(new Setter(WidthProperty, 80d));
|
||||
|
||||
Style closeButtonParent = new(x => x.Name("CommandSpace"));
|
||||
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, Avalonia.Layout.HorizontalAlignment.Right));
|
||||
|
||||
contentDialog.Styles.Add(closeButton);
|
||||
contentDialog.Styles.Add(closeButtonParent);
|
||||
|
||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||
}
|
||||
|
||||
private void Button_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
@@ -42,31 +51,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadPatronsJson()
|
||||
{
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
{
|
||||
Supporters = LocaleManager.Instance[LocaleKeys.ConnectionError];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
HttpClient httpClient = new();
|
||||
|
||||
try
|
||||
{
|
||||
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
||||
|
||||
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString));
|
||||
}
|
||||
catch
|
||||
{
|
||||
Supporters = LocaleManager.Instance[LocaleKeys.ApiError];
|
||||
}
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() => SupportersTextBlock.Text = Supporters);
|
||||
}
|
||||
|
||||
private void AmiiboLabel_OnPointerPressed(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (sender is TextBlock)
|
||||
|
@@ -2,15 +2,16 @@
|
||||
x:Class="Ryujinx.Ava.UI.Windows.MainWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
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:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
Title="Ryujinx"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
xmlns:main="clr-namespace:Ryujinx.Ava.UI.Views.Main"
|
||||
Cursor="{Binding Cursor}"
|
||||
Title="{Binding Title}"
|
||||
WindowState="{Binding WindowState}"
|
||||
Width="1280"
|
||||
Height="777"
|
||||
MinWidth="1092"
|
||||
@@ -66,206 +67,8 @@
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding ShowMenuAndStatusBar}"
|
||||
Orientation="Vertical">
|
||||
<DockPanel HorizontalAlignment="Stretch">
|
||||
<Menu
|
||||
Name="Menu"
|
||||
Height="35"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Left">
|
||||
<Menu.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<DockPanel Margin="0" HorizontalAlignment="Stretch" />
|
||||
</ItemsPanelTemplate>
|
||||
</Menu.ItemsPanel>
|
||||
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarFile}">
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenFile}"
|
||||
Header="{locale:Locale MenuBarFileOpenFromFile}"
|
||||
IsEnabled="{Binding EnableNonGameRunningControls}"
|
||||
ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenFolder}"
|
||||
Header="{locale:Locale MenuBarFileOpenUnpacked}"
|
||||
IsEnabled="{Binding EnableNonGameRunningControls}"
|
||||
ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
|
||||
<MenuItem Header="{locale:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}">
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenMiiApplet}"
|
||||
Header="Mii Edit Applet"
|
||||
ToolTip.Tip="{locale:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenRyujinxFolder}"
|
||||
Header="{locale:Locale MenuBarFileOpenEmuFolder}"
|
||||
ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenLogsFolder}"
|
||||
Header="{locale:Locale MenuBarFileOpenLogsFolder}"
|
||||
ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding CloseWindow}"
|
||||
Header="{locale:Locale MenuBarFileExit}"
|
||||
ToolTip.Tip="{locale:Locale ExitTooltip}" />
|
||||
</MenuItem>
|
||||
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarOptions}">
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ToggleFullscreen}"
|
||||
Header="{locale:Locale MenuBarOptionsToggleFullscreen}"
|
||||
InputGesture="F11" />
|
||||
<MenuItem>
|
||||
<MenuItem.Icon>
|
||||
<CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}"
|
||||
MinWidth="250">
|
||||
<TextBlock Text="{locale:Locale MenuBarOptionsStartGamesInFullscreen}"/>
|
||||
</CheckBox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem IsVisible="{Binding ShowConsoleVisible}">
|
||||
<MenuItem.Icon>
|
||||
<CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}"
|
||||
MinWidth="250">
|
||||
<TextBlock Text="{locale:Locale MenuBarOptionsShowConsole}"/>
|
||||
</CheckBox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="{locale:Locale MenuBarOptionsChangeLanguage}">
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="de_DE"
|
||||
Header="Deutsch" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="en_US"
|
||||
Header="English (US)" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="es_ES"
|
||||
Header="Español (ES)" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="fr_FR"
|
||||
Header="Français" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="it_IT"
|
||||
Header="Italiano" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="pt_BR"
|
||||
Header="Português (BR)" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="tr_TR"
|
||||
Header="Türkçe" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="el_GR"
|
||||
Header="Ελληνικά" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="pl_PL"
|
||||
Header="Polski" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="ru_RU"
|
||||
Header="Русский" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="zh_CN"
|
||||
Header="简体中文" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="zh_TW"
|
||||
Header="繁體中文" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="ja_JP"
|
||||
Header="日本語" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="ko_KR"
|
||||
Header="한국어" />
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenSettings}"
|
||||
Header="{locale:Locale MenuBarOptionsSettings}"
|
||||
ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ManageProfiles}"
|
||||
Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
|
||||
IsEnabled="{Binding EnableNonGameRunningControls}"
|
||||
ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
Name="ActionsMenuItem"
|
||||
VerticalAlignment="Center"
|
||||
Header="{locale:Locale MenuBarActions}"
|
||||
IsEnabled="{Binding IsGameRunning}">
|
||||
<MenuItem
|
||||
Click="PauseEmulation_Click"
|
||||
Header="{locale:Locale MenuBarOptionsPauseEmulation}"
|
||||
InputGesture="{Binding PauseKey}"
|
||||
IsEnabled="{Binding !IsPaused}"
|
||||
IsVisible="{Binding !IsPaused}" />
|
||||
<MenuItem
|
||||
Click="ResumeEmulation_Click"
|
||||
Header="{locale:Locale MenuBarOptionsResumeEmulation}"
|
||||
InputGesture="{Binding PauseKey}"
|
||||
IsEnabled="{Binding IsPaused}"
|
||||
IsVisible="{Binding IsPaused}" />
|
||||
<MenuItem
|
||||
Click="StopEmulation_Click"
|
||||
Header="{locale:Locale MenuBarOptionsStopEmulation}"
|
||||
InputGesture="Escape"
|
||||
IsEnabled="{Binding IsGameRunning}"
|
||||
ToolTip.Tip="{locale:Locale StopEmulationTooltip}" />
|
||||
<MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Name="ScanAmiiboMenuItem"
|
||||
AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
|
||||
Command="{ReflectionBinding OpenAmiiboWindow}"
|
||||
Header="{locale:Locale MenuBarActionsScanAmiibo}"
|
||||
IsEnabled="{Binding IsAmiiboRequested}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding TakeScreenshot}"
|
||||
Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
|
||||
InputGesture="{Binding ScreenshotKey}"
|
||||
IsEnabled="{Binding IsGameRunning}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding HideUi}"
|
||||
Header="{locale:Locale MenuBarFileToolsHideUi}"
|
||||
InputGesture="{Binding ShowUiKey}"
|
||||
IsEnabled="{Binding IsGameRunning}" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenCheatManagerForCurrentApp}"
|
||||
Header="{locale:Locale GameListContextMenuManageCheat}"
|
||||
IsEnabled="{Binding IsGameRunning}" />
|
||||
</MenuItem>
|
||||
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarTools}">
|
||||
<MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" IsEnabled="{Binding EnableNonGameRunningControls}">
|
||||
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
|
||||
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
|
||||
<MenuItem
|
||||
Name="UpdateMenuItem"
|
||||
Command="{ReflectionBinding CheckForUpdates}"
|
||||
Header="{locale:Locale MenuBarHelpCheckForUpdates}"
|
||||
ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding OpenAboutWindow}"
|
||||
Header="{locale:Locale MenuBarHelpAbout}"
|
||||
ToolTip.Tip="{locale:Locale OpenAboutTooltip}" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</DockPanel>
|
||||
<main:MainMenuBarView
|
||||
Name="MenuBarView" />
|
||||
</StackPanel>
|
||||
<ContentControl
|
||||
Name="MainContent"
|
||||
@@ -277,170 +80,14 @@
|
||||
BorderThickness="0,0,0,0"
|
||||
DockPanel.Dock="Top"
|
||||
IsVisible="{Binding ShowContent}">
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Name="GameLibrary">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<DockPanel
|
||||
Grid.Row="0"
|
||||
Margin="0,0,0,5"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Button
|
||||
Width="40"
|
||||
MinWidth="40"
|
||||
Margin="5,2,0,2"
|
||||
VerticalAlignment="Stretch"
|
||||
Command="{ReflectionBinding SetListMode}"
|
||||
IsEnabled="{Binding IsGrid}">
|
||||
<ui:FontIcon
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
|
||||
Glyph="{helpers:GlyphValueConverter List}" />
|
||||
</Button>
|
||||
<Button
|
||||
Width="40"
|
||||
MinWidth="40"
|
||||
Margin="5,2,5,2"
|
||||
VerticalAlignment="Stretch"
|
||||
Command="{ReflectionBinding SetGridMode}"
|
||||
IsEnabled="{Binding IsList}">
|
||||
<ui:FontIcon
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
|
||||
Glyph="{helpers:GlyphValueConverter Grid}" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Margin="10,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale IconSize}"
|
||||
ToolTip.Tip="{locale:Locale IconSizeTooltip}" />
|
||||
<Slider
|
||||
Width="150"
|
||||
Height="35"
|
||||
Margin="5,-10,5,0"
|
||||
VerticalAlignment="Center"
|
||||
IsSnapToTickEnabled="True"
|
||||
Maximum="4"
|
||||
Minimum="1"
|
||||
TickFrequency="1"
|
||||
ToolTip.Tip="{locale:Locale IconSizeTooltip}"
|
||||
Value="{Binding GridSizeScale}" />
|
||||
<CheckBox
|
||||
Margin="0"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding ShowNames, Mode=TwoWay}"
|
||||
IsVisible="{Binding IsGrid}">
|
||||
<TextBlock Margin="5,3,0,0" Text="{locale:Locale CommonShowNames}" />
|
||||
</CheckBox>
|
||||
<TextBox
|
||||
Name="SearchBox"
|
||||
MinWidth="200"
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Right"
|
||||
KeyUp="SearchBox_OnKeyUp"
|
||||
Text="{Binding SearchText}"
|
||||
Watermark="{locale:Locale MenuSearch}" />
|
||||
<ui:DropDownButton
|
||||
Width="150"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="{Binding SortName}"
|
||||
DockPanel.Dock="Right">
|
||||
<ui:DropDownButton.Flyout>
|
||||
<Flyout Placement="Bottom">
|
||||
<StackPanel
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel>
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale CommonFavorite}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByFavorite, Mode=OneTime}"
|
||||
Tag="Favorite" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderApplication}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
|
||||
Tag="Title" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderDeveloper}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByDeveloper, Mode=OneTime}"
|
||||
Tag="Developer" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderTimePlayed}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByTimePlayed, Mode=OneTime}"
|
||||
Tag="TotalTimePlayed" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderLastPlayed}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByLastPlayed, Mode=OneTime}"
|
||||
Tag="LastPlayed" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderFileExtension}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByType, Mode=OneTime}"
|
||||
Tag="FileType" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderFileSize}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedBySize, Mode=OneTime}"
|
||||
Tag="FileSize" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{locale:Locale GameListHeaderPath}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByPath, Mode=OneTime}"
|
||||
Tag="Path" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
Width="60"
|
||||
Height="2"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
BorderBrush="White"
|
||||
BorderThickness="0,1,0,0">
|
||||
<Separator Height="0" HorizontalAlignment="Stretch" />
|
||||
</Border>
|
||||
<RadioButton
|
||||
Checked="Order_Checked"
|
||||
Content="{locale:Locale OrderAscending}"
|
||||
GroupName="Order"
|
||||
IsChecked="{Binding IsAscending, Mode=OneTime}"
|
||||
Tag="Ascending" />
|
||||
<RadioButton
|
||||
Checked="Order_Checked"
|
||||
Content="{locale:Locale OrderDescending}"
|
||||
GroupName="Order"
|
||||
IsChecked="{Binding !IsAscending, Mode=OneTime}"
|
||||
Tag="Descending" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</ui:DropDownButton.Flyout>
|
||||
</ui:DropDownButton>
|
||||
<TextBlock
|
||||
Margin="10,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Right"
|
||||
Text="{locale:Locale CommonSort}" />
|
||||
</DockPanel>
|
||||
<main:MainViewControls
|
||||
Name="ViewControls"
|
||||
Grid.Row="0"/>
|
||||
<controls:GameListView
|
||||
x:Name="GameList"
|
||||
Grid.Row="1"
|
||||
@@ -512,7 +159,8 @@
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Text="{Binding LoadHeading}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="Wrap" />
|
||||
TextWrapping="Wrap"
|
||||
MaxWidth="500" />
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Margin="10"
|
||||
@@ -529,7 +177,6 @@
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{Binding ProgressBarBackgroundColor}"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="5"
|
||||
Foreground="{Binding ProgressBarForegroundColor}"
|
||||
@@ -545,226 +192,14 @@
|
||||
FontSize="18"
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Text="{Binding CacheLoadStatus}"
|
||||
TextAlignment="Left" />
|
||||
TextAlignment="Left"
|
||||
MaxWidth="500" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
Name="StatusBar"
|
||||
Grid.Row="2"
|
||||
Margin="0"
|
||||
MinHeight="22"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}"
|
||||
DockPanel.Dock="Bottom"
|
||||
IsVisible="{Binding ShowMenuAndStatusBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Margin="5"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding EnableNonGameRunningControls}">
|
||||
<Grid Margin="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Width="25"
|
||||
Height="25"
|
||||
MinWidth="0"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
Command="{ReflectionBinding LoadApplications}">
|
||||
<ui:SymbolIcon
|
||||
Width="50"
|
||||
Height="100"
|
||||
Symbol="Refresh" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Name="LoadStatus"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding EnableNonGameRunningControls}"
|
||||
Text="{locale:Locale StatusBarGamesLoaded}" />
|
||||
<ProgressBar
|
||||
Name="LoadProgressBar"
|
||||
Grid.Column="2"
|
||||
Height="6"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource HighlightColor}"
|
||||
IsVisible="{Binding EnableNonGameRunningControls}"
|
||||
Maximum="{Binding StatusBarProgressMaximum}"
|
||||
Value="{Binding StatusBarProgressValue}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Margin="0,2"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsGameRunning}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Name="VsyncStatus"
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{Binding VsyncColor}"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerReleased="VsyncStatus_PointerReleased"
|
||||
Text="VSync"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Name="DockedStatus"
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerReleased="DockedStatus_PointerReleased"
|
||||
Text="{Binding DockedStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Name="AspectRatioStatus"
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerReleased="AspectRatioStatus_PointerReleased"
|
||||
Text="{Binding AspectRatioStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<ui:ToggleSplitButton
|
||||
Name="VolumeStatus"
|
||||
Padding="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}"
|
||||
BorderThickness="0"
|
||||
Content="{Binding VolumeStatusText}"
|
||||
IsChecked="{Binding VolumeMuted}"
|
||||
IsVisible="{Binding !ShowLoadProgress}">
|
||||
<ui:ToggleSplitButton.Flyout>
|
||||
<Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||
<Grid Margin="0">
|
||||
<Slider
|
||||
MaxHeight="40"
|
||||
Width="150"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
IsSnapToTickEnabled="True"
|
||||
LargeChange="0.05"
|
||||
Maximum="1"
|
||||
Minimum="0"
|
||||
SmallChange="0.01"
|
||||
TickFrequency="0.05"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Value="{Binding Volume}" />
|
||||
</Grid>
|
||||
</Flyout>
|
||||
</ui:ToggleSplitButton.Flyout>
|
||||
</ui:ToggleSplitButton>
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding GameStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding FifoStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding BackendText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding GpuNameText}"
|
||||
TextAlignment="Left" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="3"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding ShowFirmwareStatus}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Name="FirmwareStatus"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale StatusBarSystemVersion}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<main:MainStatusBarView
|
||||
Name="StatusBarView"
|
||||
Grid.Row="2" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
||||
</window:StyleableWindow>
|
@@ -1,19 +1,14 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Input;
|
||||
using Ryujinx.Ava.UI.Applet;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
@@ -25,11 +20,9 @@ using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using InputManager = Ryujinx.Input.HLE.InputManager;
|
||||
|
||||
@@ -38,19 +31,14 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
public partial class MainWindow : StyleableWindow
|
||||
{
|
||||
internal static MainWindowViewModel MainWindowViewModel { get; private set; }
|
||||
private bool _canUpdate;
|
||||
private bool _isClosing;
|
||||
private bool _isLoading;
|
||||
|
||||
private Control _mainViewContent;
|
||||
private bool _isLoading;
|
||||
|
||||
private UserChannelPersistence _userChannelPersistence;
|
||||
private static bool _deferLoad;
|
||||
private static string _launchPath;
|
||||
private static bool _startFullscreen;
|
||||
private string _currentEmulatedGamePath;
|
||||
internal readonly AvaHostUiHandler UiHandler;
|
||||
private AutoResetEvent _rendererWaitEvent;
|
||||
|
||||
public VirtualFileSystem VirtualFileSystem { get; private set; }
|
||||
public ContentManager ContentManager { get; private set; }
|
||||
@@ -58,30 +46,17 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public LibHacHorizonManager LibHacHorizonManager { get; private set; }
|
||||
|
||||
internal AppHost AppHost { get; private set; }
|
||||
public InputManager InputManager { get; private set; }
|
||||
|
||||
internal RendererHost RendererControl { get; private set; }
|
||||
internal MainWindowViewModel ViewModel { get; private set; }
|
||||
public SettingsWindow SettingsWindow { get; set; }
|
||||
|
||||
public bool CanUpdate
|
||||
{
|
||||
get => _canUpdate;
|
||||
set
|
||||
{
|
||||
_canUpdate = value;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() => UpdateMenuItem.IsEnabled = _canUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ShowKeyErrorOnLoad { get; set; }
|
||||
public ApplicationLibrary ApplicationLibrary { get; set; }
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
ViewModel = new MainWindowViewModel(this);
|
||||
ViewModel = new MainWindowViewModel();
|
||||
|
||||
MainWindowViewModel = ViewModel;
|
||||
|
||||
@@ -92,10 +67,10 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
UiHandler = new AvaHostUiHandler(this);
|
||||
|
||||
Title = $"Ryujinx {Program.Version}";
|
||||
ViewModel.Title = $"Ryujinx {Program.Version}";
|
||||
|
||||
// NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point.
|
||||
double barHeight = MenuBar.MinHeight + StatusBar.MinHeight;
|
||||
double barHeight = MenuBar.MinHeight + StatusBarView.StatusBar.MinHeight;
|
||||
Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight;
|
||||
Width /= Program.WindowScaleFactor;
|
||||
|
||||
@@ -103,14 +78,37 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
Initialize();
|
||||
|
||||
ViewModel.Initialize();
|
||||
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
||||
|
||||
ViewModel.Initialize(
|
||||
ContentManager,
|
||||
ApplicationLibrary,
|
||||
VirtualFileSystem,
|
||||
AccountManager,
|
||||
InputManager,
|
||||
_userChannelPersistence,
|
||||
LibHacHorizonManager,
|
||||
UiHandler,
|
||||
ShowLoading,
|
||||
SwitchToGameControl,
|
||||
SetMainContent,
|
||||
this);
|
||||
|
||||
ViewModel.RefreshFirmwareStatus();
|
||||
|
||||
LoadGameList();
|
||||
|
||||
this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged);
|
||||
}
|
||||
|
||||
_rendererWaitEvent = new AutoResetEvent(false);
|
||||
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
||||
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
||||
ViewModel.ReloadGameList += ReloadGameList;
|
||||
}
|
||||
|
||||
private void IsActiveChanged(bool obj)
|
||||
{
|
||||
ViewModel.IsActive = obj;
|
||||
}
|
||||
|
||||
public void LoadGameList()
|
||||
@@ -122,45 +120,51 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
_isLoading = true;
|
||||
|
||||
ViewModel.LoadApplications();
|
||||
LoadApplications();
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
private void Update_StatusBar(object sender, StatusUpdatedEventArgs args)
|
||||
{
|
||||
if (ViewModel.ShowMenuAndStatusBar && !ViewModel.ShowLoadProgress)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (args.VSyncEnabled)
|
||||
{
|
||||
ViewModel.VsyncColor = new SolidColorBrush(Color.Parse("#ff2eeac9"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModel.VsyncColor = new SolidColorBrush(Color.Parse("#ffff4554"));
|
||||
}
|
||||
|
||||
ViewModel.DockedStatusText = args.DockedMode;
|
||||
ViewModel.AspectRatioStatusText = args.AspectRatio;
|
||||
ViewModel.GameStatusText = args.GameStatus;
|
||||
ViewModel.VolumeStatusText = args.VolumeStatus;
|
||||
ViewModel.FifoStatusText = args.FifoStatus;
|
||||
ViewModel.GpuNameText = args.GpuName;
|
||||
ViewModel.BackendText = args.GpuBackend;
|
||||
|
||||
ViewModel.ShowStatusSeparator = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HandleScalingChanged(double scale)
|
||||
{
|
||||
Program.DesktopScaleFactor = scale;
|
||||
base.HandleScalingChanged(scale);
|
||||
}
|
||||
|
||||
public void AddApplication(ApplicationData applicationData)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
ViewModel.Applications.Add(applicationData);
|
||||
});
|
||||
}
|
||||
|
||||
private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e)
|
||||
{
|
||||
AddApplication(e.AppData);
|
||||
}
|
||||
|
||||
private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
|
||||
{
|
||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound);
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
ViewModel.StatusBarProgressValue = e.NumAppsLoaded;
|
||||
ViewModel.StatusBarProgressMaximum = e.NumAppsFound;
|
||||
|
||||
if (e.NumAppsFound == 0)
|
||||
{
|
||||
StatusBarView.LoadProgressBar.IsVisible = false;
|
||||
}
|
||||
|
||||
if (e.NumAppsLoaded == e.NumAppsFound)
|
||||
{
|
||||
StatusBarView.LoadProgressBar.IsVisible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Application_Opened(object sender, ApplicationOpenedEventArgs args)
|
||||
{
|
||||
if (args.Application != null)
|
||||
@@ -169,50 +173,12 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
string path = new FileInfo(args.Application.Path).FullName;
|
||||
|
||||
LoadApplication(path);
|
||||
ViewModel.LoadApplication(path);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public async Task PerformanceCheck()
|
||||
{
|
||||
if (ConfigurationState.Instance.Logger.EnableTrace.Value)
|
||||
{
|
||||
string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledMessage];
|
||||
string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledConfirmMessage];
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage,
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||
|
||||
if (result != UserResult.Yes)
|
||||
{
|
||||
ConfigurationState.Instance.Logger.EnableTrace.Value = false;
|
||||
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value))
|
||||
{
|
||||
string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledMessage];
|
||||
string secondaryMessage =
|
||||
LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledConfirmMessage];
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage,
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||
|
||||
if (result != UserResult.Yes)
|
||||
{
|
||||
ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = "";
|
||||
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void DeferLoadApplication(string launchPathArg, bool startFullscreenArg)
|
||||
{
|
||||
_deferLoad = true;
|
||||
@@ -220,109 +186,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
_startFullscreen = startFullscreenArg;
|
||||
}
|
||||
|
||||
#pragma warning disable CS1998
|
||||
public async void LoadApplication(string path, bool startFullscreen = false, string titleName = "")
|
||||
#pragma warning restore CS1998
|
||||
{
|
||||
if (AppHost != null)
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedSubMessage],
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if RELEASE
|
||||
await PerformanceCheck();
|
||||
#endif
|
||||
|
||||
Logger.RestartTime();
|
||||
|
||||
if (ViewModel.SelectedIcon == null)
|
||||
{
|
||||
ViewModel.SelectedIcon = ApplicationLibrary.GetApplicationIcon(path);
|
||||
}
|
||||
|
||||
PrepareLoadScreen();
|
||||
|
||||
_mainViewContent = MainContent.Content as Control;
|
||||
|
||||
RendererControl = new RendererHost(ConfigurationState.Instance.Logger.GraphicsDebugLevel);
|
||||
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)
|
||||
{
|
||||
RendererControl.CreateOpenGL();
|
||||
}
|
||||
else
|
||||
{
|
||||
RendererControl.CreateVulkan();
|
||||
}
|
||||
|
||||
AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
if (!await AppHost.LoadGuestApplication())
|
||||
{
|
||||
AppHost.DisposeContext();
|
||||
AppHost = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CanUpdate = false;
|
||||
ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], AppHost.Device.Application.TitleName) : titleName;
|
||||
ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName;
|
||||
|
||||
SwitchToGameControl(startFullscreen);
|
||||
|
||||
_currentEmulatedGamePath = path;
|
||||
|
||||
Thread gameThread = new(InitializeGame)
|
||||
{
|
||||
Name = "GUI.WindowThread"
|
||||
};
|
||||
gameThread.Start();
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializeGame()
|
||||
{
|
||||
RendererControl.RendererInitialized += GlRenderer_Created;
|
||||
|
||||
AppHost.StatusUpdatedEvent += Update_StatusBar;
|
||||
AppHost.AppExit += AppHost_AppExit;
|
||||
|
||||
_rendererWaitEvent.WaitOne();
|
||||
|
||||
AppHost?.Start();
|
||||
|
||||
AppHost.DisposeContext();
|
||||
}
|
||||
|
||||
|
||||
private void HandleRelaunch()
|
||||
{
|
||||
if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart)
|
||||
{
|
||||
_userChannelPersistence.ShouldRestart = false;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
LoadApplication(_currentEmulatedGamePath);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, clear state.
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
_currentEmulatedGamePath = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SwitchToGameControl(bool startFullscreen = false)
|
||||
{
|
||||
ViewModel.ShowLoadProgress = false;
|
||||
@@ -331,14 +194,10 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
MainContent.Content = RendererControl;
|
||||
|
||||
if (startFullscreen && WindowState != WindowState.FullScreen)
|
||||
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
||||
{
|
||||
ViewModel.ToggleFullscreen();
|
||||
}
|
||||
|
||||
RendererControl.Focus();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -350,71 +209,16 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (startFullscreen && WindowState != WindowState.FullScreen)
|
||||
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
||||
{
|
||||
ViewModel.ToggleFullscreen();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void GlRenderer_Created(object sender, EventArgs e)
|
||||
{
|
||||
ShowLoading();
|
||||
|
||||
_rendererWaitEvent.Set();
|
||||
}
|
||||
|
||||
private void AppHost_AppExit(object sender, EventArgs e)
|
||||
{
|
||||
if (_isClosing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ViewModel.IsGameRunning = false;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
ViewModel.ShowMenuAndStatusBar = true;
|
||||
ViewModel.ShowContent = true;
|
||||
ViewModel.ShowLoadProgress = false;
|
||||
ViewModel.IsLoadingIndeterminate = false;
|
||||
CanUpdate = true;
|
||||
Cursor = Cursor.Default;
|
||||
|
||||
if (MainContent.Content != _mainViewContent)
|
||||
{
|
||||
MainContent.Content = _mainViewContent;
|
||||
}
|
||||
|
||||
AppHost = null;
|
||||
|
||||
HandleRelaunch();
|
||||
});
|
||||
|
||||
RendererControl.RendererInitialized -= GlRenderer_Created;
|
||||
RendererControl = null;
|
||||
|
||||
ViewModel.SelectedIcon = null;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Title = $"Ryujinx {Program.Version}";
|
||||
});
|
||||
}
|
||||
|
||||
public void Sort_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton button)
|
||||
{
|
||||
var sort = Enum.Parse<ApplicationSort>(button.Tag.ToString());
|
||||
ViewModel.Sort(sort);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HandleWindowStateChanged(WindowState state)
|
||||
{
|
||||
WindowState = state;
|
||||
ViewModel.WindowState = state;
|
||||
|
||||
if (state != WindowState.Minimized)
|
||||
{
|
||||
@@ -422,15 +226,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
}
|
||||
}
|
||||
|
||||
public void Order_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton button)
|
||||
{
|
||||
var tag = button.Tag.ToString();
|
||||
ViewModel.Sort(tag != "Descending");
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
@@ -457,8 +252,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
VirtualFileSystem.ReloadKeySet();
|
||||
|
||||
ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient, this);
|
||||
|
||||
RefreshFirmwareStatus();
|
||||
}
|
||||
|
||||
protected void CheckLaunchState()
|
||||
@@ -475,7 +268,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
_deferLoad = false;
|
||||
|
||||
LoadApplication(_launchPath, _startFullscreen);
|
||||
ViewModel.LoadApplication(_launchPath, _startFullscreen);
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false, this))
|
||||
@@ -487,35 +280,9 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshFirmwareStatus()
|
||||
{
|
||||
SystemVersion version = null;
|
||||
try
|
||||
{
|
||||
version = ContentManager.GetCurrentFirmwareVersion();
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
bool hasApplet = false;
|
||||
|
||||
if (version != null)
|
||||
{
|
||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion,
|
||||
version.VersionString);
|
||||
|
||||
hasApplet = version.Major > 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0");
|
||||
}
|
||||
|
||||
ViewModel.IsAppletMenuActive = hasApplet;
|
||||
}
|
||||
|
||||
private void Load()
|
||||
{
|
||||
VolumeStatus.Click += VolumeStatus_CheckedChanged;
|
||||
StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged;
|
||||
|
||||
GameGrid.ApplicationOpened += Application_Opened;
|
||||
|
||||
@@ -535,6 +302,19 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
CheckLaunchState();
|
||||
}
|
||||
|
||||
private void SetMainContent(Control content = null)
|
||||
{
|
||||
if (content == null)
|
||||
{
|
||||
content = GameLibrary;
|
||||
}
|
||||
|
||||
if (MainContent.Content != content)
|
||||
{
|
||||
MainContent.Content = content;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateGraphicsConfig()
|
||||
{
|
||||
GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale;
|
||||
@@ -553,99 +333,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
|
||||
}
|
||||
|
||||
public static void SaveConfig()
|
||||
{
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
}
|
||||
|
||||
public void UpdateGameMetadata(string titleId)
|
||||
{
|
||||
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
|
||||
{
|
||||
if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime))
|
||||
{
|
||||
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
|
||||
|
||||
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void PrepareLoadScreen()
|
||||
{
|
||||
using MemoryStream stream = new MemoryStream(ViewModel.SelectedIcon);
|
||||
using var gameIconBmp = SixLabors.ImageSharp.Image.Load<Bgra32>(stream);
|
||||
|
||||
var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>();
|
||||
|
||||
const int ColorDivisor = 4;
|
||||
|
||||
Color progressFgColor = Color.FromRgb(dominantColor.R, dominantColor.G, dominantColor.B);
|
||||
Color progressBgColor = Color.FromRgb(
|
||||
(byte)(dominantColor.R / ColorDivisor),
|
||||
(byte)(dominantColor.G / ColorDivisor),
|
||||
(byte)(dominantColor.B / ColorDivisor));
|
||||
|
||||
ViewModel.ProgressBarForegroundColor = new SolidColorBrush(progressFgColor);
|
||||
ViewModel.ProgressBarBackgroundColor = new SolidColorBrush(progressBgColor);
|
||||
}
|
||||
|
||||
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
ViewModel.SearchText = SearchBox.Text;
|
||||
}
|
||||
|
||||
private async void StopEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AppHost != null)
|
||||
{
|
||||
await AppHost.ShowExitPrompt();
|
||||
}
|
||||
}
|
||||
|
||||
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
AppHost?.Pause();
|
||||
});
|
||||
}
|
||||
|
||||
private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
AppHost?.Resume();
|
||||
});
|
||||
}
|
||||
|
||||
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (sender is MenuItem)
|
||||
{
|
||||
ViewModel.IsAmiiboRequested = AppHost.Device.System.SearchingForAmiibo(out _);
|
||||
}
|
||||
}
|
||||
|
||||
private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
AppHost.Device.EnableDeviceVsync = !AppHost.Device.EnableDeviceVsync;
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {AppHost.Device.EnableDeviceVsync}");
|
||||
}
|
||||
|
||||
private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
|
||||
}
|
||||
|
||||
private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
|
||||
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
|
||||
}
|
||||
|
||||
private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e)
|
||||
{
|
||||
var volumeSplitButton = sender as ToggleSplitButton;
|
||||
@@ -653,20 +340,20 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
if (!volumeSplitButton.IsChecked)
|
||||
{
|
||||
AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
|
||||
ViewModel.AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppHost.Device.SetVolume(0);
|
||||
ViewModel.AppHost.Device.SetVolume(0);
|
||||
}
|
||||
|
||||
ViewModel.Volume = AppHost.Device.GetVolume();
|
||||
ViewModel.Volume = ViewModel.AppHost.Device.GetVolume();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
if (!_isClosing && AppHost != null && ConfigurationState.Instance.ShowConfirmExit)
|
||||
if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit)
|
||||
{
|
||||
e.Cancel = true;
|
||||
|
||||
@@ -675,14 +362,14 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
return;
|
||||
}
|
||||
|
||||
_isClosing = true;
|
||||
ViewModel.IsClosing = true;
|
||||
|
||||
if (AppHost != null)
|
||||
if (ViewModel.AppHost != null)
|
||||
{
|
||||
AppHost.AppExit -= AppHost_AppExit;
|
||||
AppHost.AppExit += (sender, e) =>
|
||||
ViewModel.AppHost.AppExit -= ViewModel.AppHost_AppExit;
|
||||
ViewModel.AppHost.AppExit += (sender, e) =>
|
||||
{
|
||||
AppHost = null;
|
||||
ViewModel.AppHost = null;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
@@ -691,7 +378,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
Close();
|
||||
});
|
||||
};
|
||||
AppHost?.Stop();
|
||||
ViewModel.AppHost?.Stop();
|
||||
|
||||
e.Cancel = true;
|
||||
|
||||
@@ -709,13 +396,43 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
_isClosing = await ContentDialogHelper.CreateExitDialog();
|
||||
ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog();
|
||||
|
||||
if (_isClosing)
|
||||
if (ViewModel.IsClosing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async void LoadApplications()
|
||||
{
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
ViewModel.Applications.Clear();
|
||||
|
||||
StatusBarView.LoadProgressBar.IsVisible = true;
|
||||
ViewModel.StatusBarProgressMaximum = 0;
|
||||
ViewModel.StatusBarProgressValue = 0;
|
||||
|
||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||
});
|
||||
|
||||
ReloadGameList();
|
||||
}
|
||||
|
||||
private void ReloadGameList()
|
||||
{
|
||||
if (_isLoading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isLoading = true;
|
||||
|
||||
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language);
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,13 +6,12 @@
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:settings="clr-namespace:Ryujinx.Ava.UI.Views.Settings"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
Width="1100"
|
||||
Height="768"
|
||||
d:DesignWidth="800"
|
||||
d:DesignHeight="950"
|
||||
MinWidth="800"
|
||||
MinHeight="480"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
@@ -23,9 +22,6 @@
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<Window.Resources>
|
||||
<helpers:KeyValueConverter x:Key="Key" />
|
||||
</Window.Resources>
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="600">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -38,871 +34,15 @@
|
||||
IsVisible="False"
|
||||
KeyboardNavigation.IsTabStop="False"/>
|
||||
<Grid Name="Pages" IsVisible="False" Grid.Row="2">
|
||||
<ScrollViewer Name="UiPage"
|
||||
Margin="0,0,2,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGeneral}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableDiscordIntegration}">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ToggleDiscordTooltip}"
|
||||
Text="{locale:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding CheckUpdatesOnStart}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralCheckUpdatesOnLaunch}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding ShowConfirmExit}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding HideCursorOnIdle}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<ListBox
|
||||
Name="GameList"
|
||||
MinHeight="250"
|
||||
Items="{Binding GameDirectories}" />
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox
|
||||
Name="PathBox"
|
||||
Margin="0"
|
||||
ToolTip.Tip="{locale:Locale AddGameDirBoxTooltip}"
|
||||
VerticalAlignment="Stretch" />
|
||||
<Button
|
||||
Name="AddButton"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="10,0,0,0"
|
||||
ToolTip.Tip="{locale:Locale AddGameDirTooltip}"
|
||||
Click="AddButton_OnClick">
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabGeneralAdd}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="RemoveButton"
|
||||
Grid.Column="2"
|
||||
MinWidth="90"
|
||||
Margin="10,0,0,0"
|
||||
ToolTip.Tip="{locale:Locale RemoveGameDirTooltip}"
|
||||
Click="RemoveButton_OnClick">
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabGeneralRemove}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralTheme}" />
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<CheckBox IsChecked="{Binding EnableCustomTheme}"
|
||||
ToolTip.Tip="{locale:Locale CustomThemeCheckTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeEnableCustomTheme}" />
|
||||
</CheckBox>
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Margin="0,10,0,0"
|
||||
Grid.Row="1"
|
||||
Text="{locale:Locale SettingsTabGeneralThemeCustomTheme}"
|
||||
ToolTip.Tip="{locale:Locale CustomThemePathTooltip}" />
|
||||
<TextBox Margin="0,10,0,0"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Text="{Binding CustomThemePath}" />
|
||||
<Button Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="10,10,0,0"
|
||||
Command="{ReflectionBinding BrowseTheme}"
|
||||
ToolTip.Tip="{locale:Locale CustomThemeBrowseTooltip}"
|
||||
Content="{locale:Locale ButtonBrowse}" />
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Margin="0,10,0,0"
|
||||
Grid.Row="2"
|
||||
Text="{locale:Locale SettingsTabGeneralThemeBaseStyle}" />
|
||||
<ComboBox VerticalAlignment="Center"
|
||||
Margin="0,10,0,0"
|
||||
Grid.Column="1"
|
||||
Grid.Row="2"
|
||||
MinWidth="100"
|
||||
SelectedIndex="{Binding BaseStyleIndex}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleLight}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleDark}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer Name="InputPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Padding="0,0,2,0"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel Margin="4" Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<CheckBox Margin="5,0"
|
||||
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
|
||||
IsChecked="{Binding EnableDockedMode}">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
|
||||
</CheckBox>
|
||||
<CheckBox Margin="5,0"
|
||||
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
|
||||
IsChecked="{Binding EnableKeyboard}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
|
||||
</CheckBox>
|
||||
<CheckBox Margin="5,0"
|
||||
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
|
||||
IsChecked="{Binding EnableMouse}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<window:ControllerSettingsWindow Name="ControllerSettings" Margin="0,0,0,0" MinHeight="600" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer Name="HotkeysPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel Margin="10,5" Orientation="Vertical" Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ToggleVsync, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.Screenshot, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.Pause, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ToggleMute, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ResScaleUp, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ResScaleDown, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</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>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer Name="SystemPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemSystemRegion}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding Region}"
|
||||
ToolTip.Tip="{locale:Locale RegionTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionUSA}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionEurope}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionAustralia}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionChina}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionKorea}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionTaiwan}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguage}"
|
||||
ToolTip.Tip="{locale:Locale LanguageTooltip}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding Language}"
|
||||
ToolTip.Tip="{locale:Locale LanguageTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageGerman}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageItalian}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageSpanish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageChinese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageKorean}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageDutch}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguagePortuguese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageRussian}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
|
||||
ToolTip.Tip="{locale:Locale TimezoneTooltip}"
|
||||
Width="250" />
|
||||
<AutoCompleteBox
|
||||
Name="TimeZoneBox"
|
||||
Width="350"
|
||||
MaxDropDownHeight="500"
|
||||
FilterMode="Contains"
|
||||
Items="{Binding TimeZones}"
|
||||
SelectionChanged="TimeZoneBox_OnSelectionChanged"
|
||||
Text="{Binding Path=TimeZone, Mode=OneWay}"
|
||||
TextChanged="TimeZoneBox_OnTextChanged"
|
||||
ValueMemberBinding="{ReflectionBinding TzMultiBinding}"
|
||||
ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemSystemTime}"
|
||||
ToolTip.Tip="{locale:Locale TimeTooltip}"
|
||||
Width="250"/>
|
||||
<DatePicker VerticalAlignment="Center" SelectedDate="{Binding DateOffset}"
|
||||
ToolTip.Tip="{locale:Locale TimeTooltip}"
|
||||
Width="350" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="250,0,0,10" Orientation="Horizontal">
|
||||
<TimePicker
|
||||
VerticalAlignment="Center"
|
||||
ClockIdentifier="24HourClock"
|
||||
SelectedTime="{Binding TimeOffset}"
|
||||
Width="350"
|
||||
ToolTip.Tip="{locale:Locale TimeTooltip}" />
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding EnableVsync}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}"
|
||||
ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}"
|
||||
ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" />
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemHacksNote}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding ExpandDramSize}"
|
||||
ToolTip.Tip="{locale:Locale DRamTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding IgnoreMissingServices}"
|
||||
ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer
|
||||
Name="CpuPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuCache}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnablePptc}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnablePptc}"
|
||||
ToolTip.Tip="{locale:Locale PptcToggleTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuMemory}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemMemoryManagerMode}"
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding MemoryMode}"
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerSoftwareTooltip}">
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemMemoryManagerModeSoftware}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerHostTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemMemoryManagerModeHost}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem
|
||||
ToolTip.Tip="{locale:Locale MemoryManagerUnsafeTooltip}">
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabSystemMemoryManagerModeHostUnchecked}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer
|
||||
Name="GraphicsPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsAPI}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsBackend}"
|
||||
Width="250" />
|
||||
<ComboBox Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
|
||||
SelectedIndex="{Binding GraphicsBackendIndex}">
|
||||
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}">
|
||||
<TextBlock Text="Vulkan" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
|
||||
<TextBlock Text="OpenGL" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding IsVulkanSelected}">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsPreferredGpu}"
|
||||
Width="250" />
|
||||
<ComboBox Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
|
||||
SelectedIndex="{Binding PreferredGpuIndex}"
|
||||
Items="{Binding AvailableGpus}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsFeatures}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableShaderCache}"
|
||||
ToolTip.Tip="{locale:Locale ShaderCacheToggleTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsEnableShaderCache}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableTextureRecompression}"
|
||||
ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsEnableTextureRecompression}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableMacroHLE}"
|
||||
ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsResolutionScale}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding ResolutionScale}"
|
||||
Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleCustom}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleNative}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale2x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale3x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale4x}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<ui:NumberBox
|
||||
Margin="10,0,0,0"
|
||||
ToolTip.Tip="{locale:Locale ResolutionScaleEntryTooltip}"
|
||||
MinWidth="150"
|
||||
SmallChange="0.1"
|
||||
LargeChange="1"
|
||||
SimpleNumberFormat="F2"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
IsVisible="{Binding IsCustomResolutionScaleActive}"
|
||||
Maximum="100"
|
||||
Minimum="0.1"
|
||||
Value="{Binding CustomResolutionScale}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding MaxAnisotropy}"
|
||||
Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale AnisotropyTooltip}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabGraphicsAnisotropicFilteringAuto}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering2x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering4x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering8x}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering16x}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale AspectRatioTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsAspectRatio}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding AspectRatio}"
|
||||
Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale AspectRatioTooltip}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio4x3}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x9}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x10}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio21x9}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio32x9}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatioStretch}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale GraphicsBackendThreadingTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsBackendMultithreading}"
|
||||
Width="250" />
|
||||
<ComboBox Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale GalThreadingTooltip}"
|
||||
SelectedIndex="{Binding GraphicsBackendMultithreadingIndex}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale CommonAuto}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale CommonOff}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale CommonOn}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}"
|
||||
Text="{locale:Locale SettingsTabGraphicsShaderDumpPath}"
|
||||
Width="250" />
|
||||
<TextBox Text="{Binding ShaderDumpPath}"
|
||||
Width="350"
|
||||
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer
|
||||
Name="AudioPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabAudio}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemAudioBackend}"
|
||||
ToolTip.Tip="{locale:Locale AudioBackendTooltip}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding AudioBackend}"
|
||||
Width="350"
|
||||
HorizontalContentAlignment="Left">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendDummy}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsOpenAlEnabled}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendOpenAL}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsSoundIoEnabled}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSoundIO}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsSDL2Enabled}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSDL2}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabSystemAudioVolume}"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Width="250" />
|
||||
<ui:NumberBox Value="{Binding Volume}"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Width="350"
|
||||
SmallChange="1"
|
||||
LargeChange="10"
|
||||
SimpleNumberFormat="F0"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Minimum="0"
|
||||
Maximum="100" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<Slider Value="{Binding Volume}"
|
||||
Margin="250,0,0,0"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
SmallChange="5"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True"
|
||||
LargeChange="10"
|
||||
Width="350" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer
|
||||
Name="NetworkPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabNetworkConnection}" />
|
||||
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
|
||||
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer
|
||||
Name="LoggingPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingLogging}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableFileLog}"
|
||||
ToolTip.Tip="{locale:Locale FileLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableLoggingToFile}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableStub}"
|
||||
ToolTip.Tip="{locale:Locale StubLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableStubLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableInfo}"
|
||||
ToolTip.Tip="{locale:Locale InfoLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableInfoLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableWarn}"
|
||||
ToolTip.Tip="{locale:Locale WarnLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableWarningLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableError}"
|
||||
ToolTip.Tip="{locale:Locale ErrorLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableErrorLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableTrace}"
|
||||
ToolTip.Tip="{locale:Locale TraceLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableTraceLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableGuest}"
|
||||
ToolTip.Tip="{locale:Locale GuestLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableGuestLogs}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableDebug}"
|
||||
ToolTip.Tip="{locale:Locale DebugLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableDebugLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableFsAccessLog}"
|
||||
ToolTip.Tip="{locale:Locale FileAccessLogTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableFsAccessLogs}" />
|
||||
</CheckBox>
|
||||
<StackPanel Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Stretch">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale FSAccessLogModeTooltip}"
|
||||
Text="{locale:Locale SettingsTabLoggingFsGlobalAccessLogMode}"
|
||||
Width="285" />
|
||||
<ui:NumberBox
|
||||
Maximum="3"
|
||||
Minimum="0"
|
||||
Width="150"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
SmallChange="1"
|
||||
LargeChange="1"
|
||||
Value="{Binding FsGlobalAccessLogMode}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,10,0,0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevel}"
|
||||
ToolTip.Tip="{locale:Locale OpenGlLogLevel}"
|
||||
Width="285" />
|
||||
<ComboBox SelectedIndex="{Binding OpenglDebugLevel}"
|
||||
Width="150"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{locale:Locale OpenGlLogLevel}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelError}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock
|
||||
Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelPerformance}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelAll}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
<settings:SettingsUIView Name="UiPage" />
|
||||
<settings:SettingsInputView Name="InputPage" />
|
||||
<settings:SettingsHotkeysView Name="HotkeysPage" />
|
||||
<settings:SettingsSystemView Name="SystemPage" />
|
||||
<settings:SettingsCPUView Name="CpuPage" />
|
||||
<settings:SettingsGraphicsView Name="GraphicsPage" />
|
||||
<settings:SettingsAudioView Name="AudioPage" />
|
||||
<settings:SettingsNetworkView Name="NetworkPage" />
|
||||
<settings:SettingsLoggingView Name="LoggingPage" />
|
||||
</Grid>
|
||||
<ui:NavigationView Grid.Row="1"
|
||||
IsSettingsVisible="False"
|
||||
@@ -962,7 +102,7 @@
|
||||
Spacing="10"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
ReverseOrder="{ReflectionBinding IsMacOS}">
|
||||
ReverseOrder="{Binding IsMacOS}">
|
||||
<Button
|
||||
HotKey="Enter"
|
||||
Classes="accent"
|
||||
|
@@ -1,49 +1,29 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using FluentAvalonia.Core;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.Assigner;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public partial class SettingsWindow : StyleableWindow
|
||||
{
|
||||
private ButtonKeyAssigner _currentAssigner;
|
||||
|
||||
internal SettingsViewModel ViewModel { get; set; }
|
||||
|
||||
public SettingsWindow(VirtualFileSystem virtualFileSystem, ContentManager contentManager)
|
||||
{
|
||||
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.Settings]}";
|
||||
|
||||
ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this);
|
||||
ViewModel = new SettingsViewModel(virtualFileSystem, contentManager);
|
||||
DataContext = ViewModel;
|
||||
|
||||
ViewModel.CloseWindow += Close;
|
||||
ViewModel.SaveSettingsEvent += SaveSettings;
|
||||
|
||||
InitializeComponent();
|
||||
Load();
|
||||
|
||||
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
|
||||
MultiBinding tzMultiBinding = new() { Converter = converter };
|
||||
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
|
||||
tzMultiBinding.Bindings.Add(new Binding("Location"));
|
||||
tzMultiBinding.Bindings.Add(new Binding("Abbreviation"));
|
||||
|
||||
TimeZoneBox.ValueMemberBinding = tzMultiBinding;
|
||||
}
|
||||
|
||||
public SettingsWindow()
|
||||
@@ -55,6 +35,16 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
Load();
|
||||
}
|
||||
|
||||
public void SaveSettings()
|
||||
{
|
||||
InputPage.ControllerSettings?.SaveCurrentProfile();
|
||||
|
||||
if (Owner is MainWindow window && ViewModel.DirectoryChanged)
|
||||
{
|
||||
window.ViewModel.LoadApplications();
|
||||
}
|
||||
}
|
||||
|
||||
private void Load()
|
||||
{
|
||||
Pages.Children.Clear();
|
||||
@@ -62,152 +52,52 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0);
|
||||
}
|
||||
|
||||
private void Button_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ToggleButton button)
|
||||
{
|
||||
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentAssigner == null && (bool)button.IsChecked)
|
||||
{
|
||||
_currentAssigner = new ButtonKeyAssigner(button);
|
||||
|
||||
FocusManager.Instance.Focus(this, NavigationMethod.Pointer);
|
||||
|
||||
PointerPressed += MouseClick;
|
||||
|
||||
IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad(ViewModel.AvaloniaKeyboardDriver.GamepadsIds[0]);
|
||||
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
|
||||
|
||||
_currentAssigner.GetInputAndAssign(assigner);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentAssigner != null)
|
||||
{
|
||||
ToggleButton oldButton = _currentAssigner.ToggledButton;
|
||||
|
||||
_currentAssigner.Cancel();
|
||||
_currentAssigner = null;
|
||||
|
||||
button.IsChecked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Button_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
}
|
||||
|
||||
private void MouseClick(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
bool shouldUnbind = false;
|
||||
|
||||
if (e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed)
|
||||
{
|
||||
shouldUnbind = true;
|
||||
}
|
||||
|
||||
_currentAssigner?.Cancel(shouldUnbind);
|
||||
|
||||
PointerPressed -= MouseClick;
|
||||
}
|
||||
|
||||
private void NavPanelOnSelectionChanged(object sender, NavigationViewSelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.SelectedItem is NavigationViewItem navitem)
|
||||
if (e.SelectedItem is NavigationViewItem navItem && navItem.Tag is not null)
|
||||
{
|
||||
NavPanel.Content = navitem.Tag.ToString() switch
|
||||
switch (navItem.Tag.ToString())
|
||||
{
|
||||
"UiPage" => UiPage,
|
||||
"InputPage" => InputPage,
|
||||
"HotkeysPage" => HotkeysPage,
|
||||
"SystemPage" => SystemPage,
|
||||
"CpuPage" => CpuPage,
|
||||
"GraphicsPage" => GraphicsPage,
|
||||
"AudioPage" => AudioPage,
|
||||
"NetworkPage" => NetworkPage,
|
||||
"LoggingPage" => LoggingPage,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async void AddButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string path = PathBox.Text;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path))
|
||||
{
|
||||
ViewModel.GameDirectories.Add(path);
|
||||
ViewModel.DirectoryChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = await new OpenFolderDialog().ShowAsync(this);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
ViewModel.GameDirectories.Add(path);
|
||||
ViewModel.DirectoryChanged = true;
|
||||
case "UiPage":
|
||||
UiPage.ViewModel = ViewModel;
|
||||
NavPanel.Content = UiPage;
|
||||
break;
|
||||
case "InputPage":
|
||||
NavPanel.Content = InputPage;
|
||||
break;
|
||||
case "HotkeysPage":
|
||||
NavPanel.Content = HotkeysPage;
|
||||
break;
|
||||
case "SystemPage":
|
||||
SystemPage.ViewModel = ViewModel;
|
||||
NavPanel.Content = SystemPage;
|
||||
break;
|
||||
case "CpuPage":
|
||||
NavPanel.Content = CpuPage;
|
||||
break;
|
||||
case "GraphicsPage":
|
||||
NavPanel.Content = GraphicsPage;
|
||||
break;
|
||||
case "AudioPage":
|
||||
NavPanel.Content = AudioPage;
|
||||
break;
|
||||
case "NetworkPage":
|
||||
NavPanel.Content = NetworkPage;
|
||||
break;
|
||||
case "LoggingPage":
|
||||
NavPanel.Content = LoggingPage;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveButton_OnClick(object sender, RoutedEventArgs e)
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
int oldIndex = GameList.SelectedIndex;
|
||||
|
||||
foreach (string path in new List<string>(GameList.SelectedItems.Cast<string>()))
|
||||
{
|
||||
ViewModel.GameDirectories.Remove(path);
|
||||
ViewModel.DirectoryChanged = true;
|
||||
}
|
||||
|
||||
if (GameList.ItemCount > 0)
|
||||
{
|
||||
GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.AddedItems != null && e.AddedItems.Count > 0)
|
||||
{
|
||||
if (e.AddedItems[0] is TimeZone timeZone)
|
||||
{
|
||||
e.Handled = true;
|
||||
|
||||
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TimeZoneBox_OnTextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is AutoCompleteBox box)
|
||||
{
|
||||
if (box.SelectedItem != null && box.SelectedItem is TimeZone timeZone)
|
||||
{
|
||||
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
ControllerSettings.Dispose();
|
||||
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
|
||||
base.OnClosed(e);
|
||||
HotkeysPage.Dispose();
|
||||
InputPage.Dispose();
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -29,7 +29,7 @@ namespace Ryujinx.Common.Logging
|
||||
files[i].Delete();
|
||||
}
|
||||
|
||||
string version = ReleaseInformations.GetVersion();
|
||||
string version = ReleaseInformation.GetVersion();
|
||||
|
||||
// Get path for the current time
|
||||
path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.log");
|
||||
|
51
Ryujinx.Common/Memory/SpanReader.cs
Normal file
51
Ryujinx.Common/Memory/SpanReader.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Common.Memory
|
||||
{
|
||||
public ref struct SpanReader
|
||||
{
|
||||
private ReadOnlySpan<byte> _input;
|
||||
|
||||
public int Length => _input.Length;
|
||||
|
||||
public SpanReader(ReadOnlySpan<byte> input)
|
||||
{
|
||||
_input = input;
|
||||
}
|
||||
|
||||
public T Read<T>() where T : unmanaged
|
||||
{
|
||||
T value = MemoryMarshal.Cast<byte, T>(_input)[0];
|
||||
|
||||
_input = _input.Slice(Unsafe.SizeOf<T>());
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> GetSpan(int size)
|
||||
{
|
||||
ReadOnlySpan<byte> data = _input.Slice(0, size);
|
||||
|
||||
_input = _input.Slice(size);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public T ReadAt<T>(int offset) where T : unmanaged
|
||||
{
|
||||
return MemoryMarshal.Cast<byte, T>(_input.Slice(offset))[0];
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> GetSpanAt(int offset, int size)
|
||||
{
|
||||
return _input.Slice(offset, size);
|
||||
}
|
||||
|
||||
public void Skip(int size)
|
||||
{
|
||||
_input = _input.Slice(size);
|
||||
}
|
||||
}
|
||||
}
|
45
Ryujinx.Common/Memory/SpanWriter.cs
Normal file
45
Ryujinx.Common/Memory/SpanWriter.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Common.Memory
|
||||
{
|
||||
public ref struct SpanWriter
|
||||
{
|
||||
private Span<byte> _output;
|
||||
|
||||
public int Length => _output.Length;
|
||||
|
||||
public SpanWriter(Span<byte> output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
public void Write<T>(T value) where T : unmanaged
|
||||
{
|
||||
MemoryMarshal.Cast<byte, T>(_output)[0] = value;
|
||||
_output = _output.Slice(Unsafe.SizeOf<T>());
|
||||
}
|
||||
|
||||
public void Write(ReadOnlySpan<byte> data)
|
||||
{
|
||||
data.CopyTo(_output.Slice(0, data.Length));
|
||||
_output = _output.Slice(data.Length);
|
||||
}
|
||||
|
||||
public void WriteAt<T>(int offset, T value) where T : unmanaged
|
||||
{
|
||||
MemoryMarshal.Cast<byte, T>(_output.Slice(offset))[0] = value;
|
||||
}
|
||||
|
||||
public void WriteAt(int offset, ReadOnlySpan<byte> data)
|
||||
{
|
||||
data.CopyTo(_output.Slice(offset, data.Length));
|
||||
}
|
||||
|
||||
public void Skip(int size)
|
||||
{
|
||||
_output = _output.Slice(size);
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@ using System.Reflection;
|
||||
namespace Ryujinx.Common
|
||||
{
|
||||
// DO NOT EDIT, filled by CI
|
||||
public static class ReleaseInformations
|
||||
public static class ReleaseInformation
|
||||
{
|
||||
private const string FlatHubChannelOwner = "flathub";
|
||||
|
||||
@@ -50,4 +50,4 @@ namespace Ryujinx.Common
|
||||
return AppDomain.CurrentDomain.BaseDirectory;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
Ryujinx.Common/Utilities/BitfieldExtensions.cs
Normal file
57
Ryujinx.Common/Utilities/BitfieldExtensions.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
public static class BitfieldExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Extract<T>(this T value, int lsb) where T : IBinaryInteger<T>
|
||||
{
|
||||
int bitSize = Unsafe.SizeOf<T>() * 8;
|
||||
lsb &= bitSize - 1;
|
||||
|
||||
return !T.IsZero((value >>> lsb) & T.One);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Extract<T>(this T value, int lsb, int length) where T : IBinaryInteger<T>
|
||||
{
|
||||
int bitSize = Unsafe.SizeOf<T>() * 8;
|
||||
lsb &= bitSize - 1;
|
||||
|
||||
return (value >>> lsb) & (~T.Zero >>> (bitSize - length));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T ExtractSx<T>(this T value, int lsb, int length) where T : IBinaryInteger<T>
|
||||
{
|
||||
int bitSize = Unsafe.SizeOf<T>() * 8;
|
||||
int shift = lsb & (bitSize - 1);
|
||||
|
||||
return (value << (bitSize - (shift + length))) >> (bitSize - length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Insert<T>(this T value, int lsb, bool toInsert) where T : IBinaryInteger<T>
|
||||
{
|
||||
int bitSize = Unsafe.SizeOf<T>() * 8;
|
||||
lsb &= bitSize - 1;
|
||||
|
||||
T mask = T.One << lsb;
|
||||
|
||||
return (value & ~mask) | (toInsert ? mask : T.Zero);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Insert<T>(this T value, int lsb, int length, T toInsert) where T : IBinaryInteger<T>
|
||||
{
|
||||
int bitSize = Unsafe.SizeOf<T>() * 8;
|
||||
lsb &= bitSize - 1;
|
||||
|
||||
T mask = (~T.Zero >>> (bitSize - length)) << lsb;
|
||||
|
||||
return (value & ~mask) | ((toInsert << lsb) & mask);
|
||||
}
|
||||
}
|
||||
}
|
@@ -35,5 +35,27 @@ namespace Ryujinx.Cpu
|
||||
/// <param name="address">Address of the region to be invalidated</param>
|
||||
/// <param name="size">Size of the region to be invalidated</param>
|
||||
void InvalidateCacheRegion(ulong address, ulong size);
|
||||
|
||||
/// <summary>
|
||||
/// Loads cached code from disk for a given application.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the execution engine is recompiling guest code, this can be used to load cached code from disk.
|
||||
/// </remarks>
|
||||
/// <param name="titleIdText">Title ID of the application in padded hex form</param>
|
||||
/// <param name="displayVersion">Version of the application</param>
|
||||
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
|
||||
/// <returns>Disk cache load progress reporter and manager</returns>
|
||||
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Some execution engines might use this information to cache recompiled code on disk or to ensure it can be executed.
|
||||
/// </remarks>
|
||||
/// <param name="address">CPU virtual address where the code starts</param>
|
||||
/// <param name="size">Size of the code range in bytes</param>
|
||||
void PrepareCodeRange(ulong address, ulong size);
|
||||
}
|
||||
}
|
||||
|
20
Ryujinx.Cpu/IDiskCacheState.cs
Normal file
20
Ryujinx.Cpu/IDiskCacheState.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu
|
||||
{
|
||||
/// <summary>
|
||||
/// Disk cache load state report and management interface.
|
||||
/// </summary>
|
||||
public interface IDiskCacheLoadState
|
||||
{
|
||||
/// <summary>
|
||||
/// Event used to report the cache load progress.
|
||||
/// </summary>
|
||||
event Action<LoadState, int, int> StateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the disk cache load process.
|
||||
/// </summary>
|
||||
void Cancel();
|
||||
}
|
||||
}
|
@@ -37,5 +37,17 @@ namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
_translator.InvalidateJitCacheRegion(address, size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
{
|
||||
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PrepareCodeRange(ulong address, ulong size)
|
||||
{
|
||||
_translator.PrepareCodeRange(address, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs
Normal file
38
Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
public class JitDiskCacheLoadState : IDiskCacheLoadState
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public event Action<LoadState, int, int> StateChanged;
|
||||
|
||||
private readonly IPtcLoadState _loadState;
|
||||
|
||||
public JitDiskCacheLoadState(IPtcLoadState loadState)
|
||||
{
|
||||
loadState.PtcStateChanged += LoadStateChanged;
|
||||
_loadState = loadState;
|
||||
}
|
||||
|
||||
private void LoadStateChanged(PtcLoadingState newState, int current, int total)
|
||||
{
|
||||
LoadState state = newState switch
|
||||
{
|
||||
PtcLoadingState.Start => LoadState.Unloaded,
|
||||
PtcLoadingState.Loading => LoadState.Loading,
|
||||
PtcLoadingState.Loaded => LoadState.Loaded,
|
||||
_ => throw new ArgumentException($"Invalid load state \"{newState}\".")
|
||||
};
|
||||
|
||||
StateChanged?.Invoke(state, current, total);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Cancel()
|
||||
{
|
||||
_loadState.Continue();
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Cpu/LoadState.cs
Normal file
12
Ryujinx.Cpu/LoadState.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Ryujinx.Cpu
|
||||
{
|
||||
/// <summary>
|
||||
/// Load state.
|
||||
/// </summary>
|
||||
public enum LoadState
|
||||
{
|
||||
Unloaded,
|
||||
Loading,
|
||||
Loaded
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
static class BitfieldExtensions
|
||||
{
|
||||
public static bool Extract(this int value, int lsb)
|
||||
{
|
||||
return ((value >> lsb) & 1) != 0;
|
||||
}
|
||||
|
||||
public static int Extract(this int value, int lsb, int length)
|
||||
{
|
||||
return (value >> lsb) & (int)(uint.MaxValue >> (32 - length));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
@@ -1,39 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
static class BitfieldExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Extract(this int value, int lsb)
|
||||
{
|
||||
return ((value >> (lsb & 0x1f)) & 1) != 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Extract(this int value, int lsb, int length)
|
||||
{
|
||||
return (value >> (lsb & 0x1f)) & (int)(uint.MaxValue >> (32 - length));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Extract(this long value, int lsb)
|
||||
{
|
||||
return ((int)(value >> (lsb & 0x3f)) & 1) != 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Extract(this long value, int lsb, int length)
|
||||
{
|
||||
return (int)(value >> (lsb & 0x3f)) & (int)(uint.MaxValue >> (32 - length));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ExtractSx(this long value, int lsb, int length)
|
||||
{
|
||||
int shift = lsb & 0x3f;
|
||||
|
||||
return (int)((value << (64 - (shift + length))) >> (64 - length));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,20 +1,22 @@
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
using Ryujinx.Common.Utilities;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
struct BlendingSlotStruct
|
||||
{
|
||||
private long _word0;
|
||||
private long _word1;
|
||||
|
||||
public int AlphaK1 => _word0.Extract(0, 10);
|
||||
public int AlphaK2 => _word0.Extract(16, 10);
|
||||
public int SrcFactCMatchSelect => _word0.Extract(32, 3);
|
||||
public int DstFactCMatchSelect => _word0.Extract(36, 3);
|
||||
public int SrcFactAMatchSelect => _word0.Extract(40, 3);
|
||||
public int DstFactAMatchSelect => _word0.Extract(44, 3);
|
||||
public int OverrideR => _word1.Extract(66, 10);
|
||||
public int OverrideG => _word1.Extract(76, 10);
|
||||
public int OverrideB => _word1.Extract(86, 10);
|
||||
public int OverrideA => _word1.Extract(96, 10);
|
||||
public int AlphaK1 => (int)_word0.Extract(0, 10);
|
||||
public int AlphaK2 => (int)_word0.Extract(16, 10);
|
||||
public int SrcFactCMatchSelect => (int)_word0.Extract(32, 3);
|
||||
public int DstFactCMatchSelect => (int)_word0.Extract(36, 3);
|
||||
public int SrcFactAMatchSelect => (int)_word0.Extract(40, 3);
|
||||
public int DstFactAMatchSelect => (int)_word0.Extract(44, 3);
|
||||
public int OverrideR => (int)_word1.Extract(66, 10);
|
||||
public int OverrideG => (int)_word1.Extract(76, 10);
|
||||
public int OverrideB => (int)_word1.Extract(86, 10);
|
||||
public int OverrideA => (int)_word1.Extract(96, 10);
|
||||
public bool UseOverrideR => _word1.Extract(108);
|
||||
public bool UseOverrideG => _word1.Extract(109);
|
||||
public bool UseOverrideB => _word1.Extract(110);
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
using Ryujinx.Common.Utilities;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
struct ClearRectStruct
|
||||
{
|
||||
@@ -7,13 +9,13 @@
|
||||
private long _word1;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
public int ClearRect0Left => _word0.Extract(0, 14);
|
||||
public int ClearRect0Right => _word0.Extract(16, 14);
|
||||
public int ClearRect0Top => _word0.Extract(32, 14);
|
||||
public int ClearRect0Bottom => _word0.Extract(48, 14);
|
||||
public int ClearRect1Left => _word1.Extract(64, 14);
|
||||
public int ClearRect1Right => _word1.Extract(80, 14);
|
||||
public int ClearRect1Top => _word1.Extract(96, 14);
|
||||
public int ClearRect1Bottom => _word1.Extract(112, 14);
|
||||
public int ClearRect0Left => (int)_word0.Extract(0, 14);
|
||||
public int ClearRect0Right => (int)_word0.Extract(16, 14);
|
||||
public int ClearRect0Top => (int)_word0.Extract(32, 14);
|
||||
public int ClearRect0Bottom => (int)_word0.Extract(48, 14);
|
||||
public int ClearRect1Left => (int)_word1.Extract(64, 14);
|
||||
public int ClearRect1Right => (int)_word1.Extract(80, 14);
|
||||
public int ClearRect1Top => (int)_word1.Extract(96, 14);
|
||||
public int ClearRect1Bottom => (int)_word1.Extract(112, 14);
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +1,19 @@
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
using Ryujinx.Common.Utilities;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
struct LumaKeyStruct
|
||||
{
|
||||
private long _word0;
|
||||
private long _word1;
|
||||
|
||||
public int LumaCoeff0 => _word0.Extract(0, 20);
|
||||
public int LumaCoeff1 => _word0.Extract(20, 20);
|
||||
public int LumaCoeff2 => _word0.Extract(40, 20);
|
||||
public int LumaRShift => _word0.Extract(60, 4);
|
||||
public int LumaCoeff3 => _word1.Extract(64, 20);
|
||||
public int LumaKeyLower => _word1.Extract(84, 10);
|
||||
public int LumaKeyUpper => _word1.Extract(94, 10);
|
||||
public int LumaCoeff0 => (int)_word0.Extract(0, 20);
|
||||
public int LumaCoeff1 => (int)_word0.Extract(20, 20);
|
||||
public int LumaCoeff2 => (int)_word0.Extract(40, 20);
|
||||
public int LumaRShift => (int)_word0.Extract(60, 4);
|
||||
public int LumaCoeff3 => (int)_word1.Extract(64, 20);
|
||||
public int LumaKeyLower => (int)_word1.Extract(84, 10);
|
||||
public int LumaKeyUpper => (int)_word1.Extract(94, 10);
|
||||
public bool LumaKeyEnabled => _word1.Extract(104);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
using Ryujinx.Common.Utilities;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
struct MatrixStruct
|
||||
{
|
||||
@@ -7,19 +9,19 @@
|
||||
private long _word2;
|
||||
private long _word3;
|
||||
|
||||
public int MatrixCoeff00 => _word0.ExtractSx(0, 20);
|
||||
public int MatrixCoeff10 => _word0.ExtractSx(20, 20);
|
||||
public int MatrixCoeff20 => _word0.ExtractSx(40, 20);
|
||||
public int MatrixRShift => _word0.Extract(60, 4);
|
||||
public int MatrixCoeff01 => _word1.ExtractSx(64, 20);
|
||||
public int MatrixCoeff11 => _word1.ExtractSx(84, 20);
|
||||
public int MatrixCoeff21 => _word1.ExtractSx(104, 20);
|
||||
public int MatrixCoeff00 => (int)_word0.ExtractSx(0, 20);
|
||||
public int MatrixCoeff10 => (int)_word0.ExtractSx(20, 20);
|
||||
public int MatrixCoeff20 => (int)_word0.ExtractSx(40, 20);
|
||||
public int MatrixRShift => (int)_word0.Extract(60, 4);
|
||||
public int MatrixCoeff01 => (int)_word1.ExtractSx(64, 20);
|
||||
public int MatrixCoeff11 => (int)_word1.ExtractSx(84, 20);
|
||||
public int MatrixCoeff21 => (int)_word1.ExtractSx(104, 20);
|
||||
public bool MatrixEnable => _word1.Extract(127);
|
||||
public int MatrixCoeff02 => _word2.ExtractSx(128, 20);
|
||||
public int MatrixCoeff12 => _word2.ExtractSx(148, 20);
|
||||
public int MatrixCoeff22 => _word2.ExtractSx(168, 20);
|
||||
public int MatrixCoeff03 => _word3.ExtractSx(192, 20);
|
||||
public int MatrixCoeff13 => _word3.ExtractSx(212, 20);
|
||||
public int MatrixCoeff23 => _word3.ExtractSx(232, 20);
|
||||
public int MatrixCoeff02 => (int)_word2.ExtractSx(128, 20);
|
||||
public int MatrixCoeff12 => (int)_word2.ExtractSx(148, 20);
|
||||
public int MatrixCoeff22 => (int)_word2.ExtractSx(168, 20);
|
||||
public int MatrixCoeff03 => (int)_word3.ExtractSx(192, 20);
|
||||
public int MatrixCoeff13 => (int)_word3.ExtractSx(212, 20);
|
||||
public int MatrixCoeff23 => (int)_word3.ExtractSx(232, 20);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
using Ryujinx.Common.Utilities;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
struct OutputConfig
|
||||
{
|
||||
@@ -7,19 +9,19 @@
|
||||
private long _word1;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
public int AlphaFillMode => _word0.Extract(0, 3);
|
||||
public int AlphaFillSlot => _word0.Extract(3, 3);
|
||||
public int BackgroundAlpha => _word0.Extract(6, 10);
|
||||
public int BackgroundR => _word0.Extract(16, 10);
|
||||
public int BackgroundG => _word0.Extract(26, 10);
|
||||
public int BackgroundB => _word0.Extract(36, 10);
|
||||
public int RegammaMode => _word0.Extract(46, 2);
|
||||
public int AlphaFillMode => (int)_word0.Extract(0, 3);
|
||||
public int AlphaFillSlot => (int)_word0.Extract(3, 3);
|
||||
public int BackgroundAlpha => (int)_word0.Extract(6, 10);
|
||||
public int BackgroundR => (int)_word0.Extract(16, 10);
|
||||
public int BackgroundG => (int)_word0.Extract(26, 10);
|
||||
public int BackgroundB => (int)_word0.Extract(36, 10);
|
||||
public int RegammaMode => (int)_word0.Extract(46, 2);
|
||||
public bool OutputFlipX => _word0.Extract(48);
|
||||
public bool OutputFlipY => _word0.Extract(49);
|
||||
public bool OutputTranspose => _word0.Extract(50);
|
||||
public int TargetRectLeft => _word1.Extract(64, 14);
|
||||
public int TargetRectRight => _word1.Extract(80, 14);
|
||||
public int TargetRectTop => _word1.Extract(96, 14);
|
||||
public int TargetRectBottom => _word1.Extract(112, 14);
|
||||
public int TargetRectLeft => (int)_word1.Extract(64, 14);
|
||||
public int TargetRectRight => (int)_word1.Extract(80, 14);
|
||||
public int TargetRectTop => (int)_word1.Extract(96, 14);
|
||||
public int TargetRectBottom => (int)_word1.Extract(112, 14);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
using Ryujinx.Common.Utilities;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
struct OutputSurfaceConfig
|
||||
{
|
||||
@@ -8,15 +10,15 @@
|
||||
#pragma warning restore CS0649
|
||||
|
||||
public PixelFormat OutPixelFormat => (PixelFormat)_word0.Extract(0, 7);
|
||||
public int OutChromaLocHoriz => _word0.Extract(7, 2);
|
||||
public int OutChromaLocVert => _word0.Extract(9, 2);
|
||||
public int OutBlkKind => _word0.Extract(11, 4);
|
||||
public int OutBlkHeight => _word0.Extract(15, 4);
|
||||
public int OutSurfaceWidth => _word0.Extract(32, 14);
|
||||
public int OutSurfaceHeight => _word0.Extract(46, 14);
|
||||
public int OutLumaWidth => _word1.Extract(64, 14);
|
||||
public int OutLumaHeight => _word1.Extract(78, 14);
|
||||
public int OutChromaWidth => _word1.Extract(96, 14);
|
||||
public int OutChromaHeight => _word1.Extract(110, 14);
|
||||
public int OutChromaLocHoriz => (int)_word0.Extract(7, 2);
|
||||
public int OutChromaLocVert => (int)_word0.Extract(9, 2);
|
||||
public int OutBlkKind => (int)_word0.Extract(11, 4);
|
||||
public int OutBlkHeight => (int)_word0.Extract(15, 4);
|
||||
public int OutSurfaceWidth => (int)_word0.Extract(32, 14);
|
||||
public int OutSurfaceHeight => (int)_word0.Extract(46, 14);
|
||||
public int OutLumaWidth => (int)_word1.Extract(64, 14);
|
||||
public int OutLumaHeight => (int)_word1.Extract(78, 14);
|
||||
public int OutChromaWidth => (int)_word1.Extract(96, 14);
|
||||
public int OutChromaHeight => (int)_word1.Extract(110, 14);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
using Ryujinx.Common.Utilities;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
struct PipeConfig
|
||||
{
|
||||
@@ -7,7 +9,7 @@
|
||||
private long _word1;
|
||||
#pragma warning restore CS0169, CS0649
|
||||
|
||||
public int DownsampleHoriz => _word0.Extract(0, 11);
|
||||
public int DownsampleVert => _word0.Extract(16, 11);
|
||||
public int DownsampleHoriz => (int)_word0.Extract(0, 11);
|
||||
public int DownsampleVert => (int)_word0.Extract(16, 11);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
using Ryujinx.Common.Utilities;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
struct SlotConfig
|
||||
{
|
||||
@@ -28,36 +30,36 @@
|
||||
public bool PpMotionFieldEnable => _word0.Extract(14);
|
||||
public bool CombMotionFieldEnable => _word0.Extract(15);
|
||||
public FrameFormat FrameFormat => (FrameFormat)_word0.Extract(16, 4);
|
||||
public int FilterLengthY => _word0.Extract(20, 2);
|
||||
public int FilterLengthX => _word0.Extract(22, 2);
|
||||
public int Panoramic => _word0.Extract(24, 12);
|
||||
public int DetailFltClamp => _word0.Extract(58, 6);
|
||||
public int FilterNoise => _word1.Extract(64, 10);
|
||||
public int FilterDetail => _word1.Extract(74, 10);
|
||||
public int ChromaNoise => _word1.Extract(84, 10);
|
||||
public int ChromaDetail => _word1.Extract(94, 10);
|
||||
public int FilterLengthY => (int)_word0.Extract(20, 2);
|
||||
public int FilterLengthX => (int)_word0.Extract(22, 2);
|
||||
public int Panoramic => (int)_word0.Extract(24, 12);
|
||||
public int DetailFltClamp => (int)_word0.Extract(58, 6);
|
||||
public int FilterNoise => (int)_word1.Extract(64, 10);
|
||||
public int FilterDetail => (int)_word1.Extract(74, 10);
|
||||
public int ChromaNoise => (int)_word1.Extract(84, 10);
|
||||
public int ChromaDetail => (int)_word1.Extract(94, 10);
|
||||
public DeinterlaceMode DeinterlaceMode => (DeinterlaceMode)_word1.Extract(104, 4);
|
||||
public int MotionAccumWeight => _word1.Extract(108, 3);
|
||||
public int NoiseIir => _word1.Extract(111, 11);
|
||||
public int LightLevel => _word1.Extract(122, 4);
|
||||
public int SoftClampLow => _word2.Extract(128, 10);
|
||||
public int SoftClampHigh => _word2.Extract(138, 10);
|
||||
public int PlanarAlpha => _word2.Extract(160, 10);
|
||||
public int MotionAccumWeight => (int)_word1.Extract(108, 3);
|
||||
public int NoiseIir => (int)_word1.Extract(111, 11);
|
||||
public int LightLevel => (int)_word1.Extract(122, 4);
|
||||
public int SoftClampLow => (int)_word2.Extract(128, 10);
|
||||
public int SoftClampHigh => (int)_word2.Extract(138, 10);
|
||||
public int PlanarAlpha => (int)_word2.Extract(160, 10);
|
||||
public bool ConstantAlpha => _word2.Extract(170);
|
||||
public int StereoInterleave => _word2.Extract(171, 3);
|
||||
public int StereoInterleave => (int)_word2.Extract(171, 3);
|
||||
public bool ClipEnabled => _word2.Extract(174);
|
||||
public int ClearRectMask => _word2.Extract(175, 8);
|
||||
public int DegammaMode => _word2.Extract(183, 2);
|
||||
public int ClearRectMask => (int)_word2.Extract(175, 8);
|
||||
public int DegammaMode => (int)_word2.Extract(183, 2);
|
||||
public bool DecompressEnable => _word2.Extract(186);
|
||||
public int DecompressCtbCount => _word3.Extract(192, 8);
|
||||
public int DecompressZbcColor => _word3.Extract(200, 32);
|
||||
public int SourceRectLeft => _word4.Extract(256, 30);
|
||||
public int SourceRectRight => _word4.Extract(288, 30);
|
||||
public int SourceRectTop => _word5.Extract(320, 30);
|
||||
public int SourceRectBottom => _word5.Extract(352, 30);
|
||||
public int DstRectLeft => _word6.Extract(384, 14);
|
||||
public int DstRectRight => _word6.Extract(400, 14);
|
||||
public int DstRectTop => _word6.Extract(416, 14);
|
||||
public int DstRectBottom => _word6.Extract(432, 14);
|
||||
public int DecompressCtbCount => (int)_word3.Extract(192, 8);
|
||||
public int DecompressZbcColor => (int)_word3.Extract(200, 32);
|
||||
public int SourceRectLeft => (int)_word4.Extract(256, 30);
|
||||
public int SourceRectRight => (int)_word4.Extract(288, 30);
|
||||
public int SourceRectTop => (int)_word5.Extract(320, 30);
|
||||
public int SourceRectBottom => (int)_word5.Extract(352, 30);
|
||||
public int DstRectLeft => (int)_word6.Extract(384, 14);
|
||||
public int DstRectRight => (int)_word6.Extract(400, 14);
|
||||
public int DstRectTop => (int)_word6.Extract(416, 14);
|
||||
public int DstRectBottom => (int)_word6.Extract(432, 14);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
using Ryujinx.Common.Utilities;
|
||||
|
||||
namespace Ryujinx.Graphics.Vic.Types
|
||||
{
|
||||
struct SlotSurfaceConfig
|
||||
{
|
||||
@@ -6,16 +8,16 @@
|
||||
private long _word1;
|
||||
|
||||
public PixelFormat SlotPixelFormat => (PixelFormat)_word0.Extract(0, 7);
|
||||
public int SlotChromaLocHoriz => _word0.Extract(7, 2);
|
||||
public int SlotChromaLocVert => _word0.Extract(9, 2);
|
||||
public int SlotBlkKind => _word0.Extract(11, 4);
|
||||
public int SlotBlkHeight => _word0.Extract(15, 4);
|
||||
public int SlotCacheWidth => _word0.Extract(19, 3);
|
||||
public int SlotSurfaceWidth => _word0.Extract(32, 14);
|
||||
public int SlotSurfaceHeight => _word0.Extract(46, 14);
|
||||
public int SlotLumaWidth => _word1.Extract(64, 14);
|
||||
public int SlotLumaHeight => _word1.Extract(78, 14);
|
||||
public int SlotChromaWidth => _word1.Extract(96, 14);
|
||||
public int SlotChromaHeight => _word1.Extract(110, 14);
|
||||
public int SlotChromaLocHoriz => (int)_word0.Extract(7, 2);
|
||||
public int SlotChromaLocVert => (int)_word0.Extract(9, 2);
|
||||
public int SlotBlkKind => (int)_word0.Extract(11, 4);
|
||||
public int SlotBlkHeight => (int)_word0.Extract(15, 4);
|
||||
public int SlotCacheWidth => (int)_word0.Extract(19, 3);
|
||||
public int SlotSurfaceWidth => (int)_word0.Extract(32, 14);
|
||||
public int SlotSurfaceHeight => (int)_word0.Extract(46, 14);
|
||||
public int SlotLumaWidth => (int)_word1.Extract(64, 14);
|
||||
public int SlotLumaHeight => (int)_word1.Extract(78, 14);
|
||||
public int SlotChromaWidth => (int)_word1.Extract(96, 14);
|
||||
public int SlotChromaHeight => (int)_word1.Extract(110, 14);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using LibHac;
|
||||
using LibHac.Account;
|
||||
using LibHac.Common;
|
||||
@@ -14,8 +13,8 @@ using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
@@ -67,6 +66,8 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
public string TitleIdText => TitleId.ToString("x16");
|
||||
|
||||
public IDiskCacheLoadState DiskCacheLoadState { get; private set; }
|
||||
|
||||
public ApplicationLoader(Switch device)
|
||||
{
|
||||
_device = device;
|
||||
@@ -94,7 +95,7 @@ namespace Ryujinx.HLE.HOS
|
||||
EnsureSaveData(new ApplicationId(TitleId));
|
||||
}
|
||||
|
||||
LoadExeFs(codeFs, metaData);
|
||||
LoadExeFs(codeFs, string.Empty, metaData);
|
||||
}
|
||||
|
||||
public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex)
|
||||
@@ -302,12 +303,6 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
public void LoadServiceNca(string ncaFile)
|
||||
{
|
||||
// Disable PPTC here as it does not support multiple processes running.
|
||||
// TODO: This should be eventually removed and it should stop using global state and
|
||||
// instead manage the cache per process.
|
||||
Ptc.Close();
|
||||
PtcProfiler.Stop();
|
||||
|
||||
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
||||
Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
||||
|
||||
@@ -369,16 +364,12 @@ namespace Ryujinx.HLE.HOS
|
||||
// Collect the nsos, ignoring ones that aren't used.
|
||||
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
|
||||
|
||||
MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode;
|
||||
|
||||
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
|
||||
{
|
||||
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
|
||||
}
|
||||
string displayVersion = _device.System.ContentManager.GetCurrentFirmwareVersion().VersionString;
|
||||
bool usePtc = _device.System.EnablePtc;
|
||||
|
||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: false);
|
||||
ProgramLoader.LoadNsos(_device.System.KernelContext, out _, metaData, programInfo, executables: programs);
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false);
|
||||
ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
|
||||
|
||||
string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16");
|
||||
bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
|
||||
@@ -477,9 +468,11 @@ namespace Ryujinx.HLE.HOS
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
|
||||
|
||||
string displayVersion = string.Empty;
|
||||
|
||||
if (controlNca != null)
|
||||
{
|
||||
ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref _displayVersion);
|
||||
ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref displayVersion);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -493,9 +486,11 @@ namespace Ryujinx.HLE.HOS
|
||||
string dummyTitleName = "";
|
||||
BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1);
|
||||
|
||||
ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref _displayVersion);
|
||||
ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref displayVersion);
|
||||
}
|
||||
|
||||
_displayVersion = displayVersion;
|
||||
|
||||
if (dataStorage == null)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA");
|
||||
@@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS
|
||||
EnsureSaveData(new ApplicationId(TitleId & ~0xFul));
|
||||
}
|
||||
|
||||
LoadExeFs(codeFs, metaData);
|
||||
LoadExeFs(codeFs, displayVersion, metaData);
|
||||
|
||||
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]");
|
||||
}
|
||||
@@ -584,7 +579,7 @@ namespace Ryujinx.HLE.HOS
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadExeFs(IFileSystem codeFs, MetaLoader metaData = null, bool isHomebrew = false)
|
||||
private void LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false)
|
||||
{
|
||||
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
|
||||
{
|
||||
@@ -649,23 +644,23 @@ namespace Ryujinx.HLE.HOS
|
||||
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
|
||||
}
|
||||
|
||||
Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode);
|
||||
|
||||
// We allow it for nx-hbloader because it can be used to launch homebrew.
|
||||
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew;
|
||||
|
||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit);
|
||||
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: programs);
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit);
|
||||
ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
|
||||
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
|
||||
DiskCacheLoadState = result.DiskCacheLoadState;
|
||||
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
|
||||
}
|
||||
|
||||
public void LoadProgram(string filePath)
|
||||
{
|
||||
MetaLoader metaData = GetDefaultNpdm();
|
||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: true);
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, string.Empty, diskCacheEnabled: false, allowCodeMemoryForJit: true);
|
||||
|
||||
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
|
||||
|
||||
@@ -761,9 +756,11 @@ namespace Ryujinx.HLE.HOS
|
||||
Graphics.Gpu.GraphicsConfig.TitleId = null;
|
||||
_device.Gpu.HostInitalized.Set();
|
||||
|
||||
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: executable);
|
||||
ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable);
|
||||
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
|
||||
DiskCacheLoadState = result.DiskCacheLoadState;
|
||||
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
|
||||
}
|
||||
|
||||
private MetaLoader GetDefaultNpdm()
|
||||
|
@@ -6,7 +6,17 @@ using Ryujinx.Memory;
|
||||
|
||||
namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
class ArmProcessContext<T> : IProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
|
||||
interface IArmProcessContext : IProcessContext
|
||||
{
|
||||
IDiskCacheLoadState Initialize(
|
||||
string titleIdText,
|
||||
string displayVersion,
|
||||
bool diskCacheEnabled,
|
||||
ulong codeAddress,
|
||||
ulong codeSize);
|
||||
}
|
||||
|
||||
class ArmProcessContext<T> : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
|
||||
{
|
||||
private readonly ulong _pid;
|
||||
private readonly GpuContext _gpuContext;
|
||||
@@ -40,6 +50,17 @@ namespace Ryujinx.HLE.HOS
|
||||
_cpuContext.Execute(context, codeAddress);
|
||||
}
|
||||
|
||||
public IDiskCacheLoadState Initialize(
|
||||
string titleIdText,
|
||||
string displayVersion,
|
||||
bool diskCacheEnabled,
|
||||
ulong codeAddress,
|
||||
ulong codeSize)
|
||||
{
|
||||
_cpuContext.PrepareCodeRange(codeAddress, codeSize);
|
||||
return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled);
|
||||
}
|
||||
|
||||
public void InvalidateCacheRegion(ulong address, ulong size)
|
||||
{
|
||||
_cpuContext.InvalidateCacheRegion(address, size);
|
||||
|
@@ -13,11 +13,30 @@ namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
private readonly ICpuEngine _cpuEngine;
|
||||
private readonly GpuContext _gpu;
|
||||
private readonly string _titleIdText;
|
||||
private readonly string _displayVersion;
|
||||
private readonly bool _diskCacheEnabled;
|
||||
private readonly ulong _codeAddress;
|
||||
private readonly ulong _codeSize;
|
||||
|
||||
public ArmProcessContextFactory(ICpuEngine cpuEngine, GpuContext gpu)
|
||||
public IDiskCacheLoadState DiskCacheLoadState { get; private set; }
|
||||
|
||||
public ArmProcessContextFactory(
|
||||
ICpuEngine cpuEngine,
|
||||
GpuContext gpu,
|
||||
string titleIdText,
|
||||
string displayVersion,
|
||||
bool diskCacheEnabled,
|
||||
ulong codeAddress,
|
||||
ulong codeSize)
|
||||
{
|
||||
_cpuEngine = cpuEngine;
|
||||
_gpu = gpu;
|
||||
_titleIdText = titleIdText;
|
||||
_displayVersion = displayVersion;
|
||||
_diskCacheEnabled = diskCacheEnabled;
|
||||
_codeAddress = codeAddress;
|
||||
_codeSize = codeSize;
|
||||
}
|
||||
|
||||
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
||||
@@ -29,21 +48,29 @@ namespace Ryujinx.HLE.HOS
|
||||
mode = MemoryManagerMode.SoftwarePageTable;
|
||||
}
|
||||
|
||||
IArmProcessContext processContext;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case MemoryManagerMode.SoftwarePageTable:
|
||||
var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
return new ArmProcessContext<MemoryManager>(pid, _cpuEngine, _gpu, memoryManager, for64Bit);
|
||||
processContext = new ArmProcessContext<MemoryManager>(pid, _cpuEngine, _gpu, memoryManager, for64Bit);
|
||||
break;
|
||||
|
||||
case MemoryManagerMode.HostMapped:
|
||||
case MemoryManagerMode.HostMappedUnsafe:
|
||||
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
||||
var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler);
|
||||
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit);
|
||||
processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize);
|
||||
|
||||
return processContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
|
||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.Horizon;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -122,6 +123,8 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
internal LibHacHorizonManager LibHacHorizonManager { get; private set; }
|
||||
|
||||
internal ServiceTable ServiceTable { get; private set; }
|
||||
|
||||
public bool IsPaused { get; private set; }
|
||||
|
||||
public Horizon(Switch device)
|
||||
@@ -319,6 +322,43 @@ namespace Ryujinx.HLE.HOS
|
||||
ViServer = new ServerBase(KernelContext, "ViServerU");
|
||||
ViServerM = new ServerBase(KernelContext, "ViServerM");
|
||||
ViServerS = new ServerBase(KernelContext, "ViServerS");
|
||||
|
||||
StartNewServices();
|
||||
}
|
||||
|
||||
private void StartNewServices()
|
||||
{
|
||||
ServiceTable = new ServiceTable();
|
||||
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices));
|
||||
|
||||
foreach (var service in services)
|
||||
{
|
||||
const ProcessCreationFlags flags =
|
||||
ProcessCreationFlags.EnableAslr |
|
||||
ProcessCreationFlags.AddressSpace64Bit |
|
||||
ProcessCreationFlags.Is64Bit |
|
||||
ProcessCreationFlags.PoolPartitionSystem;
|
||||
|
||||
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
|
||||
|
||||
int[] defaultCapabilities = new int[]
|
||||
{
|
||||
0x030363F7,
|
||||
0x1FFFFFCF,
|
||||
0x207FFFEF,
|
||||
0x47E0060F,
|
||||
0x0048BFFF,
|
||||
0x01007FFF
|
||||
};
|
||||
|
||||
// TODO:
|
||||
// - Pass enough information (capabilities, process creation info, etc) on ServiceEntry for proper initialization.
|
||||
// - Have the ThreadStart function take the syscall, address space and thread context parameters instead of passing them here.
|
||||
KernelStatic.StartInitialProcess(KernelContext, creationInfo, defaultCapabilities, 44, () =>
|
||||
{
|
||||
service.Start(KernelContext.Syscall, KernelStatic.GetCurrentProcess().CpuMemory, KernelStatic.GetCurrentThread().ThreadContext);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadKip(string kipPath)
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
@@ -16,24 +17,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
_referenceCount = 1;
|
||||
}
|
||||
|
||||
public virtual KernelResult SetName(string name)
|
||||
public virtual Result SetName(string name)
|
||||
{
|
||||
if (!KernelContext.AutoObjectNames.TryAdd(name, this))
|
||||
{
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static KernelResult RemoveName(KernelContext context, string name)
|
||||
public static Result RemoveName(KernelContext context, string name)
|
||||
{
|
||||
if (!context.AutoObjectNames.TryRemove(name, out _))
|
||||
{
|
||||
return KernelResult.NotFound;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static KAutoObject FindNamedObject(KernelContext context, string name)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
@@ -159,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
}
|
||||
}
|
||||
|
||||
public KernelResult SetLimitValue(LimitableResource resource, long limit)
|
||||
public Result SetLimitValue(LimitableResource resource, long limit)
|
||||
{
|
||||
int index = GetIndex(resource);
|
||||
|
||||
@@ -170,7 +171,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
_limit[index] = limit;
|
||||
_peak[index] = _current[index];
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -44,7 +44,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
|
||||
public void ScheduleFutureInvocation(IKFutureSchedulerObject schedulerObj, long timeout)
|
||||
{
|
||||
long timePoint = PerformanceCounter.ElapsedTicks + ConvertNanosecondsToHostTicks(timeout);
|
||||
long startTime = PerformanceCounter.ElapsedTicks;
|
||||
long timePoint = startTime + ConvertNanosecondsToHostTicks(timeout);
|
||||
|
||||
if (timePoint < startTime)
|
||||
{
|
||||
timePoint = long.MaxValue;
|
||||
}
|
||||
|
||||
lock (_context.CriticalSection.Lock)
|
||||
{
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
@@ -21,9 +22,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
|
||||
public static void InitializeResourceLimit(KResourceLimit resourceLimit, MemorySize size)
|
||||
{
|
||||
void EnsureSuccess(KernelResult result)
|
||||
void EnsureSuccess(Result result)
|
||||
{
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException($"Unexpected result \"{result}\".");
|
||||
}
|
||||
|
@@ -1,37 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
enum KernelResult
|
||||
{
|
||||
Success = 0,
|
||||
SessionCountExceeded = 0xe01,
|
||||
InvalidCapability = 0x1c01,
|
||||
ThreadNotStarted = 0x7201,
|
||||
ThreadTerminating = 0x7601,
|
||||
InvalidSize = 0xca01,
|
||||
InvalidAddress = 0xcc01,
|
||||
OutOfResource = 0xce01,
|
||||
OutOfMemory = 0xd001,
|
||||
HandleTableFull = 0xd201,
|
||||
InvalidMemState = 0xd401,
|
||||
InvalidPermission = 0xd801,
|
||||
InvalidMemRange = 0xdc01,
|
||||
InvalidPriority = 0xe001,
|
||||
InvalidCpuCore = 0xe201,
|
||||
InvalidHandle = 0xe401,
|
||||
UserCopyFailed = 0xe601,
|
||||
InvalidCombination = 0xe801,
|
||||
TimedOut = 0xea01,
|
||||
Cancelled = 0xec01,
|
||||
MaximumExceeded = 0xee01,
|
||||
InvalidEnumValue = 0xf001,
|
||||
NotFound = 0xf201,
|
||||
InvalidThread = 0xf401,
|
||||
PortRemoteClosed = 0xf601,
|
||||
InvalidState = 0xfa01,
|
||||
ReservedValue = 0xfc01,
|
||||
PortClosed = 0x10601,
|
||||
ResLimitExceeded = 0x10801,
|
||||
OutOfVaSpace = 0x20601,
|
||||
CmdBufferTooSmall = 0x20801
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
readonly struct OnScopeExit : IDisposable
|
||||
{
|
||||
private readonly Action _action;
|
||||
public OnScopeExit(Action action) => _action = action;
|
||||
public void Dispose() => _action();
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
@@ -20,38 +20,38 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
_exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
|
||||
}
|
||||
|
||||
public KernelResult AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||
public Result AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||
{
|
||||
return Add(_sendBufferDescriptors, src, dst, size, state);
|
||||
}
|
||||
|
||||
public KernelResult AddReceiveBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||
public Result AddReceiveBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||
{
|
||||
return Add(_receiveBufferDescriptors, src, dst, size, state);
|
||||
}
|
||||
|
||||
public KernelResult AddExchangeBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||
public Result AddExchangeBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||
{
|
||||
return Add(_exchangeBufferDescriptors, src, dst, size, state);
|
||||
}
|
||||
|
||||
private KernelResult Add(List<KBufferDescriptor> list, ulong src, ulong dst, ulong size, MemoryState state)
|
||||
private Result Add(List<KBufferDescriptor> list, ulong src, ulong dst, ulong size, MemoryState state)
|
||||
{
|
||||
if (list.Count < MaxInternalBuffersCount)
|
||||
{
|
||||
list.Add(new KBufferDescriptor(src, dst, size, state));
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
|
||||
public KernelResult CopyBuffersToClient(KPageTableBase memoryManager)
|
||||
public Result CopyBuffersToClient(KPageTableBase memoryManager)
|
||||
{
|
||||
KernelResult result = CopyToClient(memoryManager, _receiveBufferDescriptors);
|
||||
Result result = CopyToClient(memoryManager, _receiveBufferDescriptors);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
return CopyToClient(memoryManager, _exchangeBufferDescriptors);
|
||||
}
|
||||
|
||||
private KernelResult CopyToClient(KPageTableBase memoryManager, List<KBufferDescriptor> list)
|
||||
private Result CopyToClient(KPageTableBase memoryManager, List<KBufferDescriptor> list)
|
||||
{
|
||||
foreach (KBufferDescriptor desc in list)
|
||||
{
|
||||
@@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
copySize = desc.Size;
|
||||
}
|
||||
|
||||
KernelResult result = memoryManager.CopyDataFromCurrentProcess(
|
||||
Result result = memoryManager.CopyDataFromCurrentProcess(
|
||||
desc.ClientAddress,
|
||||
copySize,
|
||||
stateMask,
|
||||
@@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
MemoryAttribute.None,
|
||||
desc.ServerAddress);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
if (clientEndAddrTruncated < clientEndAddrRounded &&
|
||||
(clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated))
|
||||
{
|
||||
KernelResult result = memoryManager.CopyDataFromCurrentProcess(
|
||||
Result result = memoryManager.CopyDataFromCurrentProcess(
|
||||
clientEndAddrTruncated,
|
||||
clientEndAddr - clientEndAddrTruncated,
|
||||
stateMask,
|
||||
@@ -130,28 +130,28 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
MemoryAttribute.None,
|
||||
serverEndAddrTruncated);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public KernelResult UnmapServerBuffers(KPageTableBase memoryManager)
|
||||
public Result UnmapServerBuffers(KPageTableBase memoryManager)
|
||||
{
|
||||
KernelResult result = UnmapServer(memoryManager, _sendBufferDescriptors);
|
||||
Result result = UnmapServer(memoryManager, _sendBufferDescriptors);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = UnmapServer(memoryManager, _receiveBufferDescriptors);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -159,36 +159,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
return UnmapServer(memoryManager, _exchangeBufferDescriptors);
|
||||
}
|
||||
|
||||
private KernelResult UnmapServer(KPageTableBase memoryManager, List<KBufferDescriptor> list)
|
||||
private Result UnmapServer(KPageTableBase memoryManager, List<KBufferDescriptor> list)
|
||||
{
|
||||
foreach (KBufferDescriptor descriptor in list)
|
||||
{
|
||||
KernelResult result = memoryManager.UnmapNoAttributeIfStateEquals(
|
||||
Result result = memoryManager.UnmapNoAttributeIfStateEquals(
|
||||
descriptor.ServerAddress,
|
||||
descriptor.Size,
|
||||
descriptor.State);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public KernelResult RestoreClientBuffers(KPageTableBase memoryManager)
|
||||
public Result RestoreClientBuffers(KPageTableBase memoryManager)
|
||||
{
|
||||
KernelResult result = RestoreClient(memoryManager, _sendBufferDescriptors);
|
||||
Result result = RestoreClient(memoryManager, _sendBufferDescriptors);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = RestoreClient(memoryManager, _receiveBufferDescriptors);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -196,22 +196,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
return RestoreClient(memoryManager, _exchangeBufferDescriptors);
|
||||
}
|
||||
|
||||
private KernelResult RestoreClient(KPageTableBase memoryManager, List<KBufferDescriptor> list)
|
||||
private Result RestoreClient(KPageTableBase memoryManager, List<KBufferDescriptor> list)
|
||||
{
|
||||
foreach (KBufferDescriptor descriptor in list)
|
||||
{
|
||||
KernelResult result = memoryManager.UnmapIpcRestorePermission(
|
||||
Result result = memoryManager.UnmapIpcRestorePermission(
|
||||
descriptor.ClientAddress,
|
||||
descriptor.Size,
|
||||
descriptor.State);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
@@ -19,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
public KernelResult Connect(out KClientSession clientSession)
|
||||
public Result Connect(out KClientSession clientSession)
|
||||
{
|
||||
clientSession = null;
|
||||
|
||||
@@ -40,9 +41,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
|
||||
KSession session = new KSession(KernelContext, this);
|
||||
|
||||
KernelResult result = _parent.EnqueueIncomingSession(session.ServerSession);
|
||||
Result result = _parent.EnqueueIncomingSession(session.ServerSession);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
session.ClientSession.DecrementReferenceCount();
|
||||
session.ServerSession.DecrementReferenceCount();
|
||||
@@ -55,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
return result;
|
||||
}
|
||||
|
||||
public KernelResult ConnectLight(out KLightClientSession clientSession)
|
||||
public Result ConnectLight(out KLightClientSession clientSession)
|
||||
{
|
||||
clientSession = null;
|
||||
|
||||
@@ -76,9 +77,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
|
||||
KLightSession session = new KLightSession(KernelContext);
|
||||
|
||||
KernelResult result = _parent.EnqueueIncomingLightSession(session.ServerSession);
|
||||
Result result = _parent.EnqueueIncomingLightSession(session.ServerSession);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
session.ClientSession.DecrementReferenceCount();
|
||||
session.ServerSession.DecrementReferenceCount();
|
||||
@@ -128,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
}
|
||||
}
|
||||
|
||||
public new static KernelResult RemoveName(KernelContext context, string name)
|
||||
public new static Result RemoveName(KernelContext context, string name)
|
||||
{
|
||||
KAutoObject foundObj = FindNamedObject(context, name);
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
{
|
||||
@@ -27,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
CreatorProcess.IncrementReferenceCount();
|
||||
}
|
||||
|
||||
public KernelResult SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||
public Result SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
|
||||
@@ -36,13 +37,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
currentThread.SignaledObj = null;
|
||||
currentThread.ObjSyncResult = KernelResult.Success;
|
||||
currentThread.ObjSyncResult = Result.Success;
|
||||
|
||||
KernelResult result = _parent.ServerSession.EnqueueRequest(request);
|
||||
Result result = _parent.ServerSession.EnqueueRequest(request);
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
if (result == KernelResult.Success)
|
||||
if (result == Result.Success)
|
||||
{
|
||||
result = currentThread.ObjSyncResult;
|
||||
}
|
||||
@@ -50,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
return result;
|
||||
}
|
||||
|
||||
public KernelResult SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||
public Result SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
|
||||
@@ -58,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
KernelResult result = _parent.ServerSession.EnqueueRequest(request);
|
||||
Result result = _parent.ServerSession.EnqueueRequest(request);
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.Horizon.Common;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
{
|
||||
@@ -7,26 +8,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
public KServerPort ServerPort { get; }
|
||||
public KClientPort ClientPort { get; }
|
||||
|
||||
private long _nameAddress;
|
||||
private string _name;
|
||||
|
||||
private ChannelState _state;
|
||||
|
||||
public bool IsLight { get; private set; }
|
||||
|
||||
public KPort(KernelContext context, int maxSessions, bool isLight, long nameAddress) : base(context)
|
||||
public KPort(KernelContext context, int maxSessions, bool isLight, string name) : base(context)
|
||||
{
|
||||
ServerPort = new KServerPort(context, this);
|
||||
ClientPort = new KClientPort(context, this, maxSessions);
|
||||
|
||||
IsLight = isLight;
|
||||
_nameAddress = nameAddress;
|
||||
IsLight = isLight;
|
||||
_name = name;
|
||||
|
||||
_state = ChannelState.Open;
|
||||
}
|
||||
|
||||
public KernelResult EnqueueIncomingSession(KServerSession session)
|
||||
public Result EnqueueIncomingSession(KServerSession session)
|
||||
{
|
||||
KernelResult result;
|
||||
Result result;
|
||||
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
@@ -34,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
{
|
||||
ServerPort.EnqueueIncomingSession(session);
|
||||
|
||||
result = KernelResult.Success;
|
||||
result = Result.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -46,9 +47,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
return result;
|
||||
}
|
||||
|
||||
public KernelResult EnqueueIncomingLightSession(KLightServerSession session)
|
||||
public Result EnqueueIncomingLightSession(KLightServerSession session)
|
||||
{
|
||||
KernelResult result;
|
||||
Result result;
|
||||
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
@@ -56,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
{
|
||||
ServerPort.EnqueueIncomingLightSession(session);
|
||||
|
||||
result = KernelResult.Success;
|
||||
result = Result.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -3,6 +3,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
@@ -178,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
_requests = new LinkedList<KSessionRequest>();
|
||||
}
|
||||
|
||||
public KernelResult EnqueueRequest(KSessionRequest request)
|
||||
public Result EnqueueRequest(KSessionRequest request)
|
||||
{
|
||||
if (_parent.ClientSession.State != ChannelState.Open)
|
||||
{
|
||||
@@ -203,10 +204,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
Signal();
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public KernelResult Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||
public Result Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||
{
|
||||
KThread serverThread = KernelStatic.GetCurrentThread();
|
||||
KProcess serverProcess = serverThread.Owner;
|
||||
@@ -249,12 +250,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg);
|
||||
MessageHeader serverHeader = GetServerMessageHeader(serverMsg);
|
||||
|
||||
KernelResult serverResult = KernelResult.NotFound;
|
||||
KernelResult clientResult = KernelResult.Success;
|
||||
Result serverResult = KernelResult.NotFound;
|
||||
Result clientResult = Result.Success;
|
||||
|
||||
void CleanUpForError()
|
||||
{
|
||||
if (request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager) == KernelResult.Success)
|
||||
if (request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager) == Result.Success)
|
||||
{
|
||||
request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
|
||||
}
|
||||
@@ -348,7 +349,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
int newHandle = 0;
|
||||
int handle = clientProcess.CpuMemory.Read<int>(clientMsg.Address + offset * 4);
|
||||
|
||||
if (clientResult == KernelResult.Success && handle != 0)
|
||||
if (clientResult == Result.Success && handle != 0)
|
||||
{
|
||||
clientResult = GetCopyObjectHandle(clientThread, serverProcess, handle, out newHandle);
|
||||
}
|
||||
@@ -365,7 +366,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
|
||||
if (handle != 0)
|
||||
{
|
||||
if (clientResult == KernelResult.Success)
|
||||
if (clientResult == Result.Success)
|
||||
{
|
||||
clientResult = GetMoveObjectHandle(clientProcess, serverProcess, handle, out newHandle);
|
||||
}
|
||||
@@ -380,7 +381,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
if (clientResult != Result.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
@@ -412,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
ref recvListDstOffset,
|
||||
out ulong recvListBufferAddress);
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
if (clientResult != Result.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
@@ -429,7 +430,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
MemoryAttribute.Uncached,
|
||||
MemoryAttribute.None);
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
if (clientResult != Result.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
@@ -498,7 +499,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
notReceiveDesc,
|
||||
out dstAddress);
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
if (clientResult != Result.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
@@ -518,7 +519,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
clientResult = request.BufferDescriptorTable.AddExchangeBuffer(bufferAddress, dstAddress, bufferSize, state);
|
||||
}
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
if (clientResult != Result.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
@@ -573,7 +574,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
serverProcess.CpuMemory.Write(copyDst, clientProcess.CpuMemory.GetSpan(copySrc, (int)copySize));
|
||||
}
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
if (clientResult != Result.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
@@ -581,10 +582,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
}
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public KernelResult Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||
public Result Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||
{
|
||||
KThread serverThread = KernelStatic.GetCurrentThread();
|
||||
KProcess serverProcess = serverThread.Owner;
|
||||
@@ -618,8 +619,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg);
|
||||
MessageHeader serverHeader = GetServerMessageHeader(serverMsg);
|
||||
|
||||
KernelResult clientResult = KernelResult.Success;
|
||||
KernelResult serverResult = KernelResult.Success;
|
||||
Result clientResult = Result.Success;
|
||||
Result serverResult = Result.Success;
|
||||
|
||||
void CleanUpForError()
|
||||
{
|
||||
@@ -683,7 +684,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
// Copy receive and exchange buffers.
|
||||
clientResult = request.BufferDescriptorTable.CopyBuffersToClient(clientProcess.MemoryManager);
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
if (clientResult != Result.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
@@ -734,7 +735,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
|
||||
if (handle != 0)
|
||||
{
|
||||
if (clientResult == KernelResult.Success)
|
||||
if (clientResult == Result.Success)
|
||||
{
|
||||
clientResult = GetMoveObjectHandle(serverProcess, clientProcess, handle, out newHandle);
|
||||
}
|
||||
@@ -776,7 +777,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
ref recvListDstOffset,
|
||||
out recvListBufferAddress);
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
if (clientResult != Result.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
@@ -793,7 +794,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
MemoryAttribute.None,
|
||||
descriptor.BufferAddress);
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
if (clientResult != Result.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
@@ -888,7 +889,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
return new MessageHeader(word0, word1, word2);
|
||||
}
|
||||
|
||||
private KernelResult GetCopyObjectHandle(KThread srcThread, KProcess dstProcess, int srcHandle, out int dstHandle)
|
||||
private Result GetCopyObjectHandle(KThread srcThread, KProcess dstProcess, int srcHandle, out int dstHandle)
|
||||
{
|
||||
dstHandle = 0;
|
||||
|
||||
@@ -919,7 +920,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
}
|
||||
}
|
||||
|
||||
private KernelResult GetMoveObjectHandle(KProcess srcProcess, KProcess dstProcess, int srcHandle, out int dstHandle)
|
||||
private Result GetMoveObjectHandle(KProcess srcProcess, KProcess dstProcess, int srcHandle, out int dstHandle)
|
||||
{
|
||||
dstHandle = 0;
|
||||
|
||||
@@ -927,7 +928,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
KernelResult result = dstProcess.HandleTable.GenerateHandle(obj, out dstHandle);
|
||||
Result result = dstProcess.HandleTable.GenerateHandle(obj, out dstHandle);
|
||||
|
||||
srcProcess.HandleTable.CloseHandle(srcHandle);
|
||||
|
||||
@@ -964,7 +965,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
return receiveList;
|
||||
}
|
||||
|
||||
private KernelResult GetReceiveListAddress(
|
||||
private Result GetReceiveListAddress(
|
||||
PointerBufferDesc descriptor,
|
||||
Message message,
|
||||
uint recvListType,
|
||||
@@ -1038,7 +1039,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
|
||||
address = recvListBufferAddress;
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private void CloseAllHandles(Message message, MessageHeader header, KProcess process)
|
||||
@@ -1166,19 +1167,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
return hasRequest;
|
||||
}
|
||||
|
||||
private void FinishRequest(KSessionRequest request, KernelResult result)
|
||||
private void FinishRequest(KSessionRequest request, Result result)
|
||||
{
|
||||
KProcess clientProcess = request.ClientThread.Owner;
|
||||
KProcess serverProcess = request.ServerProcess;
|
||||
|
||||
KernelResult unmapResult = KernelResult.Success;
|
||||
Result unmapResult = Result.Success;
|
||||
|
||||
if (serverProcess != null)
|
||||
{
|
||||
unmapResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
|
||||
}
|
||||
|
||||
if (unmapResult == KernelResult.Success)
|
||||
if (unmapResult == Result.Success)
|
||||
{
|
||||
request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
|
||||
}
|
||||
@@ -1186,7 +1187,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
WakeClientThread(request, result);
|
||||
}
|
||||
|
||||
private void WakeClientThread(KSessionRequest request, KernelResult result)
|
||||
private void WakeClientThread(KSessionRequest request, Result result)
|
||||
{
|
||||
// Wait client thread waiting for a response for the given request.
|
||||
if (request.AsyncEvent != null)
|
||||
@@ -1203,16 +1204,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
}
|
||||
}
|
||||
|
||||
private void SendResultToAsyncRequestClient(KSessionRequest request, KernelResult result)
|
||||
private void SendResultToAsyncRequestClient(KSessionRequest request, Result result)
|
||||
{
|
||||
KProcess clientProcess = request.ClientThread.Owner;
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
ulong address = request.CustomCmdBuffAddr;
|
||||
|
||||
clientProcess.CpuMemory.Write<ulong>(address, 0);
|
||||
clientProcess.CpuMemory.Write(address + 8, (int)result);
|
||||
clientProcess.CpuMemory.Write(address + 8, result.ErrorCode);
|
||||
}
|
||||
|
||||
clientProcess.MemoryManager.UnborrowIpcBuffer(request.CustomCmdBuffAddr, request.CustomCmdBuffSize);
|
||||
@@ -1220,24 +1221,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
request.AsyncEvent.Signal();
|
||||
}
|
||||
|
||||
private void WakeServerThreads(KernelResult result)
|
||||
private void WakeServerThreads(Result result)
|
||||
{
|
||||
// Wake all server threads waiting for requests.
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
foreach (KThread thread in WaitingThreads)
|
||||
{
|
||||
WakeAndSetResult(thread, result);
|
||||
WakeAndSetResult(thread, result, this);
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
private void WakeAndSetResult(KThread thread, KernelResult result)
|
||||
private void WakeAndSetResult(KThread thread, Result result, KSynchronizationObject signaledObj = null)
|
||||
{
|
||||
if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
|
||||
{
|
||||
thread.SignaledObj = null;
|
||||
thread.SignaledObj = signaledObj;
|
||||
thread.ObjSyncResult = result;
|
||||
|
||||
thread.Reschedule(ThreadSchedState.Running);
|
||||
|
@@ -2,6 +2,7 @@
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
@@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||
[ThreadStatic]
|
||||
private static KThread CurrentThread;
|
||||
|
||||
public static KernelResult StartInitialProcess(
|
||||
public static Result StartInitialProcess(
|
||||
KernelContext context,
|
||||
ProcessCreationInfo creationInfo,
|
||||
ReadOnlySpan<int> capabilities,
|
||||
@@ -24,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
KProcess process = new KProcess(context);
|
||||
|
||||
KernelResult result = process.Initialize(
|
||||
Result result = process.Initialize(
|
||||
creationInfo,
|
||||
capabilities,
|
||||
context.ResourceLimit,
|
||||
@@ -32,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||
null,
|
||||
customThreadStart);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
@@ -21,13 +22,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public KernelResult Initialize(ulong address, ulong size)
|
||||
public Result Initialize(ulong address, ulong size)
|
||||
{
|
||||
Owner = KernelStatic.GetCurrentProcess();
|
||||
|
||||
KernelResult result = Owner.MemoryManager.BorrowCodeMemory(_pageList, address, size);
|
||||
Result result = Owner.MemoryManager.BorrowCodeMemory(_pageList, address, size);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -39,10 +40,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
_isMapped = false;
|
||||
_isOwnerMapped = false;
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public KernelResult Map(ulong address, ulong size, KMemoryPermission perm)
|
||||
public Result Map(ulong address, ulong size, KMemoryPermission perm)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
|
||||
{
|
||||
@@ -58,9 +59,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
|
||||
KernelResult result = process.MemoryManager.MapPages(address, _pageList, MemoryState.CodeWritable, KMemoryPermission.ReadAndWrite);
|
||||
Result result = process.MemoryManager.MapPages(address, _pageList, MemoryState.CodeWritable, KMemoryPermission.ReadAndWrite);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -68,10 +69,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
_isMapped = true;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public KernelResult MapToOwner(ulong address, ulong size, KMemoryPermission permission)
|
||||
public Result MapToOwner(ulong address, ulong size, KMemoryPermission permission)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
|
||||
{
|
||||
@@ -87,9 +88,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
Debug.Assert(permission == KMemoryPermission.Read || permission == KMemoryPermission.ReadAndExecute);
|
||||
|
||||
KernelResult result = Owner.MemoryManager.MapPages(address, _pageList, MemoryState.CodeReadOnly, permission);
|
||||
Result result = Owner.MemoryManager.MapPages(address, _pageList, MemoryState.CodeReadOnly, permission);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -97,10 +98,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
_isOwnerMapped = true;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public KernelResult Unmap(ulong address, ulong size)
|
||||
public Result Unmap(ulong address, ulong size)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
|
||||
{
|
||||
@@ -111,9 +112,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
|
||||
KernelResult result = process.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeWritable);
|
||||
Result result = process.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeWritable);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -123,10 +124,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
_isMapped = false;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public KernelResult UnmapFromOwner(ulong address, ulong size)
|
||||
public Result UnmapFromOwner(ulong address, ulong size)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
|
||||
{
|
||||
@@ -135,9 +136,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
KernelResult result = Owner.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeReadOnly);
|
||||
Result result = Owner.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeReadOnly);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -147,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
_isOwnerMapped = false;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
@@ -156,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
ulong size = _pageList.GetPagesCount() * KPageTableBase.PageSize;
|
||||
|
||||
if (Owner.MemoryManager.UnborrowCodeMemory(_address, size, _pageList) != KernelResult.Success)
|
||||
if (Owner.MemoryManager.UnborrowCodeMemory(_address, size, _pageList) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes.");
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
using Ryujinx.Common.Collections;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
@@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
_blockTree = new IntrusiveRedBlackTree<KMemoryBlock>();
|
||||
}
|
||||
|
||||
public KernelResult Initialize(ulong addrSpaceStart, ulong addrSpaceEnd, KMemoryBlockSlabManager slabManager)
|
||||
public Result Initialize(ulong addrSpaceStart, ulong addrSpaceEnd, KMemoryBlockSlabManager slabManager)
|
||||
{
|
||||
_slabManager = slabManager;
|
||||
_addrSpaceStart = addrSpaceStart;
|
||||
@@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
KMemoryPermission.None,
|
||||
MemoryAttribute.None));
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void InsertBlock(
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
@@ -25,20 +25,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
_pageHeap.UpdateUsedSize();
|
||||
}
|
||||
|
||||
public KernelResult AllocatePages(out KPageList pageList, ulong pagesCount)
|
||||
public Result AllocatePages(out KPageList pageList, ulong pagesCount)
|
||||
{
|
||||
if (pagesCount == 0)
|
||||
{
|
||||
pageList = new KPageList();
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
lock (_pageHeap)
|
||||
{
|
||||
KernelResult result = AllocatePagesImpl(out pageList, pagesCount, false);
|
||||
Result result = AllocatePagesImpl(out pageList, pagesCount, false);
|
||||
|
||||
if (result == KernelResult.Success)
|
||||
if (result == Result.Success)
|
||||
{
|
||||
foreach (var node in pageList)
|
||||
{
|
||||
@@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
}
|
||||
}
|
||||
|
||||
private KernelResult AllocatePagesImpl(out KPageList pageList, ulong pagesCount, bool random)
|
||||
private Result AllocatePagesImpl(out KPageList pageList, ulong pagesCount, bool random)
|
||||
{
|
||||
pageList = new KPageList();
|
||||
|
||||
@@ -95,9 +95,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
break;
|
||||
}
|
||||
|
||||
KernelResult result = pageList.AddRange(allocatedBlock, pagesPerAlloc);
|
||||
Result result = pageList.AddRange(allocatedBlock, pagesPerAlloc);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
if (result != Result.Success)
|
||||
{
|
||||
FreePages(pageList);
|
||||
_pageHeap.Free(allocatedBlock, pagesPerAlloc);
|
||||
@@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private ulong AllocatePagesContiguousImpl(ulong pagesCount, ulong alignPages, bool random)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user