Compare commits

...

16 Commits

Author SHA1 Message Date
yell0wsuit
2b6cc4b353 Add the "Auto" theme option in setting (#6611)
* Add "Follow OS theme" option

* Update App.axaml.cs

* Add "Follow OS theme" option

* Update App.axaml.cs

* Remove `this`

* Remove annotation for nullable reference

* Change into switch expression to make it concise

* Change comments to XML docs

* Update en_US.json

* Fix icons in About dialog do not response to "auto" theme

The theme icons seemingly use Dark variant event when the OS theme is light. In addition, I added ThemeManager common to make it accessible for both App and AboutWindow

* Newline at the end

* newline moment

* Update ThemeManager.cs

* bait to switch to lf

* change to lf

* temp. revert

* Add back ThemeManager.cs common, pls pass the format check

* I found the mistake: should have put `ThemeManager.OnThemeChanged();` in try block

Finally solve the formatting check

* test formatting

* Update App.axaml.cs

* Ok i seem to forget to add version lol

* Fix info CA1816
2024-05-14 17:00:03 +02:00
Gavin Zyonse
075575200d Update compatibility information in README.md (#6801) 2024-05-14 16:58:48 +02:00
gdkchan
3a3b51893e Add support for bindless textures from storage buffer on Vulkan (#6721)
* Halve primitive ID when converting quads to triangles

* Shader cache version bump

* Add support for bindless textures from storage buffer on Vulkan
2024-05-14 16:47:16 +02:00
MutantAura
44dbab3848 discordRPC: Truncate game title and details if they exceed discord byte limit. (#6581)
* Truncate game title and details if they exceed DiscordRPC limit.

* Update implementation to a byte total check.

* Track if the string has actually been modified correctly.

* Allow an early function return and simplify logic.

* Better handling of large input strings and minimise loop opportunities.

* Remove unused using.

* Update to `applicationName` over `titleName`.
2024-05-14 16:36:44 +02:00
Tsubasa0504
cada4d04ef HID: Stub IHidServer: 134 (SetNpadAnalogStickUseCenterClamp) (#6664)
* Add files via upload

* Update IHidServer.cs

mistakes...

* format

how do i do it

* Update src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs

Co-authored-by: Agatem <agaatem@outlook.com>

* Update src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs

Co-authored-by: Agatem <agaatem@outlook.com>

* bruh

* Apply suggestions from code review

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* use readuint32 instead

* second thought

* i hope it works

thanks someone higher up with the same thing

* pid

* Apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* styles i think

* Apply suggestions from code review

Co-authored-by: makigumo <makigumo@users.noreply.github.com>

---------

Co-authored-by: Agatem <agaatem@outlook.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: makigumo <makigumo@users.noreply.github.com>
2024-05-14 16:26:49 +02:00
Luke Warner
e9edf0ab7f Update outdated Windows version warning (#6481)
* Change text

* clarify version number

* update gtk
2024-05-14 16:19:43 +02:00
TSRBerry
6e40b64554 Add linux specific files to local builds (#6762) 2024-05-14 16:06:40 +02:00
Isaac Marovitz
1a676ee913 Update DotSettings (#6535) 2024-05-14 15:59:28 +02:00
Marco Carvalho
a23d8cb92f Replace "List.ForEach" for "foreach" (#6783)
* Replace "List.ForEach" for "foreach"

* dotnet format

* Update Ptc.cs

* Update GpuContext.cs
2024-05-08 13:53:25 +02:00
Nicolas Abram
ab12fbe963 Fix system dateTime loading in avalonia LoadCurrentConfiguration (#6676)
* Fix system dateTime loading in avalonia LoadCurrentConfiguration

* Rename local var to not use upper camel case
2024-05-02 13:33:28 +02:00
MutantAura
d0cc13ce0b UI: Fix some MainWindow bugs and implement menubar items to change window size. (#6750)
* Do not save window dimensions when maximized.

* Implement option to disable window size/position memory.

* Remove logging statements

* Implement menubar items for common window sizes.

* formatting fixes

* Set 720p window as a composite value.

* Remove unused using

* Fix exception paramter.

* Force restore window when a size change is requested

* Fix some resizing bugs.
2024-05-01 18:21:24 +02:00
Andrew Glaze
65c035cdf8 Fix alt key appearing as control in settings menus (#6742) 2024-04-29 18:18:27 +01:00
Exhigh
56c5dbe557 Fix Cursor States On Windows (#6725)
* [Ava]: Fix Cursor States On Windows

It's been sometime since the last PR #5415 was made and last time i was waiting for Ava 11 to be merged before re-writing the code and along the way forgot about the PR.

Anyway this PR supersedes both #5288 and #5415, and fixes issue: #5136

* Now, the bounds for which the cursor should be detected in renderer should be accurate to any scaling / resolution, taking into account the status and the menu bar. ( This issue was partially resolved by #6450 )

* Reduced the number of times the cursor updates from per frame update to updating only when the cursor state needs to be changed.

* Fixed the issue wherein you weren't able to resize the window, because of the cursor passthrough which caused the cursor to reset from the reset icon or flicker.

* Fixed the issue caused by #6450 which caused the cursor to disappear over the submenus while cursor was set to always hide.

* Changed the cursor state to not disappear while the game is being loaded. ( Needs Feedback ).

* Removed an unused library import.

* PR feedback

* Fix excessive line breaks and whitespaces and other feedback
* Add a check before calculating cursor idle time, such that it calculates only while the cursor mode is OnIdle.

* PR Feedback

* Rework the cursor state check code block

Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com>

* PR Feedback

* A simpler version of the previous implementation.

Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com>

* PR Feedback

* PR Feedback

---------

Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
2024-04-28 20:21:08 -03:00
MaxLastBreath
5976a5161b Fix direct keyboard not working when using a Controller. (#6716)
* Fix direct keyboard not working when connected with a controller

- Pass KeyboardDriver to NpadController.GetHLEKeyboardInput().
- Always fetch all keyboards if Direct Keyboard is turned on.
- Remove unnecessary return null.

* Get Keyboard Inputs outside of the controller loop.

- Moved GetHLEKeyboardInput outside of the controller loop.
- Made GetHLEKeyboardInput public static from public

* Removed extra newline

* Update src/Ryujinx.Input/HLE/NpadManager.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update src/Ryujinx.Input/HLE/NpadController.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2024-04-28 19:02:29 +02:00
riperiperi
3d4dea624d HID: Correct direct mouse deltas (#6736)
The delta position of the mouse should be the difference between the current and last position. Subtracting the last deltas doesn't really make sense.

Won't implement pointer lock for first person games, but might stop some super weird behaviour with the mouse values appearing totally random.
2024-04-28 18:55:37 +02:00
TSRBerry
89a274c6a6 ci: Replace macos-latest label with macos-13 (#6729)
Due to a change to the GitHub runner labels a few days ago (see: actions/runner#3256) our build workflows for macOS x64 didn't work anymore.
According to https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories the macos-13 label is not using arm64 yet.

Until a better solution is offered in the linked issue above, we'll keep using the macos-13 label which hopefully doesn't switch to arm64 soon.
2024-04-26 20:36:35 -03:00
45 changed files with 529 additions and 146 deletions

View File

@@ -20,7 +20,7 @@ jobs:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
- { name: osx-x64, os: macOS-latest, zip_os_name: osx_x64 }
- { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }
fail-fast: false
steps:
@@ -41,12 +41,12 @@ jobs:
- name: Change config filename
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Change config filename for macOS
run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
if: github.event_name == 'pull_request' && matrix.platform.os == 'macOS-latest'
if: github.event_name == 'pull_request' && matrix.platform.os == 'macos-13'
- name: Build
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
@@ -61,15 +61,15 @@ jobs:
- name: Publish Ryujinx
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Publish Ryujinx.Headless.SDL2
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Publish Ryujinx.Gtk3
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_gtk -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Gtk3 --self-contained true
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Set executable bit
run: |
@@ -83,21 +83,21 @@ jobs:
with:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
path: publish
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v4
with:
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
path: publish_sdl2_headless
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Upload Ryujinx.Gtk3 artifact
uses: actions/upload-artifact@v4
with:
name: gtk-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
path: publish_gtk
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest'
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
build_macos:
name: macOS Universal (${{ matrix.configuration }})

View File

@@ -36,8 +36,8 @@
## Compatibility
As of October 2023, Ryujinx has been tested on approximately 4,200 titles;
over 4,150 boot past menus and into gameplay, with roughly 3,500 of those being considered playable.
As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).

View File

@@ -4,6 +4,8 @@
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASET/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean>

View File

@@ -857,8 +857,14 @@ namespace ARMeilleure.Translation.PTC
Stopwatch sw = Stopwatch.StartNew();
threads.ForEach((thread) => thread.Start());
threads.ForEach((thread) => thread.Join());
foreach (var thread in threads)
{
thread.Start();
}
foreach (var thread in threads)
{
thread.Join();
}
threads.Clear();

View File

@@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsMismatchingViewFormat;
public readonly bool SupportsCubemapView;
public readonly bool SupportsNonConstantTextureOffset;
public readonly bool SupportsQuads;
public readonly bool SupportsSeparateSampler;
public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence;
@@ -93,6 +94,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsMismatchingViewFormat,
bool supportsCubemapView,
bool supportsNonConstantTextureOffset,
bool supportsQuads,
bool supportsSeparateSampler,
bool supportsShaderBallot,
bool supportsShaderBarrierDivergence,
@@ -146,6 +148,7 @@ namespace Ryujinx.Graphics.GAL
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
SupportsCubemapView = supportsCubemapView;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
SupportsQuads = supportsQuads;
SupportsSeparateSampler = supportsSeparateSampler;
SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;

View File

@@ -395,8 +395,14 @@ namespace Ryujinx.Graphics.Gpu
{
Renderer.CreateSync(SyncNumber, strict);
SyncActions.ForEach(action => action.SyncPreAction(syncpoint));
SyncpointActions.ForEach(action => action.SyncPreAction(syncpoint));
foreach (var action in SyncActions)
{
action.SyncPreAction(syncpoint);
}
foreach (var action in SyncpointActions)
{
action.SyncPreAction(syncpoint);
}
SyncNumber++;

View File

@@ -18,6 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private readonly ShaderSpecializationState _newSpecState;
private readonly int _stageIndex;
private readonly bool _isVulkan;
private readonly bool _hasGeometryShader;
private readonly bool _supportsQuads;
/// <summary>
/// Creates a new instance of the cached GPU state accessor for shader translation.
@@ -29,6 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <param name="newSpecState">Shader specialization state of the recompiled shader</param>
/// <param name="counts">Resource counts shared across all shader stages</param>
/// <param name="stageIndex">Shader stage index</param>
/// <param name="hasGeometryShader">Indicates if a geometry shader is present</param>
public DiskCacheGpuAccessor(
GpuContext context,
ReadOnlyMemory<byte> data,
@@ -36,7 +39,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
ShaderSpecializationState oldSpecState,
ShaderSpecializationState newSpecState,
ResourceCounts counts,
int stageIndex) : base(context, counts, stageIndex)
int stageIndex,
bool hasGeometryShader) : base(context, counts, stageIndex)
{
_data = data;
_cb1Data = cb1Data;
@@ -44,6 +48,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
_newSpecState = newSpecState;
_stageIndex = stageIndex;
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_hasGeometryShader = hasGeometryShader;
_supportsQuads = context.Capabilities.SupportsQuads;
if (stageIndex == (int)ShaderStage.Geometry - 1)
{
@@ -100,7 +106,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <inheritdoc/>
public GpuGraphicsState QueryGraphicsState()
{
return _oldSpecState.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _oldSpecState.GraphicsState.YNegateEnabled);
return _oldSpecState.GraphicsState.CreateShaderGraphicsState(
!_isVulkan,
_supportsQuads,
_hasGeometryShader,
_isVulkan || _oldSpecState.GraphicsState.YNegateEnabled);
}
/// <inheritdoc/>

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 6577;
private const uint CodeGenVersion = 5936;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -601,6 +601,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
TargetApi api = _context.Capabilities.Api;
bool hasCachedGs = guestShaders[4].HasValue;
for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
{
if (guestShaders[stageIndex + 1].HasValue)
@@ -610,7 +612,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
byte[] guestCode = shader.Code;
byte[] cb1Data = shader.Cb1Data;
DiskCacheGpuAccessor gpuAccessor = new(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex);
DiskCacheGpuAccessor gpuAccessor = new(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex, hasCachedGs);
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0);
if (nextStage != null)
@@ -623,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
byte[] guestCodeA = guestShaders[0].Value.Code;
byte[] cb1DataA = guestShaders[0].Value.Cb1Data;
DiskCacheGpuAccessor gpuAccessorA = new(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0);
DiskCacheGpuAccessor gpuAccessorA = new(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0, hasCachedGs);
translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0);
}
@@ -711,7 +713,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
GuestCodeAndCbData shader = guestShaders[0].Value;
ResourceCounts counts = new();
ShaderSpecializationState newSpecState = new(ref specState.ComputeState);
DiskCacheGpuAccessor gpuAccessor = new(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0);
DiskCacheGpuAccessor gpuAccessor = new(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0, false);
gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false);
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0);

View File

@@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly int _stageIndex;
private readonly bool _compute;
private readonly bool _isVulkan;
private readonly bool _hasGeometryShader;
private readonly bool _supportsQuads;
/// <summary>
/// Creates a new instance of the GPU state accessor for graphics shader translation.
@@ -25,12 +27,20 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="channel">GPU channel</param>
/// <param name="state">Current GPU state</param>
/// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param>
public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state, int stageIndex) : base(context, state.ResourceCounts, stageIndex)
/// <param name="hasGeometryShader">Indicates if a geometry shader is present</param>
public GpuAccessor(
GpuContext context,
GpuChannel channel,
GpuAccessorState state,
int stageIndex,
bool hasGeometryShader) : base(context, state.ResourceCounts, stageIndex)
{
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_channel = channel;
_state = state;
_stageIndex = stageIndex;
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_hasGeometryShader = hasGeometryShader;
_supportsQuads = context.Capabilities.SupportsQuads;
if (stageIndex == (int)ShaderStage.Geometry - 1)
{
@@ -105,7 +115,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <inheritdoc/>
public GpuGraphicsState QueryGraphicsState()
{
return _state.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _state.GraphicsState.YNegateEnabled);
return _state.GraphicsState.CreateShaderGraphicsState(
!_isVulkan,
_supportsQuads,
_hasGeometryShader,
_isVulkan || _state.GraphicsState.YNegateEnabled);
}
/// <inheritdoc/>

View File

@@ -106,8 +106,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Creates a new graphics state from this state that can be used for shader generation.
/// </summary>
/// <param name="hostSupportsAlphaTest">Indicates if the host API supports alpha test operations</param>
/// <param name="hostSupportsQuads">Indicates if the host API supports quad primitives</param>
/// <param name="hasGeometryShader">Indicates if a geometry shader is used</param>
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
/// <returns>GPU graphics state that can be used for shader translation</returns>
public readonly GpuGraphicsState CreateShaderGraphicsState(bool hostSupportsAlphaTest, bool originUpperLeft)
public readonly GpuGraphicsState CreateShaderGraphicsState(bool hostSupportsAlphaTest, bool hostSupportsQuads, bool hasGeometryShader, bool originUpperLeft)
{
AlphaTestOp alphaTestOp;
@@ -130,6 +133,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
};
}
bool isQuad = Topology == PrimitiveTopology.Quads || Topology == PrimitiveTopology.QuadStrip;
bool halvePrimitiveId = !hostSupportsQuads && !hasGeometryShader && isQuad;
return new GpuGraphicsState(
EarlyZForce,
ConvertToInputTopology(Topology, TessellationMode),
@@ -149,7 +155,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
in FragmentOutputTypes,
DualSourceBlendEnable,
YNegateEnabled,
originUpperLeft);
originUpperLeft,
halvePrimitiveId);
}
/// <summary>

View File

@@ -339,7 +339,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (gpuVa != 0)
{
GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState, stageIndex);
GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState, stageIndex, addresses.Geometry != 0);
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa);
if (nextStage != null)

View File

@@ -161,6 +161,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsBgraFormat: false,
supportsR4G4Format: false,
supportsR4G4B4A4Format: true,
supportsScaledVertexFormats: true,
supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true,
supportsSparseBuffer: false,
@@ -175,7 +176,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
supportsCubemapView: true,
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
supportsScaledVertexFormats: true,
supportsQuads: HwCapabilities.SupportsQuads,
supportsSeparateSampler: false,
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),

View File

@@ -102,6 +102,11 @@ namespace Ryujinx.Graphics.Shader
/// </summary>
public readonly bool OriginUpperLeft;
/// <summary>
/// Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion.
/// </summary>
public readonly bool HalvePrimitiveId;
/// <summary>
/// Creates a new GPU graphics state.
/// </summary>
@@ -124,6 +129,7 @@ namespace Ryujinx.Graphics.Shader
/// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param>
/// <param name="yNegateEnabled">Indicates if negation of the viewport Y axis is enabled</param>
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
/// <param name="halvePrimitiveId">Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion</param>
public GpuGraphicsState(
bool earlyZForce,
InputTopology topology,
@@ -143,7 +149,8 @@ namespace Ryujinx.Graphics.Shader
in Array8<AttributeType> fragmentOutputTypes,
bool dualSourceBlendEnable,
bool yNegateEnabled,
bool originUpperLeft)
bool originUpperLeft,
bool halvePrimitiveId)
{
EarlyZForce = earlyZForce;
Topology = topology;
@@ -164,6 +171,7 @@ namespace Ryujinx.Graphics.Shader
DualSourceBlendEnable = dualSourceBlendEnable;
YNegateEnabled = yNegateEnabled;
OriginUpperLeft = originUpperLeft;
HalvePrimitiveId = halvePrimitiveId;
}
}
}

View File

@@ -135,6 +135,7 @@ namespace Ryujinx.Graphics.Shader
default,
false,
false,
false,
false);
}

View File

@@ -84,6 +84,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
value = context.IConvertU32ToFP32(value);
}
}
else if (offset == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
{
value = context.ShiftRightS32(value, Const(1));
}
context.Copy(Register(rd), value);
}
@@ -187,6 +191,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
}
else if (op.Imm10 == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
{
// If quads are used, but the host does not support them, they need to be converted to triangles.
// Since each quad becomes 2 triangles, we need to compensate here and divide primitive ID by 2.
res = context.ShiftRightS32(res, Const(1));
}
else if (op.Imm10 == AttributeConsts.FrontFacing && context.TranslatorContext.GpuAccessor.QueryHostHasFrontFacingBug())
{
// gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs.

View File

@@ -66,9 +66,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (nvHandle.AsgOp is not Operation handleOp ||
handleOp.Inst != Instruction.Load ||
handleOp.StorageKind != StorageKind.Input)
(handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
{
// Right now, we only allow bindless access when the handle comes from a shader input.
// Right now, we only allow bindless access when the handle comes from a shader input or storage buffer.
// This is an artificial limitation to prevent it from being used in cases where it
// would have a large performance impact of loading all textures in the pool.
// It might be removed in the future, if we can mitigate the performance impact.

View File

@@ -45,6 +45,8 @@ namespace Ryujinx.Graphics.Shader.Translation
public bool YNegateEnabled => _graphicsState.YNegateEnabled;
public bool OriginUpperLeft => _graphicsState.OriginUpperLeft;
public bool HalvePrimitiveId => _graphicsState.HalvePrimitiveId;
public ImapPixelType[] ImapTypes { get; }
public bool IaIndexing { get; private set; }
public bool OaIndexing { get; private set; }

View File

@@ -691,6 +691,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsBgraFormat: true,
supportsR4G4Format: false,
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat,
supportsSparseBuffer: features2.Features.SparseBinding && mainQueueProperties.QueueFlags.HasFlag(QueueFlags.SparseBindingBit),
@@ -705,7 +706,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsMismatchingViewFormat: true,
supportsCubemapView: !IsAmdGcn,
supportsNonConstantTextureOffset: false,
supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
supportsQuads: false,
supportsSeparateSampler: true,
supportsShaderBallot: false,
supportsShaderBarrierDivergence: Vendor != Vendor.Intel,

View File

@@ -75,7 +75,7 @@ namespace Ryujinx
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
{
MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning);
MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning);
}
// Parse arguments

View File

@@ -63,7 +63,7 @@
</Content>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'">
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
<Content Include="..\..\distribution\linux\Ryujinx.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

View File

@@ -23,8 +23,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
newState.Buttons = (MouseButton)buttons;
newState.X = mouseX;
newState.Y = mouseY;
newState.DeltaX = mouseX - previousEntry.DeltaX;
newState.DeltaY = mouseY - previousEntry.DeltaY;
newState.DeltaX = mouseX - previousEntry.X;
newState.DeltaY = mouseY - previousEntry.Y;
newState.WheelDeltaX = scrollX;
newState.WheelDeltaY = scrollY;
newState.Attributes = connected ? MouseAttribute.IsConnected : MouseAttribute.None;

View File

@@ -22,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private bool _sixAxisSensorFusionEnabled;
private bool _unintendedHomeButtonInputProtectionEnabled;
private bool _npadAnalogStickCenterClampEnabled;
private bool _vibrationPermitted;
private bool _usbFullKeyControllerEnabled;
private readonly bool _isFirmwareUpdateAvailableForSixAxisSensor;
@@ -1107,6 +1108,19 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// If not, it returns nothing.
}
[CommandCmif(134)] // 6.1.0+
// SetNpadUseAnalogStickUseCenterClamp(bool Enable, nn::applet::AppletResourceUserId)
public ResultCode SetNpadUseAnalogStickUseCenterClamp(ServiceCtx context)
{
ulong pid = context.RequestData.ReadUInt64();
_npadAnalogStickCenterClampEnabled = context.RequestData.ReadUInt32() != 0;
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { pid, appletResourceUserId, _npadAnalogStickCenterClampEnabled });
return ResultCode.Success;
}
[CommandCmif(200)]
// GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo
public ResultCode GetVibrationDeviceInfo(ServiceCtx context)

View File

@@ -48,7 +48,7 @@
</Content>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'">
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
<Content Include="..\..\distribution\linux\Ryujinx.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

View File

@@ -487,10 +487,10 @@ namespace Ryujinx.Input.HLE
return value;
}
public KeyboardInput? GetHLEKeyboardInput()
{
if (_gamepad is IKeyboard keyboard)
public static KeyboardInput GetHLEKeyboardInput(IGamepadDriver KeyboardDriver)
{
var keyboard = KeyboardDriver.GetGamepad("0") as IKeyboard;
KeyboardStateSnapshot keyboardState = keyboard.GetKeyboardStateSnapshot();
KeyboardInput hidKeyboard = new()
@@ -514,12 +514,9 @@ namespace Ryujinx.Input.HLE
}
return hidKeyboard;
}
return null;
}
protected virtual void Dispose(bool disposing)
{
if (disposing)

View File

@@ -231,11 +231,6 @@ namespace Ryujinx.Input.HLE
var altMotionState = isJoyconPair ? controller.GetHLEMotionState(true) : default;
motionState = (controller.GetHLEMotionState(), altMotionState);
if (_enableKeyboard)
{
hleKeyboardInput = controller.GetHLEKeyboardInput();
}
}
else
{
@@ -257,6 +252,11 @@ namespace Ryujinx.Input.HLE
}
}
if (!_blockInputUpdates && _enableKeyboard)
{
hleKeyboardInput = NpadController.GetHLEKeyboardInput(_keyboardDriver);
}
_device.Hid.Npads.Update(hleInputStates);
_device.Hid.Npads.UpdateSixAxis(hleMotionStates);

View File

@@ -15,7 +15,7 @@ namespace Ryujinx.UI.Common.Configuration
/// <summary>
/// The current version of the file format
/// </summary>
public const int CurrentVersion = 50;
public const int CurrentVersion = 51;
/// <summary>
/// Version of the configuration file format
@@ -162,6 +162,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary>
public bool ShowConfirmExit { get; set; }
/// <summary>
/// Enables or disables save window size, position and state on close.
/// </summary>
public bool RememberWindowState { get; set; }
/// <summary>
/// Enables hardware-accelerated rendering for Avalonia
/// </summary>

View File

@@ -626,6 +626,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary>
public ReactiveObject<bool> ShowConfirmExit { get; private set; }
/// <summary>
/// Enables or disables save window size, position and state on close.
/// </summary>
public ReactiveObject<bool> RememberWindowState { get; private set; }
/// <summary>
/// Enables hardware-accelerated rendering for Avalonia
/// </summary>
@@ -647,6 +652,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableDiscordIntegration = new ReactiveObject<bool>();
CheckUpdatesOnStart = new ReactiveObject<bool>();
ShowConfirmExit = new ReactiveObject<bool>();
RememberWindowState = new ReactiveObject<bool>();
EnableHardwareAcceleration = new ReactiveObject<bool>();
HideCursor = new ReactiveObject<HideCursorMode>();
}
@@ -684,6 +690,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableDiscordIntegration = EnableDiscordIntegration,
CheckUpdatesOnStart = CheckUpdatesOnStart,
ShowConfirmExit = ShowConfirmExit,
RememberWindowState = RememberWindowState,
EnableHardwareAcceleration = EnableHardwareAcceleration,
HideCursor = HideCursor,
EnableVsync = Graphics.EnableVsync,
@@ -792,6 +799,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableDiscordIntegration.Value = true;
CheckUpdatesOnStart.Value = true;
ShowConfirmExit.Value = true;
RememberWindowState.Value = true;
EnableHardwareAcceleration.Value = true;
HideCursor.Value = HideCursorMode.OnIdle;
Graphics.EnableVsync.Value = true;
@@ -1459,6 +1467,15 @@ namespace Ryujinx.UI.Common.Configuration
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 51)
{
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 51.");
configurationFileFormat.RememberWindowState = true;
configurationFileUpdated = true;
}
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
@@ -1489,6 +1506,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart;
ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit;
RememberWindowState.Value = configurationFileFormat.RememberWindowState;
EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration;
HideCursor.Value = configurationFileFormat.HideCursor;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;

View File

@@ -1,6 +1,7 @@
using DiscordRPC;
using Ryujinx.Common;
using Ryujinx.UI.Common.Configuration;
using System.Text;
namespace Ryujinx.UI.Common
{
@@ -9,6 +10,9 @@ namespace Ryujinx.UI.Common
private const string Description = "A simple, experimental Nintendo Switch emulator.";
private const string ApplicationId = "1216775165866807456";
private const int ApplicationByteLimit = 128;
private const string Ellipsis = "…";
private static DiscordRpcClient _discordClient;
private static RichPresence _discordPresenceMain;
@@ -60,18 +64,18 @@ namespace Ryujinx.UI.Common
}
}
public static void SwitchToPlayingState(string titleId, string titleName)
public static void SwitchToPlayingState(string titleId, string applicationName)
{
_discordClient?.SetPresence(new RichPresence
{
Assets = new Assets
{
LargeImageKey = "game",
LargeImageText = titleName,
LargeImageText = TruncateToByteLength(applicationName, ApplicationByteLimit),
SmallImageKey = "ryujinx",
SmallImageText = Description,
},
Details = $"Playing {titleName}",
Details = TruncateToByteLength($"Playing {applicationName}", ApplicationByteLimit),
State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(),
Timestamps = Timestamps.Now,
Buttons =
@@ -90,6 +94,28 @@ namespace Ryujinx.UI.Common
_discordClient?.SetPresence(_discordPresenceMain);
}
private static string TruncateToByteLength(string input, int byteLimit)
{
if (Encoding.UTF8.GetByteCount(input) <= byteLimit)
{
return input;
}
// Find the length to trim the string to guarantee we have space for the trailing ellipsis.
int trimLimit = byteLimit - Encoding.UTF8.GetByteCount(Ellipsis);
// Basic trim to best case scenario of 1 byte characters.
input = input[..trimLimit];
while (Encoding.UTF8.GetByteCount(input) > trimLimit)
{
// Remove one character from the end of the string at a time.
input = input[..^1];
}
return input.TrimEnd() + Ellipsis;
}
public static void Exit()
{
_discordClient?.Dispose();

View File

@@ -1,8 +1,10 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.Threading;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
@@ -84,7 +86,7 @@ namespace Ryujinx.Ava
ApplyConfiguredTheme();
}
private void ApplyConfiguredTheme()
public void ApplyConfiguredTheme()
{
try
{
@@ -92,13 +94,18 @@ namespace Ryujinx.Ava
if (string.IsNullOrWhiteSpace(baseStyle))
{
ConfigurationState.Instance.UI.BaseStyle.Value = "Dark";
ConfigurationState.Instance.UI.BaseStyle.Value = "Auto";
baseStyle = ConfigurationState.Instance.UI.BaseStyle;
}
ThemeVariant systemTheme = DetectSystemTheme();
ThemeManager.OnThemeChanged();
RequestedThemeVariant = baseStyle switch
{
"Auto" => systemTheme,
"Light" => ThemeVariant.Light,
"Dark" => ThemeVariant.Dark,
_ => ThemeVariant.Default,
@@ -111,5 +118,28 @@ namespace Ryujinx.Ava
ShowRestartDialog();
}
}
/// <summary>
/// Converts a PlatformThemeVariant value to the corresponding ThemeVariant value.
/// </summary>
public static ThemeVariant ConvertThemeVariant(PlatformThemeVariant platformThemeVariant) =>
platformThemeVariant switch
{
PlatformThemeVariant.Dark => ThemeVariant.Dark,
PlatformThemeVariant.Light => ThemeVariant.Light,
_ => ThemeVariant.Default,
};
public static ThemeVariant DetectSystemTheme()
{
if (Application.Current is App app)
{
var colorValues = app.PlatformSettings.GetColorValues();
return ConvertThemeVariant(colorValues.ThemeVariant);
}
return ThemeVariant.Default;
}
}
}

View File

@@ -94,6 +94,17 @@ namespace Ryujinx.Ava
private long _lastCursorMoveTime;
private bool _isCursorInRenderer = true;
private bool _ignoreCursorState = false;
private enum CursorStates
{
CursorIsHidden,
CursorIsVisible,
ForceChangeCursor
};
private CursorStates _cursorState = !ConfigurationState.Instance.Hid.EnableMouse.Value ?
CursorStates.CursorIsVisible : CursorStates.CursorIsHidden;
private bool _isStopped;
private bool _isActive;
@@ -201,23 +212,65 @@ namespace Ryujinx.Ava
private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e)
{
if (!_viewModel.IsActive)
{
_isCursorInRenderer = false;
_ignoreCursorState = false;
return;
}
if (sender is MainWindow window)
{
if (ConfigurationState.Instance.HideCursor.Value == HideCursorMode.OnIdle)
{
_lastCursorMoveTime = Stopwatch.GetTimestamp();
}
var point = e.GetCurrentPoint(window).Position;
var bounds = RendererHost.EmbeddedWindow.Bounds;
var windowYOffset = bounds.Y + window.MenuBarHeight;
var windowYLimit = (int)window.Bounds.Height - window.StatusBarHeight - 1;
if (!_viewModel.ShowMenuAndStatusBar)
{
windowYOffset -= window.MenuBarHeight;
windowYLimit += window.StatusBarHeight + 1;
}
_isCursorInRenderer = point.X >= bounds.X &&
point.X <= bounds.Width + bounds.X &&
point.Y >= bounds.Y &&
point.Y <= bounds.Height + bounds.Y;
Math.Ceiling(point.X) <= (int)window.Bounds.Width &&
point.Y >= windowYOffset &&
point.Y <= windowYLimit &&
!_viewModel.IsSubMenuOpen;
_ignoreCursorState = false;
}
}
private void TopLevel_PointerExited(object sender, PointerEventArgs e)
{
_isCursorInRenderer = false;
if (sender is MainWindow window)
{
var point = e.GetCurrentPoint(window).Position;
var bounds = RendererHost.EmbeddedWindow.Bounds;
var windowYOffset = bounds.Y + window.MenuBarHeight;
var windowYLimit = (int)window.Bounds.Height - window.StatusBarHeight - 1;
if (!_viewModel.ShowMenuAndStatusBar)
{
windowYOffset -= window.MenuBarHeight;
windowYLimit += window.StatusBarHeight + 1;
}
_ignoreCursorState = (point.X == bounds.X ||
Math.Ceiling(point.X) == (int)window.Bounds.Width) &&
point.Y >= windowYOffset &&
point.Y <= windowYLimit;
}
_cursorState = CursorStates.ForceChangeCursor;
}
private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e)
@@ -244,10 +297,15 @@ namespace Ryujinx.Ava
_viewModel.Cursor = Cursor.Default;
if (OperatingSystem.IsWindows())
{
if (_cursorState != CursorStates.CursorIsHidden && !_ignoreCursorState)
{
SetCursor(_defaultCursorWin);
}
}
});
_cursorState = CursorStates.CursorIsVisible;
}
private void HideCursor()
@@ -261,6 +319,8 @@ namespace Ryujinx.Ava
SetCursor(_invisibleCursorWin);
}
});
_cursorState = CursorStates.CursorIsHidden;
}
private void SetRendererWindowSize(Size size)
@@ -523,6 +583,8 @@ namespace Ryujinx.Ava
{
_lastCursorMoveTime = Stopwatch.GetTimestamp();
}
_cursorState = CursorStates.ForceChangeCursor;
}
public async Task<bool> LoadGuestApplication()
@@ -1037,39 +1099,33 @@ namespace Ryujinx.Ava
if (_viewModel.IsActive)
{
if (_isCursorInRenderer)
bool isCursorVisible = true;
if (_isCursorInRenderer && !_viewModel.ShowLoadProgress)
{
if (ConfigurationState.Instance.Hid.EnableMouse)
if (ConfigurationState.Instance.Hid.EnableMouse.Value)
{
HideCursor();
isCursorVisible = ConfigurationState.Instance.HideCursor.Value == HideCursorMode.Never;
}
else
{
switch (ConfigurationState.Instance.HideCursor.Value)
isCursorVisible = ConfigurationState.Instance.HideCursor.Value == HideCursorMode.Never ||
(ConfigurationState.Instance.HideCursor.Value == HideCursorMode.OnIdle &&
Stopwatch.GetTimestamp() - _lastCursorMoveTime < CursorHideIdleTime * Stopwatch.Frequency);
}
}
if (_cursorState != (isCursorVisible ? CursorStates.CursorIsVisible : CursorStates.CursorIsHidden))
{
if (isCursorVisible)
{
case HideCursorMode.Never:
ShowCursor();
break;
case HideCursorMode.OnIdle:
if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency)
{
HideCursor();
}
else
{
ShowCursor();
}
break;
case HideCursorMode.Always:
HideCursor();
break;
}
}
}
else
{
ShowCursor();
}
Dispatcher.UIThread.Post(() =>
{
@@ -1154,7 +1210,7 @@ namespace Ryujinx.Ava
// Touchscreen.
bool hasTouch = false;
if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse)
if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse.Value)
{
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
}

View File

@@ -30,6 +30,10 @@
"MenuBarToolsManageFileTypes": "Manage file types",
"MenuBarToolsInstallFileTypes": "Install file types",
"MenuBarToolsUninstallFileTypes": "Uninstall file types",
"MenuBarView": "_View",
"MenuBarViewWindow": "Window Size",
"MenuBarViewWindow720": "720p",
"MenuBarViewWindow1080": "1080p",
"MenuBarHelp": "_Help",
"MenuBarHelpCheckForUpdates": "Check for Updates",
"MenuBarHelpAbout": "About",
@@ -92,6 +96,7 @@
"SettingsTabGeneralEnableDiscordRichPresence": "Enable Discord Rich Presence",
"SettingsTabGeneralCheckUpdatesOnLaunch": "Check for Updates on Launch",
"SettingsTabGeneralShowConfirmExitDialog": "Show \"Confirm Exit\" Dialog",
"SettingsTabGeneralRememberWindowState": "Remember Window Size/Position",
"SettingsTabGeneralHideCursor": "Hide Cursor:",
"SettingsTabGeneralHideCursorNever": "Never",
"SettingsTabGeneralHideCursorOnIdle": "On Idle",
@@ -399,6 +404,7 @@
"GameListContextMenuToggleFavorite": "Toggle Favorite",
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
"SettingsTabGeneralTheme": "Theme:",
"SettingsTabGeneralThemeAuto": "Auto",
"SettingsTabGeneralThemeDark": "Dark",
"SettingsTabGeneralThemeLight": "Light",
"ControllerSettingsConfigureGeneral": "Configure",

View File

@@ -0,0 +1,14 @@
using System;
namespace Ryujinx.Ava.Common
{
public static class ThemeManager
{
public static event EventHandler ThemeChanged;
public static void OnThemeChanged()
{
ThemeChanged?.Invoke(null, EventArgs.Empty);
}
}
}

View File

@@ -40,7 +40,7 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
{
_ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
_ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
}
PreviewerDetached = true;

View File

@@ -70,7 +70,8 @@
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
<ProjectReference Include="..\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj" />
<ProjectReference Include="..\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj"
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
@@ -88,7 +89,7 @@
</Content>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'">
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
<Content Include="..\..\distribution\linux\Ryujinx.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

View File

@@ -19,8 +19,8 @@ namespace Ryujinx.Ava.UI.Helpers
{ Key.ShiftRight, LocaleKeys.KeyShiftRight },
{ Key.ControlLeft, LocaleKeys.KeyControlLeft },
{ Key.ControlRight, LocaleKeys.KeyControlRight },
{ Key.AltLeft, LocaleKeys.KeyControlLeft },
{ Key.AltRight, LocaleKeys.KeyControlRight },
{ Key.AltLeft, LocaleKeys.KeyAltLeft },
{ Key.AltRight, LocaleKeys.KeyAltRight },
{ Key.WinLeft, LocaleKeys.KeyWinLeft },
{ Key.WinRight, LocaleKeys.KeyWinRight },
{ Key.Up, LocaleKeys.KeyUp },

View File

@@ -111,8 +111,5 @@ namespace Ryujinx.Ava.UI.Helpers
[LibraryImport("user32.dll", SetLastError = true)]
public static partial IntPtr SetWindowLongPtrW(IntPtr hWnd, int nIndex, IntPtr value);
[LibraryImport("user32.dll", SetLastError = true)]
public static partial IntPtr SetWindowLongW(IntPtr hWnd, int nIndex, int value);
}
}

View File

@@ -157,7 +157,7 @@ namespace Ryujinx.Ava.UI.Renderer
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
style = ClassStyles.CsOwndc,
lpszClassName = Marshal.StringToHGlobalUni(_className),
hCursor = CreateArrowCursor(),
hCursor = CreateArrowCursor()
};
RegisterClassEx(ref wndClassEx);

View File

@@ -1,6 +1,8 @@
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.Threading;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Utilities;
using Ryujinx.UI.Common.Configuration;
@@ -11,7 +13,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.ViewModels
{
public class AboutWindowViewModel : BaseModel
public class AboutWindowViewModel : BaseModel, IDisposable
{
private Bitmap _githubLogo;
private Bitmap _discordLogo;
@@ -86,23 +88,39 @@ namespace Ryujinx.Ava.UI.ViewModels
public AboutWindowViewModel()
{
Version = Program.Version;
if (ConfigurationState.Instance.UI.BaseStyle.Value == "Light")
{
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.UI.Common")));
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.UI.Common")));
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.UI.Common")));
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.UI.Common")));
}
else
{
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.UI.Common")));
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.UI.Common")));
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.UI.Common")));
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.UI.Common")));
}
UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value);
Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);
ThemeManager.ThemeChanged += ThemeManager_ThemeChanged;
}
private void ThemeManager_ThemeChanged(object sender, EventArgs e)
{
Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value));
}
private void UpdateLogoTheme(string theme)
{
bool isDarkTheme = theme == "Dark" || (theme == "Auto" && App.DetectSystemTheme() == ThemeVariant.Dark);
string basePath = "resm:Ryujinx.UI.Common.Resources.";
string themeSuffix = isDarkTheme ? "Dark.png" : "Light.png";
GithubLogo = LoadBitmap($"{basePath}Logo_GitHub_{themeSuffix}?assembly=Ryujinx.UI.Common");
DiscordLogo = LoadBitmap($"{basePath}Logo_Discord_{themeSuffix}?assembly=Ryujinx.UI.Common");
PatreonLogo = LoadBitmap($"{basePath}Logo_Patreon_{themeSuffix}?assembly=Ryujinx.UI.Common");
TwitterLogo = LoadBitmap($"{basePath}Logo_Twitter_{themeSuffix}?assembly=Ryujinx.UI.Common");
}
private Bitmap LoadBitmap(string uri)
{
return new Bitmap(Avalonia.Platform.AssetLoader.Open(new Uri(uri)));
}
public void Dispose()
{
ThemeManager.ThemeChanged -= ThemeManager_ThemeChanged;
GC.SuppressFinalize(this);
}
private async Task DownloadPatronsJson()

View File

@@ -104,6 +104,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private double _windowHeight;
private bool _isActive;
private bool _isSubMenuOpen;
public ApplicationData ListSelectedApplication;
public ApplicationData GridSelectedApplication;
@@ -317,6 +318,17 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public bool IsSubMenuOpen
{
get => _isSubMenuOpen;
set
{
_isSubMenuOpen = value;
OnPropertyChanged();
}
}
public bool ShowAll
{
get => _showAll;

View File

@@ -131,6 +131,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool EnableDiscordIntegration { get; set; }
public bool CheckUpdatesOnStart { get; set; }
public bool ShowConfirmExit { get; set; }
public bool RememberWindowState { get; set; }
public int HideCursor { get; set; }
public bool EnableDockedMode { get; set; }
public bool EnableKeyboard { get; set; }
@@ -390,12 +391,19 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableDiscordIntegration = config.EnableDiscordIntegration;
CheckUpdatesOnStart = config.CheckUpdatesOnStart;
ShowConfirmExit = config.ShowConfirmExit;
RememberWindowState = config.RememberWindowState;
HideCursor = (int)config.HideCursor.Value;
GameDirectories.Clear();
GameDirectories.AddRange(config.UI.GameDirs.Value);
BaseStyleIndex = config.UI.BaseStyle == "Light" ? 0 : 1;
BaseStyleIndex = config.UI.BaseStyle.Value switch
{
"Auto" => 0,
"Light" => 1,
"Dark" => 2,
_ => 0
};
// Input
EnableDockedMode = config.System.EnableDockedMode;
@@ -410,10 +418,11 @@ namespace Ryujinx.Ava.UI.ViewModels
Language = (int)config.System.Language.Value;
TimeZone = config.System.TimeZone;
DateTime currentDateTime = DateTime.Now;
DateTime currentHostDateTime = DateTime.Now;
TimeSpan systemDateTimeOffset = TimeSpan.FromSeconds(config.System.SystemTimeOffset);
DateTime currentDateTime = currentHostDateTime.Add(systemDateTimeOffset);
CurrentDate = currentDateTime.Date;
CurrentTime = currentDateTime.TimeOfDay.Add(TimeSpan.FromSeconds(config.System.SystemTimeOffset));
CurrentTime = currentDateTime.TimeOfDay;
EnableVsync = config.Graphics.EnableVsync;
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
@@ -474,6 +483,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
config.ShowConfirmExit.Value = ShowConfirmExit;
config.RememberWindowState.Value = RememberWindowState;
config.HideCursor.Value = (HideCursorMode)HideCursor;
if (_directoryChanged)
@@ -482,7 +492,13 @@ namespace Ryujinx.Ava.UI.ViewModels
config.UI.GameDirs.Value = gameDirs;
}
config.UI.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark";
config.UI.BaseStyle.Value = BaseStyleIndex switch
{
0 => "Auto",
1 => "Light",
2 => "Dark",
_ => "Auto"
};
// Input
config.System.EnableDockedMode.Value = EnableDockedMode;

View File

@@ -1,4 +1,4 @@
<UserControl
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -16,7 +16,8 @@
Name="Menu"
Height="35"
Margin="0"
HorizontalAlignment="Left">
HorizontalAlignment="Left"
IsOpen="{Binding IsSubMenuOpen, Mode=OneWayToSource}">
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel Margin="0" HorizontalAlignment="Stretch" />
@@ -185,6 +186,12 @@
<MenuItem Header="{locale:Locale MenuBarToolsUninstallFileTypes}" Click="UninstallFileTypes_Click"/>
</MenuItem>
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarView}">
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarViewWindow}">
<MenuItem Header="{locale:Locale MenuBarViewWindow720}" Tag="720" Click="ChangeWindowSize_Click" />
<MenuItem Header="{locale:Locale MenuBarViewWindow1080}" Tag="1080" Click="ChangeWindowSize_Click" />
</MenuItem>
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
<MenuItem
Name="UpdateMenuItem"

View File

@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;
using LibHac.Ncm;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
@@ -211,6 +212,40 @@ namespace Ryujinx.Ava.UI.Views.Main
}
}
private async void ChangeWindowSize_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuItem item)
{
int height;
int width;
switch (item.Tag)
{
case "720":
height = 720;
width = 1280;
break;
case "1080":
height = 1080;
width = 1920;
break;
default:
throw new ArgumentNullException($"Invalid Tag for {item}");
}
await Dispatcher.UIThread.InvokeAsync(() =>
{
ViewModel.WindowState = WindowState.Normal;
height += (int)Window.StatusBarHeight + (int)Window.MenuBarHeight;
Window.Arrange(new Rect(Window.Position.X, Window.Position.Y, width, height));
});
}
}
public async void CheckForUpdates(object sender, RoutedEventArgs e)
{
if (Updater.CanUpdate(true))

View File

@@ -36,6 +36,9 @@
<CheckBox IsChecked="{Binding ShowConfirmExit}">
<TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" />
</CheckBox>
<CheckBox IsChecked="{Binding RememberWindowState}">
<TextBlock Text="{locale:Locale SettingsTabGeneralRememberWindowState}" />
</CheckBox>
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabGeneralHideCursor}"
@@ -62,6 +65,9 @@
<ComboBox SelectedIndex="{Binding BaseStyleIndex}"
HorizontalContentAlignment="Left"
MinWidth="100">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeAuto}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeLight}" />
</ComboBoxItem>

View File

@@ -2,6 +2,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Platform;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common;
@@ -56,6 +57,9 @@ namespace Ryujinx.Ava.UI.Windows
public static bool ShowKeyErrorOnLoad { get; set; }
public ApplicationLibrary ApplicationLibrary { get; set; }
public readonly double StatusBarHeight;
public readonly double MenuBarHeight;
public MainWindow()
{
ViewModel = new MainWindowViewModel();
@@ -64,8 +68,6 @@ namespace Ryujinx.Ava.UI.Windows
DataContext = ViewModel;
SetWindowSizePosition();
InitializeComponent();
Load();
@@ -74,10 +76,14 @@ namespace Ryujinx.Ava.UI.Windows
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 + StatusBarView.StatusBar.MinHeight;
StatusBarHeight = StatusBarView.StatusBar.MinHeight;
MenuBarHeight = MenuBar.MinHeight;
double barHeight = MenuBarHeight + StatusBarHeight;
Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight;
Width /= Program.WindowScaleFactor;
SetWindowSizePosition();
if (Program.PreviewerDetached)
{
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
@@ -87,6 +93,29 @@ namespace Ryujinx.Ava.UI.Windows
}
}
/// <summary>
/// Event handler for detecting OS theme change when using "Follow OS theme" option
/// </summary>
private void OnPlatformColorValuesChanged(object sender, PlatformColorValues e)
{
if (Application.Current is App app)
{
app.ApplyConfiguredTheme();
}
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
if (PlatformSettings != null)
{
/// <summary>
/// Unsubscribe to the ColorValuesChanged event
/// </summary>
PlatformSettings.ColorValuesChanged -= OnPlatformColorValuesChanged;
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
@@ -319,6 +348,17 @@ namespace Ryujinx.Ava.UI.Windows
private void SetWindowSizePosition()
{
if (!ConfigurationState.Instance.RememberWindowState)
{
ViewModel.WindowHeight = (720 + StatusBarHeight + MenuBarHeight) * Program.WindowScaleFactor;
ViewModel.WindowWidth = 1280 * Program.WindowScaleFactor;
WindowState = WindowState.Normal;
WindowStartupLocation = WindowStartupLocation.CenterScreen;
return;
}
PixelPoint savedPoint = new(ConfigurationState.Instance.UI.WindowStartup.WindowPositionX,
ConfigurationState.Instance.UI.WindowStartup.WindowPositionY);
@@ -352,14 +392,18 @@ namespace Ryujinx.Ava.UI.Windows
}
private void SaveWindowSizePosition()
{
ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized;
// Only save rectangle properties if the window is not in a maximized state.
if (WindowState != WindowState.Maximized)
{
ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight.Value = (int)Height;
ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth.Value = (int)Width;
ConfigurationState.Instance.UI.WindowStartup.WindowPositionX.Value = Position.X;
ConfigurationState.Instance.UI.WindowStartup.WindowPositionY.Value = Position.Y;
ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized;
}
MainWindowViewModel.SaveConfig();
}
@@ -370,6 +414,11 @@ namespace Ryujinx.Ava.UI.Windows
Initialize();
/// <summary>
/// Subscribe to the ColorValuesChanged event
/// </summary>
PlatformSettings.ColorValuesChanged += OnPlatformColorValuesChanged;
ViewModel.Initialize(
ContentManager,
StorageProvider,
@@ -472,7 +521,10 @@ namespace Ryujinx.Ava.UI.Windows
return;
}
if (ConfigurationState.Instance.RememberWindowState)
{
SaveWindowSizePosition();
}
ApplicationLibrary.CancelLoading();
InputManager.Dispose();