Compare commits

..

36 Commits

Author SHA1 Message Date
Isaac Marovitz
7b2225c6b0 Ava UI: Avalonia 11 & FluentAvalonia 2 Support (#4362)
* It builds

(Doesn’t run waiting on FluentAvalonia Preview 5 Release)

* Enable CompiledBindings by default

* Ignore `PointerPressedEventArgs` Init warning

* Define MIME and UTI Types

* Update `UserProfileImageSelectorView` to StorageProvider API

* PFS0 Magic

* Update `MainWindowViewModel` to StorageProvider API

* Update `SettingsUIView` to StorageProvider API

* Update `ApplicationHelper` to StorageProvider API

* Use `IsCheckChanged`

* Rename events

* Update Fluent Avalonia to Preivew 5

* More package updates

* Fix long selection bar

* return glyph value directly, instead of using a binding

* fix menu item checkboxes

* Fix build

* Update to Preview 6

Unicorn conflict

Fix remaining package oopsie

* Fix issues from merge

* Fix some warnings

* Warnings

* Squashed commit of the following:

commit 79d1c190db
Author: Mary <mary@mary.zone>
Date:   Sun Apr 16 11:38:07 2023 +0200

    chore: Update Silk.NET to 2.17.1 (#4686)

commit 2bc88467eb
Author: Ac_K <Acoustik666@gmail.com>
Date:   Sun Apr 16 09:37:31 2023 +0000

    Update README.md

commit baf8752e74
Author: Vincenzo Nizza <vincenzonizzaufficio@gmail.com>
Date:   Sun Apr 16 11:19:33 2023 +0200

    Ensure the updater doesn't delete hidden or system files (#4626)

    * Copy desktop.ini to update directory if it exists in HomeDir

    * EnumerateFilesToDelete() exclude files with "Hidden" and "System" attributes

commit d5e4378aea
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Date:   Sun Apr 16 09:02:06 2023 +0000

    nuget: bump DynamicData from 7.13.1 to 7.13.5 (#4654)

    Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.1 to 7.13.5.
    - [Release notes](https://github.com/reactiveui/DynamicData/releases)
    - [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
    - [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.1...7.13.5)

    ---
    updated-dependencies:
    - dependency-name: DynamicData
      dependency-type: direct:production
      update-type: version-update:semver-patch
    ...

    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

commit 6dbcdfea47
Author: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Date:   Sun Apr 16 09:09:02 2023 +0200

    Ava: Fix nca extraction window never closing & minor cleanup (#4569)

    * ava: Remove unused doWhileDeferred parameters

    * ava: Minimally improve swkbd dialog

    It's currently impossible to get the dialog to redirect focus to the InputBox.

    * ava: Fix nca extraction dialog never closing

    Also contains some minor cleanup

commit c5258cf082
Author: NitroTears <73270647+NitroTears@users.noreply.github.com>
Date:   Sun Apr 16 11:03:35 2023 +1000

    Ability to hide file types in Game List (#4555)

    * Added HiddenFileTypes to config state, and check to file enumeration

    * Added hiddenfiletypes checkboxes to the UI

    * Added Ava version of HiddenFileTypes

    * Inverted Hide to Show with file types, minor formatting

    * all variables with a reference to 'hidden' is now 'shown'

    * one more variable name changed

    * review feedback

    * added FileTypes extension methof to get the correlating config value

    * moved extension method to new folder and file in Ryujinx.Ui.Common

    * added default case for ToggleFileType

    * changed exception type to OutOfRangeException

commit 5c89e22bb9
Author: Daniel Shala <daniel.shala08@gmail.com>
Date:   Sat Apr 15 18:11:24 2023 +0200

    Added check for eventual symlink when displaying game files. (#4526)

    * Added check for eventual symlink when displaying game files.

    * Moved symlink check logic

    * Moved symlink check logic

    * Fixed prev commit

    ---------

    Co-authored-by: Daniel Shala <danielshala00@gmail.com>

commit 11ecff2ff0
Author: Alex Barney <thealexbarney@gmail.com>
Date:   Fri Apr 14 16:00:34 2023 -0700

    Rename Hipc to Cmif where appropriate (#3880)

commit 4c3f09644a
Author: MutantAura <44103205+MutantAura@users.noreply.github.com>
Date:   Wed Apr 12 20:18:40 2023 +0100

    Move swkbd message null check into constructor (#4671)

commit e187a8870a
Author: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Date:   Wed Apr 12 03:09:47 2023 +0200

    HLE: Deal with empty title names properly (#4643)

    * hle: Deal with empty titleNames in some languages

    * gui: Fix displaying the wrong title name

    * Remove unnecessary bounds check

    * Fix a NRE when getting the version string

    * Restore empty string logic

commit a64fee29dc
Author: riperiperi <rhy3756547@hotmail.com>
Date:   Tue Apr 11 08:23:41 2023 +0100

    Vulkan: add situational "Fast Flush" mode (#4667)

    * Flush in the middle of long command buffers.

    * Vulkan: add situational "Fast Flush" mode

    The AutoFlushCounter class was added to periodically flush Vulkan command buffers throughout a frame, which reduces latency to the GPU as commands are submitted and processed much sooner. This was done by allowing command buffers to flush when framebuffer attachments changed.

    However, some games have incredibly long render passes with a large number of draws, and really aggressive data access that forces GPU sync.

    The Vulkan backend could potentially end up building a single command buffer for 4-5ms if a pass has enough draws, such as in BOTW. In the scenario where sync is waited on immediately after submission, this would have to wait for the completion of a much longer command buffer than usual.

    The solution is to force command buffer submission periodically in a "fast flush" mode. This will end up splitting render passes, but it will only enable if sync is aggressive enough.

    This should improve performance in GPU limited scenarios, or in games that aggressively wait on synchronization. In some games, it may only kick in when res scaling. It won't trigger in games like SMO where sync is not an issue.

    Improves performance in Pokemon Scarlet/Violet (res scaled) and BOTW (in general).

    * Add conversions in milliseconds next to flush timers.

commit 9ef94c8292
Author: riperiperi <rhy3756547@hotmail.com>
Date:   Tue Apr 11 07:55:04 2023 +0100

    ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext (#4661)

    * ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext

    Some games access these system registers several tens of thousands of times in a second from many different threads. While this isn't really crippling, it is a lot of wasted time spent in a reverse pinvoke transition.

    Example games are Pokemon Scarlet/Violet and BOTW. These games have a lot of different potential bottlenecks so it's unlikely you will see a consistent improvement, but it definitely disappears from the cpu profile.

    * Remove unreachable code.

    * Add ulong conversion for offsets

    * Nit

commit 915d6d044c
Author: riperiperi <rhy3756547@hotmail.com>
Date:   Tue Apr 11 07:32:31 2023 +0100

    OpenGL: Fix OBS/Overlays again by binding FB before present (#4668)

    This seems to have been removed by the Post-Processing PR, but it is required for the display in OBS to be the right way up and properly scaled.

    I've tested this with AA and FSR on MK8D and it seems to behave properly. Testing is welcome.

commit a4780ab33b
Author: MutantAura <44103205+MutantAura@users.noreply.github.com>
Date:   Mon Apr 10 23:04:31 2023 +0100

    Force activate parent window before dialog is shown (#4663)

* Fix build

Extraction dialogue not working

* Avalonia Preview 7

Needs Fluent Avalonia update still…

* Fix Render Scaling

* Update Fluent Avalonia

* Remove `pfs0` as runnable file type

* Restore Info.plist formatting

* Plist Format

* Update Avalonia.Svg.Skia

* Update theme code (TODO)

* swtich to using theme variants  for light dark

* Fix crashes

* Text centering issues

* Update `TitleUpdateViewModel` to StorageProvider API

* Fixed for new PR

(Will crash on launch)

* Fixes…

* UI: Fix sections extraction (#4820)

* UI: Fix sections extraction

There is currently an issue when the update NCA doesn't contains the section we want to extract, this is fixed by adding a check.
I have fixed the inverted handler of ExeFs/Logo introduced in #4755.

Fixes #4521

* Addresses feedback

* Fix issues…

* Preview 8

* Fix fuck ups

* Fixes

* More cleanup

* Ava 11 RC

Maybe there is a god

* Update FluentAvalonia

* update svg

* Second RC (kill me)

* It builds

* Ava 11

* Remove unnecessary usings

* Fix build

* Formatting

* GAS GAS GAS!!!!

* Fix DLC Window Crash

* Linux runner try not to crash challenge (impossible)

* Add app.manifest

* Fix accidental Silk.NET.Vulkan bump

* Try fix truncation

* Linux fix popup Windows

* Fix cutoff text on windows

* Status bar styling fixes

* Volume Toggle Split Button Fixes

* Fix load bar color

* Fix shortcuts

* Best we're gonna get

* Fix spacing

Co-authored-by: Exhigh <exhigh01@gmail.com>

* Formatting

* Fix Profile Dropdown

* Fix Window Startup Position

* Format Fixes

* Fix stupid mistake

* Fix accidental change

* Scaling Handler (peri pls make sure is working)

* Remove Locale Reflection Binding Use + Unsued Usings

* Fix formatting

Code styling

Ughhhh

Fix interface

Make TimeZoneConverter internal

* Remove bell workaround (no longer needed)

* Disable accent menu

* Update to Ava 11.0.2

* Peri suggestions

* Formatting

* Cleanup a bunch of jank

* Dependency update

* Berry fixes and suggestions

* Final suggestions

* Rename assemblyIdentity to Ryujinx.Emulator.Avalonia

---------

Co-authored-by: Emmanuel Hansen <emmausssss@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: Exhigh <exhigh01@gmail.com>
Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
2023-08-12 02:47:22 +02:00
TSRBerry
fe15c77d30 [Hotfix] hid: Prevent out of bounds array access (#5547)
* hid: Prevent out of bounds array access

* Cast player to uint

* Replace lambda function with local function
2023-08-10 00:29:15 -03:00
TSRBerry
5e9678c8fa Allow access to code memory for exefs mods (#5518)
* Allow access to code memory for exefs mods

* Add ASLR workaround for Skyline

* Hardcode allowCodeMemoryForJit to true
2023-08-09 18:27:45 -03:00
jcm
773e239db7 Implement color space passthrough option (#5531)
Co-authored-by: jcm <butt@butts.com>
2023-08-07 18:54:05 +01:00
gdkchan
42750a74f8 Do not add more code after alpha test discard on fragment shader (#5529)
* Do not add more code after alpha test discard on fragment shader

* Shader cache version bump
2023-08-07 12:20:37 -03:00
TSRBerry
3ab0a71c7b Fix PR build concurrency and stop auto assigning reviewers for draft PRs (#5519)
* build: Remove concurrency

It's called by checks anyway.

* Only assign reviewers for PRs that are ready for reviews
2023-08-06 23:25:02 +02:00
riperiperi
6e784e0aca GPU: Don't sync/bind index buffer when it's not in use (#5526)
* GPU: Don't sync/bind index buffer when it's not in use

Sometimes draws don't use an index buffer. It's not necessary to check or upload data for the current index buffer binding as it won't be used.

This fixes Pokemon: Legends Arceus updating a stale index buffer for every draw during its TFB pass, which was all non-indexed draws.

This probably didn't cost much on normal PCs, but it had a large impact on MacOS, which the macos1 release build avoided by mirroring index buffers (the PR currently does not). Needs buffer mirrors still for the rest of the performance.

There are additional cases where index buffers are bound or checked with non-indexed draws on the backend, but this one was straightforward to fix and has the largest impact. Testing is welcome to ensure nothing weird broke.

* Fix case with _rebind
2023-08-06 16:29:20 -03:00
sunshineinabox
5a0aa074b6 Enable VK_EXT_4444_formats (#5525) 2023-08-03 17:46:23 -03:00
dependabot[bot]
93aa40f1fb nuget: bump DiscordRichPresence from 1.1.3.18 to 1.2.1.24 (#5515)
Bumps [DiscordRichPresence](https://github.com/Lachee/discord-rpc-csharp) from 1.1.3.18 to 1.2.1.24.
- [Release notes](https://github.com/Lachee/discord-rpc-csharp/releases)
- [Commits](https://github.com/Lachee/discord-rpc-csharp/commits)

---
updated-dependencies:
- dependency-name: DiscordRichPresence
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-02 17:19:38 +02:00
TSRBerry
bedee64af5 Add slightly better workaround for current workflow issues (#5507)
* checks: Add retry logic to dotnet format style step as well

I can't imagine dotnet format whitespace ever segfaulting,
so hopefully it won't be needed there.

* checks: Replace bash scripts with unstable-commands action

* build: Add unstable-commands action for test step
2023-08-01 00:15:37 +02:00
Domenico V
86931cc3f1 (Graphics.Shader): Handle EmitSuatom constant dests and EmitSuld zero dest reg. (#5504)
* (Graphics.Shader): Handle EmitSuatom constant dests.

* Proper fix for EmitSuatom; fix EmitSuld.
2023-07-30 22:31:57 -03:00
Domenico V
2be8b6ea45 CPU (A64): Add Fmaxp & Fminp Scalar Inst.s, Fast & Slow Paths; with Tests. (#5502)
* Add Fmaxp & Fminp Scalar Inst.s, Fast & Slow Paths; with Tests.

* Ptc.InternalVersion = 5502
2023-07-30 20:57:37 -03:00
gdkchan
f95b7c5877 Fix incorrect fragment origin when YNegate is enabled (#4673)
* Fix incorrect fragment origin when YNegate is enabled

* Shader cache version bump

* Do not update support buffer if shader does not read gl_FragCoord

* Pass unscaled viewport size to the support buffer
2023-07-29 18:47:03 -03:00
TSRBerry
eb528ae0f0 Add workflow to automatically check code style issues for PRs (#4670)
* Add workflow to perform automated checks for PRs

* Downgrade Microsoft.CodeAnalysis to 4.4.0

This is a workaround to fix issues with dotnet-format.
See:
- https://github.com/dotnet/format/issues/1805
- https://github.com/dotnet/format/issues/1800

* Adjust editorconfig to be more compatible with Ryujinx code-style

* Adjust .editorconfig line endings to match .gitattributes

* Disable 'prefer switch expression' rule

* Remove naming styles

These are the default rules, so we don't need to override them.

* Silence IDE0060 in .editorconfig

* Slightly adjust .editorconfig

* Add lost workflow changes

* Move .editorconfig comment to the top

* .editorconfig: private static readonly fields should be _lowerCamelCase

* .editorconfig: Remove alignment for declarations as well

* editorconfig: Add rule for local constants

* Disable CA1822 for HLE services

* Disable CA1822 for ViewModels

Bindings won't work with static members, but this issue is silently ignored.

* Run dotnet format for the whole solution

* Check result code of SDL_GetDisplayBounds

* Fix dotnet format style issues

* Add missing trailing commas

* Update Microsoft.CodeAnalysis.CSharp to 4.6.0

Skipping 4.5.0 since it breaks dotnet format

* Restore old default naming rules for dotnet format

* Add naming rule exception for CPU tests

* checks: Include all files before excluding paths

* Fix dotnet format issues

* Check dotnet format version

* checks: Run dotnet format with severity info again

* checks: Disable naming style rules until they won't crash the process anymore

* Remove unread private member

* checks: Attempt to run analyzers 3 times before giving up

* checks: Enable naming style rules again with the new retry logic
2023-07-24 18:35:04 +02:00
Mary
487261592e ava: Fix regression on title updater and dlc manager window caused by precious commit 2023-07-21 22:50:10 +02:00
MutantAura
9e04e6cba1 Ava UI: Remove IsActive checks from dialog methods (#5456)
* Remove `IsActive` checks from dialog methods

* Remove old windows bandaid

* Remove null dialog code path entirely and return nothing.
2023-07-21 12:24:13 +01:00
TSRBerry
4cf2419e6c HLE: Fix corrupted Mii structs (#5468)
* StructArrayHelpers: Add PureAttribute to all AsSpan() methods

* Fix broken Mii structs
2023-07-19 22:02:31 -03:00
Mary
440abac9f8 chore: Update Ryujinx.SDL2-CS to 2.28.1 (#5453) 2023-07-18 16:08:48 +02:00
TSRBerry
732714349e [Hotfix] sockets: Resolve empty port requests to 0 again (#5459) 2023-07-17 20:47:47 +02:00
TSRBerry
016262514d cpu: Hotfix missing ToNearest rounding mode cases 2023-07-16 20:39:08 +01:00
TSRBerry
326749498b [Ryujinx.HLE] Address dotnet-format issues (#5380)
* dotnet format style --severity info

Some changes were manually reverted.

* dotnet format analyzers --serverity info

Some changes have been minimally adapted.

* Restore a few unused methods and variables

* Silence dotnet format IDE0060 warnings

* Silence dotnet format IDE0052 warnings

* Address or silence dotnet format IDE1006 warnings

* Address dotnet format CA1816 warnings

* Address or silence dotnet format CA2208 warnings

* Address or silence dotnet format CA1806 and a few CA1854 warnings

* Address dotnet format CA2211 warnings

* Address dotnet format CA1822 warnings

* Address or silence dotnet format CA1069 warnings

* Make dotnet format succeed in style mode

* Address or silence dotnet format CA2211 warnings

* Address review comments

* Address dotnet format CA2208 warnings properly

* Make ProcessResult readonly

* Address most dotnet format whitespace warnings

* Apply dotnet format whitespace formatting

A few of them have been manually reverted and the corresponding warning was silenced

* Add previously silenced warnings back

I have no clue how these disappeared

* Revert formatting changes for while and for-loops

* Format if-blocks correctly

* Run dotnet format style after rebase

* Run dotnet format whitespace after rebase

* Run dotnet format style after rebase

* Run dotnet format analyzers after rebase

* Run dotnet format after rebase and remove unused usings

- analyzers
- style
- whitespace

* Disable 'prefer switch expression' rule

* Add comments to disabled warnings

* Fix a few disabled warnings

* Fix naming rule violation, Convert shader properties to auto-property and convert values to const

* Simplify properties and array initialization, Use const when possible, Remove trailing commas

* Start working on disabled warnings

* Fix and silence a few dotnet-format warnings again

* Run dotnet format after rebase

* Use using declaration instead of block syntax

* Address IDE0251 warnings

* Address a few disabled IDE0060 warnings

* Silence IDE0060 in .editorconfig

* Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas"

This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e.

* dotnet format whitespace after rebase

* First dotnet format pass

* Fix naming rule violations

* Fix typo

* Add trailing commas, use targeted new and use array initializer

* Fix build issues

* Fix remaining build issues

* Remove SuppressMessage for CA1069 where possible

* Address dotnet format issues

* Address formatting issues

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

* Add GetHashCode implementation for RenderingSurfaceInfo

* Explicitly silence CA1822 for every affected method in Syscall

* Address formatting issues in Demangler.cs

* Address review feedback

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

* Revert marking service methods as static

* Next dotnet format pass

* Address review feedback

---------

Co-authored-by: Ac_K <acoustik666@gmail.com>
2023-07-16 19:31:14 +02:00
Mary
fec8291c17 infra: do not assign developers team for now
Hopefully fix PR triage for real...
2023-07-14 11:32:14 +02:00
gdkchan
c5d9e67cb2 Fix some Vulkan validation errors (#5452)
* Fix some validation errors and silence the annoying pipeline barrier error

* Remove bogus decref/incref on index buffer state

* Make unsafe blit opt-in rather than opt-out

* Remove Vulkan debugger messages blacklist

* Adjust GetImageUsage to not set the storage bit for multisample textures if not supported
2023-07-14 09:08:52 +02:00
Mary
e5261228d7 infra: Fix team name in reviewer.yml 2023-07-12 19:22:09 +02:00
Mary
e61c09bc85 infra: Fix PR triage once and for all (#5442)
Switch to a custom made python script that query GitHub API to grab latest state of the PR after label assign.
2023-07-12 18:31:08 +02:00
ealekseychik
ac2444f908 Move ShaderBinaries into individual .spv files (#5280)
* Move ShaderBinaries into individual spv files

* Rename binaries directory, remove variables and add helper method instead

* Update .csproj file

* Move ShaderBinaries into individual spv files

* Rename binaries directory, remove variables and add helper method instead

* Split shader binaries into folders, use string.Join to create filepath

* Move files back to general binaries folder

* Remove ShaderSource suffix from file names

---------

Co-authored-by: Egor Alekseychik <e.alekseychik@syberry.com>
Co-authored-by: Gabriel A <gab.dark.100@gmail.com>
2023-07-11 14:41:18 -03:00
gdkchan
9c6071a645 Move support buffer update out of the backends (#5411)
* Move support buffer update out of the backends

* Fix render scale init and remove redundant state from SupportBufferUpdater

* Stop passing texture scale to the backends

* XML docs for SupportBufferUpdater
2023-07-11 14:07:41 -03:00
gleng
fa32ef9275 MacOS: Allow barriers inside a render pass for non-Apple GPUs and don't treat as TBDR (#5440)
* MoltenVK: Allow barriers inside a render pass on non-Apple GPUs

* Don't treat all non-Apple GPUs using MoltenVK as TBDR
2023-07-11 03:10:23 +02:00
gleng
7805d27e67 MacOS: Fix rendering on AMD GPUs (#5446)
* MacOS: Fix rendering on AMD GPUs

* Only disable MultiViewPort on MoltenVK for AMD GPUs
2023-07-11 03:00:19 +02:00
TSRBerry
6c515e1822 [Ryujinx.Ava] Address dotnet-format issues (#5361)
* dotnet format style --severity info

Some changes were manually reverted.

* dotnet format analyzers --serverity info

Some changes have been minimally adapted.

* Restore a few unused methods and variables

* Silence dotnet format IDE0060 warnings

* Silence dotnet format IDE0052 warnings

* Silence dotnet format IDE0059 warnings

* Address or silence dotnet format IDE1006 warnings

* Address dotnet format CA1816 warnings

* Address dotnet format CA1822 warnings

* Address or silence dotnet format CA1069 warnings

* Make dotnet format succeed in style mode

* Address dotnet format CA1401 warnings

* Address remaining dotnet format analyzer warnings

* Address review comments

* dotnet-format fixes after rebase

* Address most dotnet format whitespace warnings

* Apply dotnet format whitespace formatting

A few of them have been manually reverted and the corresponding warning was silenced

* Format if-blocks correctly

* Another rebase, another dotnet format run

* Run dotnet format whitespace after rebase

* Run dotnet format style after rebase

* Run dotnet format after rebase and remove unused usings

- analyzers
- style
- whitespace

* Add comments to disabled warnings

* Remove a few unused parameters

* Simplify properties and array initialization, Use const when possible, Remove trailing commas

* Start working on disabled warnings

* Fix and silence a few dotnet-format warnings again

* Address IDE0260 warnings

* Address a few disabled IDE0060 warnings

* Silence IDE0060 in .editorconfig

* Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas"

This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e.

* dotnet format whitespace after rebase

* dotnet format pass with new editorconfig

* Fix naming style issues

* Apply suggestions from code review

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

* Revert one suggestion

* Second dotnet format pass and fix build issues

* Final pass of dotnet format

* Add trailing commas

* Fix formatting issues

* Keep unnecessary assignment in IconColorPicker.cs

* Use using declarations and extend resource lifetimes

* Fix rebase issues

* Adjust comment spacing

* Fix typo

* Fix naming issues

* Apply suggestions from code review

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

* Revert unintentional change

* Remove unused file

* Remove static keyword from ViewModels

Binding of static members doesn't work and is silently ignored.

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-07-07 23:03:27 +02:00
Mary
8a363b5df2 Revert "sdl: set SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS to 0 (#5433)" (#5439)
This reverts commit 2b5abac809.
2023-07-06 18:08:14 +02:00
SuperSamus
2b5abac809 sdl: set SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS to 0 (#5433)
Nintendo controllers notoriously have the A/B and X/Y buttons swapped, compared to the standard.
In order to combat this, when setting the default controller layout, Ryujinx checks whether the controller name contains "Nintendo", and swaps the mapping accordingly.
However, the reason the mapping is inverted in the first place is because SDL has `SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS` set to 1 by default. By setting it to 0, the mapping will be based on the buttons' position instead.
So, by doing it (and removing the `isNintendoStyle` variable), we get the following advantages:
- The mapping will be the same on all controllers, removing the need to adjust custom mappings depending on what controller is used
- Users who already set `SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS` to 0 globally for other games/applications (like me) won't have a wrong default mapping
- Checking whether the controller name contains "Nintendo" is ugly

Disadvantages:
- Breaks the controller configuration for existing users who are using a Nintendo controller
2023-07-06 17:11:26 +02:00
Theun de Bruijn
c19c8bbade Headless: Add support for fullscreen option (#5339)
* Headless: Added support for fullscreen option

* Headless: cleanup of fullscreen support

* Headless: fullscreen support : implemented proposed changes

* Headless: fullscreen support: cleanup

* Headless: fullscreen support: fix for OpenGL scaling

* Headless: fullscreen support: cleanup

* Headless: fullscreen support: cleanup

* Headless: fullscreen support: add. macOS fullscreen fix

* Headless: fullscreen support: cleanup

* Headless: fullscreen support: cleanup

* Headless: fullscreen support: cleanup
2023-07-06 12:10:15 +02:00
gdkchan
1c7a90ef35 Stop identifying shader textures with handle and cbuf, use binding instead (#5266)
* Stop identifying shader textures with handle and cbuf, use binding instead

* Remove now unused code

* Consider image operations as having accurate type information too

I don't know why that was not the case before

* Fix missing unscale on InsertCoordNormalization, stop calling SetUsageFlagsForTextureQuery when not needed

* Shader cache version bump

* Change get texture methods to return descriptors created from ResourceManager state

 This is required to ensure that reserved textures and images will not be bound as a guest texture/image

* Fix BindlessElimination.SetHandle inserting coords at the wrong place
2023-07-03 14:29:27 -03:00
TSRBerry
3b46bb73f7 [Ryujinx.Graphics.Gpu] Address dotnet-format issues (#5367)
* dotnet format style --severity info

Some changes were manually reverted.

* dotnet format analyzers --serverity info

Some changes have been minimally adapted.

* Restore a few unused methods and variables

* Silence dotnet format IDE0060 warnings

* Silence dotnet format IDE0052 warnings

* Address dotnet format CA1816 warnings

* Address or silence dotnet format CA1069 warnings

* Address or silence dotnet format CA2211 warnings

* Address remaining dotnet format analyzer warnings

* Address review comments

* Address most dotnet format whitespace warnings

* Apply dotnet format whitespace formatting

A few of them have been manually reverted and the corresponding warning was silenced

* Format if-blocks correctly

* Run dotnet format whitespace after rebase

* Run dotnet format style after rebase

* Another rebase, another dotnet format run

* Run dotnet format style after rebase

* Run dotnet format after rebase and remove unused usings

- analyzers
- style
- whitespace

* Disable 'prefer switch expression' rule

* Add comments to disabled warnings

* Remove a few unused parameters

* Replace MmeShadowScratch with Array256<uint>

* Simplify properties and array initialization, Use const when possible, Remove trailing commas

* Start working on disabled warnings

* Fix and silence a few dotnet-format warnings again

* Run dotnet format after rebase

* Address IDE0251 warnings

* Silence IDE0060 in .editorconfig

* Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas"

This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e.

* dotnet format whitespace after rebase

* First pass of dotnet format

* Add unsafe dotnet format changes

* Fix typos

* Add trailing commas

* Disable formatting for FormatTable

* Address review feedback
2023-07-02 02:47:54 +02:00
TSRBerry
2457cfc911 Fix naming issue in ControllerWindow (#5424) 2023-07-02 02:26:32 +02:00
1503 changed files with 14117 additions and 15742 deletions

View File

@@ -1,8 +1,7 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# files
[*.cs]
[*]
#### Core EditorConfig Options ####
@@ -12,8 +11,18 @@ indent_style = space
tab_width = 4
# New line preferences
end_of_line = crlf
insert_final_newline = false
end_of_line = lf
insert_final_newline = true
# JSON files
[*.json]
# Indentation and spacing
indent_size = 2
tab_width = 2
# C# files
[*.cs]
#### .NET Coding Conventions ####
@@ -59,7 +68,7 @@ dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_readonly_field = true:suggestion
# Parameter preferences
dotnet_code_quality_unused_parameters = all:suggestion
dotnet_code_quality_unused_parameters = all:silent
#### C# Coding Conventions ####
@@ -85,7 +94,7 @@ csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
csharp_style_prefer_switch_expression = false:silent
# Null-checking preferences
csharp_style_conditional_delegate_call = true:suggestion
@@ -94,6 +103,7 @@ csharp_style_conditional_delegate_call = true:suggestion
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
csharp_style_prefer_readonly_struct = true
csharp_style_prefer_method_group_conversion = true
# Code-block preferences
csharp_prefer_braces = true:silent
@@ -109,6 +119,7 @@ csharp_style_prefer_range_operator = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
csharp_style_implicit_object_creation_when_type_is_apparent = true
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
@@ -140,7 +151,6 @@ csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
@@ -158,23 +168,31 @@ csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_statements = false
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.interfaces_should_be_prefixed_with_I.severity = suggestion
dotnet_naming_rule.interfaces_should_be_prefixed_with_I.symbols = interface
dotnet_naming_rule.interfaces_should_be_prefixed_with_I.style = IPascalCase
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.types_should_be_pascal_case.style = PascalCase
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = PascalCase
dotnet_naming_rule.private_static_readonly_fields_should_be_camel_case_and_prefixed_with__.symbols = private_static_readonly_fields
dotnet_naming_rule.private_static_readonly_fields_should_be_camel_case_and_prefixed_with__.severity = suggestion
dotnet_naming_rule.private_static_readonly_fields_should_be_camel_case_and_prefixed_with__.style = _camelCase
dotnet_naming_rule.local_constants_should_be_pascal_case.symbols = local_constants
dotnet_naming_rule.local_constants_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.local_constants_should_be_pascal_case.style = PascalCase
# Symbol specifications
@@ -190,14 +208,39 @@ dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, meth
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private
dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = static, readonly
dotnet_naming_symbols.local_constants.applicable_kinds = local
dotnet_naming_symbols.local_constants.applicable_accessibilities = local
dotnet_naming_symbols.local_constants.required_modifiers = const
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style._camelCase.required_prefix = _
dotnet_naming_style._camelCase.required_suffix =
dotnet_naming_style._camelCase.word_separator =
dotnet_naming_style._camelCase.capitalization = camel_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.PascalCase.required_prefix =
dotnet_naming_style.PascalCase.required_suffix =
dotnet_naming_style.PascalCase.word_separator =
dotnet_naming_style.PascalCase.capitalization = pascal_case
dotnet_naming_style.IPascalCase.required_prefix = I
dotnet_naming_style.IPascalCase.required_suffix =
dotnet_naming_style.IPascalCase.word_separator =
dotnet_naming_style.IPascalCase.capitalization = pascal_case
[src/Ryujinx.HLE/HOS/Services/**.cs]
# Disable "mark members as static" rule for services
dotnet_diagnostic.CA1822.severity = none
[src/Ryujinx.Ava/UI/ViewModels/**.cs]
# Disable "mark members as static" rule for ViewModels
dotnet_diagnostic.CA1822.severity = none
[src/Ryujinx.Tests/Cpu/*.cs]
# Disable naming rules for CPU tests
dotnet_diagnostic.IDE1006.severity = none

View File

@@ -1,8 +0,0 @@
addReviewers: true
reviewers:
- marysaka
filterLabels:
include:
- audio

View File

@@ -1,11 +0,0 @@
addReviewers: true
reviewers:
- gdkchan
- riperiperi
- marysaka
- LDj3SNuD
filterLabels:
include:
- cpu

View File

@@ -1,4 +0,0 @@
addReviewers: true
reviewers:
- Ryujinx/developers

View File

@@ -1,10 +0,0 @@
addReviewers: true
reviewers:
- gdkchan
- riperiperi
- marysaka
filterLabels:
include:
- gpu

View File

@@ -1,11 +0,0 @@
addReviewers: true
reviewers:
- Ack77
- emmauss
- TSRBerry
- marysaka
filterLabels:
include:
- gui

View File

@@ -1,11 +0,0 @@
addReviewers: true
reviewers:
- gdkchan
- Ack77
- marysaka
- TSRBerry
filterLabels:
include:
- horizon

View File

@@ -1,9 +0,0 @@
addReviewers: true
reviewers:
- marysaka
- TSRBerry
filterLabels:
include:
- infra

32
.github/reviewers.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
audio:
- marysaka
cpu:
- gdkchan
- riperiperi
- marysaka
- LDj3SNuD
gpu:
- gdkchan
- riperiperi
- marysaka
gui:
- Ack77
- emmauss
- TSRBerry
- marysaka
horizon:
- gdkchan
- Ack77
- marysaka
- TSRBerry
infra:
- marysaka
- TSRBerry
default:
- marysaka

79
.github/update_reviewers.py vendored Normal file
View File

@@ -0,0 +1,79 @@
from pathlib import Path
from typing import List, Set
from github import Github
from github.Repository import Repository
from github.GithubException import GithubException
import sys
import yaml
def add_reviewers(
reviewers: Set[str], team_reviewers: Set[str], new_entries: List[str]
):
for reviewer in new_entries:
if reviewer.startswith("@"):
team_reviewers.add(reviewer[1:])
else:
reviewers.add(reviewer)
def update_reviewers(config, repo: Repository, pr_id: int) -> int:
pull_request = repo.get_pull(pr_id)
if not pull_request:
sys.stderr.writable(f"Unknown PR #{pr_id}\n")
return 1
pull_request_author = pull_request.user.login
reviewers = set()
team_reviewers = set()
for label in pull_request.labels:
if label.name in config:
add_reviewers(reviewers, team_reviewers, config[label.name])
if "default" in config:
add_reviewers(reviewers, team_reviewers, config["default"])
if pull_request_author in reviewers:
reviewers.remove(pull_request_author)
try:
reviewers = list(reviewers)
team_reviewers = list(team_reviewers)
print(
f"Attempting to assign reviewers ({reviewers}) and team_reviewers ({team_reviewers})"
)
pull_request.create_review_request(reviewers, team_reviewers)
return 0
except GithubException as e:
sys.stderr.write(f"Cannot assign review request for PR #{pr_id}: {e}\n")
return 1
if __name__ == "__main__":
if len(sys.argv) != 5:
sys.stderr.write("usage: <token> <repo_path> <pr_id> <config_path>\n")
sys.exit(1)
token = sys.argv[1]
repo_path = sys.argv[2]
pr_id = int(sys.argv[3])
config_path = Path(sys.argv[4])
g = Github(token)
repo = g.get_repo(repo_path)
if not repo:
sys.stderr.write("Repository not found!\n")
sys.exit(1)
if not config_path.exists():
sys.stderr.write(f'Config "{config_path}" not found!\n')
sys.exit(1)
with open(config_path, "r") as f:
config = yaml.safe_load(f)
sys.exit(update_reviewers(config, repo, pr_id))

View File

@@ -1,20 +1,7 @@
name: Build job
on:
workflow_dispatch:
inputs: {}
pull_request:
branches: [ master ]
paths-ignore:
- '.github/**'
- '*.yml'
- '*.json'
- '*.config'
- 'README.md'
concurrency:
group: pr-checks-${{ github.event.number }}
cancel-in-progress: true
workflow_call:
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
@@ -63,7 +50,11 @@ jobs:
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
- name: Test
run: dotnet test --no-build -c "${{ matrix.configuration }}"
uses: TSRBerry/unstable-commands@v1
with:
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
timeout-minutes: 10
retry-codes: 139
- name: Publish Ryujinx
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -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
@@ -150,4 +141,4 @@ jobs:
with:
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish_ava/*.tar.gz"
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request'

71
.github/workflows/checks.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
name: Perform checks
on:
pull_request:
branches: [ master ]
paths:
- '**'
- '!.github/**'
- '!*.yml'
- '!*.config'
- '!README.md'
- '.github/workflows/*.yml'
permissions:
pull-requests: write
checks: write
concurrency:
group: pr-checks-${{ github.event.number }}
cancel-in-progress: true
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-dotnet@v3
with:
global-json-file: global.json
- run: dotnet restore
- name: Print dotnet format version
run: dotnet format --version
- name: Run dotnet format whitespace
run: |
dotnet format whitespace --verify-no-changes --report ./whitespace-report.json -v d
# For some unknown reason this step sometimes fails with exit code 139 (segfault?),
# so in that case we'll try again (3 tries max).
- name: Run dotnet format style
uses: TSRBerry/unstable-commands@v1
with:
commands: dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d
timeout-minutes: 5
retry-codes: 139
# For some unknown reason this step sometimes fails with exit code 139 (segfault?),
# so in that case we'll try again (3 tries max).
- name: Run dotnet format analyzers
uses: TSRBerry/unstable-commands@v1
with:
commands: dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d
timeout-minutes: 5
retry-codes: 139
- name: Upload report
if: failure()
uses: actions/upload-artifact@v3
with:
name: dotnet-format
path: ./*-report.json
pr_build:
uses: ./.github/workflows/build.yml
needs: format
secrets: inherit

View File

@@ -1,8 +1,10 @@
name: Comment PR artifacts links
on:
workflow_run:
workflows: ['Build job']
workflows: ['Perform checks']
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'

View File

@@ -12,43 +12,24 @@ jobs:
runs-on: ubuntu-latest
steps:
# Grab sources to get update_reviewers.py and reviewers.yml
- name: Fetch sources
uses: actions/checkout@v3
with:
# Ensure we pin the source origin as pull_request_target run under forks.
fetch-depth: 0
repository: Ryujinx/Ryujinx
ref: master
- name: Update labels based on changes
uses: actions/labeler@v4
with:
sync-labels: true
dot: true
- name: Auto Assign [Audio]
uses: kentaro-m/auto-assign-action@v1.2.5
with:
configuration-path: '.github/assign/audio.yml'
- name: Auto Assign [CPU]
uses: kentaro-m/auto-assign-action@v1.2.5
with:
configuration-path: '.github/assign/cpu.yml'
- name: Auto Assign [GPU]
uses: kentaro-m/auto-assign-action@v1.2.5
with:
configuration-path: '.github/assign/gpu.yml'
- name: Auto Assign [GUI]
uses: kentaro-m/auto-assign-action@v1.2.5
with:
configuration-path: '.github/assign/gui.yml'
- name: Auto Assign [Horizon]
uses: kentaro-m/auto-assign-action@v1.2.5
with:
configuration-path: '.github/assign/horizon.yml'
- name: Auto Assign [Infra]
uses: kentaro-m/auto-assign-action@v1.2.5
with:
configuration-path: '.github/assign/infra.yml'
- name: Auto Assign [Global]
uses: kentaro-m/auto-assign-action@v1.2.5
with:
configuration-path: '.github/assign/global.yml'
- name: Assign reviewers
if: ! github.event.pull_request.draft
run: |
pip3 install PyGithub
python3 .github/update_reviewers.py ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${{ github.event.pull_request.number }} .github/reviewers.yml
shell: bash

View File

@@ -3,24 +3,24 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="0.10.21" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.21" />
<PackageVersion Include="Avalonia.Desktop" Version="0.10.21" />
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.21" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.21" />
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
<PackageVersion Include="Avalonia" Version="11.0.3" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.3" />
<PackageVersion Include="Avalonia.Desktop" Version="11.0.3" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.3" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.3" />
<PackageVersion Include="Avalonia.Svg" Version="11.0.0" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="7.14.2" />
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.1" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
<PackageVersion Include="LibHac" Version="0.18.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
@@ -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.26.3-build25" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.28.1-build28" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
@@ -48,6 +48,5 @@
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
<PackageVersion Include="System.Management" Version="7.0.2" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
</ItemGroup>
</Project>

View File

@@ -44,10 +44,115 @@
<string>public.app-category.games</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeDescription</key>
<string>Extensible Application Markup Language</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.ryujinx.xaml</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>xaml</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeDescription</key>
<string>Nintendo Submission Package</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.ryujinx.nsp</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>nsp</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeDescription</key>
<string>Nintendo Switch Cartridge</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.ryujinx.xci</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>xci</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeDescription</key>
<string>Nintendo Content Archive</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.ryujinx.nca</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>nca</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeDescription</key>
<string>Nintendo Relocatable Object</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.ryujinx.nro</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>nro</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeDescription</key>
<string>Nintendo Shared Object</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.ryujinx.nso</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>nso</string>
</array>
</dict>
</dict>
</array>
<key>LSEnvironment</key>
<dict>
<key>COMPlus_DefaultStackSize</key>
<key>DOTNET_DefaultStackSize</key>
<string>200000</string>
</dict>
</dict>
</plist>
</plist>

View File

@@ -20,4 +20,4 @@
}
}
}
}
}

View File

@@ -330,6 +330,7 @@ namespace ARMeilleure.Decoders
SetA64("011111100x110000110010xxxxxxxxxx", InstName.Fmaxnmp_S, InstEmit.Fmaxnmp_S, OpCodeSimd.Create);
SetA64("0>1011100<1xxxxx110001xxxxxxxxxx", InstName.Fmaxnmp_V, InstEmit.Fmaxnmp_V, OpCodeSimdReg.Create);
SetA64("0110111000110000110010xxxxxxxxxx", InstName.Fmaxnmv_V, InstEmit.Fmaxnmv_V, OpCodeSimd.Create);
SetA64("011111100x110000111110xxxxxxxxxx", InstName.Fmaxp_S, InstEmit.Fmaxp_S, OpCodeSimd.Create);
SetA64("0>1011100<1xxxxx111101xxxxxxxxxx", InstName.Fmaxp_V, InstEmit.Fmaxp_V, OpCodeSimdReg.Create);
SetA64("0110111000110000111110xxxxxxxxxx", InstName.Fmaxv_V, InstEmit.Fmaxv_V, OpCodeSimd.Create);
SetA64("000111100x1xxxxx010110xxxxxxxxxx", InstName.Fmin_S, InstEmit.Fmin_S, OpCodeSimdReg.Create);
@@ -339,6 +340,7 @@ namespace ARMeilleure.Decoders
SetA64("011111101x110000110010xxxxxxxxxx", InstName.Fminnmp_S, InstEmit.Fminnmp_S, OpCodeSimd.Create);
SetA64("0>1011101<1xxxxx110001xxxxxxxxxx", InstName.Fminnmp_V, InstEmit.Fminnmp_V, OpCodeSimdReg.Create);
SetA64("0110111010110000110010xxxxxxxxxx", InstName.Fminnmv_V, InstEmit.Fminnmv_V, OpCodeSimd.Create);
SetA64("011111101x110000111110xxxxxxxxxx", InstName.Fminp_S, InstEmit.Fminp_S, OpCodeSimd.Create);
SetA64("0>1011101<1xxxxx111101xxxxxxxxxx", InstName.Fminp_V, InstEmit.Fminp_V, OpCodeSimdReg.Create);
SetA64("0110111010110000111110xxxxxxxxxx", InstName.Fminv_V, InstEmit.Fminv_V, OpCodeSimd.Create);
SetA64("010111111xxxxxxx0001x0xxxxxxxxxx", InstName.Fmla_Se, InstEmit.Fmla_Se, OpCodeSimdRegElemF.Create);

View File

@@ -883,6 +883,31 @@ namespace ARMeilleure.Instructions
}
}
public static void Fmaxp_S(ArmEmitterContext context)
{
if (Optimizations.UseAdvSimd)
{
InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FmaxpS);
}
else if (Optimizations.FastFP && Optimizations.UseSse41)
{
EmitSse2ScalarPairwiseOpF(context, (op1, op2) =>
{
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
}, scalar: true, op1, op2);
});
}
else
{
EmitScalarPairwiseOpF(context, (op1, op2) =>
{
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax), op1, op2);
});
}
}
public static void Fmaxp_V(ArmEmitterContext context)
{
if (Optimizations.UseAdvSimd)
@@ -1081,6 +1106,31 @@ namespace ARMeilleure.Instructions
}
}
public static void Fminp_S(ArmEmitterContext context)
{
if (Optimizations.UseAdvSimd)
{
InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FminpS);
}
else if (Optimizations.FastFP && Optimizations.UseSse41)
{
EmitSse2ScalarPairwiseOpF(context, (op1, op2) =>
{
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
}, scalar: true, op1, op2);
});
}
else
{
EmitScalarPairwiseOpF(context, (op1, op2) =>
{
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin), op1, op2);
});
}
}
public static void Fminp_V(ArmEmitterContext context)
{
if (Optimizations.UseAdvSimd)

View File

@@ -228,6 +228,7 @@ namespace ARMeilleure.Instructions
Fmaxnmp_S,
Fmaxnmp_V,
Fmaxnmv_V,
Fmaxp_S,
Fmaxp_V,
Fmaxv_V,
Fmin_S,
@@ -237,6 +238,7 @@ namespace ARMeilleure.Instructions
Fminnmp_S,
Fminnmp_V,
Fminnmv_V,
Fminp_S,
Fminp_V,
Fminv_V,
Fmla_Se,

View File

@@ -1448,6 +1448,7 @@ namespace ARMeilleure.Instructions
{
var overflowToInf = fpcr.GetRoundingMode() switch
{
FPRoundingMode.ToNearest => true,
FPRoundingMode.TowardsPlusInfinity => !sign,
FPRoundingMode.TowardsMinusInfinity => sign,
FPRoundingMode.TowardsZero => false,
@@ -2879,6 +2880,7 @@ namespace ARMeilleure.Instructions
{
var overflowToInf = fpcr.GetRoundingMode() switch
{
FPRoundingMode.ToNearest => true,
FPRoundingMode.TowardsPlusInfinity => !sign,
FPRoundingMode.TowardsMinusInfinity => sign,
FPRoundingMode.TowardsZero => false,

View File

@@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 5343; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 5518; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";

View File

@@ -27,6 +27,26 @@ namespace ARMeilleure.Translation.PTC
return dictionary;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Dictionary<TKey, TValue> DeserializeAndUpdateDictionary<TKey, TValue>(Stream stream, Func<Stream, TValue> valueFunc, Func<TKey, TValue, (TKey, TValue)> updateFunc) where TKey : struct
{
Dictionary<TKey, TValue> dictionary = new();
int count = DeserializeStructure<int>(stream);
for (int i = 0; i < count; i++)
{
TKey key = DeserializeStructure<TKey>(stream);
TValue value = valueFunc(stream);
(key, value) = updateFunc(key, value);
dictionary.Add(key, value);
}
return dictionary;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T> DeserializeList<T>(Stream stream) where T : struct
{

View File

@@ -9,10 +9,13 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Timers;
using static ARMeilleure.Translation.PTC.PtcFormatter;
using Timer = System.Timers.Timer;
namespace ARMeilleure.Translation.PTC
{
@@ -20,7 +23,11 @@ namespace ARMeilleure.Translation.PTC
{
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
private const uint InternalVersion = 1866; //! Not to be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 5518; //! Not to be incremented manually for each change to the ARMeilleure project.
private static readonly uint[] _migrateInternalVersions = {
1866,
};
private const int SaveInterval = 30; // Seconds.
@@ -28,7 +35,7 @@ namespace ARMeilleure.Translation.PTC
private readonly Ptc _ptc;
private readonly System.Timers.Timer _timer;
private readonly Timer _timer;
private readonly ulong _outerHeaderMagic;
@@ -51,7 +58,7 @@ namespace ARMeilleure.Translation.PTC
{
_ptc = ptc;
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
_timer = new Timer(SaveInterval * 1000d);
_timer.Elapsed += PreSave;
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
@@ -168,7 +175,7 @@ namespace ARMeilleure.Translation.PTC
return false;
}
if (outerHeader.InfoFileVersion != InternalVersion)
if (outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion))
{
InvalidateCompressedStream(compressedStream);
@@ -211,7 +218,19 @@ namespace ARMeilleure.Translation.PTC
return false;
}
ProfiledFuncs = Deserialize(stream);
switch (outerHeader.InfoFileVersion)
{
case InternalVersion:
ProfiledFuncs = Deserialize(stream);
break;
case 1866:
ProfiledFuncs = Deserialize(stream, (address, profile) => (address + 0x500000UL, profile));
break;
default:
Logger.Error?.Print(LogClass.Ptc, $"No migration path for {nameof(outerHeader.InfoFileVersion)} '{outerHeader.InfoFileVersion}'. Discarding cache.");
InvalidateCompressedStream(compressedStream);
return false;
}
Debug.Assert(stream.Position == stream.Length);
@@ -225,9 +244,14 @@ namespace ARMeilleure.Translation.PTC
return true;
}
private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream)
private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream, Func<ulong, FuncProfile, (ulong, FuncProfile)> migrateEntryFunc = null)
{
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
if (migrateEntryFunc != null)
{
return DeserializeAndUpdateDictionary(stream, DeserializeStructure<FuncProfile>, migrateEntryFunc);
}
return DeserializeDictionary<ulong, FuncProfile>(stream, DeserializeStructure<FuncProfile>);
}
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
@@ -240,7 +264,7 @@ namespace ARMeilleure.Translation.PTC
compressedStream.SetLength(0L);
}
private void PreSave(object source, System.Timers.ElapsedEventArgs e)
private void PreSave(object source, ElapsedEventArgs e)
{
_waitEvent.Reset();
@@ -277,7 +301,7 @@ namespace ARMeilleure.Translation.PTC
{
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
stream.Seek(Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
lock (_lock)
{
@@ -288,7 +312,7 @@ namespace ARMeilleure.Translation.PTC
Debug.Assert(stream.Position == stream.Length);
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
stream.Seek(Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
stream.Seek(0L, SeekOrigin.Begin);
@@ -332,7 +356,7 @@ namespace ARMeilleure.Translation.PTC
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
{
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
SerializeDictionary(stream, profiledFuncs, SerializeStructure);
}
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)]

View File

@@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
_stillRunning = true;
_updaterThread = new Thread(Update)
{
Name = "HardwareDeviceDriver.OpenAL"
Name = "HardwareDeviceDriver.OpenAL",
};
_updaterThread.Start();

View File

@@ -67,7 +67,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
{
DriverIdentifier = buffer.DataPointer,
BufferId = AL.GenBuffer(),
SampleCount = GetSampleCount(buffer)
SampleCount = GetSampleCount(buffer),
};
AL.BufferData(driverBuffer.BufferId, _targetFormat, buffer.Data, (int)RequestedSampleRate);

View File

@@ -7,7 +7,6 @@ using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
using static SDL2.SDL;
@@ -111,7 +110,7 @@ namespace Ryujinx.Audio.Backends.SDL2
channels = (byte)requestedChannelCount,
format = GetSDL2Format(requestedSampleFormat),
freq = (int)requestedSampleRate,
samples = (ushort)sampleCount
samples = (ushort)sampleCount,
};
}

View File

@@ -8,6 +8,6 @@
Alsa = 3,
CoreAudio = 4,
Wasapi = 5,
Dummy = 6
Dummy = 6,
}
}

View File

@@ -3,6 +3,6 @@
public enum SoundIoDeviceAim
{
SoundIoDeviceAimInput = 0,
SoundIoDeviceAimOutput = 1
SoundIoDeviceAimOutput = 1,
}
}

View File

@@ -3,7 +3,15 @@
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sty="using:FluentAvalonia.Styling">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<MergeResourceInclude Source="/Assets/Styles/Themes.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<Application.Styles>
<sty:FluentAvaloniaTheme PreferSystemTheme="False" />
<StyleInclude Source="/Assets/Styles/Styles.xaml"/>
</Application.Styles>
</Application>

View File

@@ -3,9 +3,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.Styling;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
@@ -25,6 +23,11 @@ namespace Ryujinx.Ava
Name = $"Ryujinx {Program.Version}";
AvaloniaXamlLoader.Load(this);
if (OperatingSystem.IsMacOS())
{
Process.Start("/usr/bin/defaults", "write org.ryujinx.Ryujinx ApplePressAndHoldEnabled -bool false");
}
}
public override void OnFrameworkInitializationCompleted()
@@ -67,7 +70,7 @@ namespace Ryujinx.Ava
if (result == UserResult.Yes)
{
var path = Process.GetCurrentProcess().MainModule.FileName;
var path = Environment.ProcessPath;
var proc = Process.Start(path, CommandLineState.Arguments);
desktop.Shutdown();
Environment.Exit(0);
@@ -90,8 +93,6 @@ namespace Ryujinx.Ava
string themePath = ConfigurationState.Instance.Ui.CustomThemePath;
bool enableCustomTheme = ConfigurationState.Instance.Ui.EnableCustomTheme;
const string BaseStyleUrl = "avares://Ryujinx.Ava/Assets/Styles/Base{0}.xaml";
if (string.IsNullOrWhiteSpace(baseStyle))
{
ConfigurationState.Instance.Ui.BaseStyle.Value = "Dark";
@@ -99,31 +100,12 @@ namespace Ryujinx.Ava
baseStyle = ConfigurationState.Instance.Ui.BaseStyle;
}
var theme = AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>();
theme.RequestedTheme = baseStyle;
var currentStyles = this.Styles;
// Remove all styles except the base style.
if (currentStyles.Count > 1)
RequestedThemeVariant = baseStyle switch
{
currentStyles.RemoveRange(1, currentStyles.Count - 1);
}
IStyle newStyles = null;
// Load requested style, and fallback to Dark theme if loading failed.
try
{
newStyles = (Styles)AvaloniaXamlLoader.Load(new Uri(string.Format(BaseStyleUrl, baseStyle), UriKind.Absolute));
}
catch (XamlLoadException)
{
newStyles = (Styles)AvaloniaXamlLoader.Load(new Uri(string.Format(BaseStyleUrl, "Dark"), UriKind.Absolute));
}
currentStyles.Add(newStyles);
"Light" => ThemeVariant.Light,
"Dark" => ThemeVariant.Dark,
_ => ThemeVariant.Default
};
if (enableCustomTheme)
{
@@ -134,7 +116,7 @@ namespace Ryujinx.Ava
var themeContent = File.ReadAllText(themePath);
var customStyle = AvaloniaRuntimeXamlLoader.Parse<IStyle>(themeContent);
currentStyles.Add(customStyle);
Styles.Add(customStyle);
}
catch (Exception ex)
{
@@ -151,4 +133,4 @@ namespace Ryujinx.Ava
}
}
}
}
}

View File

@@ -1,7 +1,9 @@
using ARMeilleure.Translation;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
using Avalonia.Rendering;
using Avalonia.Threading;
using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.Dummy;
@@ -26,6 +28,7 @@ using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
@@ -41,7 +44,6 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SPB.Graphics.Exceptions;
using SPB.Graphics.Vulkan;
using System;
using System.Collections.Generic;
@@ -50,10 +52,13 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
using Image = SixLabors.ImageSharp.Image;
using InputManager = Ryujinx.Input.HLE.InputManager;
using IRenderer = Ryujinx.Graphics.GAL.IRenderer;
using Key = Ryujinx.Input.Key;
using MouseButton = Ryujinx.Input.MouseButton;
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
using Size = Avalonia.Size;
using Switch = Ryujinx.HLE.Switch;
@@ -61,31 +66,31 @@ namespace Ryujinx.Ava
{
internal class AppHost
{
private const int CursorHideIdleTime = 5; // Hide Cursor seconds.
private const int CursorHideIdleTime = 5; // Hide Cursor seconds.
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
private const int TargetFps = 60;
private const float VolumeDelta = 0.05f;
private const int TargetFps = 60;
private const float VolumeDelta = 0.05f;
private static readonly Cursor InvisibleCursor = new(StandardCursorType.None);
private readonly IntPtr InvisibleCursorWin;
private readonly IntPtr DefaultCursorWin;
private static readonly Cursor _invisibleCursor = new(StandardCursorType.None);
private readonly IntPtr _invisibleCursorWin;
private readonly IntPtr _defaultCursorWin;
private readonly long _ticksPerFrame;
private readonly long _ticksPerFrame;
private readonly Stopwatch _chrono;
private long _ticks;
private long _ticks;
private readonly AccountManager _accountManager;
private readonly AccountManager _accountManager;
private readonly UserChannelPersistence _userChannelPersistence;
private readonly InputManager _inputManager;
private readonly InputManager _inputManager;
private readonly MainWindowViewModel _viewModel;
private readonly IKeyboard _keyboardInterface;
private readonly TopLevel _topLevel;
public RendererHost _rendererHost;
private readonly IKeyboard _keyboardInterface;
private readonly TopLevel _topLevel;
public RendererHost RendererHost;
private readonly GraphicsDebugLevel _glLogLevel;
private float _newVolume;
private KeyboardHotkeyState _prevHotkeyState;
private float _newVolume;
private KeyboardHotkeyState _prevHotkeyState;
private long _lastCursorMoveTime;
private bool _isCursorInRenderer = true;
@@ -94,14 +99,14 @@ namespace Ryujinx.Ava
private bool _isActive;
private bool _renderingStarted;
private ManualResetEvent _gpuDoneEvent;
private readonly ManualResetEvent _gpuDoneEvent;
private IRenderer _renderer;
private readonly Thread _renderingThread;
private IRenderer _renderer;
private readonly Thread _renderingThread;
private readonly CancellationTokenSource _gpuCancellationTokenSource;
private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
private bool _dialogShown;
private bool _dialogShown;
private readonly bool _isFirmwareTitle;
private readonly object _lockObject = new();
@@ -109,107 +114,105 @@ namespace Ryujinx.Ava
public event EventHandler AppExit;
public event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
public VirtualFileSystem VirtualFileSystem { get; }
public ContentManager ContentManager { get; }
public NpadManager NpadManager { get; }
public VirtualFileSystem VirtualFileSystem { get; }
public ContentManager ContentManager { get; }
public NpadManager NpadManager { get; }
public TouchScreenManager TouchScreenManager { get; }
public Switch Device { get; set; }
public Switch Device { get; set; }
public int Width { get; private set; }
public int Height { get; private set; }
public string ApplicationPath { get; private set; }
public bool ScreenshotRequested { get; set; }
public int Width { get; private set; }
public int Height { get; private set; }
public string ApplicationPath { get; private set; }
public bool ScreenshotRequested { get; set; }
public AppHost(
RendererHost renderer,
InputManager inputManager,
string applicationPath,
VirtualFileSystem virtualFileSystem,
ContentManager contentManager,
AccountManager accountManager,
RendererHost renderer,
InputManager inputManager,
string applicationPath,
VirtualFileSystem virtualFileSystem,
ContentManager contentManager,
AccountManager accountManager,
UserChannelPersistence userChannelPersistence,
MainWindowViewModel viewmodel,
TopLevel topLevel)
MainWindowViewModel viewmodel,
TopLevel topLevel)
{
_viewModel = viewmodel;
_inputManager = inputManager;
_accountManager = accountManager;
_viewModel = viewmodel;
_inputManager = inputManager;
_accountManager = accountManager;
_userChannelPersistence = userChannelPersistence;
_renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" };
_lastCursorMoveTime = Stopwatch.GetTimestamp();
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
_topLevel = topLevel;
_renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" };
_lastCursorMoveTime = Stopwatch.GetTimestamp();
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
_topLevel = topLevel;
_inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer));
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
NpadManager = _inputManager.CreateNpadManager();
NpadManager = _inputManager.CreateNpadManager();
TouchScreenManager = _inputManager.CreateTouchScreenManager();
ApplicationPath = applicationPath;
VirtualFileSystem = virtualFileSystem;
ContentManager = contentManager;
ApplicationPath = applicationPath;
VirtualFileSystem = virtualFileSystem;
ContentManager = contentManager;
_rendererHost = renderer;
RendererHost = renderer;
_chrono = new Stopwatch();
_chrono = new Stopwatch();
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
if (ApplicationPath.StartsWith("@SystemContent"))
{
ApplicationPath = _viewModel.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
ApplicationPath = VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
_isFirmwareTitle = true;
}
ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed;
_topLevel.PointerMoved += TopLevel_PointerEnterOrMoved;
_topLevel.PointerEnter += TopLevel_PointerEnterOrMoved;
_topLevel.PointerLeave += TopLevel_PointerLeave;
_topLevel.PointerMoved += TopLevel_PointerEnteredOrMoved;
_topLevel.PointerEntered += TopLevel_PointerEnteredOrMoved;
_topLevel.PointerExited += TopLevel_PointerExited;
if (OperatingSystem.IsWindows())
{
InvisibleCursorWin = CreateEmptyCursor();
DefaultCursorWin = CreateArrowCursor();
_invisibleCursorWin = CreateEmptyCursor();
_defaultCursorWin = CreateArrowCursor();
}
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing;
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing;
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough;
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
_gpuCancellationTokenSource = new CancellationTokenSource();
_gpuDoneEvent = new ManualResetEvent(false);
}
private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e)
{
if (sender is MainWindow window)
{
_lastCursorMoveTime = Stopwatch.GetTimestamp();
if (_rendererHost.EmbeddedWindow.TransformedBounds != null)
{
var point = e.GetCurrentPoint(window).Position;
var bounds = _rendererHost.EmbeddedWindow.TransformedBounds.Value.Clip;
var point = e.GetCurrentPoint(window).Position;
var bounds = RendererHost.EmbeddedWindow.Bounds;
_isCursorInRenderer = point.X >= bounds.X &&
point.X <= bounds.Width + bounds.X &&
point.Y >= bounds.Y &&
point.Y <= bounds.Height + bounds.Y;
}
_isCursorInRenderer = point.X >= bounds.X &&
point.X <= bounds.Width + bounds.X &&
point.Y >= bounds.Y &&
point.Y <= bounds.Height + bounds.Y;
}
}
private void TopLevel_PointerLeave(object sender, PointerEventArgs e)
private void TopLevel_PointerExited(object sender, PointerEventArgs e)
{
_isCursorInRenderer = false;
}
@@ -220,12 +223,17 @@ namespace Ryujinx.Ava
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
}
private void UpdateScalingFilter(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.ScalingFilter> e)
private void UpdateScalingFilter(object sender, ReactiveEventArgs<ScalingFilter> e)
{
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
}
private void UpdateColorSpacePassthrough(object sender, ReactiveEventArgs<bool> e)
{
_renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value);
}
private void ShowCursor()
{
Dispatcher.UIThread.Post(() =>
@@ -234,7 +242,7 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows())
{
SetCursor(DefaultCursorWin);
SetCursor(_defaultCursorWin);
}
});
}
@@ -243,11 +251,11 @@ namespace Ryujinx.Ava
{
Dispatcher.UIThread.Post(() =>
{
_viewModel.Cursor = InvisibleCursor;
_viewModel.Cursor = _invisibleCursor;
if (OperatingSystem.IsWindows())
{
SetCursor(InvisibleCursorWin);
SetCursor(_invisibleCursorWin);
}
});
}
@@ -256,7 +264,7 @@ namespace Ryujinx.Ava
{
if (_renderer != null)
{
double scale = _topLevel.PlatformImpl.RenderScaling;
double scale = _topLevel.RenderScaling;
_renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale));
}
@@ -271,12 +279,12 @@ namespace Ryujinx.Ava
lock (_lockObject)
{
DateTime currentTime = DateTime.Now;
string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
string directory = AppDataManager.Mode switch
{
AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"),
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx")
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"),
};
string path = Path.Combine(directory, filename);
@@ -305,9 +313,9 @@ namespace Ryujinx.Ava
image.Mutate(x => x.Flip(FlipMode.Vertical));
}
image.SaveAsPng(path, new PngEncoder()
image.SaveAsPng(path, new PngEncoder
{
ColorType = PngColorType.Rgb
ColorType = PngColorType.Rgb,
});
image.Dispose();
@@ -336,21 +344,21 @@ namespace Ryujinx.Ava
_viewModel.IsGameRunning = true;
var activeProcess = Device.Processes.ActiveApplication;
var activeProcess = Device.Processes.ActiveApplication;
string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}";
string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}";
string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}";
string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})";
string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)";
string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})";
string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)";
Dispatcher.UIThread.InvokeAsync(() =>
{
_viewModel.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
});
_viewModel.SetUIProgressHandlers(Device);
_viewModel.SetUiProgressHandlers(Device);
_rendererHost.SizeChanged += Window_SizeChanged;
RendererHost.BoundsChanged += Window_BoundsChanged;
_isActive = true;
@@ -379,7 +387,7 @@ namespace Ryujinx.Ava
}
}
private void UpdateAntiAliasing(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.AntiAliasing> e)
private void UpdateAntiAliasing(object sender, ReactiveEventArgs<AntiAliasing> e)
{
_renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue);
}
@@ -419,7 +427,7 @@ namespace Ryujinx.Ava
}
_isStopped = true;
_isActive = false;
_isActive = false;
}
public void DisposeContext()
@@ -448,20 +456,21 @@ namespace Ryujinx.Ava
{
if (Device.Processes != null)
{
_viewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText);
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText);
}
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough;
_topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved;
_topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved;
_topLevel.PointerLeave -= TopLevel_PointerLeave;
_topLevel.PointerMoved -= TopLevel_PointerEnteredOrMoved;
_topLevel.PointerEntered -= TopLevel_PointerEnteredOrMoved;
_topLevel.PointerExited -= TopLevel_PointerExited;
_gpuCancellationTokenSource.Cancel();
_gpuCancellationTokenSource.Dispose();
@@ -477,7 +486,7 @@ namespace Ryujinx.Ava
_windowsMultimediaTimerResolution = null;
}
if (_rendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow)
if (RendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow)
{
// Try to bind the OpenGL context before calling the shutdown event.
openGlWindow.MakeCurrent(false, false);
@@ -508,7 +517,7 @@ namespace Ryujinx.Ava
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError))
{
@@ -526,7 +535,7 @@ namespace Ryujinx.Ava
if (result != UserResult.Yes)
{
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose();
return false;
@@ -535,7 +544,7 @@ namespace Ryujinx.Ava
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
{
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose();
return false;
@@ -558,7 +567,7 @@ namespace Ryujinx.Ava
}
else
{
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose();
return false;
@@ -727,7 +736,7 @@ namespace Ryujinx.Ava
{
renderer = new VulkanRenderer(
Vk.GetApi(),
(_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface,
(RendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface,
VulkanHelper.GetRequiredInstanceExtensions,
ConfigurationState.Instance.Graphics.PreferredGpu.Value);
}
@@ -738,18 +747,18 @@ namespace Ryujinx.Ava
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
var isGALthreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
if (isGALthreaded)
var isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
if (isGALThreaded)
{
renderer = new ThreadedRenderer(renderer);
}
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALthreaded}");
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}");
// Initialize Configuration.
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GiB : HLE.MemoryConfiguration.MemoryConfiguration4GiB;
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB;
HLE.HLEConfiguration configuration = new(VirtualFileSystem,
HLEConfiguration configuration = new(VirtualFileSystem,
_viewModel.LibHacHorizonManager,
ContentManager,
_accountManager,
@@ -780,12 +789,12 @@ namespace Ryujinx.Ava
private static IHardwareDeviceDriver InitializeAudio()
{
var availableBackends = new List<AudioBackend>()
var availableBackends = new List<AudioBackend>
{
AudioBackend.SDL2,
AudioBackend.SoundIo,
AudioBackend.OpenAl,
AudioBackend.Dummy
AudioBackend.Dummy,
};
AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value;
@@ -806,12 +815,10 @@ namespace Ryujinx.Ava
{
return new T();
}
else
{
Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}.");
return null;
}
Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}.");
return null;
}
IHardwareDeviceDriver deviceDriver = null;
@@ -819,14 +826,14 @@ namespace Ryujinx.Ava
for (int i = 0; i < availableBackends.Count; i++)
{
AudioBackend currentBackend = availableBackends[i];
AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy;
AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy;
deviceDriver = currentBackend switch
{
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
_ => new DummyHardwareDeviceDriver()
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
_ => new DummyHardwareDeviceDriver(),
};
if (deviceDriver != null)
@@ -841,9 +848,9 @@ namespace Ryujinx.Ava
return deviceDriver;
}
private void Window_SizeChanged(object sender, Size e)
private void Window_BoundsChanged(object sender, Size e)
{
Width = (int)e.Width;
Width = (int)e.Width;
Height = (int)e.Height;
SetRendererWindowSize(e);
@@ -879,18 +886,19 @@ namespace Ryujinx.Ava
_renderer.ScreenCaptured += Renderer_ScreenCaptured;
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer);
(RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer);
Device.Gpu.Renderer.Initialize(_glLogLevel);
_renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value);
_renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
_renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
_renderer?.Window?.SetColorSpacePassthrough(ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value);
Width = (int)_rendererHost.Bounds.Width;
Height = (int)_rendererHost.Bounds.Height;
Width = (int)RendererHost.Bounds.Width;
Height = (int)RendererHost.Bounds.Height;
_renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling));
_renderer.Window.SetSize((int)(Width * _topLevel.RenderScaling), (int)(Height * _topLevel.RenderScaling));
_chrono.Start();
@@ -923,7 +931,7 @@ namespace Ryujinx.Ava
_viewModel.SwitchToRenderer(false);
}
Device.PresentFrame(() => (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers());
Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers());
}
if (_ticks >= _ticksPerFrame)
@@ -941,7 +949,7 @@ namespace Ryujinx.Ava
_gpuDoneEvent.Set();
});
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true);
(RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true);
}
public void UpdateStatus()
@@ -1175,4 +1183,4 @@ namespace Ryujinx.Ava
return state;
}
}
}
}

View File

@@ -620,6 +620,8 @@
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
"SettingsEnableMacroHLE": "Enable Macro HLE",
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.",
"SettingsEnableColorSpacePassthrough": "Color Space Passthrough",
"SettingsEnableColorSpacePassthroughTooltip": "Directs the Vulkan backend to pass through color information without specifying a color space. For users with wide gamut displays, this may result in more vibrant colors, at the cost of color correctness.",
"VolumeShort": "Vol",
"UserProfilesManageSaves": "Manage Saves",
"DeleteUserSave": "Do you want to delete user save for this game?",

View File

@@ -1,65 +0,0 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StyleInclude Source="avares://Ryujinx.Ava/Assets/Styles/Styles.xaml" />
<Design.PreviewWith>
<Border Height="2000" Padding="20">
<StackPanel Spacing="5">
<TextBlock Text="Code Font Family" />
<Grid RowDefinitions="*,Auto">
<Menu Grid.Row="1" Width="100">
<MenuItem Header="File">
<MenuItem Header="Test 1" />
<MenuItem Header="Test 2" />
<MenuItem Header="Test 3">
<MenuItem.Icon>
<CheckBox Margin="0" IsChecked="{Binding Checkbox, Mode=TwoWay}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
<StackPanel Orientation="Horizontal">
<Button
Name="btnAdd"
HorizontalAlignment="Right"
Content="Add" />
<Button
Name="btnRem"
HorizontalAlignment="Right"
Content="Add" />
<TextBox
Width="100"
VerticalAlignment="Center"
Text="Rrrrr"
UseFloatingWatermark="True"
Watermark="Hello" />
<CheckBox>Test Check</CheckBox>
</StackPanel>
</Grid>
</StackPanel>
</Border>
</Design.PreviewWith>
<Styles.Resources>
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" />
<SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" />
<Color x:Key="ControlFillColorSecondary">#008AA8</Color>
<SolidColorBrush x:Key="ControlFillColorSecondaryBrush" Color="{StaticResource ControlFillColorSecondary}" />
<StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="ControlFillColorSecondaryBrush" />
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark1">#FF99b000</Color>
<Color x:Key="SystemAccentColorDark2">#FF006d7d</Color>
<Color x:Key="SystemAccentColorDark3">#FF00525E</Color>
<Color x:Key="SystemAccentColorLight1">#FF00dbff</Color>
<Color x:Key="SystemAccentColorLight2">#FF19dfff</Color>
<Color x:Key="SystemAccentColorLight3">#FF33e3ff</Color>
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
<Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
<Color x:Key="ThemeControlBorderColor">#FF505050</Color>
<Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
</Styles.Resources>
</Styles>

View File

@@ -1,57 +0,0 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StyleInclude Source="avares://Ryujinx.Ava/Assets/Styles/Styles.xaml" />
<Design.PreviewWith>
<Border Height="2000" Padding="20">
<StackPanel Spacing="5">
<TextBlock Text="Code Font Family" />
<Grid RowDefinitions="*,Auto">
<Menu Grid.Row="1" Width="100">
<MenuItem Header="File">
<MenuItem Header="Test 1" />
<MenuItem Header="Test 2" />
<MenuItem Header="Test 3">
<MenuItem.Icon>
<CheckBox Margin="0" IsChecked="{Binding Checkbox, Mode=TwoWay}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
<StackPanel Orientation="Horizontal">
<Button
Name="btnAdd"
HorizontalAlignment="Right"
Content="Add" />
<Button
Name="btnRem"
HorizontalAlignment="Right"
Content="Add" />
<TextBox
Width="100"
VerticalAlignment="Center"
Text="Rrrrr"
UseFloatingWatermark="True"
Watermark="Hello" />
<CheckBox>Test Check</CheckBox>
</StackPanel>
</Grid>
</StackPanel>
</Border>
</Design.PreviewWith>
<Styles.Resources>
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" />
<SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" />
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
<Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color>
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
<Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color>
<Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color>
<Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FF000000</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
<Color x:Key="SecondaryTextColor">#A0000000</Color>
</Styles.Resources>
</Styles>

View File

@@ -3,17 +3,20 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
<Design.PreviewWith>
<Border Height="2000" Padding="20">
<Border Height="2000"
Padding="20">
<StackPanel Spacing="5">
<TextBlock Text="Code Font Family" />
<Grid RowDefinitions="*,Auto">
<Menu Grid.Row="1" Width="100">
<Menu Grid.Row="1"
Width="100">
<MenuItem Header="File">
<MenuItem Header="Test 1" />
<MenuItem Header="Test 2" />
<MenuItem Header="Test 3">
<MenuItem.Icon>
<CheckBox Margin="0" IsChecked="{Binding Checkbox, Mode=TwoWay}" />
<CheckBox Margin="0"
IsChecked="{ReflectionBinding Checkbox, Mode=TwoWay}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
@@ -42,57 +45,80 @@
</Border>
</Design.PreviewWith>
<Style Selector="Border.small">
<Setter Property="Width" Value="100" />
<Setter Property="Width"
Value="100" />
</Style>
<Style Selector="Border.normal">
<Setter Property="Width" Value="130" />
<Setter Property="Width"
Value="130" />
</Style>
<Style Selector="Border.large">
<Setter Property="Width" Value="160" />
<Setter Property="Width"
Value="160" />
</Style>
<Style Selector="Border.huge">
<Setter Property="Width" Value="200" />
<Setter Property="Width"
Value="200" />
</Style>
<Style Selector="Border.settings">
<Setter Property="Background" Value="{DynamicResource ThemeDarkColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderColor}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="5" />
<Setter Property="Background"
Value="{DynamicResource ThemeDarkColor}" />
<Setter Property="BorderBrush"
Value="{DynamicResource MenuFlyoutPresenterBorderColor}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="CornerRadius"
Value="5" />
</Style>
<Style Selector="Image.small">
<Setter Property="Width" Value="50" />
<Setter Property="Width"
Value="50" />
</Style>
<Style Selector="Image.normal">
<Setter Property="Width" Value="80" />
<Setter Property="Width"
Value="80" />
</Style>
<Style Selector="Image.large">
<Setter Property="Width" Value="100" />
<Setter Property="Width"
Value="100" />
</Style>
<Style Selector="Image.huge">
<Setter Property="Width" Value="120" />
<Setter Property="Width"
Value="120" />
</Style>
<Style Selector="#TitleBarHost &gt; Image">
<Setter Property="Margin" Value="10" />
<Setter Property="Margin"
Value="10" />
</Style>
<Style Selector="#TitleBarHost &gt; Label">
<Setter Property="Margin" Value="5" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Margin"
Value="5" />
<Setter Property="FontSize"
Value="14" />
</Style>
<Style Selector="Button.SystemCaption">
<Setter Property="MinWidth" Value="10" />
<Setter Property="MinWidth"
Value="10" />
</Style>
<Style Selector="DataGridColumnHeader">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="SeparatorBrush" Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="Padding" Value="5" />
<Setter Property="Background" Value="{DynamicResource ThemeContentBackgroundColor}" />
<Setter Property="Foreground"
Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="SeparatorBrush"
Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="Padding"
Value="5" />
<Setter Property="Background"
Value="{DynamicResource ThemeContentBackgroundColor}" />
<Setter Property="Template">
<ControlTemplate>
<Grid Background="{TemplateBinding Background}" ColumnDefinitions="*,Auto">
<Grid Background="{TemplateBinding Background}"
ColumnDefinitions="*,Auto">
<Grid
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
@@ -122,193 +148,262 @@
</Setter>
</Style>
<Style Selector="DataGrid">
<Setter Property="RowBackground" Value="{DynamicResource ThemeAccentBrush4}" />
<Setter Property="AlternatingRowBackground" Value="#00FFFFFF" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowColor}" />
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" />
<Setter Property="RowBackground"
Value="{DynamicResource ThemeAccentBrush4}" />
<Setter Property="Background"
Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource ThemeBorderLowColor}" />
<Setter Property="BorderThickness"
Value="{DynamicResource ThemeBorderThickness}" />
</Style>
<Style Selector="DataGridRow:selected:focus /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
<Setter Property="Fill"
Value="{DynamicResource SystemAccentColor}" />
<Setter Property="Opacity"
Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource SystemListLowColor}" />
<Setter Property="Fill"
Value="{DynamicResource SystemListLowColor}" />
</Style>
<Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
<Setter Property="Fill"
Value="{DynamicResource SystemAccentColor}" />
<Setter Property="Opacity"
Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRow:selected:pointerover /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
<Setter Property="Fill"
Value="{DynamicResource SystemAccentColor}" />
<Setter Property="Opacity"
Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRow:selected:pointerover:focus /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
<Setter Property="Fill"
Value="{DynamicResource SystemAccentColor}" />
<Setter Property="Opacity"
Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
</Style>
<Style Selector="DataGridCell">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment"
Value="Center" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
</Style>
<Style Selector="DataGridCell.Left">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="HorizontalAlignment"
Value="Left" />
</Style>
<Style Selector="CheckBox">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderThickness"
Value="1" />
</Style>
<Style Selector="MenuItem">
<Setter Property="Height" Value="{DynamicResource MenuItemHeight}" />
<Setter Property="Padding" Value="{DynamicResource MenuItemPadding}" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Height"
Value="{DynamicResource MenuItemHeight}" />
<Setter Property="Padding"
Value="{DynamicResource MenuItemPadding}" />
<Setter Property="FontSize"
Value="12" />
</Style>
<Style Selector="MenuItem:selected /template/ Border#root">
<Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="Background"
Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="BorderBrush"
Value="{DynamicResource ThemeControlBorderColor}" />
</Style>
<Style Selector="TabItem > ScrollViewer">
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundColor}" />
<Setter Property="Margin" Value="0,-5,0,0" />
<Setter Property="Background"
Value="{DynamicResource ThemeBackgroundColor}" />
<Setter Property="Margin"
Value="0,-5,0,0" />
</Style>
<Style Selector="TabItem > ScrollViewer > Border">
<Setter Property="BorderThickness" Value="0,1,0,0" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource HighlightBrush}" />
<Setter Property="BorderThickness"
Value="0,1,0,0" />
<Setter Property="Background"
Value="{DynamicResource ThemeBackgroundColor}" />
<Setter Property="BorderBrush"
Value="{DynamicResource HighlightBrush}" />
</Style>
<Style Selector="Button">
<Setter Property="MinWidth" Value="80" />
<Setter Property="MinWidth"
Value="80" />
</Style>
<Style Selector="ProgressBar /template/ Border#ProgressBarTrack">
<Setter Property="IsVisible" Value="False" />
<Setter Property="IsVisible"
Value="False" />
</Style>
<Style Selector="ToggleButton">
<Setter Property="Padding" Value="0,-5,0,0" />
<Setter Property="Padding"
Value="0,-5,0,0" />
</Style>
<Style Selector="TabItem">
<Setter Property="FontSize" Value="14" />
<Setter Property="BorderThickness" Value="0,0,1,0" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeButtonForegroundColor}" />
<Setter Property="Background" Value="{DynamicResource HighlightColor}" />
<Setter Property="FontSize"
Value="14" />
<Setter Property="BorderThickness"
Value="0,0,1,0" />
<Setter Property="BorderBrush"
Value="{DynamicResource ThemeButtonForegroundColor}" />
<Setter Property="Background"
Value="{DynamicResource SystemAccentColorLight2}" />
</Style>
<Style Selector="TabItem:pointerover">
<Setter Property="Foreground" Value="{DynamicResource ThemeButtonForegroundColor}" />
<Setter Property="Foreground"
Value="{DynamicResource ThemeButtonForegroundColor}" />
</Style>
<Style Selector="TabItem:selected">
<Setter Property="Background" Value="{DynamicResource HighlightColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeBackgroundColor}" />
<Setter Property="Background"
Value="{DynamicResource SystemAccentColorLight2}" />
<Setter Property="Foreground"
Value="{DynamicResource ThemeBackgroundColor}" />
</Style>
<Style Selector="TextBlock">
<Setter Property="Margin" Value="{DynamicResource TextMargin}" />
<Setter Property="FontSize" Value="{DynamicResource FontSize}" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
<Setter Property="Margin"
Value="{DynamicResource TextMargin}" />
<Setter Property="FontSize"
Value="{DynamicResource FontSize}" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="TextWrapping"
Value="WrapWithOverflow" />
</Style>
<Style Selector="TextBlock.h1">
<Setter Property="Margin" Value="{DynamicResource TextMargin}" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontSize" Value="16" />
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
<Setter Property="Margin"
Value="{DynamicResource TextMargin}" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="FontWeight"
Value="Bold" />
<Setter Property="FontSize"
Value="16" />
<Setter Property="TextWrapping"
Value="WrapWithOverflow" />
</Style>
<Style Selector="Separator">
<Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="MinHeight" Value="1" />
<Setter Property="Background"
Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="Foreground"
Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="MinHeight"
Value="1" />
</Style>
<Style Selector=":is(Button).DateTimeFlyoutButtonStyle">
<Setter Property="Background" Value="{DynamicResource HighlightColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeBackgroundColor}" />
<Setter Property="Background"
Value="{DynamicResource SystemAccentColorLight2}" />
<Setter Property="Foreground"
Value="{DynamicResource ThemeBackgroundColor}" />
</Style>
<Style Selector="DatePickerPresenter">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeButtonForegroundColor}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="BorderBrush"
Value="{DynamicResource ThemeButtonForegroundColor}" />
</Style>
<Style Selector="DataGridCell">
<Setter Property="FontSize" Value="14" />
<Setter Property="FontSize"
Value="14" />
</Style>
<Style Selector="CheckBox TextBlock">
<Setter Property="Margin" Value="0,5,0,0" />
<Setter Property="Margin"
Value="0,5,0,0" />
</Style>
<Style Selector="ContextMenu">
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource MenuFlyoutPresenterBorderThemeThickness}" />
<Setter Property="BorderBrush"
Value="{DynamicResource MenuFlyoutPresenterBorderBrush}" />
<Setter Property="BorderThickness"
Value="{DynamicResource MenuFlyoutPresenterBorderThemeThickness}" />
</Style>
<Style Selector="TextBox">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
</Style>
<Style Selector="TextBox.NumberBoxTextBoxStyle">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
<Setter Property="Foreground"
Value="{DynamicResource ThemeForegroundColor}" />
</Style>
<Style Selector="ListBox ListBoxItem">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="CornerRadius" Value="5" />
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Padding"
Value="0" />
<Setter Property="Margin"
Value="0" />
<Setter Property="CornerRadius"
Value="5" />
<Setter Property="Background"
Value="{DynamicResource AppListBackgroundColor}" />
<Setter Property="BorderThickness"
Value="2"/>
<Style.Animations>
<Animation Duration="0:0:0.7">
<KeyFrame Cue="0%">
<Setter Property="MaxHeight" Value="0" />
<Setter Property="Opacity" Value="0.0" />
<Setter Property="MaxHeight"
Value="0" />
<Setter Property="Opacity"
Value="0.0" />
</KeyFrame>
<KeyFrame Cue="50%">
<Setter Property="MaxHeight" Value="1000" />
<Setter Property="Opacity" Value="0.3" />
<Setter Property="MaxHeight"
Value="1000" />
<Setter Property="Opacity"
Value="0.3" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="MaxHeight" Value="1000" />
<Setter Property="Opacity" Value="1.0" />
<Setter Property="MaxHeight"
Value="1000" />
<Setter Property="Opacity"
Value="1.0" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="ListBox ListBoxItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
<Setter Property="Background"
Value="{DynamicResource AppListBackgroundColor}" />
</Style>
<Style Selector="ListBox">
<Setter Property="Background"
Value="{DynamicResource ThemeContentBackgroundColor}" />
</Style>
<Style Selector="FlyoutPresenter, ContextMenu, MenuFlyoutPresenter">
<Setter Property="BorderBrush"
Value="{DynamicResource MenuFlyoutPresenterBorderColor}" />
</Style>
<Style Selector="ListBox ListBoxItem:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
<Setter Property="Background"
Value="{DynamicResource AppListHoverBackgroundColor}" />
</Style>
<Styles.Resources>
<SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" />
<StaticResource x:Key="ListViewItemBackgroundSelected" ResourceKey="ThemeAccentColorBrush" />
<StaticResource x:Key="ListViewItemBackgroundPressed" ResourceKey="SystemAccentColorDark1" />
<StaticResource x:Key="ListViewItemBackgroundPointerOver" ResourceKey="SystemAccentColorDark2" />
<StaticResource x:Key="ListViewItemBackgroundSelectedPressed" ResourceKey="ThemeAccentColorBrush" />
<StaticResource x:Key="ListViewItemBackgroundSelectedPointerOver" ResourceKey="SystemAccentColorDark2" />
<SolidColorBrush x:Key="ThemeAccentColorBrush"
Color="{DynamicResource SystemAccentColor}" />
<StaticResource x:Key="ListViewItemBackgroundSelected"
ResourceKey="ThemeAccentColorBrush" />
<StaticResource x:Key="ListViewItemBackgroundPressed"
ResourceKey="SystemAccentColorDark1" />
<StaticResource x:Key="ListViewItemBackgroundPointerOver"
ResourceKey="SystemAccentColorDark2" />
<StaticResource x:Key="ListViewItemBackgroundSelectedPressed"
ResourceKey="ThemeAccentColorBrush" />
<StaticResource x:Key="ListViewItemBackgroundSelectedPointerOver"
ResourceKey="SystemAccentColorDark2" />
<SolidColorBrush
x:Key="DataGridGridLinesBrush"
Opacity="0.4"
Color="{DynamicResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" />
<SolidColorBrush x:Key="MenuFlyoutPresenterBorderBrush" Color="{DynamicResource MenuFlyoutPresenterBorderColor}" />
<SolidColorBrush x:Key="FlyoutBorderThemeBrush" Color="{DynamicResource MenuFlyoutPresenterBorderColor}" />
<SolidColorBrush x:Key="ListBoxBackground" Color="{DynamicResource ThemeContentBackgroundColor}" />
<SolidColorBrush x:Key="ThemeForegroundBrush" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" />
<SolidColorBrush x:Key="SplitButtonBackgroundChecked" Color="#00E81123" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver" Color="#00E81123" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed" Color="#00E81123" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedDisabled" Color="#00E81123" />
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
Color="{DynamicResource DataGridSelectionColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundChecked"
Color="#00E81123" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver"
Color="#00E81123" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed"
Color="#00E81123" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedDisabled"
Color="#00E81123" />
<Thickness x:Key="PageMargin">40 0 40 0</Thickness>
<Thickness x:Key="Margin">0 5 0 5</Thickness>
<Thickness x:Key="MenuItemPadding">5 0 5 0</Thickness>
<Color x:Key="MenuFlyoutPresenterBorderColor">#00000000</Color>
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color>
<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>
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
<Color x:Key="SecondaryTextColor">#A0FFFFFF</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>

View File

@@ -0,0 +1,85 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
Color="{DynamicResource DataGridSelectionColor}" />
<SolidColorBrush x:Key="ThemeAccentColorBrush"
Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="ThemeAccentBrush4"
Color="{DynamicResource ThemeAccentColor4}" />
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color>
<Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color>
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
<Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color>
<Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color>
<Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FF000000</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
<Color x:Key="SecondaryTextColor">#A0000000</Color>
<Color x:Key="VsyncEnabled">#FF2EEAC9</Color>
<Color x:Key="VsyncDisabled">#FFFF4554</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
Color="{DynamicResource DataGridSelectionColor}" />
<SolidColorBrush x:Key="ThemeAccentColorBrush"
Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="ThemeAccentBrush4"
Color="{DynamicResource ThemeAccentColor4}" />
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color>
<Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color>
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
<Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color>
<Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color>
<Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FF000000</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
<Color x:Key="SecondaryTextColor">#A0000000</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
Color="{DynamicResource DataGridSelectionColor}" />
<SolidColorBrush x:Key="ThemeAccentColorBrush"
Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="ThemeAccentBrush4"
Color="{DynamicResource ThemeAccentColor4}" />
<Color x:Key="ControlFillColorSecondary">#008AA8</Color>
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
<Color x:Key="SystemAccentColorDark1">#FF99b000</Color>
<Color x:Key="SystemAccentColorDark2">#FF006d7d</Color>
<Color x:Key="SystemAccentColorDark3">#FF00525E</Color>
<Color x:Key="SystemAccentColorLight1">#FF00dbff</Color>
<Color x:Key="SystemAccentColorLight2">#FF19dfff</Color>
<Color x:Key="SystemAccentColorLight3">#FF33e3ff</Color>
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
<Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
<Color x:Key="ThemeControlBorderColor">#FF505050</Color>
<Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -1,5 +1,5 @@
using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using LibHac;
using LibHac.Account;
@@ -18,7 +18,6 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common.Helper;
@@ -27,6 +26,7 @@ using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ApplicationId = LibHac.Ncm.ApplicationId;
using Path = System.IO.Path;
namespace Ryujinx.Ava.Common
@@ -57,7 +57,7 @@ namespace Ryujinx.Ava.Common
Logger.Info?.Print(LogClass.Application, $"Creating save directory for Title: {titleName} [{titleId:x16}]");
if (Utilities.IsZeros(controlHolder.ByteSpan))
if (controlHolder.ByteSpan.IsZeros())
{
// If the current application doesn't have a loaded control property, create a dummy one
// and set the savedata sizes so a user savedata will be created.
@@ -72,7 +72,7 @@ namespace Ryujinx.Ava.Common
Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user);
result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new ApplicationId(titleId), in control, in user);
if (result.IsFailure())
{
Dispatcher.UIThread.InvokeAsync(async () =>
@@ -114,7 +114,7 @@ namespace Ryujinx.Ava.Common
public static void OpenSaveDir(ulong saveDataId)
{
string saveRootPath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}");
string saveRootPath = Path.Combine(VirtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}");
if (!Directory.Exists(saveRootPath))
{
@@ -143,162 +143,166 @@ namespace Ryujinx.Ava.Common
}
}
public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
{
OpenFolderDialog folderDialog = new()
var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
};
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
AllowMultiple = false
});
string destination = await folderDialog.ShowAsync(_owner);
var cancellationToken = new CancellationTokenSource();
if (result.Count == 0)
{
return;
}
var destination = result[0].Path.LocalPath;
var cancellationToken = new CancellationTokenSource();
UpdateWaitWindow waitingDialog = new(
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
cancellationToken);
if (!string.IsNullOrWhiteSpace(destination))
Thread extractorThread = new(() =>
{
Thread extractorThread = new(() =>
Dispatcher.UIThread.Post(waitingDialog.Show);
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
Nca mainNca = null;
Nca patchNca = null;
string extension = Path.GetExtension(titleFilePath).ToLower();
if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
{
Dispatcher.UIThread.Post(waitingDialog.Show);
PartitionFileSystem pfs;
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
Nca mainNca = null;
Nca patchNca = null;
string extension = Path.GetExtension(titleFilePath).ToLower();
if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
if (extension == ".xci")
{
PartitionFileSystem pfs;
pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
}
else
{
pfs = new PartitionFileSystem(file.AsStorage());
}
if (extension == ".xci")
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
if (nca.Header.ContentType == NcaContentType.Program)
{
pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
}
else
{
pfs = new PartitionFileSystem(file.AsStorage());
}
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
if (nca.Header.ContentType == NcaContentType.Program)
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{
patchNca = nca;
}
else
{
mainNca = nca;
}
patchNca = nca;
}
else
{
mainNca = nca;
}
}
}
else if (extension == ".nca")
}
else if (extension == ".nca")
{
mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
}
if (mainNca == null)
{
Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");
Dispatcher.UIThread.InvokeAsync(async () =>
{
mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
waitingDialog.Close();
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
});
return;
}
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
if (updatePatchNca != null)
{
patchNca = updatePatchNca;
}
int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType);
try
{
bool sectionExistsInPatch = false;
if (patchNca != null)
{
sectionExistsInPatch = patchNca.CanOpenSection(index);
}
if (mainNca == null)
IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
FileSystemClient fsClient = _horizonClient.Fs;
string source = DateTime.Now.ToFileTime().ToString()[10..];
string output = DateTime.Now.ToFileTime().ToString()[10..];
using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref);
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref);
(Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token);
if (!canceled)
{
Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");
Dispatcher.UIThread.InvokeAsync(async () =>
if (resultCode.Value.IsFailure())
{
waitingDialog.Close();
Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}");
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
});
return;
}
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
if (updatePatchNca != null)
{
patchNca = updatePatchNca;
}
int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType);
try
{
bool sectionExistsInPatch = false;
if (patchNca != null)
{
sectionExistsInPatch = patchNca.CanOpenSection(index);
}
IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
FileSystemClient fsClient = _horizonClient.Fs;
string source = DateTime.Now.ToFileTime().ToString()[10..];
string output = DateTime.Now.ToFileTime().ToString()[10..];
using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref);
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref);
(Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token);
if (!canceled)
{
if (resultCode.Value.IsFailure())
Dispatcher.UIThread.InvokeAsync(async () =>
{
Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}");
waitingDialog.Close();
Dispatcher.UIThread.InvokeAsync(async () =>
{
waitingDialog.Close();
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
});
}
else if (resultCode.Value.IsSuccess())
{
Dispatcher.UIThread.Post(waitingDialog.Close);
NotificationHelper.Show(
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
NotificationType.Information);
}
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
});
}
fsClient.Unmount(source.ToU8Span());
fsClient.Unmount(output.ToU8Span());
}
catch (ArgumentException ex)
{
Logger.Error?.Print(LogClass.Application, $"{ex.Message}");
Dispatcher.UIThread.InvokeAsync(async () =>
else if (resultCode.Value.IsSuccess())
{
waitingDialog.Close();
Dispatcher.UIThread.Post(waitingDialog.Close);
await ContentDialogHelper.CreateErrorDialog(ex.Message);
});
NotificationHelper.Show(
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
NotificationType.Information);
}
}
});
extractorThread.Name = "GUI.NcaSectionExtractorThread";
extractorThread.IsBackground = true;
extractorThread.Start();
}
fsClient.Unmount(source.ToU8Span());
fsClient.Unmount(output.ToU8Span());
}
catch (ArgumentException ex)
{
Logger.Error?.Print(LogClass.Application, $"{ex.Message}");
Dispatcher.UIThread.InvokeAsync(async () =>
{
waitingDialog.Close();
await ContentDialogHelper.CreateErrorDialog(ex.Message);
});
}
})
{
Name = "GUI.NcaSectionExtractorThread",
IsBackground = true,
};
extractorThread.Start();
}
public static (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath, CancellationToken token)
@@ -413,4 +417,4 @@ namespace Ryujinx.Ava.Common
return Result.Success;
}
}
}
}

View File

@@ -10,6 +10,6 @@ namespace Ryujinx.Ava.Common
FileType,
FileSize,
Path,
Favorite
Favorite,
}
}
}

View File

@@ -11,6 +11,6 @@
ResScaleUp,
ResScaleDown,
VolumeUp,
VolumeDown
VolumeDown,
}
}
}

View File

@@ -20,11 +20,11 @@ namespace Ryujinx.Ava.Common.Locale
ReflectionBindingExtension binding = new($"[{keyToUse}]")
{
Mode = BindingMode.OneWay,
Source = LocaleManager.Instance
Mode = BindingMode.OneWay,
Source = LocaleManager.Instance,
};
return binding.ProvideValue(serviceProvider);
}
}
}
}

View File

@@ -13,17 +13,17 @@ namespace Ryujinx.Ava.Common.Locale
{
private const string DefaultLanguageCode = "en_US";
private Dictionary<LocaleKeys, string> _localeStrings;
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
private readonly Dictionary<LocaleKeys, string> _localeStrings;
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
public static LocaleManager Instance { get; } = new LocaleManager();
public static LocaleManager Instance { get; } = new();
public LocaleManager()
{
_localeStrings = new Dictionary<LocaleKeys, string>();
_localeStrings = new Dictionary<LocaleKeys, string>();
_localeDefaultStrings = new Dictionary<LocaleKeys, string>();
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
Load();
}
@@ -126,11 +126,11 @@ namespace Ryujinx.Ava.Common.Locale
}
}
private Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)
{
var localeStrings = new Dictionary<LocaleKeys, string>();
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
var localeStrings = new Dictionary<LocaleKeys, string>();
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
foreach (var item in strings)
{
@@ -143,4 +143,4 @@ namespace Ryujinx.Ava.Common.Locale
return localeStrings;
}
}
}
}

View File

@@ -12,25 +12,25 @@ namespace Ryujinx.Ava.Input
internal class AvaloniaKeyboard : IKeyboard
{
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
private readonly AvaloniaKeyboardDriver _driver;
private StandardKeyboardInputConfig _configuration;
private readonly AvaloniaKeyboardDriver _driver;
private StandardKeyboardInputConfig _configuration;
private readonly object _userMappingLock = new();
public string Id { get; }
public string Id { get; }
public string Name { get; }
public bool IsConnected => true;
public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
public bool IsConnected => true;
public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
private class ButtonMappingEntry
{
public readonly Key From;
public readonly Key From;
public readonly GamepadButtonInputId To;
public ButtonMappingEntry(GamepadButtonInputId to, Key from)
{
To = to;
To = to;
From = from;
}
}
@@ -40,8 +40,8 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping = new List<ButtonMappingEntry>();
_driver = driver;
Id = id;
Name = name;
Id = id;
Name = name;
}
public KeyboardStateSnapshot GetKeyboardStateSnapshot()
@@ -52,7 +52,7 @@ namespace Ryujinx.Ava.Input
public GamepadStateSnapshot GetMappedStateSnapshot()
{
KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot();
GamepadStateSnapshot result = default;
GamepadStateSnapshot result = default;
lock (_userMappingLock)
{
@@ -75,10 +75,10 @@ namespace Ryujinx.Ava.Input
}
}
(short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick);
(short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick);
(short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick);
result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY));
result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY));
result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY));
}
@@ -120,6 +120,7 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Clear();
#pragma warning disable IDE0055 // Disable formatting
// Left JoyCon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp));
@@ -143,6 +144,7 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl));
#pragma warning restore IDE0055
}
}
@@ -198,4 +200,4 @@ namespace Ryujinx.Ava.Input
public void Dispose() { }
}
}
}

View File

@@ -13,25 +13,24 @@ namespace Ryujinx.Ava.Input
internal class AvaloniaKeyboardDriver : IGamepadDriver
{
private static readonly string[] _keyboardIdentifers = new string[1] { "0" };
private readonly Control _control;
private readonly Control _control;
private readonly HashSet<AvaKey> _pressedKeys;
public event EventHandler<KeyEventArgs> KeyPressed;
public event EventHandler<KeyEventArgs> KeyRelease;
public event EventHandler<string> TextInput;
public event EventHandler<string> TextInput;
public string DriverName => "AvaloniaKeyboardDriver";
public string DriverName => "AvaloniaKeyboardDriver";
public ReadOnlySpan<string> GamepadsIds => _keyboardIdentifers;
public AvaloniaKeyboardDriver(Control control)
{
_control = control;
_control = control;
_pressedKeys = new HashSet<AvaKey>();
_control.KeyDown += OnKeyPress;
_control.KeyUp += OnKeyRelease;
_control.KeyDown += OnKeyPress;
_control.KeyUp += OnKeyRelease;
_control.TextInput += Control_TextInput;
_control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble);
}
private void Control_TextInput(object sender, TextInputEventArgs e)
@@ -39,21 +38,15 @@ namespace Ryujinx.Ava.Input
TextInput?.Invoke(this, e.Text);
}
private void Control_LastChanceTextInput(object sender, TextInputEventArgs e)
{
// Swallow event
e.Handled = true;
}
public event Action<string> OnGamepadConnected
{
add { }
add { }
remove { }
}
public event Action<string> OnGamepadDisconnected
{
add { }
add { }
remove { }
}
@@ -71,7 +64,7 @@ namespace Ryujinx.Ava.Input
{
if (disposing)
{
_control.KeyUp -= OnKeyPress;
_control.KeyUp -= OnKeyPress;
_control.KeyDown -= OnKeyRelease;
}
}
@@ -112,4 +105,4 @@ namespace Ryujinx.Ava.Input
Dispose(true);
}
}
}
}

View File

@@ -143,7 +143,7 @@ namespace Ryujinx.Ava.Input
AvaKey.OemBackslash,
// NOTE: invalid
AvaKey.None
AvaKey.None,
};
private static readonly Dictionary<AvaKey, Key> _avaKeyMapping;
@@ -182,4 +182,4 @@ namespace Ryujinx.Ava.Input
return _avaKeyMapping.GetValueOrDefault(key, Key.Unknown);
}
}
}
}

View File

@@ -10,12 +10,12 @@ namespace Ryujinx.Ava.Input
{
private AvaloniaMouseDriver _driver;
public string Id => "0";
public string Id => "0";
public string Name => "AvaloniaMouse";
public bool IsConnected => true;
public GamepadFeaturesFlag Features => throw new NotImplementedException();
public bool[] Buttons => _driver.PressedButtons;
public bool IsConnected => true;
public GamepadFeaturesFlag Features => throw new NotImplementedException();
public bool[] Buttons => _driver.PressedButtons;
public AvaloniaMouse(AvaloniaMouseDriver driver)
{
@@ -84,4 +84,4 @@ namespace Ryujinx.Ava.Input
_driver = null;
}
}
}
}

View File

@@ -11,16 +11,16 @@ namespace Ryujinx.Ava.Input
{
internal class AvaloniaMouseDriver : IGamepadDriver
{
private Control _widget;
private bool _isDisposed;
private Size _size;
private Control _widget;
private bool _isDisposed;
private Size _size;
private readonly TopLevel _window;
public bool[] PressedButtons { get; }
public bool[] PressedButtons { get; }
public Vector2 CurrentPosition { get; private set; }
public Vector2 Scroll { get; private set; }
public Vector2 Scroll { get; private set; }
public string DriverName => "AvaloniaMouseDriver";
public string DriverName => "AvaloniaMouseDriver";
public ReadOnlySpan<string> GamepadsIds => new[] { "0" };
public AvaloniaMouseDriver(TopLevel window, Control parent)
@@ -28,14 +28,14 @@ namespace Ryujinx.Ava.Input
_widget = parent;
_window = window;
_widget.PointerMoved += Parent_PointerMovedEvent;
_widget.PointerPressed += Parent_PointerPressedEvent;
_widget.PointerReleased += Parent_PointerReleasedEvent;
_widget.PointerMoved += Parent_PointerMovedEvent;
_widget.PointerPressed += Parent_PointerPressedEvent;
_widget.PointerReleased += Parent_PointerReleasedEvent;
_widget.PointerWheelChanged += Parent_PointerWheelChanged;
_window.PointerMoved += Parent_PointerMovedEvent;
_window.PointerPressed += Parent_PointerPressedEvent;
_window.PointerReleased += Parent_PointerReleasedEvent;
_window.PointerMoved += Parent_PointerMovedEvent;
_window.PointerPressed += Parent_PointerPressedEvent;
_window.PointerReleased += Parent_PointerReleasedEvent;
_window.PointerWheelChanged += Parent_PointerWheelChanged;
PressedButtons = new bool[(int)MouseButton.Count];
@@ -47,13 +47,13 @@ namespace Ryujinx.Ava.Input
public event Action<string> OnGamepadConnected
{
add { }
add { }
remove { }
}
public event Action<string> OnGamepadDisconnected
{
add { }
add { }
remove { }
}
@@ -143,17 +143,17 @@ namespace Ryujinx.Ava.Input
_isDisposed = true;
_widget.PointerMoved -= Parent_PointerMovedEvent;
_widget.PointerPressed -= Parent_PointerPressedEvent;
_widget.PointerReleased -= Parent_PointerReleasedEvent;
_widget.PointerMoved -= Parent_PointerMovedEvent;
_widget.PointerPressed -= Parent_PointerPressedEvent;
_widget.PointerReleased -= Parent_PointerReleasedEvent;
_widget.PointerWheelChanged -= Parent_PointerWheelChanged;
_window.PointerMoved -= Parent_PointerMovedEvent;
_window.PointerPressed -= Parent_PointerPressedEvent;
_window.PointerReleased -= Parent_PointerReleasedEvent;
_window.PointerMoved -= Parent_PointerMovedEvent;
_window.PointerPressed -= Parent_PointerPressedEvent;
_window.PointerReleased -= Parent_PointerReleasedEvent;
_window.PointerWheelChanged -= Parent_PointerWheelChanged;
_widget = null;
}
}
}
}

View File

@@ -31,22 +31,22 @@ namespace Ryujinx.Modules
{
internal static class Updater
{
private const string GitHubApiURL = "https://api.github.com";
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private const string GitHubApiUrl = "https://api.github.com";
private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish");
private static readonly int ConnectionCount = 4;
private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish");
private static readonly int _connectionCount = 4;
private static string _buildVer;
private static string _platformExt;
private static string _buildUrl;
private static long _buildSize;
private static bool _updateSuccessful;
private static bool _running;
private static long _buildSize;
private static bool _updateSuccessful;
private static bool _running;
private static readonly string[] WindowsDependencyDirs = Array.Empty<string>();
private static readonly string[] _windowsDependencyDirs = Array.Empty<string>();
public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate)
{
@@ -99,9 +99,9 @@ namespace Ryujinx.Modules
{
using HttpClient jsonClient = ConstructHttpClient();
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse);
string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl);
var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse);
_buildVer = fetched.Name;
foreach (var asset in fetched.Assets)
@@ -195,23 +195,21 @@ namespace Ryujinx.Modules
}
// Fetch build size information to learn chunk sizes.
using (HttpClient buildSizeClient = ConstructHttpClient())
using HttpClient buildSizeClient = ConstructHttpClient();
try
{
try
{
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
_buildSize = message.Content.Headers.ContentRange.Length.Value;
}
catch (Exception ex)
{
Logger.Warning?.Print(LogClass.Application, ex.Message);
Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater");
_buildSize = message.Content.Headers.ContentRange.Length.Value;
}
catch (Exception ex)
{
Logger.Warning?.Print(LogClass.Application, ex.Message);
Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater");
_buildSize = -1;
}
_buildSize = -1;
}
Dispatcher.UIThread.Post(async () =>
@@ -248,23 +246,22 @@ namespace Ryujinx.Modules
_updateSuccessful = false;
// Empty update dir, although it shouldn't ever have anything inside it
if (Directory.Exists(UpdateDir))
if (Directory.Exists(_updateDir))
{
Directory.Delete(UpdateDir, true);
Directory.Delete(_updateDir, true);
}
Directory.CreateDirectory(UpdateDir);
Directory.CreateDirectory(_updateDir);
string updateFile = Path.Combine(UpdateDir, "update.bin");
string updateFile = Path.Combine(_updateDir, "update.bin");
TaskDialog taskDialog = new()
{
Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
Buttons = { },
Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
ShowProgressBar = true,
XamlRoot = parent
XamlRoot = parent,
};
taskDialog.Opened += (s, e) =>
@@ -301,7 +298,7 @@ namespace Ryujinx.Modules
if (OperatingSystem.IsMacOS())
{
string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", ".."));
string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app");
string newBundlePath = Path.Combine(_updateDir, "Ryujinx.app");
string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh");
string currentPid = Environment.ProcessId.ToString();
@@ -328,7 +325,7 @@ namespace Ryujinx.Modules
ProcessStartInfo processStart = new(ryuName)
{
UseShellExecute = true,
WorkingDirectory = executableDirectory
WorkingDirectory = executableDirectory,
};
foreach (string argument in CommandLineState.Arguments)
@@ -347,22 +344,22 @@ namespace Ryujinx.Modules
private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile)
{
// Multi-Threaded Updater
long chunkSize = _buildSize / ConnectionCount;
long remainderChunk = _buildSize % ConnectionCount;
long chunkSize = _buildSize / _connectionCount;
long remainderChunk = _buildSize % _connectionCount;
int completedRequests = 0;
int totalProgressPercentage = 0;
int[] progressPercentage = new int[ConnectionCount];
int completedRequests = 0;
int totalProgressPercentage = 0;
int[] progressPercentage = new int[_connectionCount];
List<byte[]> list = new(ConnectionCount);
List<WebClient> webClients = new(ConnectionCount);
List<byte[]> list = new(_connectionCount);
List<WebClient> webClients = new(_connectionCount);
for (int i = 0; i < ConnectionCount; i++)
for (int i = 0; i < _connectionCount; i++)
{
list.Add(Array.Empty<byte>());
}
for (int i = 0; i < ConnectionCount; i++)
for (int i = 0; i < _connectionCount; i++)
{
#pragma warning disable SYSLIB0014
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
@@ -371,7 +368,7 @@ namespace Ryujinx.Modules
webClients.Add(client);
if (i == ConnectionCount - 1)
if (i == _connectionCount - 1)
{
client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}");
}
@@ -388,7 +385,7 @@ namespace Ryujinx.Modules
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal);
taskDialog.SetProgressBarState(totalProgressPercentage / _connectionCount, TaskDialogProgressState.Normal);
};
client.DownloadDataCompleted += (_, args) =>
@@ -407,10 +404,10 @@ namespace Ryujinx.Modules
list[index] = args.Result;
Interlocked.Increment(ref completedRequests);
if (Equals(completedRequests, ConnectionCount))
if (Equals(completedRequests, _connectionCount))
{
byte[] mergedFileBytes = new byte[_buildSize];
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++)
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < _connectionCount; connectionIndex++)
{
Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length);
destinationOffset += list[connectionIndex].Length;
@@ -421,10 +418,9 @@ namespace Ryujinx.Modules
// On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution.
if (OperatingSystem.IsMacOS())
{
using (Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile }))
{
xattrProcess.WaitForExit();
}
using Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile });
xattrProcess.WaitForExit();
}
try
@@ -437,8 +433,6 @@ namespace Ryujinx.Modules
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
return;
}
}
};
@@ -470,31 +464,29 @@ namespace Ryujinx.Modules
// We do not want to timeout while downloading
client.Timeout = TimeSpan.FromDays(1);
using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result)
using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result)
using HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result;
using Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result;
using Stream updateFileStream = File.Open(updateFile, FileMode.Create);
long totalBytes = response.Content.Headers.ContentLength.Value;
long byteWritten = 0;
byte[] buffer = new byte[32 * 1024];
while (true)
{
using Stream updateFileStream = File.Open(updateFile, FileMode.Create);
int readSize = remoteFileStream.Read(buffer);
long totalBytes = response.Content.Headers.ContentLength.Value;
long byteWritten = 0;
byte[] buffer = new byte[32 * 1024];
while (true)
if (readSize == 0)
{
int readSize = remoteFileStream.Read(buffer);
if (readSize == 0)
{
break;
}
byteWritten += readSize;
taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal);
updateFileStream.Write(buffer, 0, readSize);
break;
}
byteWritten += readSize;
taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal);
updateFileStream.Write(buffer, 0, readSize);
}
InstallUpdate(taskDialog, updateFile);
@@ -510,7 +502,7 @@ namespace Ryujinx.Modules
{
Thread worker = new(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile))
{
Name = "Updater.SingleThreadWorker"
Name = "Updater.SingleThreadWorker",
};
worker.Start();
@@ -520,9 +512,9 @@ namespace Ryujinx.Modules
[SupportedOSPlatform("macos")]
private static void ExtractTarGzipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
{
using Stream inStream = File.OpenRead(archivePath);
using Stream inStream = File.OpenRead(archivePath);
using GZipInputStream gzipStream = new(inStream);
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
TarEntry tarEntry;
@@ -537,10 +529,8 @@ namespace Ryujinx.Modules
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (FileStream outStream = File.OpenWrite(outPath))
{
tarStream.CopyEntryContents(outStream);
}
using FileStream outStream = File.OpenWrite(outPath);
tarStream.CopyEntryContents(outStream);
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
@@ -559,24 +549,26 @@ namespace Ryujinx.Modules
private static void ExtractZipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
{
using Stream inStream = File.OpenRead(archivePath);
using ZipFile zipFile = new(inStream);
using Stream inStream = File.OpenRead(archivePath);
using ZipFile zipFile = new(inStream);
double count = 0;
foreach (ZipEntry zipEntry in zipFile)
{
count++;
if (zipEntry.IsDirectory) continue;
if (zipEntry.IsDirectory)
{
continue;
}
string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (Stream zipStream = zipFile.GetInputStream(zipEntry))
using (FileStream outStream = File.OpenWrite(outPath))
{
zipStream.CopyTo(outStream);
}
using Stream zipStream = zipFile.GetInputStream(zipEntry);
using FileStream outStream = File.OpenWrite(outPath);
zipStream.CopyTo(outStream);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
@@ -597,11 +589,11 @@ namespace Ryujinx.Modules
{
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
ExtractTarGzipFile(taskDialog, updateFile, UpdateDir);
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
}
else if (OperatingSystem.IsWindows())
{
ExtractZipFile(taskDialog, updateFile, UpdateDir);
ExtractZipFile(taskDialog, updateFile, _updateDir);
}
else
{
@@ -648,10 +640,10 @@ namespace Ryujinx.Modules
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
});
MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog);
MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
});
Directory.Delete(UpdateDir, true);
Directory.Delete(_updateDir, true);
}
_updateSuccessful = true;
@@ -738,15 +730,15 @@ namespace Ryujinx.Modules
// NOTE: This method should always reflect the latest build layout.
private static IEnumerable<string> EnumerateFilesToDelete()
{
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir.
var files = Directory.EnumerateFiles(_homeDir); // All files directly in base dir.
// Determine and exclude user files only when the updater is running, not when cleaning old files
if (_running && !OperatingSystem.IsMacOS())
{
// Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list.
var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var newFiles = Directory.EnumerateFiles(UpdatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(HomeDir, filename));
var oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var newFiles = Directory.EnumerateFiles(_updatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename));
// Remove user files from the paths in files.
files = files.Except(userFiles);
@@ -754,9 +746,9 @@ namespace Ryujinx.Modules
if (OperatingSystem.IsWindows())
{
foreach (string dir in WindowsDependencyDirs)
foreach (string dir in _windowsDependencyDirs)
{
string dirPath = Path.Combine(HomeDir, dir);
string dirPath = Path.Combine(_homeDir, dir);
if (Directory.Exists(dirPath))
{
files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories));
@@ -798,10 +790,10 @@ namespace Ryujinx.Modules
public static void CleanupUpdate()
{
foreach (string file in Directory.GetFiles(HomeDir, "*.ryuold", SearchOption.AllDirectories))
foreach (string file in Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories))
{
File.Delete(file);
}
}
}
}
}

View File

@@ -22,16 +22,16 @@ namespace Ryujinx.Ava
{
internal partial class Program
{
public static double WindowScaleFactor { get; set; }
public static double WindowScaleFactor { get; set; }
public static double DesktopScaleFactor { get; set; } = 1.0;
public static string Version { get; private set; }
public static string ConfigurationPath { get; private set; }
public static bool PreviewerDetached { get; private set; }
public static string Version { get; private set; }
public static string ConfigurationPath { get; private set; }
public static bool PreviewerDetached { get; private set; }
[LibraryImport("user32.dll", SetLastError = true)]
public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
private const uint MB_ICONWARNING = 0x30;
private const uint MbIconwarning = 0x30;
public static void Main(string[] args)
{
@@ -39,7 +39,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}", MB_ICONWARNING);
_ = 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);
}
PreviewerDetached = true;
@@ -58,16 +58,13 @@ namespace Ryujinx.Ava
.With(new X11PlatformOptions
{
EnableMultiTouch = true,
EnableIme = true,
UseEGL = false,
UseGpu = true
EnableIme = true,
RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software },
})
.With(new Win32PlatformOptions
{
EnableMultitouch = true,
UseWgl = false,
AllowEglInitialization = false,
CompositionBackdropCornerRadius = 8.0f,
WinUICompositionBackdropCornerRadius = 8.0f,
RenderingMode = new[] { Win32RenderingMode.AngleEgl, Win32RenderingMode.Software },
})
.UseSkia();
}
@@ -84,7 +81,7 @@ namespace Ryujinx.Ava
// Hook unhandled exception and process exit events.
AppDomain.CurrentDomain.UnhandledException += (sender, e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
AppDomain.CurrentDomain.ProcessExit += (sender, e) => Exit();
AppDomain.CurrentDomain.ProcessExit += (sender, e) => Exit();
// Setup base data directory.
AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
@@ -130,7 +127,7 @@ namespace Ryujinx.Ava
public static void ReloadConfig()
{
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
// Now load the configuration as the other subsystems are now registered
@@ -192,7 +189,7 @@ namespace Ryujinx.Ava
"never" => HideCursorMode.Never,
"onidle" => HideCursorMode.OnIdle,
"always" => HideCursorMode.Always,
_ => ConfigurationState.Instance.HideCursor.Value
_ => ConfigurationState.Instance.HideCursor.Value,
};
}
}
@@ -238,4 +235,4 @@ namespace Ryujinx.Ava
Logger.Shutdown();
}
}
}
}

View File

@@ -10,6 +10,8 @@
<RootNamespace>Ryujinx.Ava</RootNamespace>
<ApplicationIcon>Ryujinx.ico</ApplicationIcon>
<TieredPGO>true</TieredPGO>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
@@ -26,7 +28,7 @@
<ItemGroup>
<PackageReference Include="Avalonia" />
<PackageReference Include="Avalonia.Desktop" />
<PackageReference Include="Avalonia.Diagnostics" />
<PackageReference Include="Avalonia.Diagnostics" Condition="'$(Configuration)'=='Debug'" />
<PackageReference Include="Avalonia.Controls.DataGrid" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" />
<PackageReference Include="Avalonia.Svg" />
@@ -34,7 +36,6 @@
<PackageReference Include="jp2masa.Avalonia.Flexbox" />
<PackageReference Include="DynamicData" />
<PackageReference Include="FluentAvaloniaUI" />
<PackageReference Include="XamlNameReferenceGenerator" />
<PackageReference Include="OpenTK.Core" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
@@ -97,10 +98,7 @@
<SubType>Designer</SubType>
</AvaloniaResource>
<AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" />
<AvaloniaResource Include="Assets\Styles\BaseLight.xaml">
<Generator>MSBuild:Compile</Generator>
</AvaloniaResource>
<AvaloniaResource Include="Assets\Styles\BaseDark.xaml">
<AvaloniaResource Include="Assets\Styles\Themes.xaml">
<Generator>MSBuild:Compile</Generator>
</AvaloniaResource>
<AvaloniaResource Include="Assets\Styles\Styles.xaml" />
@@ -123,8 +121,7 @@
<None Remove="Assets\Locales\zh_CN.json" />
<None Remove="Assets\Locales\zh_TW.json" />
<None Remove="Assets\Styles\Styles.xaml" />
<None Remove="Assets\Styles\BaseDark.xaml" />
<None Remove="Assets\Styles\BaseLight.xaml" />
<None Remove="Assets\Styles\Themes.xaml" />
</ItemGroup>
<ItemGroup>

View File

@@ -53,8 +53,6 @@ namespace Ryujinx.Ava.UI.Applet
bool opened = false;
_parent.Activate();
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
title,
message,
@@ -64,7 +62,7 @@ namespace Ryujinx.Ava.UI.Applet
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
(int)Symbol.Important,
deferEvent,
async (window) =>
async window =>
{
if (opened)
{
@@ -112,7 +110,7 @@ namespace Ryujinx.Ava.UI.Applet
{
try
{
var response = await SwkbdAppletDialog.ShowInputDialog(_parent, LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
var response = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
if (response.Result == UserResult.Ok)
{
@@ -142,10 +140,7 @@ namespace Ryujinx.Ava.UI.Applet
public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value)
{
device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value);
if (_parent.ViewModel.AppHost != null)
{
_parent.ViewModel.AppHost.Stop();
}
_parent.ViewModel.AppHost?.Stop();
}
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons)
@@ -162,7 +157,7 @@ namespace Ryujinx.Ava.UI.Applet
{
Title = title,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
Width = 400
Width = 400,
};
object response = await msgDialog.Run();
@@ -194,4 +189,4 @@ namespace Ryujinx.Ava.UI.Applet
return new AvaloniaDynamicTextInputHandler(_parent);
}
}
}
}

View File

@@ -3,13 +3,11 @@ using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.Ui;
using System;
using System.Threading;
using HidKey = Ryujinx.Common.Configuration.Hid.Key;
namespace Ryujinx.Ava.UI.Applet
@@ -17,7 +15,7 @@ namespace Ryujinx.Ava.UI.Applet
class AvaloniaDynamicTextInputHandler : IDynamicTextInputHandler
{
private MainWindow _parent;
private OffscreenTextBox _hiddenTextBox;
private readonly OffscreenTextBox _hiddenTextBox;
private bool _canProcessInput;
private IDisposable _textChangedSubscription;
private IDisposable _selectionStartChangedSubscription;
@@ -76,7 +74,7 @@ namespace Ryujinx.Ava.UI.Applet
return;
}
e.RoutedEvent = _hiddenTextBox.GetKeyUpRoutedEvent();
e.RoutedEvent = OffscreenTextBox.GetKeyUpRoutedEvent();
Dispatcher.UIThread.InvokeAsync(() =>
{
@@ -96,7 +94,7 @@ namespace Ryujinx.Ava.UI.Applet
return;
}
e.RoutedEvent = _hiddenTextBox.GetKeyUpRoutedEvent();
e.RoutedEvent = OffscreenTextBox.GetKeyUpRoutedEvent();
Dispatcher.UIThread.InvokeAsync(() =>
{

View File

@@ -9,7 +9,7 @@ namespace Ryujinx.Ava.UI.Applet
{
public AvaloniaHostUiTheme(MainWindow parent)
{
FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000, 0) ? "Segoe UI Variable" : parent.FontFamily.Name;
FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) ? "Segoe UI Variable" : parent.FontFamily.Name;
DefaultBackgroundColor = BrushToThemeColor(parent.Background);
DefaultForegroundColor = BrushToThemeColor(parent.Foreground);
DefaultBorderColor = BrushToThemeColor(parent.BorderBrush);
@@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Applet
public ThemeColor SelectionBackgroundColor { get; }
public ThemeColor SelectionForegroundColor { get; }
private ThemeColor BrushToThemeColor(IBrush brush)
private static ThemeColor BrushToThemeColor(IBrush brush)
{
if (brush is SolidColorBrush solidColor)
{
@@ -34,10 +34,8 @@ namespace Ryujinx.Ava.UI.Applet
(float)solidColor.Color.G / 255,
(float)solidColor.Color.B / 255);
}
else
{
return new ThemeColor();
}
return new ThemeColor();
}
}
}

View File

@@ -38,7 +38,7 @@
Grid.Column="1"
Margin="10"
VerticalAlignment="Stretch"
Text="{Binding Message}"
Text="{ReflectionBinding Message}"
TextWrapping="Wrap" />
<StackPanel
Name="ButtonStack"

View File

@@ -1,4 +1,3 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;
@@ -19,9 +18,7 @@ namespace Ryujinx.Ava.UI.Applet
Message = message;
DataContext = this;
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
int responseId = 0;
if (buttons != null)
@@ -42,9 +39,6 @@ namespace Ryujinx.Ava.UI.Applet
{
DataContext = this;
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
public string Message { get; set; }
@@ -77,4 +71,4 @@ namespace Ryujinx.Ava.UI.Applet
return _buttonResponse;
}
}
}
}

View File

@@ -34,13 +34,13 @@
Grid.Row="1"
Grid.Column="1"
Margin="5"
Text="{Binding MainText}"
Text="{ReflectionBinding MainText}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="2"
Grid.Column="1"
Margin="5"
Text="{Binding SecondaryText}"
Text="{ReflectionBinding SecondaryText}"
TextWrapping="Wrap" />
<TextBox
Name="Input"
@@ -50,7 +50,7 @@
VerticalAlignment="Center"
Focusable="True"
KeyUp="Message_KeyUp"
Text="{Binding Message}"
Text="{ReflectionBinding Message}"
TextInput="Message_TextInput"
TextWrapping="Wrap"
UseFloatingWatermark="True" />

View File

@@ -1,13 +1,10 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
using System;
@@ -22,7 +19,7 @@ namespace Ryujinx.Ava.UI.Controls
private Predicate<string> _checkInput = _ => true;
private int _inputMax;
private int _inputMin;
private string _placeholder;
private readonly string _placeholder;
private ContentDialog _host;
@@ -57,13 +54,13 @@ namespace Ryujinx.Ava.UI.Controls
public string MainText { get; set; } = "";
public string SecondaryText { get; set; } = "";
public static async Task<(UserResult Result, string Input)> ShowInputDialog(StyleableWindow window, string title, SoftwareKeyboardUiArgs args)
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUiArgs args)
{
ContentDialog contentDialog = new ContentDialog();
ContentDialog contentDialog = new();
UserResult result = UserResult.Cancel;
SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText);
SwkbdAppletDialog content = new(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText);
string input = string.Empty;
@@ -78,15 +75,16 @@ namespace Ryujinx.Ava.UI.Controls
contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel];
contentDialog.Content = content;
TypedEventHandler<ContentDialog, ContentDialogClosedEventArgs> handler = (sender, eventArgs) =>
void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
{
if (eventArgs.Result == ContentDialogResult.Primary)
{
result = UserResult.Ok;
input = content.Input.Text;
}
};
contentDialog.Closed += handler;
}
contentDialog.Closed += Handler;
await ContentDialogHelper.ShowAsync(contentDialog);
@@ -182,4 +180,4 @@ namespace Ryujinx.Ava.UI.Controls
}
}
}
}
}

View File

@@ -2,7 +2,9 @@
x:Class="Ryujinx.Ava.UI.Controls.ApplicationContextMenu"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale">
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel">
<MenuItem
Click="RunApplication_Click"
Header="{locale:Locale GameListContextMenuRunApplication}" />

View File

@@ -18,7 +18,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Path = System.IO.Path;
using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.Controls
{
@@ -53,7 +52,7 @@ namespace Ryujinx.Ava.UI.Controls
public void OpenUserSaveDirectory_Click(object sender, RoutedEventArgs args)
{
if ((sender as MenuItem)?.DataContext is MainWindowViewModel viewModel)
if (sender is MenuItem { DataContext: MainWindowViewModel viewModel })
{
OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
}
@@ -300,7 +299,11 @@ namespace Ryujinx.Ava.UI.Controls
if (viewModel?.SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
await ApplicationHelper.ExtractSection(
viewModel.StorageProvider,
NcaSectionType.Code,
viewModel.SelectedApplication.Path,
viewModel.SelectedApplication.TitleName);
}
}
@@ -310,7 +313,11 @@ namespace Ryujinx.Ava.UI.Controls
if (viewModel?.SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Data, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
await ApplicationHelper.ExtractSection(
viewModel.StorageProvider,
NcaSectionType.Data,
viewModel.SelectedApplication.Path,
viewModel.SelectedApplication.TitleName);
}
}
@@ -320,7 +327,11 @@ namespace Ryujinx.Ava.UI.Controls
if (viewModel?.SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
await ApplicationHelper.ExtractSection(
viewModel.StorageProvider,
NcaSectionType.Logo,
viewModel.SelectedApplication.Path,
viewModel.SelectedApplication.TitleName);
}
}
@@ -334,4 +345,4 @@ namespace Ryujinx.Ava.UI.Controls
}
}
}
}
}

View File

@@ -11,7 +11,9 @@
d:DesignHeight="450"
d:DesignWidth="800"
Focusable="True"
mc:Ignorable="d">
mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<controls:ApplicationContextMenu x:Key="ApplicationContextMenu" />
@@ -27,7 +29,7 @@
VerticalAlignment="Stretch"
ContextFlyout="{StaticResource ApplicationContextMenu}"
DoubleTapped="GameList_DoubleTapped"
Items="{Binding AppsObservableList}"
ItemsSource="{Binding AppsObservableList}"
SelectionChanged="GameList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
@@ -43,8 +45,8 @@
<Setter Property="Margin" Value="5" />
<Setter Property="CornerRadius" Value="4" />
</Style>
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" />
<Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator">
<Setter Property="MinHeight" Value="{ReflectionBinding $parent[UserControl].DataContext.GridItemSelectorSize}" />
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
@@ -54,10 +56,10 @@
Margin="10"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Classes.huge="{ReflectionBinding $parent[UserControl].DataContext.IsGridHuge}"
Classes.large="{ReflectionBinding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{ReflectionBinding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{ReflectionBinding $parent[UserControl].DataContext.IsGridSmall}"
ClipToBounds="True"
CornerRadius="4">
<Grid>
@@ -76,9 +78,9 @@
Margin="0,10,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}">
IsVisible="{ReflectionBinding $parent[UserControl].DataContext.ShowNames}">
<TextBlock
HorizontalAlignment="Stretch"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding TitleName}"
TextAlignment="Center"

View File

@@ -1,7 +1,6 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.App.Common;
@@ -16,7 +15,7 @@ namespace Ryujinx.Ava.UI.Controls
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
{
add { AddHandler(ApplicationOpenedEvent, value); }
add { AddHandler(ApplicationOpenedEvent, value); }
remove { RemoveHandler(ApplicationOpenedEvent, value); }
}
@@ -25,12 +24,7 @@ namespace Ryujinx.Ava.UI.Controls
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
public void GameList_DoubleTapped(object sender, TappedEventArgs args)
{
if (sender is ListBox listBox)
{

View File

@@ -10,7 +10,9 @@
d:DesignHeight="450"
d:DesignWidth="800"
Focusable="True"
mc:Ignorable="d">
mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<controls:ApplicationContextMenu x:Key="ApplicationContextMenu" />
@@ -27,7 +29,7 @@
VerticalAlignment="Stretch"
ContextFlyout="{StaticResource ApplicationContextMenu}"
DoubleTapped="GameList_DoubleTapped"
Items="{Binding AppsObservableList}"
ItemsSource="{Binding AppsObservableList}"
SelectionChanged="GameList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
@@ -39,8 +41,8 @@
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Styles>
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.ListItemSelectorSize}" />
<Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator">
<Setter Property="MinHeight" Value="{ReflectionBinding $parent[UserControl].DataContext.ListItemSelectorSize}" />
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
@@ -65,10 +67,10 @@
Grid.RowSpan="3"
Grid.Column="0"
Margin="0"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Classes.huge="{ReflectionBinding $parent[UserControl].DataContext.IsGridHuge}"
Classes.large="{ReflectionBinding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{ReflectionBinding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{ReflectionBinding $parent[UserControl].DataContext.IsGridSmall}"
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<Border
Grid.Column="2"

View File

@@ -1,7 +1,6 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.App.Common;
@@ -16,7 +15,7 @@ namespace Ryujinx.Ava.UI.Controls
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
{
add { AddHandler(ApplicationOpenedEvent, value); }
add { AddHandler(ApplicationOpenedEvent, value); }
remove { RemoveHandler(ApplicationOpenedEvent, value); }
}
@@ -25,12 +24,7 @@ namespace Ryujinx.Ava.UI.Controls
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
public void GameList_DoubleTapped(object sender, TappedEventArgs args)
{
if (sender is ListBox listBox)
{

View File

@@ -2,7 +2,6 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using LibHac;
using LibHac.Common;
@@ -10,7 +9,6 @@ using LibHac.Fs;
using LibHac.Fs.Shim;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Views.User;
using Ryujinx.HLE.FileSystem;
@@ -19,6 +17,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Controls
@@ -56,7 +55,7 @@ namespace Ryujinx.Ava.UI.Controls
InitializeComponent();
}
public void GoBack(object parameter = null)
public void GoBack()
{
if (ContentFrame.BackStack.Count > 0)
{
@@ -75,14 +74,14 @@ namespace Ryujinx.Ava.UI.Controls
VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient)
{
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient);
ContentDialog contentDialog = new ContentDialog
ContentDialog contentDialog = new()
{
Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle],
PrimaryButtonText = "",
SecondaryButtonText = "",
CloseButtonText = "",
Content = content,
Padding = new Thickness(0)
Padding = new Thickness(0),
};
contentDialog.Closed += (sender, args) =>
@@ -125,7 +124,7 @@ namespace Ryujinx.Ava.UI.Controls
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
HashSet<HLE.HOS.Services.Account.Acc.UserId> lostAccounts = new();
HashSet<UserId> lostAccounts = new();
while (true)
{
@@ -139,15 +138,15 @@ namespace Ryujinx.Ava.UI.Controls
for (int i = 0; i < readCount; i++)
{
var save = saveDataInfo[i];
var id = new HLE.HOS.Services.Account.Acc.UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High);
if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault( x=> x.UserId == id) == null)
var id = new UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High);
if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault(x => x.UserId == id) == null)
{
lostAccounts.Add(id);
}
}
}
foreach(var account in lostAccounts)
foreach (var account in lostAccounts)
{
ViewModel.LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), this));
}
@@ -166,7 +165,7 @@ namespace Ryujinx.Ava.UI.Controls
if (profile == null)
{
async void Action()
static async void Action()
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]);
}
@@ -215,4 +214,4 @@ namespace Ryujinx.Ava.UI.Controls
Navigate(typeof(UserSaveManagerView), (this, AccountManager, HorizonClient, VirtualFileSystem));
}
}
}
}

View File

@@ -28,4 +28,4 @@ namespace Ryujinx.Ava.UI.Controls
InitializeComponent();
}
}
}
}

View File

@@ -13,4 +13,4 @@ namespace Ryujinx.Ava.UI.Helpers
RoutedEvent = routedEvent;
}
}
}
}

View File

@@ -33,4 +33,4 @@ namespace Ryujinx.Ava.UI.Helpers
throw new NotSupportedException();
}
}
}
}

View File

@@ -23,7 +23,7 @@ namespace Ryujinx.Ava.UI.Helpers
IsAssigned = isAssigned;
}
}
public ToggleButton ToggledButton { get; set; }
private bool _isWaitingForInput;

View File

@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Threading;
using FluentAvalonia.Core;
@@ -32,18 +33,17 @@ namespace Ryujinx.Ava.UI.Helpers
ContentDialog contentDialog = new()
{
Title = title,
PrimaryButtonText = primaryButton,
Title = title,
PrimaryButtonText = primaryButton,
SecondaryButtonText = secondaryButton,
CloseButtonText = closeButton,
Content = content
CloseButtonText = closeButton,
Content = content,
PrimaryButtonCommand = MiniCommand.Create(() =>
{
result = primaryButtonResult;
}),
};
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
{
result = primaryButtonResult;
});
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{
result = UserResult.No;
@@ -96,7 +96,6 @@ namespace Ryujinx.Ava.UI.Helpers
Func<Window, Task> doWhileDeferred = null)
{
bool startedDeferring = false;
UserResult result = UserResult.None;
return await ShowTextDialog(
title,
@@ -123,8 +122,6 @@ namespace Ryujinx.Ava.UI.Helpers
var deferral = args.GetDeferral();
result = primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok;
sender.PrimaryButtonClick -= DeferClose;
_ = Task.Run(() =>
@@ -150,18 +147,18 @@ namespace Ryujinx.Ava.UI.Helpers
{
Grid content = new()
{
RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() },
ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() },
RowDefinitions = new RowDefinitions { new(), new() },
ColumnDefinitions = new ColumnDefinitions { new(GridLength.Auto), new() },
MinHeight = 80
MinHeight = 80,
};
SymbolIcon icon = new()
{
Symbol = (Symbol)symbol,
Margin = new Thickness(10),
FontSize = 40,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
Symbol = (Symbol)symbol,
Margin = new Thickness(10),
FontSize = 40,
VerticalAlignment = VerticalAlignment.Center,
};
Grid.SetColumn(icon, 0);
@@ -170,18 +167,18 @@ namespace Ryujinx.Ava.UI.Helpers
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()
{
Text = secondaryText,
Margin = new Thickness(5),
Text = secondaryText,
Margin = new Thickness(5),
TextWrapping = TextWrapping.Wrap,
MaxWidth = 450
MaxWidth = 450,
};
Grid.SetColumn(primaryLabel, 1);
@@ -318,14 +315,16 @@ namespace Ryujinx.Ava.UI.Helpers
Window parent = GetMainWindow();
if (parent != null && parent.IsActive && (parent as MainWindow).ViewModel.IsGameRunning)
if (parent is MainWindow window)
{
parent.Activate();
contentDialogOverlayWindow = new()
{
Height = parent.Bounds.Height,
Width = parent.Bounds.Width,
Position = parent.PointToScreen(new Point()),
ShowInTaskbar = false
Height = parent.Bounds.Height,
Width = parent.Bounds.Width,
Position = parent.PointToScreen(new Point()),
ShowInTaskbar = false,
};
parent.PositionChanged += OverlayOnPositionChanged;
@@ -372,7 +371,9 @@ namespace Ryujinx.Ava.UI.Helpers
}
else
{
result = await contentDialog.ShowAsync();
result = ContentDialogResult.None;
Logger.Warning?.Print(LogClass.Ui, "Content dialog overlay failed to populate. Default value has been returned.");
}
return result;
@@ -389,11 +390,11 @@ namespace Ryujinx.Ava.UI.Helpers
private static Window GetMainWindow()
{
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
{
foreach (Window item in al.Windows)
{
if (item.IsActive && item is MainWindow window)
if (item is MainWindow window)
{
return window;
}
@@ -403,4 +404,4 @@ namespace Ryujinx.Ava.UI.Helpers
return null;
}
}
}
}

View File

@@ -4,6 +4,6 @@
{
List,
Grid,
Chip
Chip,
}
}

View File

@@ -1,4 +1,3 @@
using Avalonia.Data;
using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Controls;
using System;
@@ -8,13 +7,13 @@ namespace Ryujinx.Ava.UI.Helpers
{
public class GlyphValueConverter : MarkupExtension
{
private string _key;
private readonly string _key;
private static Dictionary<Glyph, string> _glyphs = new Dictionary<Glyph, string>
private static readonly Dictionary<Glyph, string> _glyphs = new()
{
{ Glyph.List, char.ConvertFromUtf32((int)Symbol.List).ToString() },
{ Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll).ToString() },
{ Glyph.Chip, char.ConvertFromUtf32(59748).ToString() }
{ Glyph.List, char.ConvertFromUtf32((int)Symbol.List) },
{ Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll) },
{ Glyph.Chip, char.ConvertFromUtf32(59748) },
};
public GlyphValueConverter(string key)
@@ -37,13 +36,7 @@ namespace Ryujinx.Ava.UI.Helpers
public override object ProvideValue(IServiceProvider serviceProvider)
{
Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension binding = new($"[{_key}]")
{
Mode = BindingMode.OneWay,
Source = this
};
return binding.ProvideValue(serviceProvider);
return this[_key];
}
}
}
}

View File

@@ -1,52 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using System;
using System.Windows.Input;
namespace Ryujinx.Ava.UI.Helpers
{
public class HotKeyControl : ContentControl, ICommandSource
{
public static readonly StyledProperty<object> CommandParameterProperty =
AvaloniaProperty.Register<HotKeyControl, object>(nameof(CommandParameter));
public static readonly DirectProperty<HotKeyControl, ICommand> CommandProperty =
AvaloniaProperty.RegisterDirect<HotKeyControl, ICommand>(nameof(Command),
control => control.Command, (control, command) => control.Command = command, enableDataValidation: true);
public static readonly StyledProperty<KeyGesture> HotKeyProperty = HotKeyManager.HotKeyProperty.AddOwner<Button>();
private ICommand _command;
private bool _commandCanExecute;
public ICommand Command
{
get { return _command; }
set { SetAndRaise(CommandProperty, ref _command, value); }
}
public KeyGesture HotKey
{
get { return GetValue(HotKeyProperty); }
set { SetValue(HotKeyProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public void CanExecuteChanged(object sender, EventArgs e)
{
var canExecute = Command == null || Command.CanExecute(CommandParameter);
if (canExecute != _commandCanExecute)
{
_commandCanExecute = canExecute;
UpdateIsEffectivelyEnabled();
}
}
}
}

View File

@@ -43,4 +43,4 @@ namespace Ryujinx.Ava.UI.Helpers
return key;
}
}
}
}

View File

@@ -1,15 +1,17 @@
using Avalonia.Logging;
using Avalonia.Utilities;
using Ryujinx.Common.Logging;
using System;
using System.Text;
namespace Ryujinx.Ava.UI.Helpers
{
using AvaLogger = Avalonia.Logging.Logger;
using AvaLogLevel = Avalonia.Logging.LogEventLevel;
using RyuLogClass = Ryujinx.Common.Logging.LogClass;
using RyuLogger = Ryujinx.Common.Logging.Logger;
using AvaLogger = Avalonia.Logging.Logger;
using AvaLogLevel = LogEventLevel;
using RyuLogClass = LogClass;
using RyuLogger = Ryujinx.Common.Logging.Logger;
internal class LoggerAdapter : Avalonia.Logging.ILogSink
internal class LoggerAdapter : ILogSink
{
public static void Register()
{
@@ -20,13 +22,13 @@ namespace Ryujinx.Ava.UI.Helpers
{
return level switch
{
AvaLogLevel.Verbose => RyuLogger.Debug,
AvaLogLevel.Debug => RyuLogger.Debug,
AvaLogLevel.Verbose => RyuLogger.Debug,
AvaLogLevel.Debug => RyuLogger.Debug,
AvaLogLevel.Information => RyuLogger.Debug,
AvaLogLevel.Warning => RyuLogger.Debug,
AvaLogLevel.Error => RyuLogger.Error,
AvaLogLevel.Fatal => RyuLogger.Error,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
AvaLogLevel.Warning => RyuLogger.Debug,
AvaLogLevel.Error => RyuLogger.Error,
AvaLogLevel.Fatal => RyuLogger.Error,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
};
}
@@ -40,21 +42,6 @@ namespace Ryujinx.Ava.UI.Helpers
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, null));
}
public void Log<T0>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0)
{
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, 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(level, 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(level, 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(level, area, messageTemplate, source, propertyValues));
@@ -112,4 +99,4 @@ namespace Ryujinx.Ava.UI.Helpers
return result.ToString();
}
}
}
}

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Ava.UI.Helpers
{
private readonly Action<T> _callback;
private bool _busy;
private Func<T, Task> _asyncCallback;
private readonly Func<T, Task> _asyncCallback;
public MiniCommand(Action<T> callback)
{
@@ -68,4 +68,4 @@ namespace Ryujinx.Ava.UI.Helpers
public abstract void Execute(object parameter);
public abstract event EventHandler CanExecuteChanged;
}
}
}

View File

@@ -12,12 +12,12 @@ namespace Ryujinx.Ava.UI.Helpers
{
public static class NotificationHelper
{
private const int MaxNotifications = 4;
private const int MaxNotifications = 4;
private const int NotificationDelayInMs = 5000;
private static WindowNotificationManager _notificationManager;
private static readonly BlockingCollection<Notification> _notifications = new();
private static readonly BlockingCollection<Notification> _notifications = new();
public static void SetNotificationManager(Window host)
{
@@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Helpers
{
Position = NotificationPosition.BottomRight,
MaxItems = MaxNotifications,
Margin = new Thickness(0, 0, 15, 40)
Margin = new Thickness(0, 0, 15, 40),
};
var maybeAsyncWorkQueue = new Lazy<AsyncWorkQueue<Notification>>(
@@ -67,4 +67,4 @@ namespace Ryujinx.Ava.UI.Helpers
Show(LocaleManager.Instance[LocaleKeys.DialogErrorTitle], $"{LocaleManager.Instance[LocaleKeys.DialogErrorMessage]}\n\n{message}", NotificationType.Error);
}
}
}
}

View File

@@ -35,4 +35,4 @@ namespace Ryujinx.Ava.UI.Helpers
return _instance;
}
}
}
}

View File

@@ -6,12 +6,12 @@ namespace Ryujinx.Ava.UI.Helpers
{
public class OffscreenTextBox : TextBox
{
public RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent()
public static RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent()
{
return KeyDownEvent;
}
public RoutedEvent<KeyEventArgs> GetKeyUpRoutedEvent()
public static RoutedEvent<KeyEventArgs> GetKeyUpRoutedEvent()
{
return KeyUpEvent;
}
@@ -28,13 +28,12 @@ namespace Ryujinx.Ava.UI.Helpers
public void SendText(string text)
{
OnTextInput(new TextInputEventArgs()
OnTextInput(new TextInputEventArgs
{
Text = text,
Device = KeyboardDevice.Instance,
Source = this,
RoutedEvent = TextInputEvent
RoutedEvent = TextInputEvent,
});
}
}
}
}

View File

@@ -0,0 +1,28 @@
using Avalonia.Data.Converters;
using System;
using System.Globalization;
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
namespace Ryujinx.Ava.UI.Helpers
{
internal class TimeZoneConverter : IValueConverter
{
public static TimeZoneConverter Instance = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
var timeZone = (TimeZone)value;
return string.Format("{0} {1} {2}", timeZone.UtcDifference, timeZone.Location, timeZone.Abbreviation);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,5 +1,4 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Helper;
using System.Threading.Tasks;
@@ -24,7 +23,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailed],
UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFound],
UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknown],
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefined]
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefined],
};
}
@@ -37,7 +36,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailedDescription],
UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFoundDescription],
UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknownDescription],
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefinedDescription]
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefinedDescription],
};
}
@@ -48,7 +47,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserError.NoKeys or
UserError.NoFirmware or
UserError.FirmwareParsingFailed => true,
_ => false
_ => false,
};
}
@@ -63,11 +62,11 @@ namespace Ryujinx.Ava.UI.Helpers
{
UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys",
UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware",
_ => SetupGuideUrl
_ => SetupGuideUrl,
};
}
public static async Task ShowUserErrorDialog(UserError error, StyleableWindow owner)
public static async Task ShowUserErrorDialog(UserError error)
{
string errorCode = GetErrorCode(error);
@@ -88,4 +87,4 @@ namespace Ryujinx.Ava.UI.Helpers
}
}
}
}
}

View File

@@ -9,4 +9,4 @@ namespace Ryujinx.Ava.UI.Helpers
Cancel,
None,
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
@@ -10,46 +11,47 @@ namespace Ryujinx.Ava.UI.Helpers
[Flags]
public enum ClassStyles : uint
{
CS_CLASSDC = 0x40,
CS_OWNDC = 0x20,
CsClassdc = 0x40,
CsOwndc = 0x20,
}
[Flags]
public enum WindowStyles : uint
{
WS_CHILD = 0x40000000
WsChild = 0x40000000,
}
public enum Cursors : uint
{
IDC_ARROW = 32512
IdcArrow = 32512,
}
[SuppressMessage("Design", "CA1069: Enums values should not be duplicated")]
public enum WindowsMessages : uint
{
MOUSEMOVE = 0x0200,
LBUTTONDOWN = 0x0201,
LBUTTONUP = 0x0202,
LBUTTONDBLCLK = 0x0203,
RBUTTONDOWN = 0x0204,
RBUTTONUP = 0x0205,
RBUTTONDBLCLK = 0x0206,
MBUTTONDOWN = 0x0207,
MBUTTONUP = 0x0208,
MBUTTONDBLCLK = 0x0209,
MOUSEWHEEL = 0x020A,
XBUTTONDOWN = 0x020B,
XBUTTONUP = 0x020C,
XBUTTONDBLCLK = 0x020D,
MOUSEHWHEEL = 0x020E,
MOUSELAST = 0x020E
Mousemove = 0x0200,
Lbuttondown = 0x0201,
Lbuttonup = 0x0202,
Lbuttondblclk = 0x0203,
Rbuttondown = 0x0204,
Rbuttonup = 0x0205,
Rbuttondblclk = 0x0206,
Mbuttondown = 0x0207,
Mbuttonup = 0x0208,
Mbuttondblclk = 0x0209,
Mousewheel = 0x020A,
Xbuttondown = 0x020B,
Xbuttonup = 0x020C,
Xbuttondblclk = 0x020D,
Mousehwheel = 0x020E,
Mouselast = 0x020E,
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
internal delegate IntPtr WindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
public struct WNDCLASSEX
public struct WndClassEx
{
public int cbSize;
public ClassStyles style;
@@ -64,9 +66,9 @@ namespace Ryujinx.Ava.UI.Helpers
public IntPtr lpszClassName;
public IntPtr hIconSm;
public WNDCLASSEX()
public WndClassEx()
{
cbSize = Marshal.SizeOf<WNDCLASSEX>();
cbSize = Marshal.SizeOf<WndClassEx>();
}
}
@@ -77,17 +79,17 @@ namespace Ryujinx.Ava.UI.Helpers
public static IntPtr CreateArrowCursor()
{
return LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW);
return LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IdcArrow);
}
[LibraryImport("user32.dll")]
public static partial IntPtr SetCursor(IntPtr handle);
[LibraryImport("user32.dll")]
public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvANDPlane, byte[] pvXORPlane);
public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvAndPlane, byte[] pvXorPlane);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")]
public static partial ushort RegisterClassEx(ref WNDCLASSEX param);
public static partial ushort RegisterClassEx(ref WndClassEx param);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "UnregisterClassW")]
public static partial short UnregisterClass([MarshalAs(UnmanagedType.LPWStr)] string lpClassName, IntPtr instance);

View File

@@ -11,8 +11,8 @@ namespace Ryujinx.Ava.UI.Models
public CheatModel(string name, string buildId, bool isEnabled)
{
Name = name;
BuildId = buildId;
Name = name;
BuildId = buildId;
IsEnabled = isEnabled;
}
@@ -35,6 +35,6 @@ namespace Ryujinx.Ava.UI.Models
public string Name { get; }
public string CleanName => Name.Substring(1, Name.Length - 8);
public string CleanName => Name[1..^7];
}
}
}

View File

@@ -10,13 +10,13 @@ namespace Ryujinx.Ava.UI.Models
public CheatsList(string buildId, string path)
{
BuildId = buildId;
Path = path;
Path = path;
CollectionChanged += CheatsList_CollectionChanged;
}
public string BuildId { get; }
public string Path { get; }
public string Path { get; }
public bool IsEnabled
{
@@ -48,4 +48,4 @@ namespace Ryujinx.Ava.UI.Models
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled)));
}
}
}
}

View File

@@ -4,6 +4,6 @@ namespace Ryujinx.Ava.UI.Models
{
None,
Keyboard,
Controller
Controller,
}
}
}

View File

@@ -18,18 +18,18 @@ namespace Ryujinx.Ava.UI.Models
}
}
public string TitleId { get; }
public string TitleId { get; }
public string ContainerPath { get; }
public string FullPath { get; }
public string FullPath { get; }
public string FileName => Path.GetFileName(ContainerPath);
public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled)
{
TitleId = titleId;
TitleId = titleId;
ContainerPath = containerPath;
FullPath = fullPath;
Enabled = enabled;
FullPath = fullPath;
Enabled = enabled;
}
}
}
}

View File

@@ -29,4 +29,4 @@ namespace Ryujinx.Ava.UI.Models.Generic
return (IsAscending ? 1 : -1) * DateTime.Compare(bValue.Value, aValue.Value);
}
}
}
}

View File

@@ -7,16 +7,16 @@ using System;
namespace Ryujinx.Ava.UI.Models
{
internal class InputConfiguration<Key, Stick> : BaseModel
internal class InputConfiguration<TKey, TStick> : BaseModel
{
private float _deadzoneRight;
private float _triggerThreshold;
private float _deadzoneLeft;
private double _gyroDeadzone;
private int _sensitivity;
private bool enableMotion;
private float weakRumble;
private float strongRumble;
private bool _enableMotion;
private float _weakRumble;
private float _strongRumble;
private float _rangeLeft;
private float _rangeRight;
@@ -37,17 +37,17 @@ namespace Ryujinx.Ava.UI.Models
/// </summary>
public PlayerIndex PlayerIndex { get; set; }
public Stick LeftJoystick { get; set; }
public TStick LeftJoystick { get; set; }
public bool LeftInvertStickX { get; set; }
public bool LeftInvertStickY { get; set; }
public bool RightRotate90 { get; set; }
public Key LeftControllerStickButton { get; set; }
public TKey LeftControllerStickButton { get; set; }
public Stick RightJoystick { get; set; }
public TStick RightJoystick { get; set; }
public bool RightInvertStickX { get; set; }
public bool RightInvertStickY { get; set; }
public bool LeftRotate90 { get; set; }
public Key RightControllerStickButton { get; set; }
public TKey RightControllerStickButton { get; set; }
public float DeadzoneLeft
{
@@ -106,38 +106,37 @@ namespace Ryujinx.Ava.UI.Models
public MotionInputBackendType MotionBackend { get; set; }
public Key ButtonMinus { get; set; }
public Key ButtonL { get; set; }
public Key ButtonZl { get; set; }
public Key LeftButtonSl { get; set; }
public Key LeftButtonSr { get; set; }
public Key DpadUp { get; set; }
public Key DpadDown { get; set; }
public Key DpadLeft { get; set; }
public Key DpadRight { get; set; }
public TKey ButtonMinus { get; set; }
public TKey ButtonL { get; set; }
public TKey ButtonZl { get; set; }
public TKey LeftButtonSl { get; set; }
public TKey LeftButtonSr { get; set; }
public TKey DpadUp { get; set; }
public TKey DpadDown { get; set; }
public TKey DpadLeft { get; set; }
public TKey DpadRight { get; set; }
public Key ButtonPlus { get; set; }
public Key ButtonR { get; set; }
public Key ButtonZr { get; set; }
public Key RightButtonSl { get; set; }
public Key RightButtonSr { get; set; }
public Key ButtonX { get; set; }
public Key ButtonB { get; set; }
public Key ButtonY { get; set; }
public Key ButtonA { get; set; }
public TKey ButtonPlus { get; set; }
public TKey ButtonR { get; set; }
public TKey ButtonZr { get; set; }
public TKey RightButtonSl { get; set; }
public TKey RightButtonSr { get; set; }
public TKey ButtonX { get; set; }
public TKey ButtonB { get; set; }
public TKey ButtonY { get; set; }
public TKey ButtonA { get; set; }
public TKey LeftStickUp { get; set; }
public TKey LeftStickDown { get; set; }
public TKey LeftStickLeft { get; set; }
public TKey LeftStickRight { get; set; }
public TKey LeftKeyboardStickButton { get; set; }
public Key LeftStickUp { get; set; }
public Key LeftStickDown { get; set; }
public Key LeftStickLeft { get; set; }
public Key LeftStickRight { get; set; }
public Key LeftKeyboardStickButton { get; set; }
public Key RightStickUp { get; set; }
public Key RightStickDown { get; set; }
public Key RightStickLeft { get; set; }
public Key RightStickRight { get; set; }
public Key RightKeyboardStickButton { get; set; }
public TKey RightStickUp { get; set; }
public TKey RightStickDown { get; set; }
public TKey RightStickLeft { get; set; }
public TKey RightStickRight { get; set; }
public TKey RightKeyboardStickButton { get; set; }
public int Sensitivity
{
@@ -163,9 +162,9 @@ namespace Ryujinx.Ava.UI.Models
public bool EnableMotion
{
get => enableMotion; set
get => _enableMotion; set
{
enableMotion = value;
_enableMotion = value;
OnPropertyChanged();
}
@@ -181,18 +180,18 @@ namespace Ryujinx.Ava.UI.Models
public bool EnableRumble { get; set; }
public float WeakRumble
{
get => weakRumble; set
get => _weakRumble; set
{
weakRumble = value;
_weakRumble = value;
OnPropertyChanged();
}
}
public float StrongRumble
{
get => strongRumble; set
get => _strongRumble; set
{
strongRumble = value;
_strongRumble = value;
OnPropertyChanged();
}
@@ -209,71 +208,71 @@ namespace Ryujinx.Ava.UI.Models
if (config is StandardKeyboardInputConfig keyboardConfig)
{
LeftStickUp = (Key)(object)keyboardConfig.LeftJoyconStick.StickUp;
LeftStickDown = (Key)(object)keyboardConfig.LeftJoyconStick.StickDown;
LeftStickLeft = (Key)(object)keyboardConfig.LeftJoyconStick.StickLeft;
LeftStickRight = (Key)(object)keyboardConfig.LeftJoyconStick.StickRight;
LeftKeyboardStickButton = (Key)(object)keyboardConfig.LeftJoyconStick.StickButton;
LeftStickUp = (TKey)(object)keyboardConfig.LeftJoyconStick.StickUp;
LeftStickDown = (TKey)(object)keyboardConfig.LeftJoyconStick.StickDown;
LeftStickLeft = (TKey)(object)keyboardConfig.LeftJoyconStick.StickLeft;
LeftStickRight = (TKey)(object)keyboardConfig.LeftJoyconStick.StickRight;
LeftKeyboardStickButton = (TKey)(object)keyboardConfig.LeftJoyconStick.StickButton;
RightStickUp = (Key)(object)keyboardConfig.RightJoyconStick.StickUp;
RightStickDown = (Key)(object)keyboardConfig.RightJoyconStick.StickDown;
RightStickLeft = (Key)(object)keyboardConfig.RightJoyconStick.StickLeft;
RightStickRight = (Key)(object)keyboardConfig.RightJoyconStick.StickRight;
RightKeyboardStickButton = (Key)(object)keyboardConfig.RightJoyconStick.StickButton;
RightStickUp = (TKey)(object)keyboardConfig.RightJoyconStick.StickUp;
RightStickDown = (TKey)(object)keyboardConfig.RightJoyconStick.StickDown;
RightStickLeft = (TKey)(object)keyboardConfig.RightJoyconStick.StickLeft;
RightStickRight = (TKey)(object)keyboardConfig.RightJoyconStick.StickRight;
RightKeyboardStickButton = (TKey)(object)keyboardConfig.RightJoyconStick.StickButton;
ButtonA = (Key)(object)keyboardConfig.RightJoycon.ButtonA;
ButtonB = (Key)(object)keyboardConfig.RightJoycon.ButtonB;
ButtonX = (Key)(object)keyboardConfig.RightJoycon.ButtonX;
ButtonY = (Key)(object)keyboardConfig.RightJoycon.ButtonY;
ButtonR = (Key)(object)keyboardConfig.RightJoycon.ButtonR;
RightButtonSl = (Key)(object)keyboardConfig.RightJoycon.ButtonSl;
RightButtonSr = (Key)(object)keyboardConfig.RightJoycon.ButtonSr;
ButtonZr = (Key)(object)keyboardConfig.RightJoycon.ButtonZr;
ButtonPlus = (Key)(object)keyboardConfig.RightJoycon.ButtonPlus;
ButtonA = (TKey)(object)keyboardConfig.RightJoycon.ButtonA;
ButtonB = (TKey)(object)keyboardConfig.RightJoycon.ButtonB;
ButtonX = (TKey)(object)keyboardConfig.RightJoycon.ButtonX;
ButtonY = (TKey)(object)keyboardConfig.RightJoycon.ButtonY;
ButtonR = (TKey)(object)keyboardConfig.RightJoycon.ButtonR;
RightButtonSl = (TKey)(object)keyboardConfig.RightJoycon.ButtonSl;
RightButtonSr = (TKey)(object)keyboardConfig.RightJoycon.ButtonSr;
ButtonZr = (TKey)(object)keyboardConfig.RightJoycon.ButtonZr;
ButtonPlus = (TKey)(object)keyboardConfig.RightJoycon.ButtonPlus;
DpadUp = (Key)(object)keyboardConfig.LeftJoycon.DpadUp;
DpadDown = (Key)(object)keyboardConfig.LeftJoycon.DpadDown;
DpadLeft = (Key)(object)keyboardConfig.LeftJoycon.DpadLeft;
DpadRight = (Key)(object)keyboardConfig.LeftJoycon.DpadRight;
ButtonMinus = (Key)(object)keyboardConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (Key)(object)keyboardConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (Key)(object)keyboardConfig.LeftJoycon.ButtonSr;
ButtonZl = (Key)(object)keyboardConfig.LeftJoycon.ButtonZl;
ButtonL = (Key)(object)keyboardConfig.LeftJoycon.ButtonL;
DpadUp = (TKey)(object)keyboardConfig.LeftJoycon.DpadUp;
DpadDown = (TKey)(object)keyboardConfig.LeftJoycon.DpadDown;
DpadLeft = (TKey)(object)keyboardConfig.LeftJoycon.DpadLeft;
DpadRight = (TKey)(object)keyboardConfig.LeftJoycon.DpadRight;
ButtonMinus = (TKey)(object)keyboardConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (TKey)(object)keyboardConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (TKey)(object)keyboardConfig.LeftJoycon.ButtonSr;
ButtonZl = (TKey)(object)keyboardConfig.LeftJoycon.ButtonZl;
ButtonL = (TKey)(object)keyboardConfig.LeftJoycon.ButtonL;
}
else if (config is StandardControllerInputConfig controllerConfig)
{
LeftJoystick = (Stick)(object)controllerConfig.LeftJoyconStick.Joystick;
LeftJoystick = (TStick)(object)controllerConfig.LeftJoyconStick.Joystick;
LeftInvertStickX = controllerConfig.LeftJoyconStick.InvertStickX;
LeftInvertStickY = controllerConfig.LeftJoyconStick.InvertStickY;
LeftRotate90 = controllerConfig.LeftJoyconStick.Rotate90CW;
LeftControllerStickButton = (Key)(object)controllerConfig.LeftJoyconStick.StickButton;
LeftControllerStickButton = (TKey)(object)controllerConfig.LeftJoyconStick.StickButton;
RightJoystick = (Stick)(object)controllerConfig.RightJoyconStick.Joystick;
RightJoystick = (TStick)(object)controllerConfig.RightJoyconStick.Joystick;
RightInvertStickX = controllerConfig.RightJoyconStick.InvertStickX;
RightInvertStickY = controllerConfig.RightJoyconStick.InvertStickY;
RightRotate90 = controllerConfig.RightJoyconStick.Rotate90CW;
RightControllerStickButton = (Key)(object)controllerConfig.RightJoyconStick.StickButton;
RightControllerStickButton = (TKey)(object)controllerConfig.RightJoyconStick.StickButton;
ButtonA = (Key)(object)controllerConfig.RightJoycon.ButtonA;
ButtonB = (Key)(object)controllerConfig.RightJoycon.ButtonB;
ButtonX = (Key)(object)controllerConfig.RightJoycon.ButtonX;
ButtonY = (Key)(object)controllerConfig.RightJoycon.ButtonY;
ButtonR = (Key)(object)controllerConfig.RightJoycon.ButtonR;
RightButtonSl = (Key)(object)controllerConfig.RightJoycon.ButtonSl;
RightButtonSr = (Key)(object)controllerConfig.RightJoycon.ButtonSr;
ButtonZr = (Key)(object)controllerConfig.RightJoycon.ButtonZr;
ButtonPlus = (Key)(object)controllerConfig.RightJoycon.ButtonPlus;
ButtonA = (TKey)(object)controllerConfig.RightJoycon.ButtonA;
ButtonB = (TKey)(object)controllerConfig.RightJoycon.ButtonB;
ButtonX = (TKey)(object)controllerConfig.RightJoycon.ButtonX;
ButtonY = (TKey)(object)controllerConfig.RightJoycon.ButtonY;
ButtonR = (TKey)(object)controllerConfig.RightJoycon.ButtonR;
RightButtonSl = (TKey)(object)controllerConfig.RightJoycon.ButtonSl;
RightButtonSr = (TKey)(object)controllerConfig.RightJoycon.ButtonSr;
ButtonZr = (TKey)(object)controllerConfig.RightJoycon.ButtonZr;
ButtonPlus = (TKey)(object)controllerConfig.RightJoycon.ButtonPlus;
DpadUp = (Key)(object)controllerConfig.LeftJoycon.DpadUp;
DpadDown = (Key)(object)controllerConfig.LeftJoycon.DpadDown;
DpadLeft = (Key)(object)controllerConfig.LeftJoycon.DpadLeft;
DpadRight = (Key)(object)controllerConfig.LeftJoycon.DpadRight;
ButtonMinus = (Key)(object)controllerConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (Key)(object)controllerConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (Key)(object)controllerConfig.LeftJoycon.ButtonSr;
ButtonZl = (Key)(object)controllerConfig.LeftJoycon.ButtonZl;
ButtonL = (Key)(object)controllerConfig.LeftJoycon.ButtonL;
DpadUp = (TKey)(object)controllerConfig.LeftJoycon.DpadUp;
DpadDown = (TKey)(object)controllerConfig.LeftJoycon.DpadDown;
DpadLeft = (TKey)(object)controllerConfig.LeftJoycon.DpadLeft;
DpadRight = (TKey)(object)controllerConfig.LeftJoycon.DpadRight;
ButtonMinus = (TKey)(object)controllerConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (TKey)(object)controllerConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (TKey)(object)controllerConfig.LeftJoycon.ButtonSr;
ButtonZl = (TKey)(object)controllerConfig.LeftJoycon.ButtonZl;
ButtonL = (TKey)(object)controllerConfig.LeftJoycon.ButtonL;
DeadzoneLeft = controllerConfig.DeadzoneLeft;
DeadzoneRight = controllerConfig.DeadzoneRight;
@@ -317,65 +316,66 @@ namespace Ryujinx.Ava.UI.Models
{
if (Backend == InputBackendType.WindowKeyboard)
{
return new StandardKeyboardInputConfig()
return new StandardKeyboardInputConfig
{
Id = Id,
Backend = Backend,
PlayerIndex = PlayerIndex,
ControllerType = ControllerType,
LeftJoycon = new LeftJoyconCommonConfig<Ryujinx.Common.Configuration.Hid.Key>()
LeftJoycon = new LeftJoyconCommonConfig<Key>
{
DpadUp = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadUp,
DpadDown = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadDown,
DpadLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadLeft,
DpadRight = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadRight,
ButtonL = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonL,
ButtonZl = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonZl,
ButtonSl = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftButtonSl,
ButtonSr = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftButtonSr,
ButtonMinus = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonMinus
DpadUp = (Key)(object)DpadUp,
DpadDown = (Key)(object)DpadDown,
DpadLeft = (Key)(object)DpadLeft,
DpadRight = (Key)(object)DpadRight,
ButtonL = (Key)(object)ButtonL,
ButtonZl = (Key)(object)ButtonZl,
ButtonSl = (Key)(object)LeftButtonSl,
ButtonSr = (Key)(object)LeftButtonSr,
ButtonMinus = (Key)(object)ButtonMinus,
},
RightJoycon = new RightJoyconCommonConfig<Ryujinx.Common.Configuration.Hid.Key>()
RightJoycon = new RightJoyconCommonConfig<Key>
{
ButtonA = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonA,
ButtonB = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonB,
ButtonX = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonX,
ButtonY = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonY,
ButtonPlus = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonPlus,
ButtonSl = (Ryujinx.Common.Configuration.Hid.Key)(object)RightButtonSl,
ButtonSr = (Ryujinx.Common.Configuration.Hid.Key)(object)RightButtonSr,
ButtonR = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonR,
ButtonZr = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonZr
ButtonA = (Key)(object)ButtonA,
ButtonB = (Key)(object)ButtonB,
ButtonX = (Key)(object)ButtonX,
ButtonY = (Key)(object)ButtonY,
ButtonPlus = (Key)(object)ButtonPlus,
ButtonSl = (Key)(object)RightButtonSl,
ButtonSr = (Key)(object)RightButtonSr,
ButtonR = (Key)(object)ButtonR,
ButtonZr = (Key)(object)ButtonZr,
},
LeftJoyconStick = new JoyconConfigKeyboardStick<Ryujinx.Common.Configuration.Hid.Key>()
LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickUp,
StickDown = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickDown,
StickRight = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickRight,
StickLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickLeft,
StickButton = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftKeyboardStickButton
StickUp = (Key)(object)LeftStickUp,
StickDown = (Key)(object)LeftStickDown,
StickRight = (Key)(object)LeftStickRight,
StickLeft = (Key)(object)LeftStickLeft,
StickButton = (Key)(object)LeftKeyboardStickButton,
},
RightJoyconStick = new JoyconConfigKeyboardStick<Ryujinx.Common.Configuration.Hid.Key>()
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickUp,
StickDown = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickDown,
StickLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickLeft,
StickRight = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickRight,
StickButton = (Ryujinx.Common.Configuration.Hid.Key)(object)RightKeyboardStickButton
StickUp = (Key)(object)RightStickUp,
StickDown = (Key)(object)RightStickDown,
StickLeft = (Key)(object)RightStickLeft,
StickRight = (Key)(object)RightStickRight,
StickButton = (Key)(object)RightKeyboardStickButton,
},
Version = InputConfig.CurrentVersion
Version = InputConfig.CurrentVersion,
};
}
else if (Backend == InputBackendType.GamepadSDL2)
if (Backend == InputBackendType.GamepadSDL2)
{
var config = new StandardControllerInputConfig()
var config = new StandardControllerInputConfig
{
Id = Id,
Backend = Backend,
PlayerIndex = PlayerIndex,
ControllerType = ControllerType,
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>()
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>
{
DpadUp = (GamepadInputId)(object)DpadUp,
DpadDown = (GamepadInputId)(object)DpadDown,
@@ -387,7 +387,7 @@ namespace Ryujinx.Ava.UI.Models
ButtonSr = (GamepadInputId)(object)LeftButtonSr,
ButtonMinus = (GamepadInputId)(object)ButtonMinus,
},
RightJoycon = new RightJoyconCommonConfig<GamepadInputId>()
RightJoycon = new RightJoyconCommonConfig<GamepadInputId>
{
ButtonA = (GamepadInputId)(object)ButtonA,
ButtonB = (GamepadInputId)(object)ButtonB,
@@ -399,7 +399,7 @@ namespace Ryujinx.Ava.UI.Models
ButtonR = (GamepadInputId)(object)ButtonR,
ButtonZr = (GamepadInputId)(object)ButtonZr,
},
LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>()
LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
{
Joystick = (StickInputId)(object)LeftJoystick,
InvertStickX = LeftInvertStickX,
@@ -407,7 +407,7 @@ namespace Ryujinx.Ava.UI.Models
Rotate90CW = LeftRotate90,
StickButton = (GamepadInputId)(object)LeftControllerStickButton,
},
RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>()
RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
{
Joystick = (StickInputId)(object)RightJoystick,
InvertStickX = RightInvertStickX,
@@ -415,11 +415,11 @@ namespace Ryujinx.Ava.UI.Models
Rotate90CW = RightRotate90,
StickButton = (GamepadInputId)(object)RightControllerStickButton,
},
Rumble = new RumbleConfigController()
Rumble = new RumbleConfigController
{
EnableRumble = EnableRumble,
WeakRumble = WeakRumble,
StrongRumble = StrongRumble
StrongRumble = StrongRumble,
},
Version = InputConfig.CurrentVersion,
DeadzoneLeft = DeadzoneLeft,
@@ -428,19 +428,19 @@ namespace Ryujinx.Ava.UI.Models
RangeRight = RangeRight,
TriggerThreshold = TriggerThreshold,
Motion = EnableCemuHookMotion
? new CemuHookMotionConfigController()
{
DsuServerHost = DsuServerHost,
DsuServerPort = DsuServerPort,
Slot = Slot,
AltSlot = AltSlot,
MirrorInput = MirrorInput,
MotionBackend = MotionInputBackendType.CemuHook
}
: new StandardMotionConfigController()
{
MotionBackend = MotionInputBackendType.GamepadDriver
}
? new CemuHookMotionConfigController
{
DsuServerHost = DsuServerHost,
DsuServerPort = DsuServerPort,
Slot = Slot,
AltSlot = AltSlot,
MirrorInput = MirrorInput,
MotionBackend = MotionInputBackendType.CemuHook,
}
: new StandardMotionConfigController
{
MotionBackend = MotionInputBackendType.GamepadDriver,
},
};
config.Motion.Sensitivity = Sensitivity;
@@ -453,4 +453,4 @@ namespace Ryujinx.Ava.UI.Models
return null;
}
}
}
}

View File

@@ -29,4 +29,4 @@ namespace Ryujinx.Ava.UI.Models
}
}
}
}
}

View File

@@ -8,6 +8,7 @@ using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Path = System.IO.Path;
namespace Ryujinx.Ava.UI.Models
{
@@ -58,7 +59,7 @@ namespace Ryujinx.Ava.UI.Models
return "0 KiB";
}
public SaveModel(SaveDataInfo info, VirtualFileSystem virtualFileSystem)
public SaveModel(SaveDataInfo info)
{
SaveId = info.SaveDataId;
TitleId = info.ProgramId;
@@ -81,10 +82,11 @@ namespace Ryujinx.Ava.UI.Models
Task.Run(() =>
{
var saveRoot = System.IO.Path.Combine(virtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}");
var saveRoot = Path.Combine(VirtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}");
long total_size = GetDirectorySize(saveRoot);
long GetDirectorySize(string path)
long totalSize = GetDirectorySize(saveRoot);
static long GetDirectorySize(string path)
{
long size = 0;
if (Directory.Exists(path))
@@ -105,7 +107,7 @@ namespace Ryujinx.Ava.UI.Models
return size;
}
Size = total_size;
Size = totalSize;
});
}

View File

@@ -25,4 +25,4 @@ namespace Ryujinx.Ava.UI.Models
GpuName = gpuName;
}
}
}
}

View File

@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Models
private string _name = String.Empty;
private UserId _userId;
public uint MaxProfileNameLength => 0x20;
public static uint MaxProfileNameLength => 0x20;
public byte[] Image
{
@@ -58,4 +58,4 @@ namespace Ryujinx.Ava.UI.Models
}
}
}
}
}

View File

@@ -13,4 +13,4 @@ namespace Ryujinx.Ava.UI.Models
public string Location { get; set; }
public string Abbreviation { get; set; }
}
}
}

View File

@@ -16,4 +16,4 @@ namespace Ryujinx.Ava.UI.Models
Path = path;
}
}
}
}

View File

@@ -87,7 +87,8 @@ namespace Ryujinx.Ava.UI.Models
private void UpdateBackground()
{
Avalonia.Application.Current.Styles.TryGetResource("ControlFillColorSecondary", out object color);
var currentApplication = Avalonia.Application.Current;
currentApplication.Styles.TryGetResource("ControlFillColorSecondary", currentApplication.ActualThemeVariant, out object color);
if (color is not null)
{
@@ -100,4 +101,4 @@ namespace Ryujinx.Ava.UI.Models
_owner.Navigate(typeof(UserEditorView), (_owner, userProfile, true));
}
}
}
}

View File

@@ -21,20 +21,20 @@ namespace Ryujinx.Ava.UI.Renderer
public class EmbeddedWindow : NativeControlHost
{
private WindowProc _wndProcDelegate;
private string _className;
private string _className;
protected GLXWindow X11Window { get; set; }
protected IntPtr WindowHandle { get; set; }
protected IntPtr X11Display { get; set; }
protected IntPtr NsView { get; set; }
protected IntPtr MetalLayer { get; set; }
protected IntPtr X11Display { get; set; }
protected IntPtr NsView { get; set; }
protected IntPtr MetalLayer { get; set; }
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
public event EventHandler<IntPtr> WindowCreated;
public event EventHandler<Size> SizeChanged;
public event EventHandler<Size> BoundsChanged;
public EmbeddedWindow()
{
@@ -50,9 +50,9 @@ namespace Ryujinx.Ava.UI.Renderer
protected virtual void OnWindowDestroying()
{
WindowHandle = IntPtr.Zero;
X11Display = IntPtr.Zero;
NsView = IntPtr.Zero;
MetalLayer = IntPtr.Zero;
X11Display = IntPtr.Zero;
NsView = IntPtr.Zero;
MetalLayer = IntPtr.Zero;
}
private void OnNativeEmbeddedWindowCreated(object sender, EventArgs e)
@@ -67,7 +67,7 @@ namespace Ryujinx.Ava.UI.Renderer
private void StateChanged(Rect rect)
{
SizeChanged?.Invoke(this, rect.Size);
BoundsChanged?.Invoke(this, rect.Size);
_updateBoundsCallback?.Invoke(rect);
}
@@ -77,11 +77,13 @@ namespace Ryujinx.Ava.UI.Renderer
{
return CreateLinux(control);
}
else if (OperatingSystem.IsWindows())
if (OperatingSystem.IsWindows())
{
return CreateWin32(control);
}
else if (OperatingSystem.IsMacOS())
if (OperatingSystem.IsMacOS())
{
return CreateMacOS();
}
@@ -127,7 +129,7 @@ namespace Ryujinx.Ava.UI.Renderer
}
WindowHandle = X11Window.WindowHandle.RawHandle;
X11Display = X11Window.DisplayHandle.RawHandle;
X11Display = X11Window.DisplayHandle.RawHandle;
return new PlatformHandle(WindowHandle, "X11");
}
@@ -141,28 +143,29 @@ namespace Ryujinx.Ava.UI.Renderer
{
if (VisualRoot != null)
{
if (msg == WindowsMessages.LBUTTONDOWN ||
msg == WindowsMessages.RBUTTONDOWN ||
msg == WindowsMessages.LBUTTONUP ||
msg == WindowsMessages.RBUTTONUP ||
msg == WindowsMessages.MOUSEMOVE)
if (msg == WindowsMessages.Lbuttondown ||
msg == WindowsMessages.Rbuttondown ||
msg == WindowsMessages.Lbuttonup ||
msg == WindowsMessages.Rbuttonup ||
msg == WindowsMessages.Mousemove)
{
Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value;
Pointer pointer = new(0, PointerType.Mouse, true);
Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), this).Value;
Pointer pointer = new(0, PointerType.Mouse, true);
#pragma warning disable CS0618 // Type or member is obsolete (As of Avalonia 11, the constructors for PointerPressedEventArgs & PointerEventArgs are marked as obsolete)
switch (msg)
{
case WindowsMessages.LBUTTONDOWN:
case WindowsMessages.RBUTTONDOWN:
case WindowsMessages.Lbuttondown:
case WindowsMessages.Rbuttondown:
{
bool isLeft = msg == WindowsMessages.LBUTTONDOWN;
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
bool isLeft = msg == WindowsMessages.Lbuttondown;
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
var evnt = new PointerPressedEventArgs(
this,
pointer,
VisualRoot,
this,
rootVisualPosition,
(ulong)Environment.TickCount64,
properties,
@@ -172,17 +175,17 @@ namespace Ryujinx.Ava.UI.Renderer
break;
}
case WindowsMessages.LBUTTONUP:
case WindowsMessages.RBUTTONUP:
case WindowsMessages.Lbuttonup:
case WindowsMessages.Rbuttonup:
{
bool isLeft = msg == WindowsMessages.LBUTTONUP;
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
bool isLeft = msg == WindowsMessages.Lbuttonup;
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
var evnt = new PointerReleasedEventArgs(
this,
pointer,
VisualRoot,
this,
rootVisualPosition,
(ulong)Environment.TickCount64,
properties,
@@ -193,13 +196,13 @@ namespace Ryujinx.Ava.UI.Renderer
break;
}
case WindowsMessages.MOUSEMOVE:
case WindowsMessages.Mousemove:
{
var evnt = new PointerEventArgs(
PointerMovedEvent,
this,
pointer,
VisualRoot,
this,
rootVisualPosition,
(ulong)Environment.TickCount64,
new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
@@ -210,25 +213,26 @@ namespace Ryujinx.Ava.UI.Renderer
break;
}
}
#pragma warning restore CS0618
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
};
WNDCLASSEX wndClassEx = new()
WndClassEx wndClassEx = new()
{
cbSize = Marshal.SizeOf<WNDCLASSEX>(),
hInstance = GetModuleHandle(null),
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
style = ClassStyles.CS_OWNDC,
cbSize = Marshal.SizeOf<WndClassEx>(),
hInstance = GetModuleHandle(null),
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
style = ClassStyles.CsOwndc,
lpszClassName = Marshal.StringToHGlobalUni(_className),
hCursor = CreateArrowCursor()
hCursor = CreateArrowCursor(),
};
RegisterClassEx(ref wndClassEx);
WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WS_CHILD, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WsChild, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
Marshal.FreeHGlobal(wndClassEx.lpszClassName);
@@ -280,9 +284,11 @@ namespace Ryujinx.Ava.UI.Renderer
}
[SupportedOSPlatform("macos")]
#pragma warning disable CA1822 // Mark member as static
void DestroyMacOS()
{
// TODO
}
#pragma warning restore CA1822
}
}
}

View File

@@ -91,4 +91,4 @@ namespace Ryujinx.Ava.UI.Renderer
MakeCurrent();
}
}
}
}

View File

@@ -34,9 +34,9 @@ namespace Ryujinx.Ava.UI.Renderer
return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, nativeWindowBase));
}
public SurfaceKHR CreateSurface(Instance instance, Vk api)
public SurfaceKHR CreateSurface(Instance instance, Vk _)
{
return CreateSurface(instance);
}
}
}
}

View File

@@ -17,4 +17,4 @@ namespace Ryujinx.Ava.UI.Renderer
return _getProcAddress(procName);
}
}
}
}

View File

@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Renderer
public readonly EmbeddedWindow EmbeddedWindow;
public event EventHandler<EventArgs> WindowCreated;
public event Action<object, Size> SizeChanged;
public event Action<object, Size> BoundsChanged;
public RendererHost()
{
@@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.Renderer
private void Initialize()
{
EmbeddedWindow.WindowCreated += CurrentWindow_WindowCreated;
EmbeddedWindow.SizeChanged += CurrentWindow_SizeChanged;
EmbeddedWindow.BoundsChanged += CurrentWindow_BoundsChanged;
Content = EmbeddedWindow;
}
@@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Renderer
if (EmbeddedWindow != null)
{
EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated;
EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged;
EmbeddedWindow.BoundsChanged -= CurrentWindow_BoundsChanged;
}
GC.SuppressFinalize(this);
@@ -55,9 +55,9 @@ namespace Ryujinx.Ava.UI.Renderer
Dispose();
}
private void CurrentWindow_SizeChanged(object sender, Size e)
private void CurrentWindow_BoundsChanged(object sender, Size e)
{
SizeChanged?.Invoke(sender, e);
BoundsChanged?.Invoke(sender, e);
}
private void CurrentWindow_WindowCreated(object sender, IntPtr e)
@@ -65,4 +65,4 @@ namespace Ryujinx.Ava.UI.Renderer
WindowCreated?.Invoke(this, EventArgs.Empty);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More