From 28dd7d80af56701887dbb538b56aa58edaf39d91 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 18 Oct 2023 01:47:22 -0300 Subject: [PATCH 1/7] Enable copy between MS and non-MS textures with different height (#5801) --- src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index eafa50b2e..3a0efcdda 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -374,6 +374,13 @@ namespace Ryujinx.Graphics.Gpu.Image return stride == rhs.Stride ? TextureViewCompatibility.CopyOnly : TextureViewCompatibility.LayoutIncompatible; } + else if (lhs.Target.IsMultisample() != rhs.Target.IsMultisample() && alignedWidthMatches && lhsAlignedSize.Height == rhsAlignedSize.Height) + { + // Copy between multisample and non-multisample textures with mismatching size is allowed, + // as long aligned size matches. + + return TextureViewCompatibility.CopyOnly; + } else { return TextureViewCompatibility.LayoutIncompatible; From 76b53e018a2e867899dbce2f3ce5173bbc4eed22 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 20 Oct 2023 14:05:09 +0100 Subject: [PATCH 2/7] GPU: Add fallback when textureGatherOffsets is not supported (#5792) * GPU: Add fallback when textureGatherOffsets is not supported. This PR adds a fallback for GPUs or APIs that don't support an equivalent to the method `textureGatherOffsets`, where each of the 4 gathered texels has an individual offset. This is done by reusing the existing code to handle non-const offsets for texture instructions, though it has also been corrected as there were a few implementation issues. MoltenVK reports support for this capability, and it didn't error when we initially released the MacOS build, but that has since changed. MVK still reports support, but spirv-cross has been fixed in a way that it _attempts_ to use this capability, but the metal compiler errors since it doesn't exist. Some other fixes: - textureGatherOffsets emulation has been changed significantly. It now uses 4 texture sample instructions (not gather), calculates a base texel (i=0 j=0) and adds the offsets onto it before converting into a tex coord. The final result is offset into a texel center, so it shouldn't be subject to interpolation, though this isn't perfect and could have some error with floating point formats with linear sampling. It is subject to texture wrap mode as it should be, which is why texelFetch was not used. - Maybe gather should be used here with component `w` (i=0, j=0), though this multiplies number of texels fetched by 4... The way it was doing this before _was_ wrong_, but doing it right would avoid issues with texel center precision. - textureGatherOffset (singular) now performs textureGather with the offset applied to the coords, rather than the slower fallback where each texel is fetched individually. * Increment shader cache version, remove unused arg * Use base texture size for gather coord offset. Implicit LOD for gather is not supported. * Use 4 texture gathers for offsets emulation Avoids issues with interpolation at cost of performance (not sure how bad this is) * Address Feedback --- src/Ryujinx.Graphics.GAL/Capabilities.cs | 3 + .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 2 + src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 1 + src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 +++ .../Translation/Transforms/TexturePass.cs | 64 ++++++++++++++++--- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 1 + 7 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs index 756422049..8959bf93e 100644 --- a/src/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.GAL public readonly bool SupportsShaderBallot; public readonly bool SupportsShaderBarrierDivergence; public readonly bool SupportsShaderFloat64; + public readonly bool SupportsTextureGatherOffsets; public readonly bool SupportsTextureShadowLod; public readonly bool SupportsVertexStoreAndAtomics; public readonly bool SupportsViewportIndexVertexTessellation; @@ -92,6 +93,7 @@ namespace Ryujinx.Graphics.GAL bool supportsShaderBallot, bool supportsShaderBarrierDivergence, bool supportsShaderFloat64, + bool supportsTextureGatherOffsets, bool supportsTextureShadowLod, bool supportsVertexStoreAndAtomics, bool supportsViewportIndexVertexTessellation, @@ -142,6 +144,7 @@ namespace Ryujinx.Graphics.GAL SupportsShaderBallot = supportsShaderBallot; SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence; SupportsShaderFloat64 = supportsShaderFloat64; + SupportsTextureGatherOffsets = supportsTextureGatherOffsets; SupportsTextureShadowLod = supportsTextureShadowLod; SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics; SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 0f1aa6a96..0dc4b1a72 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5767; + private const uint CodeGenVersion = 5791; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 9d030cd60..a5b31363b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -186,6 +186,8 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat; + public bool QueryHostSupportsTextureGatherOffsets() => _context.Capabilities.SupportsTextureGatherOffsets; + public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod; public bool QueryHostSupportsTransformFeedback() => _context.Capabilities.SupportsTransformFeedback; diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 3eba15e34..667ea7825 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -163,6 +163,7 @@ namespace Ryujinx.Graphics.OpenGL supportsShaderBallot: HwCapabilities.SupportsShaderBallot, supportsShaderBarrierDivergence: !(intelWindows || intelUnix), supportsShaderFloat64: true, + supportsTextureGatherOffsets: true, supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod, supportsVertexStoreAndAtomics: true, supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 4dc75a3e1..29a5435e3 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -339,6 +339,15 @@ namespace Ryujinx.Graphics.Shader return true; } + /// + /// Queries host GPU texture gather with multiple offsets support. + /// + /// True if the GPU and driver supports texture gather offsets, false otherwise + bool QueryHostSupportsTextureGatherOffsets() + { + return true; + } + /// /// Queries host GPU texture shadow LOD support. /// diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs index dbfe6269e..495ea8a94 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs @@ -303,7 +303,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; - bool hasInvalidOffset = (hasOffset || hasOffsets) && !gpuAccessor.QueryHostSupportsNonConstantTextureOffset(); + bool needsOffsetsEmulation = hasOffsets && !gpuAccessor.QueryHostSupportsTextureGatherOffsets(); + + bool hasInvalidOffset = needsOffsetsEmulation || ((hasOffset || hasOffsets) && !gpuAccessor.QueryHostSupportsNonConstantTextureOffset()); bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; @@ -402,11 +404,14 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms offsets[index] = offset; } - hasInvalidOffset &= !areAllOffsetsConstant; - - if (!hasInvalidOffset) + if (!needsOffsetsEmulation) { - return node; + hasInvalidOffset &= !areAllOffsetsConstant; + + if (!hasInvalidOffset) + { + return node; + } } if (hasLodBias) @@ -434,13 +439,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms LinkedListNode oldNode = node; - if (isGather && !isShadow) + if (isGather && !isShadow && hasOffsets) { Operand[] newSources = new Operand[sources.Length]; sources.CopyTo(newSources, 0); - Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage); + Operand[] texSizes = InsertTextureBaseSize(node, texOp, bindlessHandle, coordsCount); int destIndex = 0; @@ -455,7 +460,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms { Operand offset = Local(); - Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)]; + Operand intOffset = offsets[index + compIndex * coordsCount]; node.List.AddBefore(node, new Operation( Instruction.FP32 | Instruction.Divide, @@ -478,7 +483,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms texOp.Format, texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), texOp.Binding, - 1, + 1 << 3, // W component: i=0, j=0 new[] { dests[destIndex++] }, newSources); @@ -502,7 +507,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms } else { - Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage); + Operand[] texSizes = isGather + ? InsertTextureBaseSize(node, texOp, bindlessHandle, coordsCount) + : InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage); for (int index = 0; index < coordsCount; index++) { @@ -549,6 +556,43 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms return node; } + private static Operand[] InsertTextureBaseSize( + LinkedListNode node, + TextureOperation texOp, + Operand bindlessHandle, + int coordsCount) + { + Operand[] texSizes = new Operand[coordsCount]; + + for (int index = 0; index < coordsCount; index++) + { + texSizes[index] = Local(); + + Operand[] texSizeSources; + + if (bindlessHandle != null) + { + texSizeSources = new Operand[] { bindlessHandle, Const(0) }; + } + else + { + texSizeSources = new Operand[] { Const(0) }; + } + + node.List.AddBefore(node, new TextureOperation( + Instruction.TextureQuerySize, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.Binding, + index, + new[] { texSizes[index] }, + texSizeSources)); + } + + return texSizes; + } + private static Operand[] InsertTextureLod( LinkedListNode node, TextureOperation texOp, diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index a483dc599..ab8e61371 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -605,6 +605,7 @@ namespace Ryujinx.Graphics.Vulkan supportsShaderBallot: false, supportsShaderBarrierDivergence: Vendor != Vendor.Intel, supportsShaderFloat64: Capabilities.SupportsShaderFloat64, + supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk, supportsTextureShadowLod: false, supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics, supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex, From 6fdf7748455b2b71f99885239f8dc31390de2687 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Fri, 20 Oct 2023 09:41:50 -0400 Subject: [PATCH 3/7] Ava UI: Update to 11.0.5 (#5815) * Bump bump bump * Missed one --- Directory.Packages.props | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index cde8742f9..b34b882b4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,13 +3,13 @@ true - - - - - - - + + + + + + + From b4bb22ba06f89168c948e6001c51972575ca968b Mon Sep 17 00:00:00 2001 From: Ahmad Tantowi Date: Fri, 20 Oct 2023 21:02:12 +0700 Subject: [PATCH 4/7] Avalonia: Make slider scrollable with mouse wheel (#5760) * Add scrollable custom control based on TickFrequency * Use custom slider to update value when pointer wheel scrolled * Remove extra xaml file * Address formatting issues * Only scroll one element at a time * Add OnPointerWheelChanged event to VolumeStatus button Co-authored-by: Ahmad Tantowi --------- Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> --- .../UI/Controls/SliderScroll.axaml.cs | 31 +++++++++++++++++++ .../UI/Views/Input/ControllerInputView.axaml | 13 ++++---- .../UI/Views/Input/MotionInputView.axaml | 11 ++++--- .../UI/Views/Input/RumbleInputView.axaml | 7 +++-- .../UI/Views/Main/MainStatusBarView.axaml | 4 ++- .../UI/Views/Main/MainStatusBarView.axaml.cs | 15 +++++++++ .../UI/Views/Main/MainViewControls.axaml | 5 +-- .../UI/Views/Settings/SettingsAudioView.axaml | 9 +++--- .../Views/Settings/SettingsGraphicsView.axaml | 3 +- 9 files changed, 76 insertions(+), 22 deletions(-) create mode 100644 src/Ryujinx.Ava/UI/Controls/SliderScroll.axaml.cs diff --git a/src/Ryujinx.Ava/UI/Controls/SliderScroll.axaml.cs b/src/Ryujinx.Ava/UI/Controls/SliderScroll.axaml.cs new file mode 100644 index 000000000..81d3bc303 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Controls/SliderScroll.axaml.cs @@ -0,0 +1,31 @@ +using Avalonia.Controls; +using Avalonia.Input; +using System; + +namespace Ryujinx.Ava.UI.Controls +{ + public class SliderScroll : Slider + { + protected override Type StyleKeyOverride => typeof(Slider); + + protected override void OnPointerWheelChanged(PointerWheelEventArgs e) + { + var newValue = Value + e.Delta.Y * TickFrequency; + + if (newValue < Minimum) + { + Value = Minimum; + } + else if (newValue > Maximum) + { + Value = Maximum; + } + else + { + Value = newValue; + } + + e.Handled = true; + } + } +} diff --git a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml index 2ab42e6ee..d636873a3 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml @@ -5,6 +5,7 @@ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" @@ -460,7 +461,7 @@ HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal"> - - - - - - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml index a98f08825..a6b587f67 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" @@ -23,11 +24,11 @@ Margin="0" HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsMotionGyroSensitivity}" /> - - - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml index f633c0ed2..5b7087a47 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml @@ -1,6 +1,7 @@ - - - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml index 32524740b..01133a4bc 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" @@ -176,6 +177,7 @@ Content="{Binding VolumeStatusText}" IsChecked="{Binding VolumeMuted}" IsVisible="{Binding !ShowLoadProgress}" + PointerWheelChanged="VolumeStatus_OnPointerWheelChanged" Background="Transparent" BorderThickness="0" CornerRadius="0"> @@ -192,7 +194,7 @@ - 0, + > 1 => 1, + _ => newValue, + }; + + e.Handled = true; + } } } diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml index 34624b222..cc21b5c60 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml +++ b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" @@ -50,7 +51,7 @@ VerticalAlignment="Center" Text="{locale:Locale IconSize}" ToolTip.Tip="{locale:Locale IconSizeTooltip}" /> - - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml index 5dc0fef5d..657e07ee7 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml @@ -4,6 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" @@ -63,13 +64,13 @@ Maximum="100" /> - @@ -77,4 +78,4 @@ - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml index f6ba0a4c0..224494786 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml @@ -4,6 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" @@ -173,7 +174,7 @@ - Date: Sat, 21 Oct 2023 05:51:15 +1100 Subject: [PATCH 5/7] Add "Create Shortcut" To app context menu (#4734) * Added basic implementation for shortcut creation Currently bitmaps (.bmp) are used as the source file, colours are good (unlike .ico rn) but are scaled poorly on desktop. * Icons display properly in shortcut * code cleanup * Moved shortcut logic to specific file, added Ava UI for shortcuts * Added linux .desktop shortcut creation * fixes to .shortcut data * code issue fixes * Added basic implementation for shortcut creation Currently bitmaps (.bmp) are used as the source file, colours are good (unlike .ico rn) but are scaled poorly on desktop. * Icons display properly in shortcut * code cleanup * Moved shortcut logic to specific file, added Ava UI for shortcuts * Added linux .desktop shortcut creation * fixes to .shortcut data * code issue fixes * added back shortcut to new contextmenu file * Replaced COM reference with ComImport for shortcut functionality * remove specific platform values and regions * Move ShortcutHelper to Ryujinx.Ui.Common.Helpers * Adjust styling and structure * code feedback changes * Added MacOS support using .app folder * Added basic implementation for shortcut creation Currently bitmaps (.bmp) are used as the source file, colours are good (unlike .ico rn) but are scaled poorly on desktop. * Icons display properly in shortcut * code cleanup * Moved shortcut logic to specific file, added Ava UI for shortcuts * Added linux .desktop shortcut creation * fixes to .shortcut data * code issue fixes * Added basic implementation for shortcut creation Currently bitmaps (.bmp) are used as the source file, colours are good (unlike .ico rn) but are scaled poorly on desktop. * Icons display properly in shortcut * code cleanup * Moved shortcut logic to specific file, added Ava UI for shortcuts * Added linux .desktop shortcut creation * fixes to .shortcut data * code issue fixes * Replaced COM reference with ComImport for shortcut functionality * remove specific platform values and regions * Move ShortcutHelper to Ryujinx.Ui.Common.Helpers * Adjust styling and structure * code feedback changes * adjust tooltip message * added shortcut-template.desktop file * set shortcut icon location to .local/share/icons * Linux code feedback changes * change InteropServices to new securifybv.ShellLink Package * added ShellLink to readme, updated shortcut comment * Code feedback changes * Added MacOS Support (As per Jose Estrada's PR) * dotnet format * Small restructuring * Embed template files into Ryujinx.Ui.Common * Disable "CreateShortcut" option for flatpak builds --------- Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Co-authored-by: Jose Estrada --- Directory.Packages.props | 1 + README.md | 1 + distribution/legal/THIRDPARTY.md | 29 +++ distribution/linux/Ryujinx.desktop | 4 +- distribution/linux/shortcut-template.desktop | 13 ++ distribution/macos/shortcut-template.plist | 35 ++++ src/Ryujinx.Ava/Assets/Locales/en_US.json | 2 + src/Ryujinx.Ava/Ryujinx.Ava.csproj | 2 +- .../UI/Controls/ApplicationContextMenu.axaml | 7 +- .../Controls/ApplicationContextMenu.axaml.cs | 11 ++ .../UI/ViewModels/MainWindowViewModel.cs | 5 +- .../App/ApplicationLibrary.cs | 4 +- .../Helper/ShortcutHelper.cs | 171 ++++++++++++++++++ .../Ryujinx.Ui.Common.csproj | 10 + src/Ryujinx/Ryujinx.csproj | 18 +- .../Widgets/GameTableContextMenu.Designer.cs | 11 ++ .../Ui/Widgets/GameTableContextMenu.cs | 9 + 17 files changed, 316 insertions(+), 17 deletions(-) create mode 100644 distribution/linux/shortcut-template.desktop create mode 100644 distribution/macos/shortcut-template.plist create mode 100644 src/Ryujinx.Ui.Common/Helper/ShortcutHelper.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index b34b882b4..6fdaafddc 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -35,6 +35,7 @@ + diff --git a/README.md b/README.md index 7021abc45..56333278f 100644 --- a/README.md +++ b/README.md @@ -141,3 +141,4 @@ See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY - [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system. - [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation. +- [ShellLink](https://github.com/securifybv/ShellLink) is used for Windows shortcut generation. diff --git a/distribution/legal/THIRDPARTY.md b/distribution/legal/THIRDPARTY.md index 4cc8b7a45..b0bd5a690 100644 --- a/distribution/legal/THIRDPARTY.md +++ b/distribution/legal/THIRDPARTY.md @@ -681,4 +681,33 @@ END OF TERMS AND CONDITIONS ``` + + +# ShellLink (MIT) +
+ See License + + ``` + MIT License + + Copyright (c) 2017 Yorick Koster, Securify B.V. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + ```
\ No newline at end of file diff --git a/distribution/linux/Ryujinx.desktop b/distribution/linux/Ryujinx.desktop index 19cc5d6cc..a4550d104 100644 --- a/distribution/linux/Ryujinx.desktop +++ b/distribution/linux/Ryujinx.desktop @@ -3,8 +3,8 @@ Version=1.0 Name=Ryujinx Type=Application Icon=Ryujinx -Exec=env DOTNET_EnableAlternateStackCheck=1 Ryujinx %f -Comment=A Nintendo Switch Emulator +Exec=Ryujinx.sh %f +Comment=Plays Nintendo Switch applications GenericName=Nintendo Switch Emulator Terminal=false Categories=Game;Emulator; diff --git a/distribution/linux/shortcut-template.desktop b/distribution/linux/shortcut-template.desktop new file mode 100644 index 000000000..6bee0f8d1 --- /dev/null +++ b/distribution/linux/shortcut-template.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Version=1.0 +Name={0} +Type=Application +Icon={1} +Exec={2} %f +Comment=Nintendo Switch application +GenericName=Nintendo Switch Emulator +Terminal=false +Categories=Game;Emulator; +Keywords=Switch;Nintendo;Emulator; +StartupWMClass=Ryujinx +PrefersNonDefaultGPU=true diff --git a/distribution/macos/shortcut-template.plist b/distribution/macos/shortcut-template.plist new file mode 100644 index 000000000..27a9e46a9 --- /dev/null +++ b/distribution/macos/shortcut-template.plist @@ -0,0 +1,35 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + {0} + CFBundleGetInfoString + {1} + CFBundleIconFile + {2} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleVersion + 1.0 + NSHighResolutionCapable + + CSResourcesFileMapped + + NSHumanReadableCopyright + Copyright © 2018 - 2023 Ryujinx Team and Contributors. + LSApplicationCategoryType + public.app-category.games + LSMinimumSystemVersion + 11.0 + UIPrerenderedIcon + + LSEnvironment + + DOTNET_DefaultStackSize + 200000 + + + diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 53e277ba9..a67b796bd 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -72,6 +72,8 @@ "GameListContextMenuExtractDataRomFSToolTip": "Extract the RomFS section from Application's current config (including updates)", "GameListContextMenuExtractDataLogo": "Logo", "GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)", + "GameListContextMenuCreateShortcut": "Create Application Shortcut", + "GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application", "StatusBarGamesLoaded": "{0}/{1} Games Loaded", "StatusBarSystemVersion": "System Version: {0}", "LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected", diff --git a/src/Ryujinx.Ava/Ryujinx.Ava.csproj b/src/Ryujinx.Ava/Ryujinx.Ava.csproj index a4c1ebf16..f0e99f427 100644 --- a/src/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/src/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -145,4 +145,4 @@ - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml index 93638fc53..d81050f83 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml @@ -82,4 +82,9 @@ Header="{locale:Locale GameListContextMenuExtractDataLogo}" ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" /> - \ No newline at end of file + + diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index d75572e65..0f0071065 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -337,6 +337,17 @@ namespace Ryujinx.Ava.UI.Controls } } + public void CreateApplicationShortcut_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel?.SelectedApplication != null) + { + ApplicationData selectedApplication = viewModel.SelectedApplication; + ShortcutHelper.CreateAppShortcut(selectedApplication.Path, selectedApplication.TitleName, selectedApplication.TitleId, selectedApplication.Icon); + } + } + public async void RunApplication_Click(object sender, RoutedEventArgs args) { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 7a9e4df14..b14905204 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -356,6 +356,8 @@ namespace Ryujinx.Ava.UI.ViewModels public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; + public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild(); + public string LoadHeading { get => _loadHeading; @@ -1488,7 +1490,7 @@ namespace Ryujinx.Ava.UI.ViewModels Logger.RestartTime(); - SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path); + SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path, ConfigurationState.Instance.System.Language); PrepareLoadScreen(); @@ -1696,7 +1698,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } } - #endregion } } diff --git a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 33e6c4aad..36b2b727d 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -546,7 +546,7 @@ namespace Ryujinx.Ui.App.Common return appMetadata; } - public byte[] GetApplicationIcon(string applicationPath) + public byte[] GetApplicationIcon(string applicationPath, Language desiredTitleLanguage) { byte[] applicationIcon = null; @@ -600,7 +600,7 @@ namespace Ryujinx.Ui.App.Common { using var icon = new UniqueRef(); - controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + controlFs.OpenFile(ref icon.Ref, $"/icon_{desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); using MemoryStream stream = new(); diff --git a/src/Ryujinx.Ui.Common/Helper/ShortcutHelper.cs b/src/Ryujinx.Ui.Common/Helper/ShortcutHelper.cs new file mode 100644 index 000000000..dab473fa3 --- /dev/null +++ b/src/Ryujinx.Ui.Common/Helper/ShortcutHelper.cs @@ -0,0 +1,171 @@ +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using ShellLink; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Versioning; +using Image = System.Drawing.Image; + +namespace Ryujinx.Ui.Common.Helper +{ + public static class ShortcutHelper + { + [SupportedOSPlatform("windows")] + private static void CreateShortcutWindows(string applicationFilePath, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath) + { + string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + ".exe"); + iconPath += ".ico"; + + MemoryStream iconDataStream = new(iconData); + using Image image = Image.FromStream(iconDataStream); + using Bitmap bitmap = new(128, 128); + using System.Drawing.Graphics graphic = System.Drawing.Graphics.FromImage(bitmap); + graphic.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphic.DrawImage(image, 0, 0, 128, 128); + SaveBitmapAsIcon(bitmap, iconPath); + + var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(basePath, applicationFilePath), iconPath, 0); + shortcut.StringData.NameString = cleanedAppName; + shortcut.WriteToFile(Path.Combine(desktopPath, cleanedAppName + ".lnk")); + } + + [SupportedOSPlatform("linux")] + private static void CreateShortcutLinux(string applicationFilePath, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName) + { + string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh"); + var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.Ui.Common/shortcut-template.desktop"); + iconPath += ".png"; + + var image = SixLabors.ImageSharp.Image.Load(iconData); + image.SaveAsPng(iconPath); + + using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop")); + outputFile.Write(desktopFile, cleanedAppName, iconPath, GetArgsString(basePath, applicationFilePath)); + } + + [SupportedOSPlatform("macos")] + private static void CreateShortcutMacos(string appFilePath, byte[] iconData, string desktopPath, string cleanedAppName) + { + string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName); + var plistFile = EmbeddedResources.ReadAllText("Ryujinx.Ui.Common/shortcut-template.plist"); + // Macos .App folder + string contentFolderPath = Path.Combine(desktopPath, cleanedAppName + ".app", "Contents"); + string scriptFolderPath = Path.Combine(contentFolderPath, "MacOS"); + + if (!Directory.Exists(scriptFolderPath)) + { + Directory.CreateDirectory(scriptFolderPath); + } + + // Runner script + const string ScriptName = "runner.sh"; + string scriptPath = Path.Combine(scriptFolderPath, ScriptName); + using StreamWriter scriptFile = new(scriptPath); + + scriptFile.WriteLine("#!/bin/sh"); + scriptFile.WriteLine(GetArgsString(basePath, appFilePath)); + + // Set execute permission + FileInfo fileInfo = new(scriptPath); + fileInfo.UnixFileMode |= UnixFileMode.UserExecute; + + // img + string resourceFolderPath = Path.Combine(contentFolderPath, "Resources"); + if (!Directory.Exists(resourceFolderPath)) + { + Directory.CreateDirectory(resourceFolderPath); + } + + const string IconName = "icon.png"; + var image = SixLabors.ImageSharp.Image.Load(iconData); + image.SaveAsPng(Path.Combine(resourceFolderPath, IconName)); + + // plist file + using StreamWriter outputFile = new(Path.Combine(contentFolderPath, "Info.plist")); + outputFile.Write(plistFile, ScriptName, cleanedAppName, IconName); + } + + public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData) + { + string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); + string cleanedAppName = string.Join("_", applicationName.Split(Path.GetInvalidFileNameChars())); + + if (OperatingSystem.IsWindows()) + { + string iconPath = Path.Combine(AppDataManager.BaseDirPath, "games", applicationId, "app"); + + CreateShortcutWindows(applicationFilePath, iconData, iconPath, cleanedAppName, desktopPath); + + return; + } + + if (OperatingSystem.IsLinux()) + { + string iconPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "icons", "Ryujinx"); + + Directory.CreateDirectory(iconPath); + CreateShortcutLinux(applicationFilePath, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName); + + return; + } + + if (OperatingSystem.IsMacOS()) + { + CreateShortcutMacos(applicationFilePath, iconData, desktopPath, cleanedAppName); + + return; + } + + throw new NotImplementedException("Shortcut support has not been implemented yet for this OS."); + } + + private static string GetArgsString(string basePath, string appFilePath) + { + // args are first defined as a list, for easier adjustments in the future + var argsList = new List + { + basePath, + }; + + if (!string.IsNullOrEmpty(CommandLineState.BaseDirPathArg)) + { + argsList.Add("--root-data-dir"); + argsList.Add($"\"{CommandLineState.BaseDirPathArg}\""); + } + + argsList.Add($"\"{appFilePath}\""); + + + return String.Join(" ", argsList); + } + + /// + /// Creates a Icon (.ico) file using the source bitmap image at the specified file path. + /// + /// The source bitmap image that will be saved as an .ico file + /// The location that the new .ico file will be saved too (Make sure to include '.ico' in the path). + [SupportedOSPlatform("windows")] + private static void SaveBitmapAsIcon(Bitmap source, string filePath) + { + // Code Modified From https://stackoverflow.com/a/11448060/368354 by Benlitz + byte[] header = { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 22, 0, 0, 0 }; + using FileStream fs = new(filePath, FileMode.Create); + + fs.Write(header); + // Writing actual data + source.Save(fs, ImageFormat.Png); + // Getting data length (file length minus header) + long dataLength = fs.Length - header.Length; + // Write it in the correct place + fs.Seek(14, SeekOrigin.Begin); + fs.WriteByte((byte)dataLength); + fs.WriteByte((byte)(dataLength >> 8)); + } + } +} diff --git a/src/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj b/src/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj index 511a03897..3da47431f 100644 --- a/src/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj +++ b/src/Ryujinx.Ui.Common/Ryujinx.Ui.Common.csproj @@ -45,8 +45,18 @@
+ + + + + + + + + + diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index cf4435e57..5b5ed4637 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -63,15 +63,15 @@ - - + + Always - - - Always - mime\Ryujinx.xml - - + + + Always + mime\Ryujinx.xml + + @@ -101,4 +101,4 @@ - \ No newline at end of file + diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs index 0f7b4f22b..75b166136 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs @@ -23,6 +23,7 @@ namespace Ryujinx.Ui.Widgets private MenuItem _purgeShaderCacheMenuItem; private MenuItem _openPtcDirMenuItem; private MenuItem _openShaderCacheDirMenuItem; + private MenuItem _createShortcutMenuItem; private void InitializeComponent() { @@ -187,6 +188,15 @@ namespace Ryujinx.Ui.Widgets }; _openShaderCacheDirMenuItem.Activated += OpenShaderCacheDir_Clicked; + // + // _createShortcutMenuItem + // + _createShortcutMenuItem = new MenuItem("Create Application Shortcut") + { + TooltipText = "Create a Desktop Shortcut that launches the selected Application." + }; + _createShortcutMenuItem.Activated += CreateShortcut_Clicked; + ShowComponent(); } @@ -213,6 +223,7 @@ namespace Ryujinx.Ui.Widgets Add(new SeparatorMenuItem()); Add(_manageCacheMenuItem); Add(_extractMenuItem); + Add(_createShortcutMenuItem); ShowAll(); } diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index c2e0d8ebc..ea60421f8 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -10,6 +10,7 @@ using LibHac.Ns; using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; @@ -77,6 +78,8 @@ namespace Ryujinx.Ui.Widgets _extractExeFsMenuItem.Sensitive = hasNca; _extractLogoMenuItem.Sensitive = hasNca; + _createShortcutMenuItem.Sensitive = !ReleaseInformation.IsFlatHubBuild(); + PopupAtPointer(null); } @@ -629,5 +632,11 @@ namespace Ryujinx.Ui.Widgets } } } + + private void CreateShortcut_Clicked(object sender, EventArgs args) + { + byte[] appIcon = new ApplicationLibrary(_virtualFileSystem).GetApplicationIcon(_titleFilePath, ConfigurationState.Instance.System.Language); + ShortcutHelper.CreateAppShortcut(_titleFilePath, _titleName, _titleIdText, appIcon); + } } } From 49b37550cae6b3c69f59a9c7a44b17e3c12a813b Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Sat, 21 Oct 2023 07:26:51 -0400 Subject: [PATCH 6/7] Ava UI: Input Menu Refactor (#4998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * So much boilerplate * Slow and steady * Restructure + Ack suggestions * Restructure + Ack suggestions * Restructure * Clean * Propogate those fields i forgot about * It builds * Progress * Almost there * Fix stupid mistake * Fix more stupid mistakes * Actually fix fuck ups * Start localising * r/therestofthefuckingowl * Localise ButtonKeyAssigner * Are you feeling it now mr krabs * We’re done at last * Crimes against code * Try me in the Hague * Please be quiet * Crimes are here to stay * Dispose stuff * Cleanup a couple things * Visual fixes and improvements One weird bug * Fix rebase errors * Fixes * Ack Suggestions Remaining ack suggestions Update src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs Co-authored-by: Ac_K Update src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs Co-authored-by: Ac_K * Formatting and error More Ava 11-ness Whoops * Code style fixes * Style fixes * Analyzer fix * Remove all ReflectionBindings * Remove ambigious object * Remove redundant property * Old man yells at formatter * r e a d o n l y * Fix profiles * Use new Sliders --------- Co-authored-by: Ac_K --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 99 +++ src/Ryujinx.Ava/Assets/Styles/Styles.xaml | 5 +- .../UI/Helpers/ButtonKeyAssigner.cs | 28 +- .../UI/Helpers/KeyValueConverter.cs | 167 ++++- .../UI/Models/Input/ControllerInputConfig.cs | 580 +++++++++++++++ .../UI/Models/Input/KeyboardInputConfig.cs | 422 +++++++++++ .../UI/Models/InputConfiguration.cs | 456 ------------ .../Input/ControllerInputViewModel.cs | 84 +++ .../InputViewModel.cs} | 103 ++- .../Input/KeyboardInputViewModel.cs | 73 ++ .../{ => Input}/MotionInputViewModel.cs | 2 +- .../{ => Input}/RumbleInputViewModel.cs | 2 +- .../UI/Views/Input/ControllerInputView.axaml | 616 ++++------------ .../Views/Input/ControllerInputView.axaml.cs | 160 +++-- .../UI/Views/Input/InputView.axaml | 225 ++++++ .../UI/Views/Input/InputView.axaml.cs | 61 ++ .../UI/Views/Input/KeyboardInputView.axaml | 675 ++++++++++++++++++ .../UI/Views/Input/KeyboardInputView.axaml.cs | 210 ++++++ .../UI/Views/Input/MotionInputView.axaml | 2 +- .../UI/Views/Input/MotionInputView.axaml.cs | 8 +- .../UI/Views/Input/RumbleInputView.axaml | 2 +- .../UI/Views/Input/RumbleInputView.axaml.cs | 8 +- .../UI/Views/Settings/SettingsInputView.axaml | 4 +- .../Views/Settings/SettingsInputView.axaml.cs | 2 +- .../UI/Windows/SettingsWindow.axaml.cs | 2 +- .../Assigner/GamepadButtonAssigner.cs | 6 +- src/Ryujinx.Input/Assigner/IButtonAssigner.cs | 2 +- .../Assigner/KeyboardKeyAssigner.cs | 10 +- src/Ryujinx.Input/ButtonValue.cs | 48 ++ src/Ryujinx/Ui/Windows/ControllerWindow.cs | 2 +- 30 files changed, 2914 insertions(+), 1150 deletions(-) create mode 100644 src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs create mode 100644 src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs delete mode 100644 src/Ryujinx.Ava/UI/Models/InputConfiguration.cs create mode 100644 src/Ryujinx.Ava/UI/ViewModels/Input/ControllerInputViewModel.cs rename src/Ryujinx.Ava/UI/ViewModels/{ControllerInputViewModel.cs => Input/InputViewModel.cs} (92%) create mode 100644 src/Ryujinx.Ava/UI/ViewModels/Input/KeyboardInputViewModel.cs rename src/Ryujinx.Ava/UI/ViewModels/{ => Input}/MotionInputViewModel.cs (97%) rename src/Ryujinx.Ava/UI/ViewModels/{ => Input}/RumbleInputViewModel.cs (92%) create mode 100644 src/Ryujinx.Ava/UI/Views/Input/InputView.axaml create mode 100644 src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs create mode 100644 src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml create mode 100644 src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs create mode 100644 src/Ryujinx.Input/ButtonValue.cs diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index a67b796bd..fc65fe4a0 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -263,6 +263,105 @@ "ControllerSettingsMotionGyroDeadzone": "Gyro Deadzone:", "ControllerSettingsSave": "Save", "ControllerSettingsClose": "Close", + "KeyUnknown": "Unknown", + "KeyShiftLeft": "Shift Left", + "KeyShiftRight": "Shift Right", + "KeyControlLeft": "Control Left", + "KeyControlRight": "Control Right", + "KeyAltLeft": "Alt Left", + "KeyAltRight": "Alt Right", + "KeyOptLeft": "⌥ Left", + "KeyOptRight": "⌥ Right", + "KeyWinLeft": "⊞ Left", + "KeyWinRight": "⊞ Right", + "KeyCmdLeft": "⌘ Left", + "KeyCmdRight": "⌘ Right", + "KeyMenu": "Menu", + "KeyUp": "Up", + "KeyDown": "Down", + "KeyLeft": "Left", + "KeyRight": "Right", + "KeyEnter": "Enter", + "KeyEscape": "Escape", + "KeySpace": "Space", + "KeyTab": "Tab", + "KeyBackSpace": "Backspace", + "KeyInsert": "Insert", + "KeyDelete": "Delete", + "KeyPageUp": "Page Up", + "KeyPageDown": "Page Down", + "KeyHome": "Home", + "KeyEnd": "End", + "KeyCapsLock": "Caps Lock", + "KeyScrollLock": "Scroll Lock", + "KeyPrintScreen": "Print Screen", + "KeyPause": "Pause", + "KeyNumLock": "Num Lock", + "KeyClear": "Clear", + "KeyKeypad0": "Keypad 0", + "KeyKeypad1": "Keypad 1", + "KeyKeypad2": "Keypad 2", + "KeyKeypad3": "Keypad 3", + "KeyKeypad4": "Keypad 4", + "KeyKeypad5": "Keypad 5", + "KeyKeypad6": "Keypad 6", + "KeyKeypad7": "Keypad 7", + "KeyKeypad8": "Keypad 8", + "KeyKeypad9": "Keypad 9", + "KeyKeypadDivide": "Keypad Divide", + "KeyKeypadMultiply": "Keypad Multiply", + "KeyKeypadSubtract": "Keypad Subtract", + "KeyKeypadAdd": "Keypad Add", + "KeyKeypadDecimal": "Keypad Decimal", + "KeyKeypadEnter": "Keypad Enter", + "KeyNumber0": "0", + "KeyNumber1": "1", + "KeyNumber2": "2", + "KeyNumber3": "3", + "KeyNumber4": "4", + "KeyNumber5": "5", + "KeyNumber6": "6", + "KeyNumber7": "7", + "KeyNumber8": "8", + "KeyNumber9": "9", + "KeyTilde": "~", + "KeyGrave": "`", + "KeyMinus": "-", + "KeyPlus": "+", + "KeyBracketLeft": "[", + "KeyBracketRight": "]", + "KeySemicolon": ";", + "KeyQuote": "\"", + "KeyComma": ",", + "KeyPeriod": ".", + "KeySlash": "/", + "KeyBackSlash": "\\", + "KeyUnbound": "Unbound", + "GamepadLeftStick": "Left Stick Button", + "GamepadRightStick": "Right Stick Button", + "GamepadLeftShoulder": "Left Shoulder", + "GamepadRightShoulder": "Right Shoulder", + "GamepadLeftTrigger": "Left Trigger", + "GamepadRightTrigger": "Right Trigger", + "GamepadDpadUp": "Up", + "GamepadDpadDown": "Down", + "GamepadDpadLeft": "Left", + "GamepadDpadRight": "Right", + "GamepadMinus": "-", + "GamepadPlus": "+", + "GamepadGuide": "Guide", + "GamepadMisc1": "Misc", + "GamepadPaddle1": "Paddle 1", + "GamepadPaddle2": "Paddle 2", + "GamepadPaddle3": "Paddle 3", + "GamepadPaddle4": "Paddle 4", + "GamepadTouchpad": "Touchpad", + "GamepadSingleLeftTrigger0": "Left Trigger 0", + "GamepadSingleRightTrigger0": "Right Trigger 0", + "GamepadSingleLeftTrigger1": "Left Trigger 1", + "GamepadSingleRightTrigger1": "Right Trigger 1", + "StickLeft": "Left Stick", + "StickRight": "Right Stick", "UserProfilesSelectedUserProfile": "Selected User Profile:", "UserProfilesSaveProfileName": "Save Profile Name", "UserProfilesChangeProfileImage": "Change Profile Image", diff --git a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml index f7f64be22..b3a6f59c8 100644 --- a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml +++ b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml @@ -15,8 +15,7 @@ - + @@ -393,4 +392,4 @@ 600 756 - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs b/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs index 7e8ba7342..54e0918a5 100644 --- a/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs +++ b/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs @@ -1,11 +1,8 @@ -using Avalonia.Controls; using Avalonia.Controls.Primitives; -using Avalonia.LogicalTree; using Avalonia.Threading; using Ryujinx.Input; using Ryujinx.Input.Assigner; using System; -using System.Linq; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Helpers @@ -15,12 +12,12 @@ namespace Ryujinx.Ava.UI.Helpers internal class ButtonAssignedEventArgs : EventArgs { public ToggleButton Button { get; } - public bool IsAssigned { get; } + public ButtonValue? ButtonValue { get; } - public ButtonAssignedEventArgs(ToggleButton button, bool isAssigned) + public ButtonAssignedEventArgs(ToggleButton button, ButtonValue? buttonValue) { Button = button; - IsAssigned = isAssigned; + ButtonValue = buttonValue; } } @@ -78,15 +75,11 @@ namespace Ryujinx.Ava.UI.Helpers await Dispatcher.UIThread.InvokeAsync(() => { - string pressedButton = assigner.GetPressedButton(); + ButtonValue? pressedButton = assigner.GetPressedButton(); if (_shouldUnbind) { - SetButtonText(ToggledButton, "Unbound"); - } - else if (pressedButton != "") - { - SetButtonText(ToggledButton, pressedButton); + pressedButton = null; } _shouldUnbind = false; @@ -94,17 +87,8 @@ namespace Ryujinx.Ava.UI.Helpers ToggledButton.IsChecked = false; - ButtonAssigned?.Invoke(this, new ButtonAssignedEventArgs(ToggledButton, pressedButton != null)); + ButtonAssigned?.Invoke(this, new ButtonAssignedEventArgs(ToggledButton, pressedButton)); - static void SetButtonText(ToggleButton button, string text) - { - ILogical textBlock = button.GetLogicalDescendants().First(x => x is TextBlock); - - if (textBlock != null && textBlock is TextBlock block) - { - block.Text = text; - } - } }); } diff --git a/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs index 028ed6bf4..1c4aa7b21 100644 --- a/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs @@ -1,7 +1,9 @@ using Avalonia.Data.Converters; +using Ryujinx.Ava.Common.Locale; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using System; +using System.Collections.Generic; using System.Globalization; namespace Ryujinx.Ava.UI.Helpers @@ -10,37 +12,158 @@ namespace Ryujinx.Ava.UI.Helpers { public static KeyValueConverter Instance = new(); + private static readonly Dictionary _keysMap = new() + { + { Key.Unknown, LocaleKeys.KeyUnknown }, + { Key.ShiftLeft, LocaleKeys.KeyShiftLeft }, + { Key.ShiftRight, LocaleKeys.KeyShiftRight }, + { Key.ControlLeft, LocaleKeys.KeyControlLeft }, + { Key.ControlRight, LocaleKeys.KeyControlRight }, + { Key.AltLeft, OperatingSystem.IsMacOS() ? LocaleKeys.KeyOptLeft : LocaleKeys.KeyAltLeft }, + { Key.AltRight, OperatingSystem.IsMacOS() ? LocaleKeys.KeyOptRight : LocaleKeys.KeyAltRight }, + { Key.WinLeft, OperatingSystem.IsMacOS() ? LocaleKeys.KeyCmdLeft : LocaleKeys.KeyWinLeft }, + { Key.WinRight, OperatingSystem.IsMacOS() ? LocaleKeys.KeyCmdRight : LocaleKeys.KeyWinRight }, + { Key.Up, LocaleKeys.KeyUp }, + { Key.Down, LocaleKeys.KeyDown }, + { Key.Left, LocaleKeys.KeyLeft }, + { Key.Right, LocaleKeys.KeyRight }, + { Key.Enter, LocaleKeys.KeyEnter }, + { Key.Escape, LocaleKeys.KeyEscape }, + { Key.Space, LocaleKeys.KeySpace }, + { Key.Tab, LocaleKeys.KeyTab }, + { Key.BackSpace, LocaleKeys.KeyBackSpace }, + { Key.Insert, LocaleKeys.KeyInsert }, + { Key.Delete, LocaleKeys.KeyDelete }, + { Key.PageUp, LocaleKeys.KeyPageUp }, + { Key.PageDown, LocaleKeys.KeyPageDown }, + { Key.Home, LocaleKeys.KeyHome }, + { Key.End, LocaleKeys.KeyEnd }, + { Key.CapsLock, LocaleKeys.KeyCapsLock }, + { Key.ScrollLock, LocaleKeys.KeyScrollLock }, + { Key.PrintScreen, LocaleKeys.KeyPrintScreen }, + { Key.Pause, LocaleKeys.KeyPause }, + { Key.NumLock, LocaleKeys.KeyNumLock }, + { Key.Clear, LocaleKeys.KeyClear }, + { Key.Keypad0, LocaleKeys.KeyKeypad0 }, + { Key.Keypad1, LocaleKeys.KeyKeypad1 }, + { Key.Keypad2, LocaleKeys.KeyKeypad2 }, + { Key.Keypad3, LocaleKeys.KeyKeypad3 }, + { Key.Keypad4, LocaleKeys.KeyKeypad4 }, + { Key.Keypad5, LocaleKeys.KeyKeypad5 }, + { Key.Keypad6, LocaleKeys.KeyKeypad6 }, + { Key.Keypad7, LocaleKeys.KeyKeypad7 }, + { Key.Keypad8, LocaleKeys.KeyKeypad8 }, + { Key.Keypad9, LocaleKeys.KeyKeypad9 }, + { Key.KeypadDivide, LocaleKeys.KeyKeypadDivide }, + { Key.KeypadMultiply, LocaleKeys.KeyKeypadMultiply }, + { Key.KeypadSubtract, LocaleKeys.KeyKeypadSubtract }, + { Key.KeypadAdd, LocaleKeys.KeyKeypadAdd }, + { Key.KeypadDecimal, LocaleKeys.KeyKeypadDecimal }, + { Key.KeypadEnter, LocaleKeys.KeyKeypadEnter }, + { Key.Number0, LocaleKeys.KeyNumber0 }, + { Key.Number1, LocaleKeys.KeyNumber1 }, + { Key.Number2, LocaleKeys.KeyNumber2 }, + { Key.Number3, LocaleKeys.KeyNumber3 }, + { Key.Number4, LocaleKeys.KeyNumber4 }, + { Key.Number5, LocaleKeys.KeyNumber5 }, + { Key.Number6, LocaleKeys.KeyNumber6 }, + { Key.Number7, LocaleKeys.KeyNumber7 }, + { Key.Number8, LocaleKeys.KeyNumber8 }, + { Key.Number9, LocaleKeys.KeyNumber9 }, + { Key.Tilde, LocaleKeys.KeyTilde }, + { Key.Grave, LocaleKeys.KeyGrave }, + { Key.Minus, LocaleKeys.KeyMinus }, + { Key.Plus, LocaleKeys.KeyPlus }, + { Key.BracketLeft, LocaleKeys.KeyBracketLeft }, + { Key.BracketRight, LocaleKeys.KeyBracketRight }, + { Key.Semicolon, LocaleKeys.KeySemicolon }, + { Key.Quote, LocaleKeys.KeyQuote }, + { Key.Comma, LocaleKeys.KeyComma }, + { Key.Period, LocaleKeys.KeyPeriod }, + { Key.Slash, LocaleKeys.KeySlash }, + { Key.BackSlash, LocaleKeys.KeyBackSlash }, + { Key.Unbound, LocaleKeys.KeyUnbound }, + }; + + private static readonly Dictionary _gamepadInputIdMap = new() + { + { GamepadInputId.LeftStick, LocaleKeys.GamepadLeftStick }, + { GamepadInputId.RightStick, LocaleKeys.GamepadRightStick }, + { GamepadInputId.LeftShoulder, LocaleKeys.GamepadLeftShoulder }, + { GamepadInputId.RightShoulder, LocaleKeys.GamepadRightShoulder }, + { GamepadInputId.LeftTrigger, LocaleKeys.GamepadLeftTrigger }, + { GamepadInputId.RightTrigger, LocaleKeys.GamepadRightTrigger }, + { GamepadInputId.DpadUp, LocaleKeys.GamepadDpadUp}, + { GamepadInputId.DpadDown, LocaleKeys.GamepadDpadDown}, + { GamepadInputId.DpadLeft, LocaleKeys.GamepadDpadLeft}, + { GamepadInputId.DpadRight, LocaleKeys.GamepadDpadRight}, + { GamepadInputId.Minus, LocaleKeys.GamepadMinus}, + { GamepadInputId.Plus, LocaleKeys.GamepadPlus}, + { GamepadInputId.Guide, LocaleKeys.GamepadGuide}, + { GamepadInputId.Misc1, LocaleKeys.GamepadMisc1}, + { GamepadInputId.Paddle1, LocaleKeys.GamepadPaddle1}, + { GamepadInputId.Paddle2, LocaleKeys.GamepadPaddle2}, + { GamepadInputId.Paddle3, LocaleKeys.GamepadPaddle3}, + { GamepadInputId.Paddle4, LocaleKeys.GamepadPaddle4}, + { GamepadInputId.Touchpad, LocaleKeys.GamepadTouchpad}, + { GamepadInputId.SingleLeftTrigger0, LocaleKeys.GamepadSingleLeftTrigger0}, + { GamepadInputId.SingleRightTrigger0, LocaleKeys.GamepadSingleRightTrigger0}, + { GamepadInputId.SingleLeftTrigger1, LocaleKeys.GamepadSingleLeftTrigger1}, + { GamepadInputId.SingleRightTrigger1, LocaleKeys.GamepadSingleRightTrigger1}, + { GamepadInputId.Unbound, LocaleKeys.KeyUnbound}, + }; + + private static readonly Dictionary _stickInputIdMap = new() + { + { StickInputId.Left, LocaleKeys.StickLeft}, + { StickInputId.Right, LocaleKeys.StickRight}, + { StickInputId.Unbound, LocaleKeys.KeyUnbound}, + }; + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value == null) + string keyString = ""; + + if (value is Key key) { - return null; + if (_keysMap.TryGetValue(key, out LocaleKeys localeKey)) + { + keyString = LocaleManager.Instance[localeKey]; + } + else + { + keyString = key.ToString(); + } + } + else if (value is GamepadInputId gamepadInputId) + { + if (_gamepadInputIdMap.TryGetValue(gamepadInputId, out LocaleKeys localeKey)) + { + keyString = LocaleManager.Instance[localeKey]; + } + else + { + keyString = gamepadInputId.ToString(); + } + } + else if (value is StickInputId stickInputId) + { + if (_stickInputIdMap.TryGetValue(stickInputId, out LocaleKeys localeKey)) + { + keyString = LocaleManager.Instance[localeKey]; + } + else + { + keyString = stickInputId.ToString(); + } } - return value.ToString(); + return keyString; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - object key = null; - - if (value != null) - { - if (targetType == typeof(Key)) - { - key = Enum.Parse(value.ToString()); - } - else if (targetType == typeof(GamepadInputId)) - { - key = Enum.Parse(value.ToString()); - } - else if (targetType == typeof(StickInputId)) - { - key = Enum.Parse(value.ToString()); - } - } - - return key; + throw new NotSupportedException(); } } } diff --git a/src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs b/src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs new file mode 100644 index 000000000..4929e582e --- /dev/null +++ b/src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs @@ -0,0 +1,580 @@ +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Controller.Motion; +using System; + +namespace Ryujinx.Ava.UI.Models.Input +{ + public class ControllerInputConfig : BaseModel + { + public bool EnableCemuHookMotion { get; set; } + public string DsuServerHost { get; set; } + public int DsuServerPort { get; set; } + public int Slot { get; set; } + public int AltSlot { get; set; } + public bool MirrorInput { get; set; } + public int Sensitivity { get; set; } + public double GyroDeadzone { get; set; } + + public float WeakRumble { get; set; } + public float StrongRumble { get; set; } + + public string Id { get; set; } + public ControllerType ControllerType { get; set; } + public PlayerIndex PlayerIndex { get; set; } + + private StickInputId _leftJoystick; + public StickInputId LeftJoystick + { + get => _leftJoystick; + set + { + _leftJoystick = value; + OnPropertyChanged(); + } + } + + private bool _leftInvertStickX; + public bool LeftInvertStickX + { + get => _leftInvertStickX; + set + { + _leftInvertStickX = value; + OnPropertyChanged(); + } + } + + private bool _leftInvertStickY; + public bool LeftInvertStickY + { + get => _leftInvertStickY; + set + { + _leftInvertStickY = value; + OnPropertyChanged(); + } + } + + private bool _leftRotate90; + public bool LeftRotate90 + { + get => _leftRotate90; + set + { + _leftRotate90 = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _leftStickButton; + public GamepadInputId LeftStickButton + { + get => _leftStickButton; + set + { + _leftStickButton = value; + OnPropertyChanged(); + } + } + + private StickInputId _rightJoystick; + public StickInputId RightJoystick + { + get => _rightJoystick; + set + { + _rightJoystick = value; + OnPropertyChanged(); + } + } + + private bool _rightInvertStickX; + public bool RightInvertStickX + { + get => _rightInvertStickX; + set + { + _rightInvertStickX = value; + OnPropertyChanged(); + } + } + + private bool _rightInvertStickY; + public bool RightInvertStickY + { + get => _rightInvertStickY; + set + { + _rightInvertStickY = value; + OnPropertyChanged(); + } + } + + private bool _rightRotate90; + public bool RightRotate90 + { + get => _rightRotate90; + set + { + _rightRotate90 = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _rightStickButton; + public GamepadInputId RightStickButton + { + get => _rightStickButton; + set + { + _rightStickButton = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _dpadUp; + public GamepadInputId DpadUp + { + get => _dpadUp; + set + { + _dpadUp = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _dpadDown; + public GamepadInputId DpadDown + { + get => _dpadDown; + set + { + _dpadDown = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _dpadLeft; + public GamepadInputId DpadLeft + { + get => _dpadLeft; + set + { + _dpadLeft = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _dpadRight; + public GamepadInputId DpadRight + { + get => _dpadRight; + set + { + _dpadRight = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonL; + public GamepadInputId ButtonL + { + get => _buttonL; + set + { + _buttonL = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonMinus; + public GamepadInputId ButtonMinus + { + get => _buttonMinus; + set + { + _buttonMinus = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _leftButtonSl; + public GamepadInputId LeftButtonSl + { + get => _leftButtonSl; + set + { + _leftButtonSl = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _leftButtonSr; + public GamepadInputId LeftButtonSr + { + get => _leftButtonSr; + set + { + _leftButtonSr = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonZl; + public GamepadInputId ButtonZl + { + get => _buttonZl; + set + { + _buttonZl = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonA; + public GamepadInputId ButtonA + { + get => _buttonA; + set + { + _buttonA = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonB; + public GamepadInputId ButtonB + { + get => _buttonB; + set + { + _buttonB = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonX; + public GamepadInputId ButtonX + { + get => _buttonX; + set + { + _buttonX = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonY; + public GamepadInputId ButtonY + { + get => _buttonY; + set + { + _buttonY = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonR; + public GamepadInputId ButtonR + { + get => _buttonR; + set + { + _buttonR = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonPlus; + public GamepadInputId ButtonPlus + { + get => _buttonPlus; + set + { + _buttonPlus = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _rightButtonSl; + public GamepadInputId RightButtonSl + { + get => _rightButtonSl; + set + { + _rightButtonSl = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _rightButtonSr; + public GamepadInputId RightButtonSr + { + get => _rightButtonSr; + set + { + _rightButtonSr = value; + OnPropertyChanged(); + } + } + + private GamepadInputId _buttonZr; + public GamepadInputId ButtonZr + { + get => _buttonZr; + set + { + _buttonZr = value; + OnPropertyChanged(); + } + } + + private float _deadzoneLeft; + public float DeadzoneLeft + { + get => _deadzoneLeft; + set + { + _deadzoneLeft = MathF.Round(value, 3); + OnPropertyChanged(); + } + } + + private float _deadzoneRight; + public float DeadzoneRight + { + get => _deadzoneRight; + set + { + _deadzoneRight = MathF.Round(value, 3); + OnPropertyChanged(); + } + } + + private float _rangeLeft; + public float RangeLeft + { + get => _rangeLeft; + set + { + _rangeLeft = MathF.Round(value, 3); + OnPropertyChanged(); + } + } + + private float _rangeRight; + public float RangeRight + { + get => _rangeRight; + set + { + _rangeRight = MathF.Round(value, 3); + OnPropertyChanged(); + } + } + + private float _triggerThreshold; + public float TriggerThreshold + { + get => _triggerThreshold; + set + { + _triggerThreshold = MathF.Round(value, 3); + OnPropertyChanged(); + } + } + + private bool _enableMotion; + public bool EnableMotion + { + get => _enableMotion; + set + { + _enableMotion = value; + OnPropertyChanged(); + } + } + + private bool _enableRumble; + public bool EnableRumble + { + get => _enableRumble; + set + { + _enableRumble = value; + OnPropertyChanged(); + } + } + + public ControllerInputConfig(InputConfig config) + { + if (config != null) + { + Id = config.Id; + ControllerType = config.ControllerType; + PlayerIndex = config.PlayerIndex; + + if (config is not StandardControllerInputConfig controllerInput) + { + return; + } + + LeftJoystick = controllerInput.LeftJoyconStick.Joystick; + LeftInvertStickX = controllerInput.LeftJoyconStick.InvertStickX; + LeftInvertStickY = controllerInput.LeftJoyconStick.InvertStickY; + LeftRotate90 = controllerInput.LeftJoyconStick.Rotate90CW; + LeftStickButton = controllerInput.LeftJoyconStick.StickButton; + + RightJoystick = controllerInput.RightJoyconStick.Joystick; + RightInvertStickX = controllerInput.RightJoyconStick.InvertStickX; + RightInvertStickY = controllerInput.RightJoyconStick.InvertStickY; + RightRotate90 = controllerInput.RightJoyconStick.Rotate90CW; + RightStickButton = controllerInput.RightJoyconStick.StickButton; + + DpadUp = controllerInput.LeftJoycon.DpadUp; + DpadDown = controllerInput.LeftJoycon.DpadDown; + DpadLeft = controllerInput.LeftJoycon.DpadLeft; + DpadRight = controllerInput.LeftJoycon.DpadRight; + ButtonL = controllerInput.LeftJoycon.ButtonL; + ButtonMinus = controllerInput.LeftJoycon.ButtonMinus; + LeftButtonSl = controllerInput.LeftJoycon.ButtonSl; + LeftButtonSr = controllerInput.LeftJoycon.ButtonSr; + ButtonZl = controllerInput.LeftJoycon.ButtonZl; + + ButtonA = controllerInput.RightJoycon.ButtonA; + ButtonB = controllerInput.RightJoycon.ButtonB; + ButtonX = controllerInput.RightJoycon.ButtonX; + ButtonY = controllerInput.RightJoycon.ButtonY; + ButtonR = controllerInput.RightJoycon.ButtonR; + ButtonPlus = controllerInput.RightJoycon.ButtonPlus; + RightButtonSl = controllerInput.RightJoycon.ButtonSl; + RightButtonSr = controllerInput.RightJoycon.ButtonSr; + ButtonZr = controllerInput.RightJoycon.ButtonZr; + + DeadzoneLeft = controllerInput.DeadzoneLeft; + DeadzoneRight = controllerInput.DeadzoneRight; + RangeLeft = controllerInput.RangeLeft; + RangeRight = controllerInput.RangeRight; + TriggerThreshold = controllerInput.TriggerThreshold; + + if (controllerInput.Motion != null) + { + EnableMotion = controllerInput.Motion.EnableMotion; + GyroDeadzone = controllerInput.Motion.GyroDeadzone; + Sensitivity = controllerInput.Motion.Sensitivity; + + if (controllerInput.Motion is CemuHookMotionConfigController cemuHook) + { + EnableCemuHookMotion = true; + DsuServerHost = cemuHook.DsuServerHost; + DsuServerPort = cemuHook.DsuServerPort; + Slot = cemuHook.Slot; + AltSlot = cemuHook.AltSlot; + MirrorInput = cemuHook.MirrorInput; + } + } + + if (controllerInput.Rumble != null) + { + EnableRumble = controllerInput.Rumble.EnableRumble; + WeakRumble = controllerInput.Rumble.WeakRumble; + StrongRumble = controllerInput.Rumble.StrongRumble; + } + } + } + + public InputConfig GetConfig() + { + var config = new StandardControllerInputConfig + { + Id = Id, + Backend = InputBackendType.GamepadSDL2, + PlayerIndex = PlayerIndex, + ControllerType = ControllerType, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = DpadUp, + DpadDown = DpadDown, + DpadLeft = DpadLeft, + DpadRight = DpadRight, + ButtonL = ButtonL, + ButtonMinus = ButtonMinus, + ButtonSl = LeftButtonSl, + ButtonSr = LeftButtonSr, + ButtonZl = ButtonZl + }, + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = ButtonA, + ButtonB = ButtonB, + ButtonX = ButtonX, + ButtonY = ButtonY, + ButtonPlus = ButtonPlus, + ButtonSl = RightButtonSl, + ButtonSr = RightButtonSr, + ButtonR = ButtonR, + ButtonZr = ButtonZr + }, + LeftJoyconStick = new JoyconConfigControllerStick + { + Joystick = LeftJoystick, + InvertStickX = LeftInvertStickX, + InvertStickY = LeftInvertStickY, + Rotate90CW = LeftRotate90, + StickButton = LeftStickButton + }, + RightJoyconStick = new JoyconConfigControllerStick + { + Joystick = RightJoystick, + InvertStickX = RightInvertStickX, + InvertStickY = RightInvertStickY, + Rotate90CW = RightRotate90, + StickButton = RightStickButton + }, + Rumble = new RumbleConfigController + { + EnableRumble = EnableRumble, + WeakRumble = WeakRumble, + StrongRumble = StrongRumble + }, + Version = InputConfig.CurrentVersion, + DeadzoneLeft = DeadzoneLeft, + DeadzoneRight = DeadzoneRight, + RangeLeft = RangeLeft, + RangeRight = RangeRight, + TriggerThreshold = TriggerThreshold + }; + + if (EnableCemuHookMotion) + { + config.Motion = new CemuHookMotionConfigController + { + EnableMotion = EnableMotion, + MotionBackend = MotionInputBackendType.CemuHook, + GyroDeadzone = GyroDeadzone, + Sensitivity = Sensitivity, + DsuServerHost = DsuServerHost, + DsuServerPort = DsuServerPort, + Slot = Slot, + AltSlot = AltSlot, + MirrorInput = MirrorInput + }; + } + else + { + config.Motion = new MotionConfigController + { + EnableMotion = EnableMotion, + MotionBackend = MotionInputBackendType.GamepadDriver, + GyroDeadzone = GyroDeadzone, + Sensitivity = Sensitivity + }; + } + + return config; + } + } +} diff --git a/src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs b/src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs new file mode 100644 index 000000000..029565210 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs @@ -0,0 +1,422 @@ +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Keyboard; + +namespace Ryujinx.Ava.UI.Models.Input +{ + public class KeyboardInputConfig : BaseModel + { + public string Id { get; set; } + public ControllerType ControllerType { get; set; } + public PlayerIndex PlayerIndex { get; set; } + + private Key _leftStickUp; + public Key LeftStickUp + { + get => _leftStickUp; + set + { + _leftStickUp = value; + OnPropertyChanged(); + } + } + + private Key _leftStickDown; + public Key LeftStickDown + { + get => _leftStickDown; + set + { + _leftStickDown = value; + OnPropertyChanged(); + } + } + + private Key _leftStickLeft; + public Key LeftStickLeft + { + get => _leftStickLeft; + set + { + _leftStickLeft = value; + OnPropertyChanged(); + } + } + + private Key _leftStickRight; + public Key LeftStickRight + { + get => _leftStickRight; + set + { + _leftStickRight = value; + OnPropertyChanged(); + } + } + + private Key _leftStickButton; + public Key LeftStickButton + { + get => _leftStickButton; + set + { + _leftStickButton = value; + OnPropertyChanged(); + } + } + + private Key _rightStickUp; + public Key RightStickUp + { + get => _rightStickUp; + set + { + _rightStickUp = value; + OnPropertyChanged(); + } + } + + private Key _rightStickDown; + public Key RightStickDown + { + get => _rightStickDown; + set + { + _rightStickDown = value; + OnPropertyChanged(); + } + } + + private Key _rightStickLeft; + public Key RightStickLeft + { + get => _rightStickLeft; + set + { + _rightStickLeft = value; + OnPropertyChanged(); + } + } + + private Key _rightStickRight; + public Key RightStickRight + { + get => _rightStickRight; + set + { + _rightStickRight = value; + OnPropertyChanged(); + } + } + + private Key _rightStickButton; + public Key RightStickButton + { + get => _rightStickButton; + set + { + _rightStickButton = value; + OnPropertyChanged(); + } + } + + private Key _dpadUp; + public Key DpadUp + { + get => _dpadUp; + set + { + _dpadUp = value; + OnPropertyChanged(); + } + } + + private Key _dpadDown; + public Key DpadDown + { + get => _dpadDown; + set + { + _dpadDown = value; + OnPropertyChanged(); + } + } + + private Key _dpadLeft; + public Key DpadLeft + { + get => _dpadLeft; + set + { + _dpadLeft = value; + OnPropertyChanged(); + } + } + + private Key _dpadRight; + public Key DpadRight + { + get => _dpadRight; + set + { + _dpadRight = value; + OnPropertyChanged(); + } + } + + private Key _buttonL; + public Key ButtonL + { + get => _buttonL; + set + { + _buttonL = value; + OnPropertyChanged(); + } + } + + private Key _buttonMinus; + public Key ButtonMinus + { + get => _buttonMinus; + set + { + _buttonMinus = value; + OnPropertyChanged(); + } + } + + private Key _leftButtonSl; + public Key LeftButtonSl + { + get => _leftButtonSl; + set + { + _leftButtonSl = value; + OnPropertyChanged(); + } + } + + private Key _leftButtonSr; + public Key LeftButtonSr + { + get => _leftButtonSr; + set + { + _leftButtonSr = value; + OnPropertyChanged(); + } + } + + private Key _buttonZl; + public Key ButtonZl + { + get => _buttonZl; + set + { + _buttonZl = value; + OnPropertyChanged(); + } + } + + private Key _buttonA; + public Key ButtonA + { + get => _buttonA; + set + { + _buttonA = value; + OnPropertyChanged(); + } + } + + private Key _buttonB; + public Key ButtonB + { + get => _buttonB; + set + { + _buttonB = value; + OnPropertyChanged(); + } + } + + private Key _buttonX; + public Key ButtonX + { + get => _buttonX; + set + { + _buttonX = value; + OnPropertyChanged(); + } + } + + private Key _buttonY; + public Key ButtonY + { + get => _buttonY; + set + { + _buttonY = value; + OnPropertyChanged(); + } + } + + private Key _buttonR; + public Key ButtonR + { + get => _buttonR; + set + { + _buttonR = value; + OnPropertyChanged(); + } + } + + private Key _buttonPlus; + public Key ButtonPlus + { + get => _buttonPlus; + set + { + _buttonPlus = value; + OnPropertyChanged(); + } + } + + private Key _rightButtonSl; + public Key RightButtonSl + { + get => _rightButtonSl; + set + { + _rightButtonSl = value; + OnPropertyChanged(); + } + } + + private Key _rightButtonSr; + public Key RightButtonSr + { + get => _rightButtonSr; + set + { + _rightButtonSr = value; + OnPropertyChanged(); + } + } + + private Key _buttonZr; + public Key ButtonZr + { + get => _buttonZr; + set + { + _buttonZr = value; + OnPropertyChanged(); + } + } + + public KeyboardInputConfig(InputConfig config) + { + if (config != null) + { + Id = config.Id; + ControllerType = config.ControllerType; + PlayerIndex = config.PlayerIndex; + + if (config is not StandardKeyboardInputConfig keyboardConfig) + { + return; + } + + LeftStickUp = keyboardConfig.LeftJoyconStick.StickUp; + LeftStickDown = keyboardConfig.LeftJoyconStick.StickDown; + LeftStickLeft = keyboardConfig.LeftJoyconStick.StickLeft; + LeftStickRight = keyboardConfig.LeftJoyconStick.StickRight; + LeftStickButton = keyboardConfig.LeftJoyconStick.StickButton; + + RightStickUp = keyboardConfig.RightJoyconStick.StickUp; + RightStickDown = keyboardConfig.RightJoyconStick.StickDown; + RightStickLeft = keyboardConfig.RightJoyconStick.StickLeft; + RightStickRight = keyboardConfig.RightJoyconStick.StickRight; + RightStickButton = keyboardConfig.RightJoyconStick.StickButton; + + DpadUp = keyboardConfig.LeftJoycon.DpadUp; + DpadDown = keyboardConfig.LeftJoycon.DpadDown; + DpadLeft = keyboardConfig.LeftJoycon.DpadLeft; + DpadRight = keyboardConfig.LeftJoycon.DpadRight; + ButtonL = keyboardConfig.LeftJoycon.ButtonL; + ButtonMinus = keyboardConfig.LeftJoycon.ButtonMinus; + LeftButtonSl = keyboardConfig.LeftJoycon.ButtonSl; + LeftButtonSr = keyboardConfig.LeftJoycon.ButtonSr; + ButtonZl = keyboardConfig.LeftJoycon.ButtonZl; + + ButtonA = keyboardConfig.RightJoycon.ButtonA; + ButtonB = keyboardConfig.RightJoycon.ButtonB; + ButtonX = keyboardConfig.RightJoycon.ButtonX; + ButtonY = keyboardConfig.RightJoycon.ButtonY; + ButtonR = keyboardConfig.RightJoycon.ButtonR; + ButtonPlus = keyboardConfig.RightJoycon.ButtonPlus; + RightButtonSl = keyboardConfig.RightJoycon.ButtonSl; + RightButtonSr = keyboardConfig.RightJoycon.ButtonSr; + ButtonZr = keyboardConfig.RightJoycon.ButtonZr; + } + } + + public InputConfig GetConfig() + { + var config = new StandardKeyboardInputConfig + { + Id = Id, + Backend = InputBackendType.WindowKeyboard, + PlayerIndex = PlayerIndex, + ControllerType = ControllerType, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = DpadUp, + DpadDown = DpadDown, + DpadLeft = DpadLeft, + DpadRight = DpadRight, + ButtonL = ButtonL, + ButtonMinus = ButtonMinus, + ButtonZl = ButtonZl, + ButtonSl = LeftButtonSl, + ButtonSr = LeftButtonSr + }, + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = ButtonA, + ButtonB = ButtonB, + ButtonX = ButtonX, + ButtonY = ButtonY, + ButtonPlus = ButtonPlus, + ButtonSl = RightButtonSl, + ButtonSr = RightButtonSr, + ButtonR = ButtonR, + ButtonZr = ButtonZr + }, + LeftJoyconStick = new JoyconConfigKeyboardStick + { + StickUp = LeftStickUp, + StickDown = LeftStickDown, + StickRight = LeftStickRight, + StickLeft = LeftStickLeft, + StickButton = LeftStickButton + }, + RightJoyconStick = new JoyconConfigKeyboardStick + { + StickUp = RightStickUp, + StickDown = RightStickDown, + StickLeft = RightStickLeft, + StickRight = RightStickRight, + StickButton = RightStickButton + }, + Version = InputConfig.CurrentVersion + }; + + return config; + } + } +} diff --git a/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs b/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs deleted file mode 100644 index f1352c6d8..000000000 --- a/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs +++ /dev/null @@ -1,456 +0,0 @@ -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Common.Configuration.Hid.Controller.Motion; -using Ryujinx.Common.Configuration.Hid.Keyboard; -using System; - -namespace Ryujinx.Ava.UI.Models -{ - internal class InputConfiguration : 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 float _rangeLeft; - private float _rangeRight; - - public InputBackendType Backend { get; set; } - - /// - /// Controller id - /// - public string Id { get; set; } - - /// - /// Controller's Type - /// - public ControllerType ControllerType { get; set; } - - /// - /// Player's Index for the controller - /// - public PlayerIndex PlayerIndex { get; set; } - - public TStick LeftJoystick { get; set; } - public bool LeftInvertStickX { get; set; } - public bool LeftInvertStickY { get; set; } - public bool RightRotate90 { get; set; } - public TKey LeftControllerStickButton { get; set; } - - public TStick RightJoystick { get; set; } - public bool RightInvertStickX { get; set; } - public bool RightInvertStickY { get; set; } - public bool LeftRotate90 { get; set; } - public TKey RightControllerStickButton { get; set; } - - public float DeadzoneLeft - { - get => _deadzoneLeft; - set - { - _deadzoneLeft = MathF.Round(value, 3); - - OnPropertyChanged(); - } - } - - public float RangeLeft - { - get => _rangeLeft; - set - { - _rangeLeft = MathF.Round(value, 3); - - OnPropertyChanged(); - } - } - - public float DeadzoneRight - { - get => _deadzoneRight; - set - { - _deadzoneRight = MathF.Round(value, 3); - - OnPropertyChanged(); - } - } - - public float RangeRight - { - get => _rangeRight; - set - { - _rangeRight = MathF.Round(value, 3); - - OnPropertyChanged(); - } - } - - public float TriggerThreshold - { - get => _triggerThreshold; - set - { - _triggerThreshold = MathF.Round(value, 3); - - OnPropertyChanged(); - } - } - - public MotionInputBackendType MotionBackend { 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 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 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 - { - get => _sensitivity; - set - { - _sensitivity = value; - - OnPropertyChanged(); - } - } - - public double GyroDeadzone - { - get => _gyroDeadzone; - set - { - _gyroDeadzone = Math.Round(value, 3); - - OnPropertyChanged(); - } - } - - public bool EnableMotion - { - get => _enableMotion; set - { - _enableMotion = value; - - OnPropertyChanged(); - } - } - - public bool EnableCemuHookMotion { get; set; } - public int Slot { get; set; } - public int AltSlot { get; set; } - public bool MirrorInput { get; set; } - public string DsuServerHost { get; set; } - public int DsuServerPort { get; set; } - - public bool EnableRumble { get; set; } - public float WeakRumble - { - get => _weakRumble; set - { - _weakRumble = value; - - OnPropertyChanged(); - } - } - public float StrongRumble - { - get => _strongRumble; set - { - _strongRumble = value; - - OnPropertyChanged(); - } - } - - public InputConfiguration(InputConfig config) - { - if (config != null) - { - Backend = config.Backend; - Id = config.Id; - ControllerType = config.ControllerType; - PlayerIndex = config.PlayerIndex; - - if (config is StandardKeyboardInputConfig keyboardConfig) - { - 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 = (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 = (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 = (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 = (TStick)(object)controllerConfig.LeftJoyconStick.Joystick; - LeftInvertStickX = controllerConfig.LeftJoyconStick.InvertStickX; - LeftInvertStickY = controllerConfig.LeftJoyconStick.InvertStickY; - LeftRotate90 = controllerConfig.LeftJoyconStick.Rotate90CW; - LeftControllerStickButton = (TKey)(object)controllerConfig.LeftJoyconStick.StickButton; - - RightJoystick = (TStick)(object)controllerConfig.RightJoyconStick.Joystick; - RightInvertStickX = controllerConfig.RightJoyconStick.InvertStickX; - RightInvertStickY = controllerConfig.RightJoyconStick.InvertStickY; - RightRotate90 = controllerConfig.RightJoyconStick.Rotate90CW; - RightControllerStickButton = (TKey)(object)controllerConfig.RightJoyconStick.StickButton; - - 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 = (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; - RangeLeft = controllerConfig.RangeLeft; - RangeRight = controllerConfig.RangeRight; - TriggerThreshold = controllerConfig.TriggerThreshold; - - if (controllerConfig.Motion != null) - { - EnableMotion = controllerConfig.Motion.EnableMotion; - MotionBackend = controllerConfig.Motion.MotionBackend; - GyroDeadzone = controllerConfig.Motion.GyroDeadzone; - Sensitivity = controllerConfig.Motion.Sensitivity; - - if (controllerConfig.Motion is CemuHookMotionConfigController cemuHook) - { - EnableCemuHookMotion = true; - DsuServerHost = cemuHook.DsuServerHost; - DsuServerPort = cemuHook.DsuServerPort; - Slot = cemuHook.Slot; - AltSlot = cemuHook.AltSlot; - MirrorInput = cemuHook.MirrorInput; - } - - if (controllerConfig.Rumble != null) - { - EnableRumble = controllerConfig.Rumble.EnableRumble; - WeakRumble = controllerConfig.Rumble.WeakRumble; - StrongRumble = controllerConfig.Rumble.StrongRumble; - } - } - } - } - } - - public InputConfiguration() - { - } - - public InputConfig GetConfig() - { - if (Backend == InputBackendType.WindowKeyboard) - { - return new StandardKeyboardInputConfig - { - Id = Id, - Backend = Backend, - PlayerIndex = PlayerIndex, - ControllerType = ControllerType, - LeftJoycon = new LeftJoyconCommonConfig - { - 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 - { - 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 - { - StickUp = (Key)(object)LeftStickUp, - StickDown = (Key)(object)LeftStickDown, - StickRight = (Key)(object)LeftStickRight, - StickLeft = (Key)(object)LeftStickLeft, - StickButton = (Key)(object)LeftKeyboardStickButton, - }, - RightJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = (Key)(object)RightStickUp, - StickDown = (Key)(object)RightStickDown, - StickLeft = (Key)(object)RightStickLeft, - StickRight = (Key)(object)RightStickRight, - StickButton = (Key)(object)RightKeyboardStickButton, - }, - Version = InputConfig.CurrentVersion, - }; - - } - - if (Backend == InputBackendType.GamepadSDL2) - { - var config = new StandardControllerInputConfig - { - Id = Id, - Backend = Backend, - PlayerIndex = PlayerIndex, - ControllerType = ControllerType, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = (GamepadInputId)(object)DpadUp, - DpadDown = (GamepadInputId)(object)DpadDown, - DpadLeft = (GamepadInputId)(object)DpadLeft, - DpadRight = (GamepadInputId)(object)DpadRight, - ButtonL = (GamepadInputId)(object)ButtonL, - ButtonZl = (GamepadInputId)(object)ButtonZl, - ButtonSl = (GamepadInputId)(object)LeftButtonSl, - ButtonSr = (GamepadInputId)(object)LeftButtonSr, - ButtonMinus = (GamepadInputId)(object)ButtonMinus, - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = (GamepadInputId)(object)ButtonA, - ButtonB = (GamepadInputId)(object)ButtonB, - ButtonX = (GamepadInputId)(object)ButtonX, - ButtonY = (GamepadInputId)(object)ButtonY, - ButtonPlus = (GamepadInputId)(object)ButtonPlus, - ButtonSl = (GamepadInputId)(object)RightButtonSl, - ButtonSr = (GamepadInputId)(object)RightButtonSr, - ButtonR = (GamepadInputId)(object)ButtonR, - ButtonZr = (GamepadInputId)(object)ButtonZr, - }, - LeftJoyconStick = new JoyconConfigControllerStick - { - Joystick = (StickInputId)(object)LeftJoystick, - InvertStickX = LeftInvertStickX, - InvertStickY = LeftInvertStickY, - Rotate90CW = LeftRotate90, - StickButton = (GamepadInputId)(object)LeftControllerStickButton, - }, - RightJoyconStick = new JoyconConfigControllerStick - { - Joystick = (StickInputId)(object)RightJoystick, - InvertStickX = RightInvertStickX, - InvertStickY = RightInvertStickY, - Rotate90CW = RightRotate90, - StickButton = (GamepadInputId)(object)RightControllerStickButton, - }, - Rumble = new RumbleConfigController - { - EnableRumble = EnableRumble, - WeakRumble = WeakRumble, - StrongRumble = StrongRumble, - }, - Version = InputConfig.CurrentVersion, - DeadzoneLeft = DeadzoneLeft, - DeadzoneRight = DeadzoneRight, - RangeLeft = RangeLeft, - 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, - }, - }; - - config.Motion.Sensitivity = Sensitivity; - config.Motion.EnableMotion = EnableMotion; - config.Motion.GyroDeadzone = GyroDeadzone; - - return config; - } - - return null; - } - } -} diff --git a/src/Ryujinx.Ava/UI/ViewModels/Input/ControllerInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/Input/ControllerInputViewModel.cs new file mode 100644 index 000000000..0e23dfa76 --- /dev/null +++ b/src/Ryujinx.Ava/UI/ViewModels/Input/ControllerInputViewModel.cs @@ -0,0 +1,84 @@ +using Avalonia.Svg.Skia; +using Ryujinx.Ava.UI.Models.Input; +using Ryujinx.Ava.UI.Views.Input; + +namespace Ryujinx.Ava.UI.ViewModels.Input +{ + public class ControllerInputViewModel : BaseModel + { + private ControllerInputConfig _config; + public ControllerInputConfig Config + { + get => _config; + set + { + _config = value; + OnPropertyChanged(); + } + } + + private bool _isLeft; + public bool IsLeft + { + get => _isLeft; + set + { + _isLeft = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(HasSides)); + } + } + + private bool _isRight; + public bool IsRight + { + get => _isRight; + set + { + _isRight = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(HasSides)); + } + } + + public bool HasSides => IsLeft ^ IsRight; + + private SvgImage _image; + public SvgImage Image + { + get => _image; + set + { + _image = value; + OnPropertyChanged(); + } + } + + public InputViewModel parentModel; + + public ControllerInputViewModel(InputViewModel model, ControllerInputConfig config) + { + parentModel = model; + model.NotifyChangesEvent += OnParentModelChanged; + OnParentModelChanged(); + Config = config; + } + + public async void ShowMotionConfig() + { + await MotionInputView.Show(this); + } + + public async void ShowRumbleConfig() + { + await RumbleInputView.Show(this); + } + + public void OnParentModelChanged() + { + IsLeft = parentModel.IsLeft; + IsRight = parentModel.IsRight; + Image = parentModel.Image; + } + } +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/Input/InputViewModel.cs similarity index 92% rename from src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/Input/InputViewModel.cs index c0c625321..ef8ffd50d 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/Input/InputViewModel.cs @@ -8,7 +8,7 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; -using Ryujinx.Ava.UI.Views.Input; +using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -30,9 +30,9 @@ using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.Gamepad using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; using Key = Ryujinx.Common.Configuration.Hid.Key; -namespace Ryujinx.Ava.UI.ViewModels +namespace Ryujinx.Ava.UI.ViewModels.Input { - public class ControllerInputViewModel : BaseModel, IDisposable + public class InputViewModel : BaseModel, IDisposable { private const string Disabled = "disabled"; private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg"; @@ -48,7 +48,7 @@ namespace Ryujinx.Ava.UI.ViewModels private int _controllerNumber; private string _controllerImage; private int _device; - private object _configuration; + private object _configViewModel; private string _profileName; private bool _isLoaded; @@ -71,13 +71,14 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsLeft { get; set; } public bool IsModified { get; set; } + public event Action NotifyChangesEvent; - public object Configuration + public object ConfigViewModel { - get => _configuration; + get => _configViewModel; set { - _configuration = value; + _configViewModel = value; OnPropertyChanged(); } @@ -232,7 +233,7 @@ namespace Ryujinx.Ava.UI.ViewModels public InputConfig Config { get; set; } - public ControllerInputViewModel(UserControl owner) : this() + public InputViewModel(UserControl owner) : this() { if (Program.PreviewerDetached) { @@ -244,7 +245,6 @@ namespace Ryujinx.Ava.UI.ViewModels _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; - _mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates(); _isLoaded = false; @@ -255,7 +255,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public ControllerInputViewModel() + public InputViewModel() { PlayerIndexes = new ObservableCollection(); Controllers = new ObservableCollection(); @@ -282,12 +282,12 @@ namespace Ryujinx.Ava.UI.ViewModels if (Config is StandardKeyboardInputConfig keyboardInputConfig) { - Configuration = new InputConfiguration(keyboardInputConfig); + ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig)); } if (Config is StandardControllerInputConfig controllerInputConfig) { - Configuration = new InputConfiguration(controllerInputConfig); + ConfigViewModel = new ControllerInputViewModel(this, new ControllerInputConfig(controllerInputConfig)); } } @@ -323,16 +323,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public async void ShowMotionConfig() - { - await MotionInputView.Show(this); - } - - public async void ShowRumbleConfig() - { - await RumbleInputView.Show(this); - } - private void LoadInputDriver() { if (_device < 0) @@ -740,7 +730,7 @@ namespace Ryujinx.Ava.UI.ViewModels return; } - if (Configuration == null) + if (ConfigViewModel == null) { return; } @@ -751,35 +741,37 @@ namespace Ryujinx.Ava.UI.ViewModels return; } - - bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1; - - if (validFileName) - { - string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json"); - - InputConfig config = null; - - if (IsKeyboard) - { - config = (Configuration as InputConfiguration).GetConfig(); - } - else if (IsController) - { - config = (Configuration as InputConfiguration).GetConfig(); - } - - config.ControllerType = Controllers[_controller].Type; - - string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig); - - await File.WriteAllTextAsync(path, jsonString); - - LoadProfiles(); - } else { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]); + bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1; + + if (validFileName) + { + string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json"); + + InputConfig config = null; + + if (IsKeyboard) + { + config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig(); + } + else if (IsController) + { + config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig(); + } + + config.ControllerType = Controllers[_controller].Type; + + string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig); + + await File.WriteAllTextAsync(path, jsonString); + + LoadProfiles(); + } + else + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]); + } } } @@ -830,18 +822,18 @@ namespace Ryujinx.Ava.UI.ViewModels if (device.Type == DeviceType.Keyboard) { - var inputConfig = Configuration as InputConfiguration; + var inputConfig = (ConfigViewModel as KeyboardInputViewModel).Config; inputConfig.Id = device.Id; } else { - var inputConfig = Configuration as InputConfiguration; + var inputConfig = (ConfigViewModel as ControllerInputViewModel).Config; inputConfig.Id = device.Id.Split(" ")[0]; } var config = !IsController - ? (Configuration as InputConfiguration).GetConfig() - : (Configuration as InputConfiguration).GetConfig(); + ? (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig() + : (ConfigViewModel as ControllerInputViewModel).Config.GetConfig(); config.ControllerType = Controllers[_controller].Type; config.PlayerIndex = _playerId; @@ -872,12 +864,13 @@ namespace Ryujinx.Ava.UI.ViewModels public void NotifyChanges() { - OnPropertyChanged(nameof(Configuration)); + OnPropertyChanged(nameof(ConfigViewModel)); OnPropertyChanged(nameof(IsController)); OnPropertyChanged(nameof(ShowSettings)); OnPropertyChanged(nameof(IsKeyboard)); OnPropertyChanged(nameof(IsRight)); OnPropertyChanged(nameof(IsLeft)); + NotifyChangesEvent?.Invoke(); } public void Dispose() diff --git a/src/Ryujinx.Ava/UI/ViewModels/Input/KeyboardInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/Input/KeyboardInputViewModel.cs new file mode 100644 index 000000000..a93873063 --- /dev/null +++ b/src/Ryujinx.Ava/UI/ViewModels/Input/KeyboardInputViewModel.cs @@ -0,0 +1,73 @@ +using Avalonia.Svg.Skia; +using Ryujinx.Ava.UI.Models.Input; + +namespace Ryujinx.Ava.UI.ViewModels.Input +{ + public class KeyboardInputViewModel : BaseModel + { + private KeyboardInputConfig _config; + public KeyboardInputConfig Config + { + get => _config; + set + { + _config = value; + OnPropertyChanged(); + } + } + + private bool _isLeft; + public bool IsLeft + { + get => _isLeft; + set + { + _isLeft = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(HasSides)); + } + } + + private bool _isRight; + public bool IsRight + { + get => _isRight; + set + { + _isRight = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(HasSides)); + } + } + + public bool HasSides => IsLeft ^ IsRight; + + private SvgImage _image; + public SvgImage Image + { + get => _image; + set + { + _image = value; + OnPropertyChanged(); + } + } + + public InputViewModel parentModel; + + public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config) + { + parentModel = model; + model.NotifyChangesEvent += OnParentModelChanged; + OnParentModelChanged(); + Config = config; + } + + public void OnParentModelChanged() + { + IsLeft = parentModel.IsLeft; + IsRight = parentModel.IsRight; + Image = parentModel.Image; + } + } +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/Input/MotionInputViewModel.cs similarity index 97% rename from src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/Input/MotionInputViewModel.cs index 0b12a51f6..c9ed8f2d4 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/Input/MotionInputViewModel.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Ava.UI.ViewModels +namespace Ryujinx.Ava.UI.ViewModels.Input { public class MotionInputViewModel : BaseModel { diff --git a/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/Input/RumbleInputViewModel.cs similarity index 92% rename from src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/Input/RumbleInputViewModel.cs index 49de19937..8ad33cf4c 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/Input/RumbleInputViewModel.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Ava.UI.ViewModels +namespace Ryujinx.Ava.UI.ViewModels.Input { public class RumbleInputViewModel : BaseModel { diff --git a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml index d636873a3..08bdf90f4 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml @@ -1,13 +1,11 @@ @@ -34,191 +33,10 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Vertical"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + MinHeight="450"> @@ -257,9 +75,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerZL}" TextAlignment="Center" /> - + @@ -273,9 +91,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerL}" TextAlignment="Center" /> - + @@ -289,9 +107,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsButtonMinus}" TextAlignment="Center" /> - + @@ -311,100 +129,8 @@ Margin="0,0,0,10" HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsLStick}" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -415,9 +141,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsStickButton}" TextAlignment="Center" /> - + @@ -432,22 +158,22 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsStickStick}" TextAlignment="Center" /> - + - + - + - + + Value="{Binding Config.DeadzoneLeft, Mode=TwoWay}" /> + Text="{Binding Config.DeadzoneLeft, StringFormat=\{0:0.00\}}" /> + Value="{Binding Config.RangeLeft, Mode=TwoWay}" /> + Text="{Binding Config.RangeLeft, StringFormat=\{0:0.00\}}" /> @@ -525,9 +251,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadUp}" TextAlignment="Center" /> - + @@ -542,9 +268,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadDown}" TextAlignment="Center" /> - + @@ -559,9 +285,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadLeft}" TextAlignment="Center" /> - + @@ -576,9 +302,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadRight}" TextAlignment="Center" /> - + @@ -591,6 +317,13 @@ Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> + + + Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" /> + Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" /> - + - + IsVisible="{Binding IsLeft}" + Orientation="Horizontal"> - - - - + + + + - + IsVisible="{Binding IsLeft}" + Orientation="Horizontal"> - - - - + + + + - + IsVisible="{Binding IsRight}" + Orientation="Horizontal"> - - - - + + + + - + IsVisible="{Binding IsRight}" + Orientation="Horizontal"> - + + + + - - + HorizontalAlignment="Stretch"> @@ -720,7 +449,7 @@ Margin="10" MinWidth="0" Grid.Column="0" - IsChecked="{ReflectionBinding Configuration.EnableMotion, Mode=TwoWay}"> + IsChecked="{Binding Config.EnableMotion, Mode=TwoWay}"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs new file mode 100644 index 000000000..356381a8a --- /dev/null +++ b/src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs @@ -0,0 +1,61 @@ +using Avalonia.Controls; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.ViewModels.Input; + +namespace Ryujinx.Ava.UI.Views.Input +{ + public partial class InputView : UserControl + { + private bool _dialogOpen; + private InputViewModel ViewModel { get; set; } + + public InputView() + { + DataContext = ViewModel = new InputViewModel(this); + + InitializeComponent(); + } + + public void SaveCurrentProfile() + { + ViewModel.Save(); + } + + private async void PlayerIndexBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (ViewModel.IsModified && !_dialogOpen) + { + _dialogOpen = true; + + var result = await ContentDialogHelper.CreateConfirmationDialog( + LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmMessage], + LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmSubMessage], + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); + + if (result == UserResult.Yes) + { + ViewModel.Save(); + } + + _dialogOpen = false; + + ViewModel.IsModified = false; + + if (e.AddedItems.Count > 0) + { + var player = (PlayerModel)e.AddedItems[0]; + ViewModel.PlayerId = player.Id; + } + } + } + + public void Dispose() + { + ViewModel.Dispose(); + } + } +} diff --git a/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml new file mode 100644 index 000000000..e4566f463 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml @@ -0,0 +1,675 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs new file mode 100644 index 000000000..f7024c5d1 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs @@ -0,0 +1,210 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.LogicalTree; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.ViewModels.Input; +using Ryujinx.Input; +using Ryujinx.Input.Assigner; + +namespace Ryujinx.Ava.UI.Views.Input +{ + public partial class KeyboardInputView : UserControl + { + private ButtonKeyAssigner _currentAssigner; + + public KeyboardInputView() + { + InitializeComponent(); + + foreach (ILogical visual in SettingButtons.GetLogicalDescendants()) + { + if (visual is ToggleButton button and not CheckBox) + { + button.IsCheckedChanged += Button_IsCheckedChanged; + } + } + } + + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + base.OnPointerReleased(e); + + if (_currentAssigner != null && _currentAssigner.ToggledButton != null && !_currentAssigner.ToggledButton.IsPointerOver) + { + _currentAssigner.Cancel(); + } + } + + private void Button_IsCheckedChanged(object sender, RoutedEventArgs e) + { + if (sender is ToggleButton button) + { + if ((bool)button.IsChecked) + { + if (_currentAssigner != null && button == _currentAssigner.ToggledButton) + { + return; + } + + bool isStick = button.Tag != null && button.Tag.ToString() == "stick"; + + if (_currentAssigner == null && (bool)button.IsChecked) + { + _currentAssigner = new ButtonKeyAssigner(button); + + this.Focus(NavigationMethod.Pointer); + + PointerPressed += MouseClick; + + IKeyboard keyboard = (IKeyboard)(DataContext as KeyboardInputViewModel).parentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. + IButtonAssigner assigner = CreateButtonAssigner(isStick); + + _currentAssigner.ButtonAssigned += (sender, e) => + { + if (e.ButtonValue.HasValue) + { + var viewModel = (DataContext as KeyboardInputViewModel); + var buttonValue = e.ButtonValue.Value; + viewModel.parentModel.IsModified = true; + + switch (button.Name) + { + case "ButtonZl": + viewModel.Config.ButtonZl = buttonValue.AsKey(); + break; + case "ButtonL": + viewModel.Config.ButtonL = buttonValue.AsKey(); + break; + case "ButtonMinus": + viewModel.Config.ButtonMinus = buttonValue.AsKey(); + break; + case "LeftStickButton": + viewModel.Config.LeftStickButton = buttonValue.AsKey(); + break; + case "LeftStickUp": + viewModel.Config.LeftStickUp = buttonValue.AsKey(); + break; + case "LeftStickDown": + viewModel.Config.LeftStickDown = buttonValue.AsKey(); + break; + case "LeftStickRight": + viewModel.Config.LeftStickRight = buttonValue.AsKey(); + break; + case "LeftStickLeft": + viewModel.Config.LeftStickLeft = buttonValue.AsKey(); + break; + case "DpadUp": + viewModel.Config.DpadUp = buttonValue.AsKey(); + break; + case "DpadDown": + viewModel.Config.DpadDown = buttonValue.AsKey(); + break; + case "DpadLeft": + viewModel.Config.DpadLeft = buttonValue.AsKey(); + break; + case "DpadRight": + viewModel.Config.DpadRight = buttonValue.AsKey(); + break; + case "LeftButtonSr": + viewModel.Config.LeftButtonSr = buttonValue.AsKey(); + break; + case "LeftButtonSl": + viewModel.Config.LeftButtonSl = buttonValue.AsKey(); + break; + case "RightButtonSr": + viewModel.Config.RightButtonSr = buttonValue.AsKey(); + break; + case "RightButtonSl": + viewModel.Config.RightButtonSl = buttonValue.AsKey(); + break; + case "ButtonZr": + viewModel.Config.ButtonZr = buttonValue.AsKey(); + break; + case "ButtonR": + viewModel.Config.ButtonR = buttonValue.AsKey(); + break; + case "ButtonPlus": + viewModel.Config.ButtonPlus = buttonValue.AsKey(); + break; + case "ButtonA": + viewModel.Config.ButtonA = buttonValue.AsKey(); + break; + case "ButtonB": + viewModel.Config.ButtonB = buttonValue.AsKey(); + break; + case "ButtonX": + viewModel.Config.ButtonX = buttonValue.AsKey(); + break; + case "ButtonY": + viewModel.Config.ButtonY = buttonValue.AsKey(); + break; + case "RightStickButton": + viewModel.Config.RightStickButton = buttonValue.AsKey(); + break; + case "RightStickUp": + viewModel.Config.RightStickUp = buttonValue.AsKey(); + break; + case "RightStickDown": + viewModel.Config.RightStickDown = buttonValue.AsKey(); + break; + case "RightStickRight": + viewModel.Config.RightStickRight = buttonValue.AsKey(); + break; + case "RightStickLeft": + viewModel.Config.RightStickLeft = buttonValue.AsKey(); + break; + } + } + }; + + _currentAssigner.GetInputAndAssign(assigner, keyboard); + } + else + { + if (_currentAssigner != null) + { + ToggleButton oldButton = _currentAssigner.ToggledButton; + + _currentAssigner.Cancel(); + _currentAssigner = null; + button.IsChecked = false; + } + } + } + else + { + _currentAssigner?.Cancel(); + _currentAssigner = null; + } + } + } + + private void MouseClick(object sender, PointerPressedEventArgs e) + { + bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed; + + _currentAssigner?.Cancel(shouldUnbind); + + PointerPressed -= MouseClick; + } + + private IButtonAssigner CreateButtonAssigner(bool forStick) + { + IButtonAssigner assigner; + + assigner = new KeyboardKeyAssigner((IKeyboard)(DataContext as KeyboardInputViewModel).parentModel.SelectedGamepad); + + return assigner; + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + _currentAssigner?.Cancel(); + _currentAssigner = null; + } + } +} diff --git a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml index a6b587f67..0d018e297 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml @@ -6,7 +6,7 @@ xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" - xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input" mc:Ignorable="d" x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView" x:DataType="viewModels:MotionInputViewModel" diff --git a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs index 1b340752b..2304364b6 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs @@ -1,9 +1,7 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Models; -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Ava.UI.ViewModels.Input; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Views.Input @@ -19,7 +17,7 @@ namespace Ryujinx.Ava.UI.Views.Input public MotionInputView(ControllerInputViewModel viewModel) { - var config = viewModel.Configuration as InputConfiguration; + var config = viewModel.Config; _viewModel = new MotionInputViewModel { @@ -51,7 +49,7 @@ namespace Ryujinx.Ava.UI.Views.Input }; contentDialog.PrimaryButtonClick += (sender, args) => { - var config = viewModel.Configuration as InputConfiguration; + var config = viewModel.Config; config.Slot = content._viewModel.Slot; config.Sensitivity = content._viewModel.Sensitivity; config.GyroDeadzone = content._viewModel.GyroDeadzone; diff --git a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml index 5b7087a47..1beb1f06e 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" - xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input" mc:Ignorable="d" x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView" x:DataType="viewModels:RumbleInputViewModel" diff --git a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs index 9307f872c..58a4b416b 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs @@ -1,9 +1,7 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Models; -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Ava.UI.ViewModels.Input; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Views.Input @@ -19,7 +17,7 @@ namespace Ryujinx.Ava.UI.Views.Input public RumbleInputView(ControllerInputViewModel viewModel) { - var config = viewModel.Configuration as InputConfiguration; + var config = viewModel.Config; _viewModel = new RumbleInputViewModel { @@ -47,7 +45,7 @@ namespace Ryujinx.Ava.UI.Views.Input contentDialog.PrimaryButtonClick += (sender, args) => { - var config = viewModel.Configuration as InputConfiguration; + var config = viewModel.Config; config.StrongRumble = content._viewModel.StrongRumble; config.WeakRumble = content._viewModel.WeakRumble; }; diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml index 81f4b68b7..55c2ed6e3 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml @@ -27,9 +27,9 @@ - + Name="InputView" /> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs index 8a0cb8ab9..85ccffccd 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Views.Settings public void Dispose() { - ControllerSettings.Dispose(); + InputView.Dispose(); } } } diff --git a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs index d7bb0b883..314501c52 100644 --- a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Ava.UI.Windows public void SaveSettings() { - InputPage.ControllerSettings?.SaveCurrentProfile(); + InputPage.InputView?.SaveCurrentProfile(); if (Owner is MainWindow window && ViewModel.DirectoryChanged) { diff --git a/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs b/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs index 388ebcc07..bf8319a6a 100644 --- a/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs +++ b/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs @@ -59,16 +59,16 @@ namespace Ryujinx.Input.Assigner return _gamepad == null || !_gamepad.IsConnected; } - public string GetPressedButton() + public ButtonValue? GetPressedButton() { IEnumerable pressedButtons = _detector.GetPressedButtons(); if (pressedButtons.Any()) { - return !_forStick ? pressedButtons.First().ToString() : ((StickInputId)pressedButtons.First()).ToString(); + return !_forStick ? new(pressedButtons.First()) : new(((StickInputId)pressedButtons.First())); } - return ""; + return null; } private void CollectButtonStats() diff --git a/src/Ryujinx.Input/Assigner/IButtonAssigner.cs b/src/Ryujinx.Input/Assigner/IButtonAssigner.cs index 76a9fece4..653717133 100644 --- a/src/Ryujinx.Input/Assigner/IButtonAssigner.cs +++ b/src/Ryujinx.Input/Assigner/IButtonAssigner.cs @@ -31,6 +31,6 @@ namespace Ryujinx.Input.Assigner /// Get the pressed button that was read in by the button assigner. /// /// The pressed button that was read - string GetPressedButton(); + ButtonValue? GetPressedButton(); } } diff --git a/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs b/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs index e52ef4a2c..c66812ba0 100644 --- a/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs +++ b/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Input.Assigner public bool HasAnyButtonPressed() { - return GetPressedButton().Length != 0; + return GetPressedButton() is not null; } public bool ShouldCancel() @@ -31,20 +31,20 @@ namespace Ryujinx.Input.Assigner return _keyboardState.IsPressed(Key.Escape); } - public string GetPressedButton() + public ButtonValue? GetPressedButton() { - string keyPressed = ""; + ButtonValue? keyPressed = null; for (Key key = Key.Unknown; key < Key.Count; key++) { if (_keyboardState.IsPressed(key)) { - keyPressed = key.ToString(); + keyPressed = new(key); break; } } - return !ShouldCancel() ? keyPressed : ""; + return !ShouldCancel() ? keyPressed : null; } } } diff --git a/src/Ryujinx.Input/ButtonValue.cs b/src/Ryujinx.Input/ButtonValue.cs new file mode 100644 index 000000000..f037e6b60 --- /dev/null +++ b/src/Ryujinx.Input/ButtonValue.cs @@ -0,0 +1,48 @@ +using System.Diagnostics; + +namespace Ryujinx.Input +{ + public enum ButtonValueType { Key, GamepadButtonInputId, StickId } + + public readonly struct ButtonValue + { + private readonly ButtonValueType _type; + private readonly uint _rawValue; + + public ButtonValue(Key key) + { + _type = ButtonValueType.Key; + _rawValue = (uint)key; + } + + public ButtonValue(GamepadButtonInputId gamepad) + { + _type = ButtonValueType.GamepadButtonInputId; + _rawValue = (uint)gamepad; + } + + public ButtonValue(StickInputId stick) + { + _type = ButtonValueType.StickId; + _rawValue = (uint)stick; + } + + public Common.Configuration.Hid.Key AsKey() + { + Debug.Assert(_type == ButtonValueType.Key); + return (Common.Configuration.Hid.Key)_rawValue; + } + + public Common.Configuration.Hid.Controller.GamepadInputId AsGamepadButtonInputId() + { + Debug.Assert(_type == ButtonValueType.GamepadButtonInputId); + return (Common.Configuration.Hid.Controller.GamepadInputId)_rawValue; + } + + public Common.Configuration.Hid.Controller.StickInputId AsGamepadStickId() + { + Debug.Assert(_type == ButtonValueType.StickId); + return (Common.Configuration.Hid.Controller.StickInputId)_rawValue; + } + } +} diff --git a/src/Ryujinx/Ui/Windows/ControllerWindow.cs b/src/Ryujinx/Ui/Windows/ControllerWindow.cs index ebf22ab60..52cad5c85 100644 --- a/src/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/src/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -893,7 +893,7 @@ namespace Ryujinx.Ui.Windows } } - string pressedButton = assigner.GetPressedButton(); + string pressedButton = assigner.GetPressedButton().ToString(); Application.Invoke(delegate { From 638be5f296bf52943da4366699d33f1e8656e00c Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 21 Oct 2023 15:19:21 +0200 Subject: [PATCH 7/7] Revert "Ava UI: Input Menu Refactor (#4998)" This reverts commit 49b37550cae6b3c69f59a9c7a44b17e3c12a813b. This currently breaks the GTK GUI. --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 99 --- src/Ryujinx.Ava/Assets/Styles/Styles.xaml | 5 +- .../UI/Helpers/ButtonKeyAssigner.cs | 28 +- .../UI/Helpers/KeyValueConverter.cs | 167 +---- .../UI/Models/Input/ControllerInputConfig.cs | 580 --------------- .../UI/Models/Input/KeyboardInputConfig.cs | 422 ----------- .../UI/Models/InputConfiguration.cs | 456 ++++++++++++ ...ewModel.cs => ControllerInputViewModel.cs} | 103 +-- .../Input/ControllerInputViewModel.cs | 84 --- .../Input/KeyboardInputViewModel.cs | 73 -- .../{Input => }/MotionInputViewModel.cs | 2 +- .../{Input => }/RumbleInputViewModel.cs | 2 +- .../UI/Views/Input/ControllerInputView.axaml | 616 ++++++++++++---- .../Views/Input/ControllerInputView.axaml.cs | 164 ++--- .../UI/Views/Input/InputView.axaml | 225 ------ .../UI/Views/Input/InputView.axaml.cs | 61 -- .../UI/Views/Input/KeyboardInputView.axaml | 675 ------------------ .../UI/Views/Input/KeyboardInputView.axaml.cs | 210 ------ .../UI/Views/Input/MotionInputView.axaml | 2 +- .../UI/Views/Input/MotionInputView.axaml.cs | 8 +- .../UI/Views/Input/RumbleInputView.axaml | 2 +- .../UI/Views/Input/RumbleInputView.axaml.cs | 8 +- .../UI/Views/Settings/SettingsInputView.axaml | 4 +- .../Views/Settings/SettingsInputView.axaml.cs | 2 +- .../UI/Windows/SettingsWindow.axaml.cs | 2 +- .../Assigner/GamepadButtonAssigner.cs | 6 +- src/Ryujinx.Input/Assigner/IButtonAssigner.cs | 2 +- .../Assigner/KeyboardKeyAssigner.cs | 10 +- src/Ryujinx.Input/ButtonValue.cs | 48 -- src/Ryujinx/Ui/Windows/ControllerWindow.cs | 2 +- 30 files changed, 1152 insertions(+), 2916 deletions(-) delete mode 100644 src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs delete mode 100644 src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs create mode 100644 src/Ryujinx.Ava/UI/Models/InputConfiguration.cs rename src/Ryujinx.Ava/UI/ViewModels/{Input/InputViewModel.cs => ControllerInputViewModel.cs} (92%) delete mode 100644 src/Ryujinx.Ava/UI/ViewModels/Input/ControllerInputViewModel.cs delete mode 100644 src/Ryujinx.Ava/UI/ViewModels/Input/KeyboardInputViewModel.cs rename src/Ryujinx.Ava/UI/ViewModels/{Input => }/MotionInputViewModel.cs (97%) rename src/Ryujinx.Ava/UI/ViewModels/{Input => }/RumbleInputViewModel.cs (92%) delete mode 100644 src/Ryujinx.Ava/UI/Views/Input/InputView.axaml delete mode 100644 src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs delete mode 100644 src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml delete mode 100644 src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs delete mode 100644 src/Ryujinx.Input/ButtonValue.cs diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index fc65fe4a0..a67b796bd 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -263,105 +263,6 @@ "ControllerSettingsMotionGyroDeadzone": "Gyro Deadzone:", "ControllerSettingsSave": "Save", "ControllerSettingsClose": "Close", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Control Left", - "KeyControlRight": "Control Right", - "KeyAltLeft": "Alt Left", - "KeyAltRight": "Alt Right", - "KeyOptLeft": "⌥ Left", - "KeyOptRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyWinRight": "⊞ Right", - "KeyCmdLeft": "⌘ Left", - "KeyCmdRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "Left Stick Button", - "GamepadRightStick": "Right Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", "UserProfilesSelectedUserProfile": "Selected User Profile:", "UserProfilesSaveProfileName": "Save Profile Name", "UserProfilesChangeProfileImage": "Change Profile Image", diff --git a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml index b3a6f59c8..f7f64be22 100644 --- a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml +++ b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml @@ -15,7 +15,8 @@ - + @@ -392,4 +393,4 @@ 600 756 - + \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs b/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs index 54e0918a5..7e8ba7342 100644 --- a/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs +++ b/src/Ryujinx.Ava/UI/Helpers/ButtonKeyAssigner.cs @@ -1,8 +1,11 @@ +using Avalonia.Controls; using Avalonia.Controls.Primitives; +using Avalonia.LogicalTree; using Avalonia.Threading; using Ryujinx.Input; using Ryujinx.Input.Assigner; using System; +using System.Linq; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Helpers @@ -12,12 +15,12 @@ namespace Ryujinx.Ava.UI.Helpers internal class ButtonAssignedEventArgs : EventArgs { public ToggleButton Button { get; } - public ButtonValue? ButtonValue { get; } + public bool IsAssigned { get; } - public ButtonAssignedEventArgs(ToggleButton button, ButtonValue? buttonValue) + public ButtonAssignedEventArgs(ToggleButton button, bool isAssigned) { Button = button; - ButtonValue = buttonValue; + IsAssigned = isAssigned; } } @@ -75,11 +78,15 @@ namespace Ryujinx.Ava.UI.Helpers await Dispatcher.UIThread.InvokeAsync(() => { - ButtonValue? pressedButton = assigner.GetPressedButton(); + string pressedButton = assigner.GetPressedButton(); if (_shouldUnbind) { - pressedButton = null; + SetButtonText(ToggledButton, "Unbound"); + } + else if (pressedButton != "") + { + SetButtonText(ToggledButton, pressedButton); } _shouldUnbind = false; @@ -87,8 +94,17 @@ namespace Ryujinx.Ava.UI.Helpers ToggledButton.IsChecked = false; - ButtonAssigned?.Invoke(this, new ButtonAssignedEventArgs(ToggledButton, pressedButton)); + ButtonAssigned?.Invoke(this, new ButtonAssignedEventArgs(ToggledButton, pressedButton != null)); + static void SetButtonText(ToggleButton button, string text) + { + ILogical textBlock = button.GetLogicalDescendants().First(x => x is TextBlock); + + if (textBlock != null && textBlock is TextBlock block) + { + block.Text = text; + } + } }); } diff --git a/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs b/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs index 1c4aa7b21..028ed6bf4 100644 --- a/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs +++ b/src/Ryujinx.Ava/UI/Helpers/KeyValueConverter.cs @@ -1,9 +1,7 @@ using Avalonia.Data.Converters; -using Ryujinx.Ava.Common.Locale; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using System; -using System.Collections.Generic; using System.Globalization; namespace Ryujinx.Ava.UI.Helpers @@ -12,158 +10,37 @@ namespace Ryujinx.Ava.UI.Helpers { public static KeyValueConverter Instance = new(); - private static readonly Dictionary _keysMap = new() - { - { Key.Unknown, LocaleKeys.KeyUnknown }, - { Key.ShiftLeft, LocaleKeys.KeyShiftLeft }, - { Key.ShiftRight, LocaleKeys.KeyShiftRight }, - { Key.ControlLeft, LocaleKeys.KeyControlLeft }, - { Key.ControlRight, LocaleKeys.KeyControlRight }, - { Key.AltLeft, OperatingSystem.IsMacOS() ? LocaleKeys.KeyOptLeft : LocaleKeys.KeyAltLeft }, - { Key.AltRight, OperatingSystem.IsMacOS() ? LocaleKeys.KeyOptRight : LocaleKeys.KeyAltRight }, - { Key.WinLeft, OperatingSystem.IsMacOS() ? LocaleKeys.KeyCmdLeft : LocaleKeys.KeyWinLeft }, - { Key.WinRight, OperatingSystem.IsMacOS() ? LocaleKeys.KeyCmdRight : LocaleKeys.KeyWinRight }, - { Key.Up, LocaleKeys.KeyUp }, - { Key.Down, LocaleKeys.KeyDown }, - { Key.Left, LocaleKeys.KeyLeft }, - { Key.Right, LocaleKeys.KeyRight }, - { Key.Enter, LocaleKeys.KeyEnter }, - { Key.Escape, LocaleKeys.KeyEscape }, - { Key.Space, LocaleKeys.KeySpace }, - { Key.Tab, LocaleKeys.KeyTab }, - { Key.BackSpace, LocaleKeys.KeyBackSpace }, - { Key.Insert, LocaleKeys.KeyInsert }, - { Key.Delete, LocaleKeys.KeyDelete }, - { Key.PageUp, LocaleKeys.KeyPageUp }, - { Key.PageDown, LocaleKeys.KeyPageDown }, - { Key.Home, LocaleKeys.KeyHome }, - { Key.End, LocaleKeys.KeyEnd }, - { Key.CapsLock, LocaleKeys.KeyCapsLock }, - { Key.ScrollLock, LocaleKeys.KeyScrollLock }, - { Key.PrintScreen, LocaleKeys.KeyPrintScreen }, - { Key.Pause, LocaleKeys.KeyPause }, - { Key.NumLock, LocaleKeys.KeyNumLock }, - { Key.Clear, LocaleKeys.KeyClear }, - { Key.Keypad0, LocaleKeys.KeyKeypad0 }, - { Key.Keypad1, LocaleKeys.KeyKeypad1 }, - { Key.Keypad2, LocaleKeys.KeyKeypad2 }, - { Key.Keypad3, LocaleKeys.KeyKeypad3 }, - { Key.Keypad4, LocaleKeys.KeyKeypad4 }, - { Key.Keypad5, LocaleKeys.KeyKeypad5 }, - { Key.Keypad6, LocaleKeys.KeyKeypad6 }, - { Key.Keypad7, LocaleKeys.KeyKeypad7 }, - { Key.Keypad8, LocaleKeys.KeyKeypad8 }, - { Key.Keypad9, LocaleKeys.KeyKeypad9 }, - { Key.KeypadDivide, LocaleKeys.KeyKeypadDivide }, - { Key.KeypadMultiply, LocaleKeys.KeyKeypadMultiply }, - { Key.KeypadSubtract, LocaleKeys.KeyKeypadSubtract }, - { Key.KeypadAdd, LocaleKeys.KeyKeypadAdd }, - { Key.KeypadDecimal, LocaleKeys.KeyKeypadDecimal }, - { Key.KeypadEnter, LocaleKeys.KeyKeypadEnter }, - { Key.Number0, LocaleKeys.KeyNumber0 }, - { Key.Number1, LocaleKeys.KeyNumber1 }, - { Key.Number2, LocaleKeys.KeyNumber2 }, - { Key.Number3, LocaleKeys.KeyNumber3 }, - { Key.Number4, LocaleKeys.KeyNumber4 }, - { Key.Number5, LocaleKeys.KeyNumber5 }, - { Key.Number6, LocaleKeys.KeyNumber6 }, - { Key.Number7, LocaleKeys.KeyNumber7 }, - { Key.Number8, LocaleKeys.KeyNumber8 }, - { Key.Number9, LocaleKeys.KeyNumber9 }, - { Key.Tilde, LocaleKeys.KeyTilde }, - { Key.Grave, LocaleKeys.KeyGrave }, - { Key.Minus, LocaleKeys.KeyMinus }, - { Key.Plus, LocaleKeys.KeyPlus }, - { Key.BracketLeft, LocaleKeys.KeyBracketLeft }, - { Key.BracketRight, LocaleKeys.KeyBracketRight }, - { Key.Semicolon, LocaleKeys.KeySemicolon }, - { Key.Quote, LocaleKeys.KeyQuote }, - { Key.Comma, LocaleKeys.KeyComma }, - { Key.Period, LocaleKeys.KeyPeriod }, - { Key.Slash, LocaleKeys.KeySlash }, - { Key.BackSlash, LocaleKeys.KeyBackSlash }, - { Key.Unbound, LocaleKeys.KeyUnbound }, - }; - - private static readonly Dictionary _gamepadInputIdMap = new() - { - { GamepadInputId.LeftStick, LocaleKeys.GamepadLeftStick }, - { GamepadInputId.RightStick, LocaleKeys.GamepadRightStick }, - { GamepadInputId.LeftShoulder, LocaleKeys.GamepadLeftShoulder }, - { GamepadInputId.RightShoulder, LocaleKeys.GamepadRightShoulder }, - { GamepadInputId.LeftTrigger, LocaleKeys.GamepadLeftTrigger }, - { GamepadInputId.RightTrigger, LocaleKeys.GamepadRightTrigger }, - { GamepadInputId.DpadUp, LocaleKeys.GamepadDpadUp}, - { GamepadInputId.DpadDown, LocaleKeys.GamepadDpadDown}, - { GamepadInputId.DpadLeft, LocaleKeys.GamepadDpadLeft}, - { GamepadInputId.DpadRight, LocaleKeys.GamepadDpadRight}, - { GamepadInputId.Minus, LocaleKeys.GamepadMinus}, - { GamepadInputId.Plus, LocaleKeys.GamepadPlus}, - { GamepadInputId.Guide, LocaleKeys.GamepadGuide}, - { GamepadInputId.Misc1, LocaleKeys.GamepadMisc1}, - { GamepadInputId.Paddle1, LocaleKeys.GamepadPaddle1}, - { GamepadInputId.Paddle2, LocaleKeys.GamepadPaddle2}, - { GamepadInputId.Paddle3, LocaleKeys.GamepadPaddle3}, - { GamepadInputId.Paddle4, LocaleKeys.GamepadPaddle4}, - { GamepadInputId.Touchpad, LocaleKeys.GamepadTouchpad}, - { GamepadInputId.SingleLeftTrigger0, LocaleKeys.GamepadSingleLeftTrigger0}, - { GamepadInputId.SingleRightTrigger0, LocaleKeys.GamepadSingleRightTrigger0}, - { GamepadInputId.SingleLeftTrigger1, LocaleKeys.GamepadSingleLeftTrigger1}, - { GamepadInputId.SingleRightTrigger1, LocaleKeys.GamepadSingleRightTrigger1}, - { GamepadInputId.Unbound, LocaleKeys.KeyUnbound}, - }; - - private static readonly Dictionary _stickInputIdMap = new() - { - { StickInputId.Left, LocaleKeys.StickLeft}, - { StickInputId.Right, LocaleKeys.StickRight}, - { StickInputId.Unbound, LocaleKeys.KeyUnbound}, - }; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - string keyString = ""; - - if (value is Key key) + if (value == null) { - if (_keysMap.TryGetValue(key, out LocaleKeys localeKey)) - { - keyString = LocaleManager.Instance[localeKey]; - } - else - { - keyString = key.ToString(); - } - } - else if (value is GamepadInputId gamepadInputId) - { - if (_gamepadInputIdMap.TryGetValue(gamepadInputId, out LocaleKeys localeKey)) - { - keyString = LocaleManager.Instance[localeKey]; - } - else - { - keyString = gamepadInputId.ToString(); - } - } - else if (value is StickInputId stickInputId) - { - if (_stickInputIdMap.TryGetValue(stickInputId, out LocaleKeys localeKey)) - { - keyString = LocaleManager.Instance[localeKey]; - } - else - { - keyString = stickInputId.ToString(); - } + return null; } - return keyString; + return value.ToString(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - throw new NotSupportedException(); + object key = null; + + if (value != null) + { + if (targetType == typeof(Key)) + { + key = Enum.Parse(value.ToString()); + } + else if (targetType == typeof(GamepadInputId)) + { + key = Enum.Parse(value.ToString()); + } + else if (targetType == typeof(StickInputId)) + { + key = Enum.Parse(value.ToString()); + } + } + + return key; } } } diff --git a/src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs b/src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs deleted file mode 100644 index 4929e582e..000000000 --- a/src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs +++ /dev/null @@ -1,580 +0,0 @@ -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Common.Configuration.Hid.Controller.Motion; -using System; - -namespace Ryujinx.Ava.UI.Models.Input -{ - public class ControllerInputConfig : BaseModel - { - public bool EnableCemuHookMotion { get; set; } - public string DsuServerHost { get; set; } - public int DsuServerPort { get; set; } - public int Slot { get; set; } - public int AltSlot { get; set; } - public bool MirrorInput { get; set; } - public int Sensitivity { get; set; } - public double GyroDeadzone { get; set; } - - public float WeakRumble { get; set; } - public float StrongRumble { get; set; } - - public string Id { get; set; } - public ControllerType ControllerType { get; set; } - public PlayerIndex PlayerIndex { get; set; } - - private StickInputId _leftJoystick; - public StickInputId LeftJoystick - { - get => _leftJoystick; - set - { - _leftJoystick = value; - OnPropertyChanged(); - } - } - - private bool _leftInvertStickX; - public bool LeftInvertStickX - { - get => _leftInvertStickX; - set - { - _leftInvertStickX = value; - OnPropertyChanged(); - } - } - - private bool _leftInvertStickY; - public bool LeftInvertStickY - { - get => _leftInvertStickY; - set - { - _leftInvertStickY = value; - OnPropertyChanged(); - } - } - - private bool _leftRotate90; - public bool LeftRotate90 - { - get => _leftRotate90; - set - { - _leftRotate90 = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _leftStickButton; - public GamepadInputId LeftStickButton - { - get => _leftStickButton; - set - { - _leftStickButton = value; - OnPropertyChanged(); - } - } - - private StickInputId _rightJoystick; - public StickInputId RightJoystick - { - get => _rightJoystick; - set - { - _rightJoystick = value; - OnPropertyChanged(); - } - } - - private bool _rightInvertStickX; - public bool RightInvertStickX - { - get => _rightInvertStickX; - set - { - _rightInvertStickX = value; - OnPropertyChanged(); - } - } - - private bool _rightInvertStickY; - public bool RightInvertStickY - { - get => _rightInvertStickY; - set - { - _rightInvertStickY = value; - OnPropertyChanged(); - } - } - - private bool _rightRotate90; - public bool RightRotate90 - { - get => _rightRotate90; - set - { - _rightRotate90 = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _rightStickButton; - public GamepadInputId RightStickButton - { - get => _rightStickButton; - set - { - _rightStickButton = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _dpadUp; - public GamepadInputId DpadUp - { - get => _dpadUp; - set - { - _dpadUp = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _dpadDown; - public GamepadInputId DpadDown - { - get => _dpadDown; - set - { - _dpadDown = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _dpadLeft; - public GamepadInputId DpadLeft - { - get => _dpadLeft; - set - { - _dpadLeft = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _dpadRight; - public GamepadInputId DpadRight - { - get => _dpadRight; - set - { - _dpadRight = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonL; - public GamepadInputId ButtonL - { - get => _buttonL; - set - { - _buttonL = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonMinus; - public GamepadInputId ButtonMinus - { - get => _buttonMinus; - set - { - _buttonMinus = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _leftButtonSl; - public GamepadInputId LeftButtonSl - { - get => _leftButtonSl; - set - { - _leftButtonSl = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _leftButtonSr; - public GamepadInputId LeftButtonSr - { - get => _leftButtonSr; - set - { - _leftButtonSr = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonZl; - public GamepadInputId ButtonZl - { - get => _buttonZl; - set - { - _buttonZl = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonA; - public GamepadInputId ButtonA - { - get => _buttonA; - set - { - _buttonA = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonB; - public GamepadInputId ButtonB - { - get => _buttonB; - set - { - _buttonB = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonX; - public GamepadInputId ButtonX - { - get => _buttonX; - set - { - _buttonX = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonY; - public GamepadInputId ButtonY - { - get => _buttonY; - set - { - _buttonY = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonR; - public GamepadInputId ButtonR - { - get => _buttonR; - set - { - _buttonR = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonPlus; - public GamepadInputId ButtonPlus - { - get => _buttonPlus; - set - { - _buttonPlus = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _rightButtonSl; - public GamepadInputId RightButtonSl - { - get => _rightButtonSl; - set - { - _rightButtonSl = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _rightButtonSr; - public GamepadInputId RightButtonSr - { - get => _rightButtonSr; - set - { - _rightButtonSr = value; - OnPropertyChanged(); - } - } - - private GamepadInputId _buttonZr; - public GamepadInputId ButtonZr - { - get => _buttonZr; - set - { - _buttonZr = value; - OnPropertyChanged(); - } - } - - private float _deadzoneLeft; - public float DeadzoneLeft - { - get => _deadzoneLeft; - set - { - _deadzoneLeft = MathF.Round(value, 3); - OnPropertyChanged(); - } - } - - private float _deadzoneRight; - public float DeadzoneRight - { - get => _deadzoneRight; - set - { - _deadzoneRight = MathF.Round(value, 3); - OnPropertyChanged(); - } - } - - private float _rangeLeft; - public float RangeLeft - { - get => _rangeLeft; - set - { - _rangeLeft = MathF.Round(value, 3); - OnPropertyChanged(); - } - } - - private float _rangeRight; - public float RangeRight - { - get => _rangeRight; - set - { - _rangeRight = MathF.Round(value, 3); - OnPropertyChanged(); - } - } - - private float _triggerThreshold; - public float TriggerThreshold - { - get => _triggerThreshold; - set - { - _triggerThreshold = MathF.Round(value, 3); - OnPropertyChanged(); - } - } - - private bool _enableMotion; - public bool EnableMotion - { - get => _enableMotion; - set - { - _enableMotion = value; - OnPropertyChanged(); - } - } - - private bool _enableRumble; - public bool EnableRumble - { - get => _enableRumble; - set - { - _enableRumble = value; - OnPropertyChanged(); - } - } - - public ControllerInputConfig(InputConfig config) - { - if (config != null) - { - Id = config.Id; - ControllerType = config.ControllerType; - PlayerIndex = config.PlayerIndex; - - if (config is not StandardControllerInputConfig controllerInput) - { - return; - } - - LeftJoystick = controllerInput.LeftJoyconStick.Joystick; - LeftInvertStickX = controllerInput.LeftJoyconStick.InvertStickX; - LeftInvertStickY = controllerInput.LeftJoyconStick.InvertStickY; - LeftRotate90 = controllerInput.LeftJoyconStick.Rotate90CW; - LeftStickButton = controllerInput.LeftJoyconStick.StickButton; - - RightJoystick = controllerInput.RightJoyconStick.Joystick; - RightInvertStickX = controllerInput.RightJoyconStick.InvertStickX; - RightInvertStickY = controllerInput.RightJoyconStick.InvertStickY; - RightRotate90 = controllerInput.RightJoyconStick.Rotate90CW; - RightStickButton = controllerInput.RightJoyconStick.StickButton; - - DpadUp = controllerInput.LeftJoycon.DpadUp; - DpadDown = controllerInput.LeftJoycon.DpadDown; - DpadLeft = controllerInput.LeftJoycon.DpadLeft; - DpadRight = controllerInput.LeftJoycon.DpadRight; - ButtonL = controllerInput.LeftJoycon.ButtonL; - ButtonMinus = controllerInput.LeftJoycon.ButtonMinus; - LeftButtonSl = controllerInput.LeftJoycon.ButtonSl; - LeftButtonSr = controllerInput.LeftJoycon.ButtonSr; - ButtonZl = controllerInput.LeftJoycon.ButtonZl; - - ButtonA = controllerInput.RightJoycon.ButtonA; - ButtonB = controllerInput.RightJoycon.ButtonB; - ButtonX = controllerInput.RightJoycon.ButtonX; - ButtonY = controllerInput.RightJoycon.ButtonY; - ButtonR = controllerInput.RightJoycon.ButtonR; - ButtonPlus = controllerInput.RightJoycon.ButtonPlus; - RightButtonSl = controllerInput.RightJoycon.ButtonSl; - RightButtonSr = controllerInput.RightJoycon.ButtonSr; - ButtonZr = controllerInput.RightJoycon.ButtonZr; - - DeadzoneLeft = controllerInput.DeadzoneLeft; - DeadzoneRight = controllerInput.DeadzoneRight; - RangeLeft = controllerInput.RangeLeft; - RangeRight = controllerInput.RangeRight; - TriggerThreshold = controllerInput.TriggerThreshold; - - if (controllerInput.Motion != null) - { - EnableMotion = controllerInput.Motion.EnableMotion; - GyroDeadzone = controllerInput.Motion.GyroDeadzone; - Sensitivity = controllerInput.Motion.Sensitivity; - - if (controllerInput.Motion is CemuHookMotionConfigController cemuHook) - { - EnableCemuHookMotion = true; - DsuServerHost = cemuHook.DsuServerHost; - DsuServerPort = cemuHook.DsuServerPort; - Slot = cemuHook.Slot; - AltSlot = cemuHook.AltSlot; - MirrorInput = cemuHook.MirrorInput; - } - } - - if (controllerInput.Rumble != null) - { - EnableRumble = controllerInput.Rumble.EnableRumble; - WeakRumble = controllerInput.Rumble.WeakRumble; - StrongRumble = controllerInput.Rumble.StrongRumble; - } - } - } - - public InputConfig GetConfig() - { - var config = new StandardControllerInputConfig - { - Id = Id, - Backend = InputBackendType.GamepadSDL2, - PlayerIndex = PlayerIndex, - ControllerType = ControllerType, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = DpadUp, - DpadDown = DpadDown, - DpadLeft = DpadLeft, - DpadRight = DpadRight, - ButtonL = ButtonL, - ButtonMinus = ButtonMinus, - ButtonSl = LeftButtonSl, - ButtonSr = LeftButtonSr, - ButtonZl = ButtonZl - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = ButtonA, - ButtonB = ButtonB, - ButtonX = ButtonX, - ButtonY = ButtonY, - ButtonPlus = ButtonPlus, - ButtonSl = RightButtonSl, - ButtonSr = RightButtonSr, - ButtonR = ButtonR, - ButtonZr = ButtonZr - }, - LeftJoyconStick = new JoyconConfigControllerStick - { - Joystick = LeftJoystick, - InvertStickX = LeftInvertStickX, - InvertStickY = LeftInvertStickY, - Rotate90CW = LeftRotate90, - StickButton = LeftStickButton - }, - RightJoyconStick = new JoyconConfigControllerStick - { - Joystick = RightJoystick, - InvertStickX = RightInvertStickX, - InvertStickY = RightInvertStickY, - Rotate90CW = RightRotate90, - StickButton = RightStickButton - }, - Rumble = new RumbleConfigController - { - EnableRumble = EnableRumble, - WeakRumble = WeakRumble, - StrongRumble = StrongRumble - }, - Version = InputConfig.CurrentVersion, - DeadzoneLeft = DeadzoneLeft, - DeadzoneRight = DeadzoneRight, - RangeLeft = RangeLeft, - RangeRight = RangeRight, - TriggerThreshold = TriggerThreshold - }; - - if (EnableCemuHookMotion) - { - config.Motion = new CemuHookMotionConfigController - { - EnableMotion = EnableMotion, - MotionBackend = MotionInputBackendType.CemuHook, - GyroDeadzone = GyroDeadzone, - Sensitivity = Sensitivity, - DsuServerHost = DsuServerHost, - DsuServerPort = DsuServerPort, - Slot = Slot, - AltSlot = AltSlot, - MirrorInput = MirrorInput - }; - } - else - { - config.Motion = new MotionConfigController - { - EnableMotion = EnableMotion, - MotionBackend = MotionInputBackendType.GamepadDriver, - GyroDeadzone = GyroDeadzone, - Sensitivity = Sensitivity - }; - } - - return config; - } - } -} diff --git a/src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs b/src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs deleted file mode 100644 index 029565210..000000000 --- a/src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs +++ /dev/null @@ -1,422 +0,0 @@ -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Keyboard; - -namespace Ryujinx.Ava.UI.Models.Input -{ - public class KeyboardInputConfig : BaseModel - { - public string Id { get; set; } - public ControllerType ControllerType { get; set; } - public PlayerIndex PlayerIndex { get; set; } - - private Key _leftStickUp; - public Key LeftStickUp - { - get => _leftStickUp; - set - { - _leftStickUp = value; - OnPropertyChanged(); - } - } - - private Key _leftStickDown; - public Key LeftStickDown - { - get => _leftStickDown; - set - { - _leftStickDown = value; - OnPropertyChanged(); - } - } - - private Key _leftStickLeft; - public Key LeftStickLeft - { - get => _leftStickLeft; - set - { - _leftStickLeft = value; - OnPropertyChanged(); - } - } - - private Key _leftStickRight; - public Key LeftStickRight - { - get => _leftStickRight; - set - { - _leftStickRight = value; - OnPropertyChanged(); - } - } - - private Key _leftStickButton; - public Key LeftStickButton - { - get => _leftStickButton; - set - { - _leftStickButton = value; - OnPropertyChanged(); - } - } - - private Key _rightStickUp; - public Key RightStickUp - { - get => _rightStickUp; - set - { - _rightStickUp = value; - OnPropertyChanged(); - } - } - - private Key _rightStickDown; - public Key RightStickDown - { - get => _rightStickDown; - set - { - _rightStickDown = value; - OnPropertyChanged(); - } - } - - private Key _rightStickLeft; - public Key RightStickLeft - { - get => _rightStickLeft; - set - { - _rightStickLeft = value; - OnPropertyChanged(); - } - } - - private Key _rightStickRight; - public Key RightStickRight - { - get => _rightStickRight; - set - { - _rightStickRight = value; - OnPropertyChanged(); - } - } - - private Key _rightStickButton; - public Key RightStickButton - { - get => _rightStickButton; - set - { - _rightStickButton = value; - OnPropertyChanged(); - } - } - - private Key _dpadUp; - public Key DpadUp - { - get => _dpadUp; - set - { - _dpadUp = value; - OnPropertyChanged(); - } - } - - private Key _dpadDown; - public Key DpadDown - { - get => _dpadDown; - set - { - _dpadDown = value; - OnPropertyChanged(); - } - } - - private Key _dpadLeft; - public Key DpadLeft - { - get => _dpadLeft; - set - { - _dpadLeft = value; - OnPropertyChanged(); - } - } - - private Key _dpadRight; - public Key DpadRight - { - get => _dpadRight; - set - { - _dpadRight = value; - OnPropertyChanged(); - } - } - - private Key _buttonL; - public Key ButtonL - { - get => _buttonL; - set - { - _buttonL = value; - OnPropertyChanged(); - } - } - - private Key _buttonMinus; - public Key ButtonMinus - { - get => _buttonMinus; - set - { - _buttonMinus = value; - OnPropertyChanged(); - } - } - - private Key _leftButtonSl; - public Key LeftButtonSl - { - get => _leftButtonSl; - set - { - _leftButtonSl = value; - OnPropertyChanged(); - } - } - - private Key _leftButtonSr; - public Key LeftButtonSr - { - get => _leftButtonSr; - set - { - _leftButtonSr = value; - OnPropertyChanged(); - } - } - - private Key _buttonZl; - public Key ButtonZl - { - get => _buttonZl; - set - { - _buttonZl = value; - OnPropertyChanged(); - } - } - - private Key _buttonA; - public Key ButtonA - { - get => _buttonA; - set - { - _buttonA = value; - OnPropertyChanged(); - } - } - - private Key _buttonB; - public Key ButtonB - { - get => _buttonB; - set - { - _buttonB = value; - OnPropertyChanged(); - } - } - - private Key _buttonX; - public Key ButtonX - { - get => _buttonX; - set - { - _buttonX = value; - OnPropertyChanged(); - } - } - - private Key _buttonY; - public Key ButtonY - { - get => _buttonY; - set - { - _buttonY = value; - OnPropertyChanged(); - } - } - - private Key _buttonR; - public Key ButtonR - { - get => _buttonR; - set - { - _buttonR = value; - OnPropertyChanged(); - } - } - - private Key _buttonPlus; - public Key ButtonPlus - { - get => _buttonPlus; - set - { - _buttonPlus = value; - OnPropertyChanged(); - } - } - - private Key _rightButtonSl; - public Key RightButtonSl - { - get => _rightButtonSl; - set - { - _rightButtonSl = value; - OnPropertyChanged(); - } - } - - private Key _rightButtonSr; - public Key RightButtonSr - { - get => _rightButtonSr; - set - { - _rightButtonSr = value; - OnPropertyChanged(); - } - } - - private Key _buttonZr; - public Key ButtonZr - { - get => _buttonZr; - set - { - _buttonZr = value; - OnPropertyChanged(); - } - } - - public KeyboardInputConfig(InputConfig config) - { - if (config != null) - { - Id = config.Id; - ControllerType = config.ControllerType; - PlayerIndex = config.PlayerIndex; - - if (config is not StandardKeyboardInputConfig keyboardConfig) - { - return; - } - - LeftStickUp = keyboardConfig.LeftJoyconStick.StickUp; - LeftStickDown = keyboardConfig.LeftJoyconStick.StickDown; - LeftStickLeft = keyboardConfig.LeftJoyconStick.StickLeft; - LeftStickRight = keyboardConfig.LeftJoyconStick.StickRight; - LeftStickButton = keyboardConfig.LeftJoyconStick.StickButton; - - RightStickUp = keyboardConfig.RightJoyconStick.StickUp; - RightStickDown = keyboardConfig.RightJoyconStick.StickDown; - RightStickLeft = keyboardConfig.RightJoyconStick.StickLeft; - RightStickRight = keyboardConfig.RightJoyconStick.StickRight; - RightStickButton = keyboardConfig.RightJoyconStick.StickButton; - - DpadUp = keyboardConfig.LeftJoycon.DpadUp; - DpadDown = keyboardConfig.LeftJoycon.DpadDown; - DpadLeft = keyboardConfig.LeftJoycon.DpadLeft; - DpadRight = keyboardConfig.LeftJoycon.DpadRight; - ButtonL = keyboardConfig.LeftJoycon.ButtonL; - ButtonMinus = keyboardConfig.LeftJoycon.ButtonMinus; - LeftButtonSl = keyboardConfig.LeftJoycon.ButtonSl; - LeftButtonSr = keyboardConfig.LeftJoycon.ButtonSr; - ButtonZl = keyboardConfig.LeftJoycon.ButtonZl; - - ButtonA = keyboardConfig.RightJoycon.ButtonA; - ButtonB = keyboardConfig.RightJoycon.ButtonB; - ButtonX = keyboardConfig.RightJoycon.ButtonX; - ButtonY = keyboardConfig.RightJoycon.ButtonY; - ButtonR = keyboardConfig.RightJoycon.ButtonR; - ButtonPlus = keyboardConfig.RightJoycon.ButtonPlus; - RightButtonSl = keyboardConfig.RightJoycon.ButtonSl; - RightButtonSr = keyboardConfig.RightJoycon.ButtonSr; - ButtonZr = keyboardConfig.RightJoycon.ButtonZr; - } - } - - public InputConfig GetConfig() - { - var config = new StandardKeyboardInputConfig - { - Id = Id, - Backend = InputBackendType.WindowKeyboard, - PlayerIndex = PlayerIndex, - ControllerType = ControllerType, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = DpadUp, - DpadDown = DpadDown, - DpadLeft = DpadLeft, - DpadRight = DpadRight, - ButtonL = ButtonL, - ButtonMinus = ButtonMinus, - ButtonZl = ButtonZl, - ButtonSl = LeftButtonSl, - ButtonSr = LeftButtonSr - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = ButtonA, - ButtonB = ButtonB, - ButtonX = ButtonX, - ButtonY = ButtonY, - ButtonPlus = ButtonPlus, - ButtonSl = RightButtonSl, - ButtonSr = RightButtonSr, - ButtonR = ButtonR, - ButtonZr = ButtonZr - }, - LeftJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = LeftStickUp, - StickDown = LeftStickDown, - StickRight = LeftStickRight, - StickLeft = LeftStickLeft, - StickButton = LeftStickButton - }, - RightJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = RightStickUp, - StickDown = RightStickDown, - StickLeft = RightStickLeft, - StickRight = RightStickRight, - StickButton = RightStickButton - }, - Version = InputConfig.CurrentVersion - }; - - return config; - } - } -} diff --git a/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs b/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs new file mode 100644 index 000000000..f1352c6d8 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs @@ -0,0 +1,456 @@ +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Controller.Motion; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using System; + +namespace Ryujinx.Ava.UI.Models +{ + internal class InputConfiguration : 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 float _rangeLeft; + private float _rangeRight; + + public InputBackendType Backend { get; set; } + + /// + /// Controller id + /// + public string Id { get; set; } + + /// + /// Controller's Type + /// + public ControllerType ControllerType { get; set; } + + /// + /// Player's Index for the controller + /// + public PlayerIndex PlayerIndex { get; set; } + + public TStick LeftJoystick { get; set; } + public bool LeftInvertStickX { get; set; } + public bool LeftInvertStickY { get; set; } + public bool RightRotate90 { get; set; } + public TKey LeftControllerStickButton { get; set; } + + public TStick RightJoystick { get; set; } + public bool RightInvertStickX { get; set; } + public bool RightInvertStickY { get; set; } + public bool LeftRotate90 { get; set; } + public TKey RightControllerStickButton { get; set; } + + public float DeadzoneLeft + { + get => _deadzoneLeft; + set + { + _deadzoneLeft = MathF.Round(value, 3); + + OnPropertyChanged(); + } + } + + public float RangeLeft + { + get => _rangeLeft; + set + { + _rangeLeft = MathF.Round(value, 3); + + OnPropertyChanged(); + } + } + + public float DeadzoneRight + { + get => _deadzoneRight; + set + { + _deadzoneRight = MathF.Round(value, 3); + + OnPropertyChanged(); + } + } + + public float RangeRight + { + get => _rangeRight; + set + { + _rangeRight = MathF.Round(value, 3); + + OnPropertyChanged(); + } + } + + public float TriggerThreshold + { + get => _triggerThreshold; + set + { + _triggerThreshold = MathF.Round(value, 3); + + OnPropertyChanged(); + } + } + + public MotionInputBackendType MotionBackend { 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 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 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 + { + get => _sensitivity; + set + { + _sensitivity = value; + + OnPropertyChanged(); + } + } + + public double GyroDeadzone + { + get => _gyroDeadzone; + set + { + _gyroDeadzone = Math.Round(value, 3); + + OnPropertyChanged(); + } + } + + public bool EnableMotion + { + get => _enableMotion; set + { + _enableMotion = value; + + OnPropertyChanged(); + } + } + + public bool EnableCemuHookMotion { get; set; } + public int Slot { get; set; } + public int AltSlot { get; set; } + public bool MirrorInput { get; set; } + public string DsuServerHost { get; set; } + public int DsuServerPort { get; set; } + + public bool EnableRumble { get; set; } + public float WeakRumble + { + get => _weakRumble; set + { + _weakRumble = value; + + OnPropertyChanged(); + } + } + public float StrongRumble + { + get => _strongRumble; set + { + _strongRumble = value; + + OnPropertyChanged(); + } + } + + public InputConfiguration(InputConfig config) + { + if (config != null) + { + Backend = config.Backend; + Id = config.Id; + ControllerType = config.ControllerType; + PlayerIndex = config.PlayerIndex; + + if (config is StandardKeyboardInputConfig keyboardConfig) + { + 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 = (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 = (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 = (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 = (TStick)(object)controllerConfig.LeftJoyconStick.Joystick; + LeftInvertStickX = controllerConfig.LeftJoyconStick.InvertStickX; + LeftInvertStickY = controllerConfig.LeftJoyconStick.InvertStickY; + LeftRotate90 = controllerConfig.LeftJoyconStick.Rotate90CW; + LeftControllerStickButton = (TKey)(object)controllerConfig.LeftJoyconStick.StickButton; + + RightJoystick = (TStick)(object)controllerConfig.RightJoyconStick.Joystick; + RightInvertStickX = controllerConfig.RightJoyconStick.InvertStickX; + RightInvertStickY = controllerConfig.RightJoyconStick.InvertStickY; + RightRotate90 = controllerConfig.RightJoyconStick.Rotate90CW; + RightControllerStickButton = (TKey)(object)controllerConfig.RightJoyconStick.StickButton; + + 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 = (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; + RangeLeft = controllerConfig.RangeLeft; + RangeRight = controllerConfig.RangeRight; + TriggerThreshold = controllerConfig.TriggerThreshold; + + if (controllerConfig.Motion != null) + { + EnableMotion = controllerConfig.Motion.EnableMotion; + MotionBackend = controllerConfig.Motion.MotionBackend; + GyroDeadzone = controllerConfig.Motion.GyroDeadzone; + Sensitivity = controllerConfig.Motion.Sensitivity; + + if (controllerConfig.Motion is CemuHookMotionConfigController cemuHook) + { + EnableCemuHookMotion = true; + DsuServerHost = cemuHook.DsuServerHost; + DsuServerPort = cemuHook.DsuServerPort; + Slot = cemuHook.Slot; + AltSlot = cemuHook.AltSlot; + MirrorInput = cemuHook.MirrorInput; + } + + if (controllerConfig.Rumble != null) + { + EnableRumble = controllerConfig.Rumble.EnableRumble; + WeakRumble = controllerConfig.Rumble.WeakRumble; + StrongRumble = controllerConfig.Rumble.StrongRumble; + } + } + } + } + } + + public InputConfiguration() + { + } + + public InputConfig GetConfig() + { + if (Backend == InputBackendType.WindowKeyboard) + { + return new StandardKeyboardInputConfig + { + Id = Id, + Backend = Backend, + PlayerIndex = PlayerIndex, + ControllerType = ControllerType, + LeftJoycon = new LeftJoyconCommonConfig + { + 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 + { + 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 + { + StickUp = (Key)(object)LeftStickUp, + StickDown = (Key)(object)LeftStickDown, + StickRight = (Key)(object)LeftStickRight, + StickLeft = (Key)(object)LeftStickLeft, + StickButton = (Key)(object)LeftKeyboardStickButton, + }, + RightJoyconStick = new JoyconConfigKeyboardStick + { + StickUp = (Key)(object)RightStickUp, + StickDown = (Key)(object)RightStickDown, + StickLeft = (Key)(object)RightStickLeft, + StickRight = (Key)(object)RightStickRight, + StickButton = (Key)(object)RightKeyboardStickButton, + }, + Version = InputConfig.CurrentVersion, + }; + + } + + if (Backend == InputBackendType.GamepadSDL2) + { + var config = new StandardControllerInputConfig + { + Id = Id, + Backend = Backend, + PlayerIndex = PlayerIndex, + ControllerType = ControllerType, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = (GamepadInputId)(object)DpadUp, + DpadDown = (GamepadInputId)(object)DpadDown, + DpadLeft = (GamepadInputId)(object)DpadLeft, + DpadRight = (GamepadInputId)(object)DpadRight, + ButtonL = (GamepadInputId)(object)ButtonL, + ButtonZl = (GamepadInputId)(object)ButtonZl, + ButtonSl = (GamepadInputId)(object)LeftButtonSl, + ButtonSr = (GamepadInputId)(object)LeftButtonSr, + ButtonMinus = (GamepadInputId)(object)ButtonMinus, + }, + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = (GamepadInputId)(object)ButtonA, + ButtonB = (GamepadInputId)(object)ButtonB, + ButtonX = (GamepadInputId)(object)ButtonX, + ButtonY = (GamepadInputId)(object)ButtonY, + ButtonPlus = (GamepadInputId)(object)ButtonPlus, + ButtonSl = (GamepadInputId)(object)RightButtonSl, + ButtonSr = (GamepadInputId)(object)RightButtonSr, + ButtonR = (GamepadInputId)(object)ButtonR, + ButtonZr = (GamepadInputId)(object)ButtonZr, + }, + LeftJoyconStick = new JoyconConfigControllerStick + { + Joystick = (StickInputId)(object)LeftJoystick, + InvertStickX = LeftInvertStickX, + InvertStickY = LeftInvertStickY, + Rotate90CW = LeftRotate90, + StickButton = (GamepadInputId)(object)LeftControllerStickButton, + }, + RightJoyconStick = new JoyconConfigControllerStick + { + Joystick = (StickInputId)(object)RightJoystick, + InvertStickX = RightInvertStickX, + InvertStickY = RightInvertStickY, + Rotate90CW = RightRotate90, + StickButton = (GamepadInputId)(object)RightControllerStickButton, + }, + Rumble = new RumbleConfigController + { + EnableRumble = EnableRumble, + WeakRumble = WeakRumble, + StrongRumble = StrongRumble, + }, + Version = InputConfig.CurrentVersion, + DeadzoneLeft = DeadzoneLeft, + DeadzoneRight = DeadzoneRight, + RangeLeft = RangeLeft, + 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, + }, + }; + + config.Motion.Sensitivity = Sensitivity; + config.Motion.EnableMotion = EnableMotion; + config.Motion.GyroDeadzone = GyroDeadzone; + + return config; + } + + return null; + } + } +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs similarity index 92% rename from src/Ryujinx.Ava/UI/ViewModels/Input/InputViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs index ef8ffd50d..c0c625321 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs @@ -8,7 +8,7 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; -using Ryujinx.Ava.UI.Models.Input; +using Ryujinx.Ava.UI.Views.Input; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -30,9 +30,9 @@ using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.Gamepad using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; using Key = Ryujinx.Common.Configuration.Hid.Key; -namespace Ryujinx.Ava.UI.ViewModels.Input +namespace Ryujinx.Ava.UI.ViewModels { - public class InputViewModel : BaseModel, IDisposable + public class ControllerInputViewModel : BaseModel, IDisposable { private const string Disabled = "disabled"; private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg"; @@ -48,7 +48,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input private int _controllerNumber; private string _controllerImage; private int _device; - private object _configViewModel; + private object _configuration; private string _profileName; private bool _isLoaded; @@ -71,14 +71,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public bool IsLeft { get; set; } public bool IsModified { get; set; } - public event Action NotifyChangesEvent; - public object ConfigViewModel + public object Configuration { - get => _configViewModel; + get => _configuration; set { - _configViewModel = value; + _configuration = value; OnPropertyChanged(); } @@ -233,7 +232,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public InputConfig Config { get; set; } - public InputViewModel(UserControl owner) : this() + public ControllerInputViewModel(UserControl owner) : this() { if (Program.PreviewerDetached) { @@ -245,6 +244,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; + _mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates(); _isLoaded = false; @@ -255,7 +255,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input } } - public InputViewModel() + public ControllerInputViewModel() { PlayerIndexes = new ObservableCollection(); Controllers = new ObservableCollection(); @@ -282,12 +282,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input if (Config is StandardKeyboardInputConfig keyboardInputConfig) { - ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig)); + Configuration = new InputConfiguration(keyboardInputConfig); } if (Config is StandardControllerInputConfig controllerInputConfig) { - ConfigViewModel = new ControllerInputViewModel(this, new ControllerInputConfig(controllerInputConfig)); + Configuration = new InputConfiguration(controllerInputConfig); } } @@ -323,6 +323,16 @@ namespace Ryujinx.Ava.UI.ViewModels.Input } } + public async void ShowMotionConfig() + { + await MotionInputView.Show(this); + } + + public async void ShowRumbleConfig() + { + await RumbleInputView.Show(this); + } + private void LoadInputDriver() { if (_device < 0) @@ -730,7 +740,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input return; } - if (ConfigViewModel == null) + if (Configuration == null) { return; } @@ -741,37 +751,35 @@ namespace Ryujinx.Ava.UI.ViewModels.Input return; } + + bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1; + + if (validFileName) + { + string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json"); + + InputConfig config = null; + + if (IsKeyboard) + { + config = (Configuration as InputConfiguration).GetConfig(); + } + else if (IsController) + { + config = (Configuration as InputConfiguration).GetConfig(); + } + + config.ControllerType = Controllers[_controller].Type; + + string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig); + + await File.WriteAllTextAsync(path, jsonString); + + LoadProfiles(); + } else { - bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1; - - if (validFileName) - { - string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json"); - - InputConfig config = null; - - if (IsKeyboard) - { - config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig(); - } - else if (IsController) - { - config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig(); - } - - config.ControllerType = Controllers[_controller].Type; - - string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig); - - await File.WriteAllTextAsync(path, jsonString); - - LoadProfiles(); - } - else - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]); - } + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]); } } @@ -822,18 +830,18 @@ namespace Ryujinx.Ava.UI.ViewModels.Input if (device.Type == DeviceType.Keyboard) { - var inputConfig = (ConfigViewModel as KeyboardInputViewModel).Config; + var inputConfig = Configuration as InputConfiguration; inputConfig.Id = device.Id; } else { - var inputConfig = (ConfigViewModel as ControllerInputViewModel).Config; + var inputConfig = Configuration as InputConfiguration; inputConfig.Id = device.Id.Split(" ")[0]; } var config = !IsController - ? (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig() - : (ConfigViewModel as ControllerInputViewModel).Config.GetConfig(); + ? (Configuration as InputConfiguration).GetConfig() + : (Configuration as InputConfiguration).GetConfig(); config.ControllerType = Controllers[_controller].Type; config.PlayerIndex = _playerId; @@ -864,13 +872,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public void NotifyChanges() { - OnPropertyChanged(nameof(ConfigViewModel)); + OnPropertyChanged(nameof(Configuration)); OnPropertyChanged(nameof(IsController)); OnPropertyChanged(nameof(ShowSettings)); OnPropertyChanged(nameof(IsKeyboard)); OnPropertyChanged(nameof(IsRight)); OnPropertyChanged(nameof(IsLeft)); - NotifyChangesEvent?.Invoke(); } public void Dispose() diff --git a/src/Ryujinx.Ava/UI/ViewModels/Input/ControllerInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/Input/ControllerInputViewModel.cs deleted file mode 100644 index 0e23dfa76..000000000 --- a/src/Ryujinx.Ava/UI/ViewModels/Input/ControllerInputViewModel.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Avalonia.Svg.Skia; -using Ryujinx.Ava.UI.Models.Input; -using Ryujinx.Ava.UI.Views.Input; - -namespace Ryujinx.Ava.UI.ViewModels.Input -{ - public class ControllerInputViewModel : BaseModel - { - private ControllerInputConfig _config; - public ControllerInputConfig Config - { - get => _config; - set - { - _config = value; - OnPropertyChanged(); - } - } - - private bool _isLeft; - public bool IsLeft - { - get => _isLeft; - set - { - _isLeft = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(HasSides)); - } - } - - private bool _isRight; - public bool IsRight - { - get => _isRight; - set - { - _isRight = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(HasSides)); - } - } - - public bool HasSides => IsLeft ^ IsRight; - - private SvgImage _image; - public SvgImage Image - { - get => _image; - set - { - _image = value; - OnPropertyChanged(); - } - } - - public InputViewModel parentModel; - - public ControllerInputViewModel(InputViewModel model, ControllerInputConfig config) - { - parentModel = model; - model.NotifyChangesEvent += OnParentModelChanged; - OnParentModelChanged(); - Config = config; - } - - public async void ShowMotionConfig() - { - await MotionInputView.Show(this); - } - - public async void ShowRumbleConfig() - { - await RumbleInputView.Show(this); - } - - public void OnParentModelChanged() - { - IsLeft = parentModel.IsLeft; - IsRight = parentModel.IsRight; - Image = parentModel.Image; - } - } -} diff --git a/src/Ryujinx.Ava/UI/ViewModels/Input/KeyboardInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/Input/KeyboardInputViewModel.cs deleted file mode 100644 index a93873063..000000000 --- a/src/Ryujinx.Ava/UI/ViewModels/Input/KeyboardInputViewModel.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Avalonia.Svg.Skia; -using Ryujinx.Ava.UI.Models.Input; - -namespace Ryujinx.Ava.UI.ViewModels.Input -{ - public class KeyboardInputViewModel : BaseModel - { - private KeyboardInputConfig _config; - public KeyboardInputConfig Config - { - get => _config; - set - { - _config = value; - OnPropertyChanged(); - } - } - - private bool _isLeft; - public bool IsLeft - { - get => _isLeft; - set - { - _isLeft = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(HasSides)); - } - } - - private bool _isRight; - public bool IsRight - { - get => _isRight; - set - { - _isRight = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(HasSides)); - } - } - - public bool HasSides => IsLeft ^ IsRight; - - private SvgImage _image; - public SvgImage Image - { - get => _image; - set - { - _image = value; - OnPropertyChanged(); - } - } - - public InputViewModel parentModel; - - public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config) - { - parentModel = model; - model.NotifyChangesEvent += OnParentModelChanged; - OnParentModelChanged(); - Config = config; - } - - public void OnParentModelChanged() - { - IsLeft = parentModel.IsLeft; - IsRight = parentModel.IsRight; - Image = parentModel.Image; - } - } -} diff --git a/src/Ryujinx.Ava/UI/ViewModels/Input/MotionInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs similarity index 97% rename from src/Ryujinx.Ava/UI/ViewModels/Input/MotionInputViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs index c9ed8f2d4..0b12a51f6 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/Input/MotionInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Ava.UI.ViewModels.Input +namespace Ryujinx.Ava.UI.ViewModels { public class MotionInputViewModel : BaseModel { diff --git a/src/Ryujinx.Ava/UI/ViewModels/Input/RumbleInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs similarity index 92% rename from src/Ryujinx.Ava/UI/ViewModels/Input/RumbleInputViewModel.cs rename to src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs index 8ad33cf4c..49de19937 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/Input/RumbleInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Ava.UI.ViewModels.Input +namespace Ryujinx.Ava.UI.ViewModels { public class RumbleInputViewModel : BaseModel { diff --git a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml index 08bdf90f4..d636873a3 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml @@ -1,11 +1,13 @@ @@ -33,10 +34,191 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MinHeight="450" + IsVisible="{Binding ShowSettings}"> @@ -75,9 +257,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerZL}" TextAlignment="Center" /> - + @@ -91,9 +273,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerL}" TextAlignment="Center" /> - + @@ -107,9 +289,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsButtonMinus}" TextAlignment="Center" /> - + @@ -129,8 +311,100 @@ Margin="0,0,0,10" HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsLStick}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -141,9 +415,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsStickButton}" TextAlignment="Center" /> - + @@ -158,22 +432,22 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsStickStick}" TextAlignment="Center" /> - + - + - + - + + Value="{ReflectionBinding Configuration.DeadzoneLeft, Mode=TwoWay}" /> + Text="{ReflectionBinding Configuration.DeadzoneLeft, StringFormat=\{0:0.00\}}" /> + Value="{ReflectionBinding Configuration.RangeLeft, Mode=TwoWay}" /> + Text="{ReflectionBinding Configuration.RangeLeft, StringFormat=\{0:0.00\}}" /> @@ -251,9 +525,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadUp}" TextAlignment="Center" /> - + @@ -268,9 +542,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadDown}" TextAlignment="Center" /> - + @@ -285,9 +559,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadLeft}" TextAlignment="Center" /> - + @@ -302,9 +576,9 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsDPadRight}" TextAlignment="Center" /> - + @@ -317,13 +591,6 @@ Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - - + Value="{ReflectionBinding Configuration.TriggerThreshold, Mode=TwoWay}" /> + Text="{ReflectionBinding Configuration.TriggerThreshold, StringFormat=\{0:0.00\}}" /> - + + Text="{locale:Locale ControllerSettingsLeftSR}" + TextAlignment="Center" /> + - - - - - + + + + Text="{locale:Locale ControllerSettingsLeftSL}" + TextAlignment="Center" /> + - - - - - + + + + Text="{locale:Locale ControllerSettingsRightSR}" + TextAlignment="Center" /> + - - - - - + + + + Text="{locale:Locale ControllerSettingsRightSL}" + TextAlignment="Center" /> + - - - - + + + + HorizontalAlignment="Stretch" + IsVisible="{Binding IsController}"> @@ -449,7 +720,7 @@ Margin="10" MinWidth="0" Grid.Column="0" - IsChecked="{Binding Config.EnableMotion, Mode=TwoWay}"> + IsChecked="{ReflectionBinding Configuration.EnableMotion, Mode=TwoWay}"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs deleted file mode 100644 index 356381a8a..000000000 --- a/src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Avalonia.Controls; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Models; -using Ryujinx.Ava.UI.ViewModels.Input; - -namespace Ryujinx.Ava.UI.Views.Input -{ - public partial class InputView : UserControl - { - private bool _dialogOpen; - private InputViewModel ViewModel { get; set; } - - public InputView() - { - DataContext = ViewModel = new InputViewModel(this); - - InitializeComponent(); - } - - public void SaveCurrentProfile() - { - ViewModel.Save(); - } - - private async void PlayerIndexBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (ViewModel.IsModified && !_dialogOpen) - { - _dialogOpen = true; - - var result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmMessage], - LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmSubMessage], - LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - - if (result == UserResult.Yes) - { - ViewModel.Save(); - } - - _dialogOpen = false; - - ViewModel.IsModified = false; - - if (e.AddedItems.Count > 0) - { - var player = (PlayerModel)e.AddedItems[0]; - ViewModel.PlayerId = player.Id; - } - } - } - - public void Dispose() - { - ViewModel.Dispose(); - } - } -} diff --git a/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml deleted file mode 100644 index e4566f463..000000000 --- a/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml +++ /dev/null @@ -1,675 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs deleted file mode 100644 index f7024c5d1..000000000 --- a/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs +++ /dev/null @@ -1,210 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Primitives; -using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.LogicalTree; -using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.ViewModels.Input; -using Ryujinx.Input; -using Ryujinx.Input.Assigner; - -namespace Ryujinx.Ava.UI.Views.Input -{ - public partial class KeyboardInputView : UserControl - { - private ButtonKeyAssigner _currentAssigner; - - public KeyboardInputView() - { - InitializeComponent(); - - foreach (ILogical visual in SettingButtons.GetLogicalDescendants()) - { - if (visual is ToggleButton button and not CheckBox) - { - button.IsCheckedChanged += Button_IsCheckedChanged; - } - } - } - - protected override void OnPointerReleased(PointerReleasedEventArgs e) - { - base.OnPointerReleased(e); - - if (_currentAssigner != null && _currentAssigner.ToggledButton != null && !_currentAssigner.ToggledButton.IsPointerOver) - { - _currentAssigner.Cancel(); - } - } - - private void Button_IsCheckedChanged(object sender, RoutedEventArgs e) - { - if (sender is ToggleButton button) - { - if ((bool)button.IsChecked) - { - if (_currentAssigner != null && button == _currentAssigner.ToggledButton) - { - return; - } - - bool isStick = button.Tag != null && button.Tag.ToString() == "stick"; - - if (_currentAssigner == null && (bool)button.IsChecked) - { - _currentAssigner = new ButtonKeyAssigner(button); - - this.Focus(NavigationMethod.Pointer); - - PointerPressed += MouseClick; - - IKeyboard keyboard = (IKeyboard)(DataContext as KeyboardInputViewModel).parentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. - IButtonAssigner assigner = CreateButtonAssigner(isStick); - - _currentAssigner.ButtonAssigned += (sender, e) => - { - if (e.ButtonValue.HasValue) - { - var viewModel = (DataContext as KeyboardInputViewModel); - var buttonValue = e.ButtonValue.Value; - viewModel.parentModel.IsModified = true; - - switch (button.Name) - { - case "ButtonZl": - viewModel.Config.ButtonZl = buttonValue.AsKey(); - break; - case "ButtonL": - viewModel.Config.ButtonL = buttonValue.AsKey(); - break; - case "ButtonMinus": - viewModel.Config.ButtonMinus = buttonValue.AsKey(); - break; - case "LeftStickButton": - viewModel.Config.LeftStickButton = buttonValue.AsKey(); - break; - case "LeftStickUp": - viewModel.Config.LeftStickUp = buttonValue.AsKey(); - break; - case "LeftStickDown": - viewModel.Config.LeftStickDown = buttonValue.AsKey(); - break; - case "LeftStickRight": - viewModel.Config.LeftStickRight = buttonValue.AsKey(); - break; - case "LeftStickLeft": - viewModel.Config.LeftStickLeft = buttonValue.AsKey(); - break; - case "DpadUp": - viewModel.Config.DpadUp = buttonValue.AsKey(); - break; - case "DpadDown": - viewModel.Config.DpadDown = buttonValue.AsKey(); - break; - case "DpadLeft": - viewModel.Config.DpadLeft = buttonValue.AsKey(); - break; - case "DpadRight": - viewModel.Config.DpadRight = buttonValue.AsKey(); - break; - case "LeftButtonSr": - viewModel.Config.LeftButtonSr = buttonValue.AsKey(); - break; - case "LeftButtonSl": - viewModel.Config.LeftButtonSl = buttonValue.AsKey(); - break; - case "RightButtonSr": - viewModel.Config.RightButtonSr = buttonValue.AsKey(); - break; - case "RightButtonSl": - viewModel.Config.RightButtonSl = buttonValue.AsKey(); - break; - case "ButtonZr": - viewModel.Config.ButtonZr = buttonValue.AsKey(); - break; - case "ButtonR": - viewModel.Config.ButtonR = buttonValue.AsKey(); - break; - case "ButtonPlus": - viewModel.Config.ButtonPlus = buttonValue.AsKey(); - break; - case "ButtonA": - viewModel.Config.ButtonA = buttonValue.AsKey(); - break; - case "ButtonB": - viewModel.Config.ButtonB = buttonValue.AsKey(); - break; - case "ButtonX": - viewModel.Config.ButtonX = buttonValue.AsKey(); - break; - case "ButtonY": - viewModel.Config.ButtonY = buttonValue.AsKey(); - break; - case "RightStickButton": - viewModel.Config.RightStickButton = buttonValue.AsKey(); - break; - case "RightStickUp": - viewModel.Config.RightStickUp = buttonValue.AsKey(); - break; - case "RightStickDown": - viewModel.Config.RightStickDown = buttonValue.AsKey(); - break; - case "RightStickRight": - viewModel.Config.RightStickRight = buttonValue.AsKey(); - break; - case "RightStickLeft": - viewModel.Config.RightStickLeft = buttonValue.AsKey(); - break; - } - } - }; - - _currentAssigner.GetInputAndAssign(assigner, keyboard); - } - else - { - if (_currentAssigner != null) - { - ToggleButton oldButton = _currentAssigner.ToggledButton; - - _currentAssigner.Cancel(); - _currentAssigner = null; - button.IsChecked = false; - } - } - } - else - { - _currentAssigner?.Cancel(); - _currentAssigner = null; - } - } - } - - private void MouseClick(object sender, PointerPressedEventArgs e) - { - bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed; - - _currentAssigner?.Cancel(shouldUnbind); - - PointerPressed -= MouseClick; - } - - private IButtonAssigner CreateButtonAssigner(bool forStick) - { - IButtonAssigner assigner; - - assigner = new KeyboardKeyAssigner((IKeyboard)(DataContext as KeyboardInputViewModel).parentModel.SelectedGamepad); - - return assigner; - } - - protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnDetachedFromVisualTree(e); - _currentAssigner?.Cancel(); - _currentAssigner = null; - } - } -} diff --git a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml index 0d018e297..a6b587f67 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml @@ -6,7 +6,7 @@ xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" - xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView" x:DataType="viewModels:MotionInputViewModel" diff --git a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs index 2304364b6..1b340752b 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs @@ -1,7 +1,9 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.ViewModels.Input; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Common.Configuration.Hid.Controller; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Views.Input @@ -17,7 +19,7 @@ namespace Ryujinx.Ava.UI.Views.Input public MotionInputView(ControllerInputViewModel viewModel) { - var config = viewModel.Config; + var config = viewModel.Configuration as InputConfiguration; _viewModel = new MotionInputViewModel { @@ -49,7 +51,7 @@ namespace Ryujinx.Ava.UI.Views.Input }; contentDialog.PrimaryButtonClick += (sender, args) => { - var config = viewModel.Config; + var config = viewModel.Configuration as InputConfiguration; config.Slot = content._viewModel.Slot; config.Sensitivity = content._viewModel.Sensitivity; config.GyroDeadzone = content._viewModel.GyroDeadzone; diff --git a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml index 1beb1f06e..5b7087a47 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" - xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView" x:DataType="viewModels:RumbleInputViewModel" diff --git a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs index 58a4b416b..9307f872c 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs @@ -1,7 +1,9 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.ViewModels.Input; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Common.Configuration.Hid.Controller; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Views.Input @@ -17,7 +19,7 @@ namespace Ryujinx.Ava.UI.Views.Input public RumbleInputView(ControllerInputViewModel viewModel) { - var config = viewModel.Config; + var config = viewModel.Configuration as InputConfiguration; _viewModel = new RumbleInputViewModel { @@ -45,7 +47,7 @@ namespace Ryujinx.Ava.UI.Views.Input contentDialog.PrimaryButtonClick += (sender, args) => { - var config = viewModel.Config; + var config = viewModel.Configuration as InputConfiguration; config.StrongRumble = content._viewModel.StrongRumble; config.WeakRumble = content._viewModel.WeakRumble; }; diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml index 55c2ed6e3..81f4b68b7 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml @@ -27,9 +27,9 @@ - + Name="ControllerSettings" /> diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs index 85ccffccd..8a0cb8ab9 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Views.Settings public void Dispose() { - InputView.Dispose(); + ControllerSettings.Dispose(); } } } diff --git a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs index 314501c52..d7bb0b883 100644 --- a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Ava.UI.Windows public void SaveSettings() { - InputPage.InputView?.SaveCurrentProfile(); + InputPage.ControllerSettings?.SaveCurrentProfile(); if (Owner is MainWindow window && ViewModel.DirectoryChanged) { diff --git a/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs b/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs index bf8319a6a..388ebcc07 100644 --- a/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs +++ b/src/Ryujinx.Input/Assigner/GamepadButtonAssigner.cs @@ -59,16 +59,16 @@ namespace Ryujinx.Input.Assigner return _gamepad == null || !_gamepad.IsConnected; } - public ButtonValue? GetPressedButton() + public string GetPressedButton() { IEnumerable pressedButtons = _detector.GetPressedButtons(); if (pressedButtons.Any()) { - return !_forStick ? new(pressedButtons.First()) : new(((StickInputId)pressedButtons.First())); + return !_forStick ? pressedButtons.First().ToString() : ((StickInputId)pressedButtons.First()).ToString(); } - return null; + return ""; } private void CollectButtonStats() diff --git a/src/Ryujinx.Input/Assigner/IButtonAssigner.cs b/src/Ryujinx.Input/Assigner/IButtonAssigner.cs index 653717133..76a9fece4 100644 --- a/src/Ryujinx.Input/Assigner/IButtonAssigner.cs +++ b/src/Ryujinx.Input/Assigner/IButtonAssigner.cs @@ -31,6 +31,6 @@ namespace Ryujinx.Input.Assigner /// Get the pressed button that was read in by the button assigner. /// /// The pressed button that was read - ButtonValue? GetPressedButton(); + string GetPressedButton(); } } diff --git a/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs b/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs index c66812ba0..e52ef4a2c 100644 --- a/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs +++ b/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Input.Assigner public bool HasAnyButtonPressed() { - return GetPressedButton() is not null; + return GetPressedButton().Length != 0; } public bool ShouldCancel() @@ -31,20 +31,20 @@ namespace Ryujinx.Input.Assigner return _keyboardState.IsPressed(Key.Escape); } - public ButtonValue? GetPressedButton() + public string GetPressedButton() { - ButtonValue? keyPressed = null; + string keyPressed = ""; for (Key key = Key.Unknown; key < Key.Count; key++) { if (_keyboardState.IsPressed(key)) { - keyPressed = new(key); + keyPressed = key.ToString(); break; } } - return !ShouldCancel() ? keyPressed : null; + return !ShouldCancel() ? keyPressed : ""; } } } diff --git a/src/Ryujinx.Input/ButtonValue.cs b/src/Ryujinx.Input/ButtonValue.cs deleted file mode 100644 index f037e6b60..000000000 --- a/src/Ryujinx.Input/ButtonValue.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Diagnostics; - -namespace Ryujinx.Input -{ - public enum ButtonValueType { Key, GamepadButtonInputId, StickId } - - public readonly struct ButtonValue - { - private readonly ButtonValueType _type; - private readonly uint _rawValue; - - public ButtonValue(Key key) - { - _type = ButtonValueType.Key; - _rawValue = (uint)key; - } - - public ButtonValue(GamepadButtonInputId gamepad) - { - _type = ButtonValueType.GamepadButtonInputId; - _rawValue = (uint)gamepad; - } - - public ButtonValue(StickInputId stick) - { - _type = ButtonValueType.StickId; - _rawValue = (uint)stick; - } - - public Common.Configuration.Hid.Key AsKey() - { - Debug.Assert(_type == ButtonValueType.Key); - return (Common.Configuration.Hid.Key)_rawValue; - } - - public Common.Configuration.Hid.Controller.GamepadInputId AsGamepadButtonInputId() - { - Debug.Assert(_type == ButtonValueType.GamepadButtonInputId); - return (Common.Configuration.Hid.Controller.GamepadInputId)_rawValue; - } - - public Common.Configuration.Hid.Controller.StickInputId AsGamepadStickId() - { - Debug.Assert(_type == ButtonValueType.StickId); - return (Common.Configuration.Hid.Controller.StickInputId)_rawValue; - } - } -} diff --git a/src/Ryujinx/Ui/Windows/ControllerWindow.cs b/src/Ryujinx/Ui/Windows/ControllerWindow.cs index 52cad5c85..ebf22ab60 100644 --- a/src/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/src/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -893,7 +893,7 @@ namespace Ryujinx.Ui.Windows } } - string pressedButton = assigner.GetPressedButton().ToString(); + string pressedButton = assigner.GetPressedButton(); Application.Invoke(delegate {