Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
49b37550ca | |||
a42f0bbb87 | |||
b4bb22ba06 | |||
6fdf774845 | |||
76b53e018a | |||
28dd7d80af | |||
1e06b28b22 | |||
e768a54f17 | |||
4e2bb13080 | |||
ac4f2c1e70 | |||
e40470bbe1 | |||
f460ecc182 | |||
086564c3c8 | |||
b6ac45d36d | |||
7afae8c699 | |||
7835968214 | |||
0aceb534cb | |||
a0af6e4d07 | |||
f61b7818c3 | |||
a2a97e1b11 | |||
8b2625b0be | |||
651e24fed9 |
@ -3,13 +3,13 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia" Version="11.0.4" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.4" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.4" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.4" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.4" />
|
||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.2" />
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.2" />
|
||||
<PackageVersion Include="Avalonia" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.5" />
|
||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.3" />
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.3" />
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
@ -20,7 +20,7 @@
|
||||
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
|
||||
<PackageVersion Include="LibHac" Version="0.18.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
@ -35,6 +35,7 @@
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.28.1-build28" />
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||
|
@ -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.
|
||||
|
@ -681,4 +681,33 @@
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
```
|
||||
</details>
|
||||
|
||||
# ShellLink (MIT)
|
||||
<details>
|
||||
<summary>See License</summary>
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
</details>
|
@ -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;
|
||||
|
13
distribution/linux/shortcut-template.desktop
Normal file
13
distribution/linux/shortcut-template.desktop
Normal file
@ -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
|
35
distribution/macos/shortcut-template.plist
Normal file
35
distribution/macos/shortcut-template.plist
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>{0}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>{1}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>{2}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2018 - 2023 Ryujinx Team and Contributors.</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.games</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11.0</string>
|
||||
<key>UIPrerenderedIcon</key>
|
||||
<true/>
|
||||
<key>LSEnvironment</key>
|
||||
<dict>
|
||||
<key>DOTNET_DefaultStackSize</key>
|
||||
<string>200000</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
@ -1,6 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace ARMeilleure.Diagnostics
|
||||
{
|
||||
@ -33,7 +34,6 @@ namespace ARMeilleure.Diagnostics
|
||||
|
||||
public static string Get(ulong address)
|
||||
{
|
||||
|
||||
if (_symbols.TryGetValue(address, out string result))
|
||||
{
|
||||
return result;
|
||||
@ -48,13 +48,15 @@ namespace ARMeilleure.Diagnostics
|
||||
ulong diff = address - symbol.Start;
|
||||
ulong rem = diff % symbol.ElementSize;
|
||||
|
||||
result = symbol.Name + "_" + diff / symbol.ElementSize;
|
||||
StringBuilder resultBuilder = new();
|
||||
resultBuilder.Append($"{symbol.Name}_{diff / symbol.ElementSize}");
|
||||
|
||||
if (rem != 0)
|
||||
{
|
||||
result += "+" + rem;
|
||||
resultBuilder.Append($"+{rem}");
|
||||
}
|
||||
|
||||
result = resultBuilder.ToString();
|
||||
_symbols.TryAdd(address, result);
|
||||
|
||||
return result;
|
||||
|
@ -7,14 +7,14 @@ namespace ARMeilleure.Translation
|
||||
internal class TranslatorCache<T>
|
||||
{
|
||||
private readonly IntervalTree<ulong, T> _tree;
|
||||
private readonly ReaderWriterLock _treeLock;
|
||||
private readonly ReaderWriterLockSlim _treeLock;
|
||||
|
||||
public int Count => _tree.Count;
|
||||
|
||||
public TranslatorCache()
|
||||
{
|
||||
_tree = new IntervalTree<ulong, T>();
|
||||
_treeLock = new ReaderWriterLock();
|
||||
_treeLock = new ReaderWriterLockSlim();
|
||||
}
|
||||
|
||||
public bool TryAdd(ulong address, ulong size, T value)
|
||||
@ -24,70 +24,70 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public bool AddOrUpdate(ulong address, ulong size, T value, Func<ulong, T, T> updateFactoryCallback)
|
||||
{
|
||||
_treeLock.AcquireWriterLock(Timeout.Infinite);
|
||||
_treeLock.EnterWriteLock();
|
||||
bool result = _tree.AddOrUpdate(address, address + size, value, updateFactoryCallback);
|
||||
_treeLock.ReleaseWriterLock();
|
||||
_treeLock.ExitWriteLock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public T GetOrAdd(ulong address, ulong size, T value)
|
||||
{
|
||||
_treeLock.AcquireWriterLock(Timeout.Infinite);
|
||||
_treeLock.EnterWriteLock();
|
||||
value = _tree.GetOrAdd(address, address + size, value);
|
||||
_treeLock.ReleaseWriterLock();
|
||||
_treeLock.ExitWriteLock();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool Remove(ulong address)
|
||||
{
|
||||
_treeLock.AcquireWriterLock(Timeout.Infinite);
|
||||
_treeLock.EnterWriteLock();
|
||||
bool removed = _tree.Remove(address) != 0;
|
||||
_treeLock.ReleaseWriterLock();
|
||||
_treeLock.ExitWriteLock();
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_treeLock.AcquireWriterLock(Timeout.Infinite);
|
||||
_treeLock.EnterWriteLock();
|
||||
_tree.Clear();
|
||||
_treeLock.ReleaseWriterLock();
|
||||
_treeLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
public bool ContainsKey(ulong address)
|
||||
{
|
||||
_treeLock.AcquireReaderLock(Timeout.Infinite);
|
||||
_treeLock.EnterReadLock();
|
||||
bool result = _tree.ContainsKey(address);
|
||||
_treeLock.ReleaseReaderLock();
|
||||
_treeLock.ExitReadLock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool TryGetValue(ulong address, out T value)
|
||||
{
|
||||
_treeLock.AcquireReaderLock(Timeout.Infinite);
|
||||
_treeLock.EnterReadLock();
|
||||
bool result = _tree.TryGet(address, out value);
|
||||
_treeLock.ReleaseReaderLock();
|
||||
_treeLock.ExitReadLock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int GetOverlaps(ulong address, ulong size, ref ulong[] overlaps)
|
||||
{
|
||||
_treeLock.AcquireReaderLock(Timeout.Infinite);
|
||||
_treeLock.EnterReadLock();
|
||||
int count = _tree.Get(address, address + size, ref overlaps);
|
||||
_treeLock.ReleaseReaderLock();
|
||||
_treeLock.ExitReadLock();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public List<T> AsList()
|
||||
{
|
||||
_treeLock.AcquireReaderLock(Timeout.Infinite);
|
||||
_treeLock.EnterReadLock();
|
||||
List<T> list = _tree.AsList();
|
||||
_treeLock.ReleaseReaderLock();
|
||||
_treeLock.ExitReadLock();
|
||||
|
||||
return list;
|
||||
}
|
||||
|
@ -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",
|
||||
@ -261,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",
|
||||
|
@ -15,8 +15,7 @@
|
||||
<MenuItem Header="Test 2" />
|
||||
<MenuItem Header="Test 3">
|
||||
<MenuItem.Icon>
|
||||
<CheckBox Margin="0"
|
||||
IsChecked="{ReflectionBinding Checkbox, Mode=TwoWay}" />
|
||||
<CheckBox Margin="0" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
@ -393,4 +392,4 @@
|
||||
<x:Double x:Key="ContentDialogMaxWidth">600</x:Double>
|
||||
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
|
||||
</Styles.Resources>
|
||||
</Styles>
|
||||
</Styles>
|
||||
|
@ -145,4 +145,4 @@
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Assets\Locales\en_US.json" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -82,4 +82,9 @@
|
||||
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
|
||||
</MenuItem>
|
||||
</MenuFlyout>
|
||||
<MenuItem
|
||||
Click="CreateApplicationShortcut_Click"
|
||||
Header="{locale:Locale GameListContextMenuCreateShortcut}"
|
||||
IsEnabled="{Binding CreateShortcutEnabled}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuCreateShortcutToolTip}" />
|
||||
</MenuFlyout>
|
||||
|
@ -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;
|
||||
|
31
src/Ryujinx.Ava/UI/Controls/SliderScroll.axaml.cs
Normal file
31
src/Ryujinx.Ava/UI/Controls/SliderScroll.axaml.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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<Key, LocaleKeys> _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<GamepadInputId, LocaleKeys> _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<StickInputId, LocaleKeys> _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<Key>(value.ToString());
|
||||
}
|
||||
else if (targetType == typeof(GamepadInputId))
|
||||
{
|
||||
key = Enum.Parse<GamepadInputId>(value.ToString());
|
||||
}
|
||||
else if (targetType == typeof(StickInputId))
|
||||
{
|
||||
key = Enum.Parse<StickInputId>(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
580
src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs
Normal file
580
src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs
Normal file
@ -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<GamepadInputId>
|
||||
{
|
||||
DpadUp = DpadUp,
|
||||
DpadDown = DpadDown,
|
||||
DpadLeft = DpadLeft,
|
||||
DpadRight = DpadRight,
|
||||
ButtonL = ButtonL,
|
||||
ButtonMinus = ButtonMinus,
|
||||
ButtonSl = LeftButtonSl,
|
||||
ButtonSr = LeftButtonSr,
|
||||
ButtonZl = ButtonZl
|
||||
},
|
||||
RightJoycon = new RightJoyconCommonConfig<GamepadInputId>
|
||||
{
|
||||
ButtonA = ButtonA,
|
||||
ButtonB = ButtonB,
|
||||
ButtonX = ButtonX,
|
||||
ButtonY = ButtonY,
|
||||
ButtonPlus = ButtonPlus,
|
||||
ButtonSl = RightButtonSl,
|
||||
ButtonSr = RightButtonSr,
|
||||
ButtonR = ButtonR,
|
||||
ButtonZr = ButtonZr
|
||||
},
|
||||
LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
|
||||
{
|
||||
Joystick = LeftJoystick,
|
||||
InvertStickX = LeftInvertStickX,
|
||||
InvertStickY = LeftInvertStickY,
|
||||
Rotate90CW = LeftRotate90,
|
||||
StickButton = LeftStickButton
|
||||
},
|
||||
RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
422
src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs
Normal file
422
src/Ryujinx.Ava/UI/Models/Input/KeyboardInputConfig.cs
Normal file
@ -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<Key>
|
||||
{
|
||||
DpadUp = DpadUp,
|
||||
DpadDown = DpadDown,
|
||||
DpadLeft = DpadLeft,
|
||||
DpadRight = DpadRight,
|
||||
ButtonL = ButtonL,
|
||||
ButtonMinus = ButtonMinus,
|
||||
ButtonZl = ButtonZl,
|
||||
ButtonSl = LeftButtonSl,
|
||||
ButtonSr = LeftButtonSr
|
||||
},
|
||||
RightJoycon = new RightJoyconCommonConfig<Key>
|
||||
{
|
||||
ButtonA = ButtonA,
|
||||
ButtonB = ButtonB,
|
||||
ButtonX = ButtonX,
|
||||
ButtonY = ButtonY,
|
||||
ButtonPlus = ButtonPlus,
|
||||
ButtonSl = RightButtonSl,
|
||||
ButtonSr = RightButtonSr,
|
||||
ButtonR = ButtonR,
|
||||
ButtonZr = ButtonZr
|
||||
},
|
||||
LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
|
||||
{
|
||||
StickUp = LeftStickUp,
|
||||
StickDown = LeftStickDown,
|
||||
StickRight = LeftStickRight,
|
||||
StickLeft = LeftStickLeft,
|
||||
StickButton = LeftStickButton
|
||||
},
|
||||
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
|
||||
{
|
||||
StickUp = RightStickUp,
|
||||
StickDown = RightStickDown,
|
||||
StickLeft = RightStickLeft,
|
||||
StickRight = RightStickRight,
|
||||
StickButton = RightStickButton
|
||||
},
|
||||
Version = InputConfig.CurrentVersion
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<TKey, TStick> : BaseModel
|
||||
{
|
||||
private float _deadzoneRight;
|
||||
private float _triggerThreshold;
|
||||
private float _deadzoneLeft;
|
||||
private double _gyroDeadzone;
|
||||
private int _sensitivity;
|
||||
private bool _enableMotion;
|
||||
private float _weakRumble;
|
||||
private float _strongRumble;
|
||||
private float _rangeLeft;
|
||||
private float _rangeRight;
|
||||
|
||||
public InputBackendType Backend { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controller id
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controller's Type
|
||||
/// </summary>
|
||||
public ControllerType ControllerType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Player's Index for the controller
|
||||
/// </summary>
|
||||
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<Key>
|
||||
{
|
||||
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<Key>
|
||||
{
|
||||
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<Key>
|
||||
{
|
||||
StickUp = (Key)(object)LeftStickUp,
|
||||
StickDown = (Key)(object)LeftStickDown,
|
||||
StickRight = (Key)(object)LeftStickRight,
|
||||
StickLeft = (Key)(object)LeftStickLeft,
|
||||
StickButton = (Key)(object)LeftKeyboardStickButton,
|
||||
},
|
||||
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
|
||||
{
|
||||
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<GamepadInputId>
|
||||
{
|
||||
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<GamepadInputId>
|
||||
{
|
||||
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<GamepadInputId, StickInputId>
|
||||
{
|
||||
Joystick = (StickInputId)(object)LeftJoystick,
|
||||
InvertStickX = LeftInvertStickX,
|
||||
InvertStickY = LeftInvertStickY,
|
||||
Rotate90CW = LeftRotate90,
|
||||
StickButton = (GamepadInputId)(object)LeftControllerStickButton,
|
||||
},
|
||||
RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -327,7 +327,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
string imageUrl = _amiiboList.Find(amiibo => amiibo.Equals(selected)).Image;
|
||||
|
||||
string usageString = "";
|
||||
StringBuilder usageStringBuilder = new();
|
||||
|
||||
for (int i = 0; i < _amiiboList.Count; i++)
|
||||
{
|
||||
@ -341,20 +341,19 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
|
||||
{
|
||||
usageString += Environment.NewLine +
|
||||
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
||||
usageStringBuilder.Append($"{Environment.NewLine}- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}");
|
||||
|
||||
writable = usageItem.Write;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usageString.Length == 0)
|
||||
if (usageStringBuilder.Length == 0)
|
||||
{
|
||||
usageString = LocaleManager.Instance[LocaleKeys.Unknown] + ".";
|
||||
usageStringBuilder.Append($"{LocaleManager.Instance[LocaleKeys.Unknown]}.");
|
||||
}
|
||||
|
||||
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageString}";
|
||||
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageStringBuilder}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<PlayerModel>();
|
||||
Controllers = new ObservableCollection<ControllerModel>();
|
||||
@ -282,12 +282,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
if (Config is StandardKeyboardInputConfig keyboardInputConfig)
|
||||
{
|
||||
Configuration = new InputConfiguration<Key, ConfigStickInputId>(keyboardInputConfig);
|
||||
ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig));
|
||||
}
|
||||
|
||||
if (Config is StandardControllerInputConfig controllerInputConfig)
|
||||
{
|
||||
Configuration = new InputConfiguration<ConfigGamepadInputId, ConfigStickInputId>(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<Key, ConfigStickInputId>).GetConfig();
|
||||
}
|
||||
else if (IsController)
|
||||
{
|
||||
config = (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).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<Key, ConfigStickInputId>;
|
||||
var inputConfig = (ConfigViewModel as KeyboardInputViewModel).Config;
|
||||
inputConfig.Id = device.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
var inputConfig = Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>;
|
||||
var inputConfig = (ConfigViewModel as ControllerInputViewModel).Config;
|
||||
inputConfig.Id = device.Id.Split(" ")[0];
|
||||
}
|
||||
|
||||
var config = !IsController
|
||||
? (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig()
|
||||
: (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).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()
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
public class MotionInputViewModel : BaseModel
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
public class RumbleInputViewModel : BaseModel
|
||||
{
|
@ -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;
|
||||
@ -1278,6 +1280,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
Glyph = Glyph.Grid;
|
||||
}
|
||||
|
||||
public void SetAspectRatio(AspectRatio aspectRatio)
|
||||
{
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Value = aspectRatio;
|
||||
}
|
||||
|
||||
public async Task InstallFirmwareFromFile()
|
||||
{
|
||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
@ -1483,7 +1490,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
Logger.RestartTime();
|
||||
|
||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path);
|
||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path, ConfigurationState.Instance.System.Language);
|
||||
|
||||
PrepareLoadScreen();
|
||||
|
||||
@ -1691,7 +1698,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -147,6 +147,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool EnableTextureRecompression { get; set; }
|
||||
public bool EnableMacroHLE { get; set; }
|
||||
public bool EnableColorSpacePassthrough { get; set; }
|
||||
public bool ColorSpacePassthroughAvailable => IsMacOS;
|
||||
public bool EnableFileLog { get; set; }
|
||||
public bool EnableStub { get; set; }
|
||||
public bool EnableInfo { get; set; }
|
||||
|
@ -1,12 +1,11 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
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:models="clr-namespace:Ryujinx.Ava.UI.Models"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
@ -14,6 +13,7 @@
|
||||
d:DesignWidth="800"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Input.ControllerInputView"
|
||||
x:DataType="viewModels:ControllerInputViewModel"
|
||||
x:CompileBindings="True"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
@ -33,191 +33,10 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel
|
||||
Margin="0 0 0 5"
|
||||
Orientation="Vertical"
|
||||
Spacing="5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Player Selection -->
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsPlayer}" />
|
||||
<ComboBox
|
||||
Grid.Column="1"
|
||||
Name="PlayerIndexBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
SelectionChanged="PlayerIndexBox_OnSelectionChanged"
|
||||
ItemsSource="{Binding PlayerIndexes}"
|
||||
SelectedIndex="{Binding PlayerId}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
<!-- Profile Selection -->
|
||||
<Grid
|
||||
Grid.Column="2"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsProfile}" />
|
||||
<ui:FAComboBox
|
||||
Grid.Column="1"
|
||||
IsEditable="True"
|
||||
Name="ProfileBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
SelectedIndex="0"
|
||||
ItemsSource="{Binding ProfilesList}"
|
||||
Text="{Binding ProfileName, Mode=TwoWay}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ControllerSettingsLoadProfileToolTip}"
|
||||
Command="{ReflectionBinding LoadProfile}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Upload"
|
||||
FontSize="15"
|
||||
Height="20" />
|
||||
</Button>
|
||||
<Button
|
||||
Grid.Column="3"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ControllerSettingsSaveProfileToolTip}"
|
||||
Command="{ReflectionBinding SaveProfile}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Save"
|
||||
FontSize="15"
|
||||
Height="20" />
|
||||
</Button>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ControllerSettingsRemoveProfileToolTip}"
|
||||
Command="{ReflectionBinding RemoveProfile}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Delete"
|
||||
FontSize="15"
|
||||
Height="20" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Separator />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Input Device -->
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsInputDevice}" />
|
||||
<ComboBox
|
||||
Grid.Column="1"
|
||||
Name="DeviceBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{Binding DeviceList}"
|
||||
SelectedIndex="{Binding Device}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Command="{ReflectionBinding LoadDevices}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Refresh"
|
||||
FontSize="15"
|
||||
Height="20"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
<!-- Controller Type -->
|
||||
<Grid
|
||||
Grid.Column="2"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsControllerType}" />
|
||||
<ComboBox
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Controllers}"
|
||||
SelectedIndex="{Binding Controller}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="models:ControllerModel">
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<!-- Button / JoyStick Settings -->
|
||||
<Grid
|
||||
Name="SettingButtons"
|
||||
MinHeight="450"
|
||||
IsVisible="{Binding ShowSettings}">
|
||||
MinHeight="450">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
@ -256,9 +75,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsTriggerZL}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonZl">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonZl, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonZl, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -272,9 +91,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsTriggerL}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonL">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonL, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonL, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -288,9 +107,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonMinus}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonMinus">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonMinus, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonMinus, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -310,100 +129,8 @@
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsLStick}" />
|
||||
<!-- Left Joystick Keyboard -->
|
||||
<StackPanel
|
||||
IsVisible="{Binding !IsController}"
|
||||
Orientation="Vertical">
|
||||
<!-- Left Joystick Button -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickButton}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.LeftKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left Joystick Up -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickUp}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.LeftStickUp, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left Joystick Down -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickDown}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.LeftStickDown, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left Joystick Left -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickLeft}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.LeftStickLeft, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left Joystick Right -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickRight}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.LeftStickRight, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<!-- Left Joystick Controller -->
|
||||
<StackPanel
|
||||
IsVisible="{Binding IsController}"
|
||||
Orientation="Vertical">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<!-- Left Joystick Button -->
|
||||
<StackPanel
|
||||
Orientation="Horizontal">
|
||||
@ -414,9 +141,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickButton}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="LeftStickButton">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.LeftControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftStickButton, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -431,22 +158,22 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickStick}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Tag="stick">
|
||||
<ToggleButton Name="LeftJoystick" Tag="stick">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.LeftJoystick, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftJoystick, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<Separator
|
||||
Margin="0,8,0,8"
|
||||
Height="1" />
|
||||
<CheckBox IsChecked="{ReflectionBinding Configuration.LeftInvertStickX}">
|
||||
<CheckBox IsChecked="{Binding Config.LeftInvertStickX}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsStickInvertXAxis}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{ReflectionBinding Configuration.LeftInvertStickY}">
|
||||
<CheckBox IsChecked="{Binding Config.LeftInvertStickY}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsStickInvertYAxis}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{ReflectionBinding Configuration.LeftRotate90}">
|
||||
<CheckBox IsChecked="{Binding Config.LeftRotate90}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsRotate90}" />
|
||||
</CheckBox>
|
||||
<Separator
|
||||
@ -460,18 +187,18 @@
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Width="130"
|
||||
Maximum="1"
|
||||
TickFrequency="0.01"
|
||||
IsSnapToTickEnabled="True"
|
||||
SmallChange="0.01"
|
||||
Minimum="0"
|
||||
Value="{ReflectionBinding Configuration.DeadzoneLeft, Mode=TwoWay}" />
|
||||
Value="{Binding Config.DeadzoneLeft, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Width="25"
|
||||
Text="{ReflectionBinding Configuration.DeadzoneLeft, StringFormat=\{0:0.00\}}" />
|
||||
Text="{Binding Config.DeadzoneLeft, StringFormat=\{0:0.00\}}" />
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
@ -480,18 +207,18 @@
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Width="130"
|
||||
Maximum="2"
|
||||
TickFrequency="0.01"
|
||||
IsSnapToTickEnabled="True"
|
||||
SmallChange="0.01"
|
||||
Minimum="0"
|
||||
Value="{ReflectionBinding Configuration.RangeLeft, Mode=TwoWay}" />
|
||||
Value="{Binding Config.RangeLeft, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Width="25"
|
||||
Text="{ReflectionBinding Configuration.RangeLeft, StringFormat=\{0:0.00\}}" />
|
||||
Text="{Binding Config.RangeLeft, StringFormat=\{0:0.00\}}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
@ -524,9 +251,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsDPadUp}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="DpadUp">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.DpadUp, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadUp, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -541,9 +268,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsDPadDown}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="DpadDown">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.DpadDown, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadDown, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -558,9 +285,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsDPadLeft}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="DpadLeft">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.DpadLeft, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadLeft, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -575,9 +302,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsDPadRight}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="DpadRight">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.DpadRight, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadRight, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -590,6 +317,13 @@
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<!-- Controller Picture -->
|
||||
<Image
|
||||
Margin="0,10"
|
||||
MaxHeight="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Source="{Binding Image}" />
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
@ -604,99 +338,96 @@
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Width="130"
|
||||
Maximum="1"
|
||||
TickFrequency="0.01"
|
||||
IsSnapToTickEnabled="True"
|
||||
SmallChange="0.01"
|
||||
Minimum="0"
|
||||
Value="{ReflectionBinding Configuration.TriggerThreshold, Mode=TwoWay}" />
|
||||
Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Width="25"
|
||||
Text="{ReflectionBinding Configuration.TriggerThreshold, StringFormat=\{0:0.00\}}" />
|
||||
Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !IsRight}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
Orientation="Vertical"
|
||||
IsVisible="{Binding HasSides}">
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsLeftSR}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
IsVisible="{Binding IsLeft}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.LeftButtonSr, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsLeftSR}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !IsRight}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
<ToggleButton Name="LeftButtonSr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftButtonSr, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsLeftSL}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
IsVisible="{Binding IsLeft}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.LeftButtonSl, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsLeftSL}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !IsLeft}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
<ToggleButton Name="LeftButtonSl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftButtonSl, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRightSR}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
IsVisible="{Binding IsRight}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.RightButtonSr, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRightSR}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !IsLeft}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
<ToggleButton Name="RightButtonSr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightButtonSr, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRightSL}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
IsVisible="{Binding IsRight}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.RightButtonSl, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRightSL}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
<ToggleButton Name="RightButtonSl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightButtonSl, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<!-- Controller Picture -->
|
||||
<Image
|
||||
Margin="0,10,0,0"
|
||||
MaxHeight="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Source="{Binding Image}" />
|
||||
<!-- Motion + Rumble -->
|
||||
<StackPanel
|
||||
Margin="0,10,0,0"
|
||||
@ -708,8 +439,7 @@
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding IsController}">
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
@ -719,7 +449,7 @@
|
||||
Margin="10"
|
||||
MinWidth="0"
|
||||
Grid.Column="0"
|
||||
IsChecked="{ReflectionBinding Configuration.EnableMotion, Mode=TwoWay}">
|
||||
IsChecked="{Binding Config.EnableMotion, Mode=TwoWay}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsMotion}" />
|
||||
</CheckBox>
|
||||
<Button
|
||||
@ -735,7 +465,6 @@
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding IsController}"
|
||||
Margin="0,-1,0,0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
@ -746,7 +475,7 @@
|
||||
Margin="10"
|
||||
MinWidth="0"
|
||||
Grid.Column="0"
|
||||
IsChecked="{ReflectionBinding Configuration.EnableRumble, Mode=TwoWay}">
|
||||
IsChecked="{Binding Config.EnableRumble, Mode=TwoWay}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsRumble}" />
|
||||
</CheckBox>
|
||||
<Button
|
||||
@ -792,9 +521,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsTriggerZR}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonZr">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonZr, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonZr, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -810,9 +539,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsTriggerR}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonR">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonR, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonR, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -828,15 +557,15 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonPlus}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonPlus">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonPlus, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonPlus, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<!-- Right Joystick -->
|
||||
<!-- Right Buttons -->
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
@ -863,9 +592,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonA}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonA">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonA, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonA, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -880,9 +609,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonB}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonB">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonB, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonB, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -897,9 +626,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonX}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonX">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonX, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonX, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -914,9 +643,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonY}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="ButtonY">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.ButtonY, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonY, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -936,100 +665,8 @@
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRStick}" />
|
||||
<!-- Right Joystick Keyboard -->
|
||||
<StackPanel
|
||||
IsVisible="{Binding !IsController}"
|
||||
Orientation="Vertical">
|
||||
<!-- Right Joystick Button -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickButton}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.RightKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Joystick Up -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickUp}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.RightStickUp, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Joystick Down -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickDown}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.RightStickDown, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Joystick Left -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickLeft}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.RightStickLeft, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Joystick Right -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickRight}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.RightStickRight, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<!-- Right Joystick Controller -->
|
||||
<StackPanel
|
||||
IsVisible="{Binding IsController}"
|
||||
Orientation="Vertical">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<!-- Right Joystick Button -->
|
||||
<StackPanel
|
||||
Orientation="Horizontal">
|
||||
@ -1040,9 +677,9 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickButton}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton>
|
||||
<ToggleButton Name="RightStickButton">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.RightControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightStickButton, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@ -1058,20 +695,20 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickStick}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Tag="stick">
|
||||
<ToggleButton Name="RightJoystick" Tag="stick">
|
||||
<TextBlock
|
||||
Text="{ReflectionBinding Configuration.RightJoystick, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightJoystick, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<Separator Margin="0,8,0,8" Height="1" />
|
||||
<CheckBox IsChecked="{ReflectionBinding Configuration.RightInvertStickX}">
|
||||
<CheckBox IsChecked="{Binding Config.RightInvertStickX}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsStickInvertXAxis}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{ReflectionBinding Configuration.RightInvertStickY}">
|
||||
<CheckBox IsChecked="{Binding Config.RightInvertStickY}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsStickInvertYAxis}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{ReflectionBinding Configuration.RightRotate90}">
|
||||
<CheckBox IsChecked="{Binding Config.RightRotate90}">
|
||||
<TextBlock Text="{locale:Locale ControllerSettingsRotate90}" />
|
||||
</CheckBox>
|
||||
<Separator Margin="0,8,0,8" Height="1" />
|
||||
@ -1083,7 +720,7 @@
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Width="130"
|
||||
Maximum="1"
|
||||
TickFrequency="0.01"
|
||||
@ -1092,11 +729,11 @@
|
||||
Padding="0"
|
||||
VerticalAlignment="Center"
|
||||
Minimum="0"
|
||||
Value="{ReflectionBinding Configuration.DeadzoneRight, Mode=TwoWay}" />
|
||||
Value="{Binding Config.DeadzoneRight, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Width="25"
|
||||
Text="{ReflectionBinding Configuration.DeadzoneRight, StringFormat=\{0:0.00\}}" />
|
||||
Text="{Binding Config.DeadzoneRight, StringFormat=\{0:0.00\}}" />
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
@ -1105,18 +742,18 @@
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Width="130"
|
||||
Maximum="2"
|
||||
TickFrequency="0.01"
|
||||
IsSnapToTickEnabled="True"
|
||||
SmallChange="0.01"
|
||||
Minimum="0"
|
||||
Value="{ReflectionBinding Configuration.RangeRight, Mode=TwoWay}" />
|
||||
Value="{Binding Config.RangeRight, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Width="25"
|
||||
Text="{ReflectionBinding Configuration.RangeRight, StringFormat=\{0:0.00\}}" />
|
||||
Text="{Binding Config.RangeRight, StringFormat=\{0:0.00\}}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
@ -1125,4 +762,4 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -1,35 +1,28 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.Assigner;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
public partial class ControllerInputView : UserControl
|
||||
{
|
||||
private bool _dialogOpen;
|
||||
|
||||
private ButtonKeyAssigner _currentAssigner;
|
||||
internal ControllerInputViewModel ViewModel { get; set; }
|
||||
|
||||
public ControllerInputView()
|
||||
{
|
||||
DataContext = ViewModel = new ControllerInputViewModel(this);
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
foreach (ILogical visual in SettingButtons.GetLogicalDescendants())
|
||||
{
|
||||
if (visual is ToggleButton button && visual is not CheckBox)
|
||||
if (visual is ToggleButton button and not CheckBox)
|
||||
{
|
||||
button.IsCheckedChanged += Button_IsCheckedChanged;
|
||||
}
|
||||
@ -59,7 +52,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
|
||||
bool isStick = button.Tag != null && button.Tag.ToString() == "stick";
|
||||
|
||||
if (_currentAssigner == null)
|
||||
if (_currentAssigner == null && (bool)button.IsChecked)
|
||||
{
|
||||
_currentAssigner = new ButtonKeyAssigner(button);
|
||||
|
||||
@ -67,14 +60,86 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
|
||||
PointerPressed += MouseClick;
|
||||
|
||||
IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
|
||||
IKeyboard keyboard = (IKeyboard)(DataContext as ControllerInputViewModel).parentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
|
||||
IButtonAssigner assigner = CreateButtonAssigner(isStick);
|
||||
|
||||
_currentAssigner.ButtonAssigned += (sender, e) =>
|
||||
{
|
||||
if (e.IsAssigned)
|
||||
if (e.ButtonValue.HasValue)
|
||||
{
|
||||
ViewModel.IsModified = true;
|
||||
var viewModel = (DataContext as ControllerInputViewModel);
|
||||
var buttonValue = e.ButtonValue.Value;
|
||||
viewModel.parentModel.IsModified = true;
|
||||
|
||||
switch (button.Name)
|
||||
{
|
||||
case "ButtonZl":
|
||||
viewModel.Config.ButtonZl = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "ButtonL":
|
||||
viewModel.Config.ButtonL = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "ButtonMinus":
|
||||
viewModel.Config.ButtonMinus = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "LeftStickButton":
|
||||
viewModel.Config.LeftStickButton = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "LeftJoystick":
|
||||
viewModel.Config.LeftJoystick = buttonValue.AsGamepadStickId();
|
||||
break;
|
||||
case "DpadUp":
|
||||
viewModel.Config.DpadUp = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "DpadDown":
|
||||
viewModel.Config.DpadDown = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "DpadLeft":
|
||||
viewModel.Config.DpadLeft = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "DpadRight":
|
||||
viewModel.Config.DpadRight = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "LeftButtonSr":
|
||||
viewModel.Config.LeftButtonSr = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "LeftButtonSl":
|
||||
viewModel.Config.LeftButtonSl = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "RightButtonSr":
|
||||
viewModel.Config.RightButtonSr = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "RightButtonSl":
|
||||
viewModel.Config.RightButtonSl = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "ButtonZr":
|
||||
viewModel.Config.ButtonZr = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "ButtonR":
|
||||
viewModel.Config.ButtonR = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "ButtonPlus":
|
||||
viewModel.Config.ButtonPlus = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "ButtonA":
|
||||
viewModel.Config.ButtonA = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "ButtonB":
|
||||
viewModel.Config.ButtonB = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "ButtonX":
|
||||
viewModel.Config.ButtonX = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "ButtonY":
|
||||
viewModel.Config.ButtonY = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "RightStickButton":
|
||||
viewModel.Config.RightStickButton = buttonValue.AsGamepadButtonInputId();
|
||||
break;
|
||||
case "RightJoystick":
|
||||
viewModel.Config.RightJoystick = buttonValue.AsGamepadStickId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -100,82 +165,29 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveCurrentProfile()
|
||||
{
|
||||
ViewModel.Save();
|
||||
}
|
||||
|
||||
private IButtonAssigner CreateButtonAssigner(bool forStick)
|
||||
{
|
||||
IButtonAssigner assigner;
|
||||
|
||||
var device = ViewModel.Devices[ViewModel.Device];
|
||||
|
||||
if (device.Type == DeviceType.Keyboard)
|
||||
{
|
||||
assigner = new KeyboardKeyAssigner((IKeyboard)ViewModel.SelectedGamepad);
|
||||
}
|
||||
else if (device.Type == DeviceType.Controller)
|
||||
{
|
||||
assigner = new GamepadButtonAssigner(ViewModel.SelectedGamepad, (ViewModel.Config as StandardControllerInputConfig).TriggerThreshold, forStick);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Controller not supported");
|
||||
}
|
||||
|
||||
return assigner;
|
||||
}
|
||||
|
||||
private void MouseClick(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
bool shouldUnbind = false;
|
||||
|
||||
if (e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed)
|
||||
{
|
||||
shouldUnbind = true;
|
||||
}
|
||||
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
|
||||
|
||||
_currentAssigner?.Cancel(shouldUnbind);
|
||||
|
||||
PointerPressed -= MouseClick;
|
||||
}
|
||||
|
||||
private async void PlayerIndexBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
private IButtonAssigner CreateButtonAssigner(bool forStick)
|
||||
{
|
||||
if (ViewModel.IsModified && !_dialogOpen)
|
||||
{
|
||||
_dialogOpen = true;
|
||||
IButtonAssigner assigner;
|
||||
|
||||
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]);
|
||||
assigner = new GamepadButtonAssigner((DataContext as ControllerInputViewModel).parentModel.SelectedGamepad, ((DataContext as ControllerInputViewModel).parentModel.Config as StandardControllerInputConfig).TriggerThreshold, forStick);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return assigner;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
ViewModel.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
225
src/Ryujinx.Ava/UI/Views/Input/InputView.axaml
Normal file
225
src/Ryujinx.Ava/UI/Views/Input/InputView.axaml
Normal file
@ -0,0 +1,225 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
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:models="clr-namespace:Ryujinx.Ava.UI.Models"
|
||||
xmlns:views="clr-namespace:Ryujinx.Ava.UI.Views.Input"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
d:DesignHeight="800"
|
||||
d:DesignWidth="800"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Input.InputView"
|
||||
x:DataType="viewModels:InputViewModel"
|
||||
x:CompileBindings="True"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
<viewModels:InputViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="ToggleButton">
|
||||
<Setter Property="Width" Value="90" />
|
||||
<Setter Property="Height" Value="27" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel
|
||||
Margin="0 0 0 5"
|
||||
Orientation="Vertical"
|
||||
Spacing="5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Player Selection -->
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsPlayer}" />
|
||||
<ComboBox
|
||||
Grid.Column="1"
|
||||
Name="PlayerIndexBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
SelectionChanged="PlayerIndexBox_OnSelectionChanged"
|
||||
ItemsSource="{Binding PlayerIndexes}"
|
||||
SelectedIndex="{Binding PlayerId}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
<!-- Profile Selection -->
|
||||
<Grid
|
||||
Grid.Column="2"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsProfile}" />
|
||||
<ui:FAComboBox
|
||||
Grid.Column="1"
|
||||
IsEditable="True"
|
||||
Name="ProfileBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
SelectedIndex="0"
|
||||
ItemsSource="{Binding ProfilesList}"
|
||||
Text="{Binding ProfileName, Mode=TwoWay}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ControllerSettingsLoadProfileToolTip}"
|
||||
Command="{Binding LoadProfile}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Upload"
|
||||
FontSize="15"
|
||||
Height="20" />
|
||||
</Button>
|
||||
<Button
|
||||
Grid.Column="3"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ControllerSettingsSaveProfileToolTip}"
|
||||
Command="{Binding SaveProfile}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Save"
|
||||
FontSize="15"
|
||||
Height="20" />
|
||||
</Button>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale ControllerSettingsRemoveProfileToolTip}"
|
||||
Command="{Binding RemoveProfile}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Delete"
|
||||
FontSize="15"
|
||||
Height="20" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Separator />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Input Device -->
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsInputDevice}" />
|
||||
<ComboBox
|
||||
Grid.Column="1"
|
||||
Name="DeviceBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{Binding DeviceList}"
|
||||
SelectedIndex="{Binding Device}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
MinWidth="0"
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding LoadDevices}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Refresh"
|
||||
FontSize="15"
|
||||
Height="20"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
<!-- Controller Type -->
|
||||
<Grid
|
||||
Grid.Column="2"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsControllerType}" />
|
||||
<ComboBox
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Controllers}"
|
||||
SelectedIndex="{Binding Controller}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="models:ControllerModel">
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<ContentControl Content="{Binding ConfigViewModel}" IsVisible="{Binding ShowSettings}">
|
||||
<ContentControl.DataTemplates>
|
||||
<DataTemplate DataType="viewModels:ControllerInputViewModel">
|
||||
<views:ControllerInputView />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="viewModels:KeyboardInputViewModel">
|
||||
<views:KeyboardInputView />
|
||||
</DataTemplate>
|
||||
</ContentControl.DataTemplates>
|
||||
</ContentControl>
|
||||
</StackPanel>
|
||||
</UserControl>
|
61
src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs
Normal file
61
src/Ryujinx.Ava/UI/Views/Input/InputView.axaml.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
675
src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml
Normal file
675
src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml
Normal file
@ -0,0 +1,675 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
d:DesignHeight="800"
|
||||
d:DesignWidth="800"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Input.KeyboardInputView"
|
||||
x:DataType="viewModels:KeyboardInputViewModel"
|
||||
x:CompileBindings="True"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
<viewModels:KeyboardInputViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Resources>
|
||||
<helpers:KeyValueConverter x:Key="Key" />
|
||||
</UserControl.Resources>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="ToggleButton">
|
||||
<Setter Property="Width" Value="90" />
|
||||
<Setter Property="Height" Value="27" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<!-- Button / JoyStick Settings -->
|
||||
<Grid
|
||||
Name="SettingButtons"
|
||||
MinHeight="450">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Left Controls -->
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
Margin="0,0,5,0"
|
||||
Grid.Column="0">
|
||||
<!-- Left Triggers -->
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding IsLeft}"
|
||||
MinHeight="90"
|
||||
CornerRadius="5">
|
||||
<Grid
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsTriggerZL}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonZl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonZl, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsTriggerL}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonL">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonL, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonMinus}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonMinus">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonMinus, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<!-- Left Joystick -->
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding IsLeft}"
|
||||
Margin="0,5,0,0"
|
||||
CornerRadius="5">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsLStick}" />
|
||||
<!-- Left Joystick Keyboard -->
|
||||
<StackPanel Orientation="Vertical">
|
||||
<!-- Left Joystick Button -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickButton}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickButton">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickButton, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left Joystick Up -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickUp}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickUp">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickUp, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left Joystick Down -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickDown}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickDown">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickDown, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left Joystick Left -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickLeft}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickLeft">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickLeft, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left Joystick Right -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickRight}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickRight">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickRight, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<!-- Left DPad -->
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
VerticalAlignment="Top"
|
||||
IsVisible="{Binding IsLeft}"
|
||||
Margin="0,5,0,0"
|
||||
CornerRadius="5">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsDPad}" />
|
||||
<StackPanel Orientation="Vertical">
|
||||
<!-- Left DPad Up -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsDPadUp}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadUp">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadUp, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left DPad Down -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsDPadDown}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadDown">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadDown, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left DPad Left -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsDPadLeft}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadLeft">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadLeft, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Left DPad Right -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsDPadRight}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadRight">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadRight, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<!-- Triggers & Side Buttons -->
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<!-- Controller Picture -->
|
||||
<Image
|
||||
Margin="0,10"
|
||||
MaxHeight="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Source="{Binding Image}" />
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
MinHeight="90"
|
||||
IsVisible="{Binding HasSides}">
|
||||
<StackPanel
|
||||
Margin="8"
|
||||
Orientation="Vertical">
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsLeft}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsLeftSR}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftButtonSr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftButtonSr, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsLeft}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsLeftSL}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftButtonSl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftButtonSl, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsRight}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRightSR}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightButtonSr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightButtonSr, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsRight}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRightSL}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightButtonSl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightButtonSl, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<!-- Right Controls -->
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
Margin="5,0,0,0"
|
||||
Grid.Column="2">
|
||||
<!-- Right Triggers -->
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding IsRight}"
|
||||
MinHeight="90"
|
||||
CornerRadius="5">
|
||||
<Grid
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsTriggerZR}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonZr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonZr, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsTriggerR}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonR">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonR, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonPlus}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonPlus">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonPlus, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<!-- Right Buttons -->
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding IsRight}"
|
||||
Margin="0,5,0,0"
|
||||
CornerRadius="5">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtons}" />
|
||||
<StackPanel
|
||||
Orientation="Vertical">
|
||||
<!-- Right Buttons A -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="120"
|
||||
Margin="0,0,10,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonA}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonA">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonA, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Buttons B -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="120"
|
||||
Margin="0,0,10,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonB}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonB">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonB, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Buttons X -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="120"
|
||||
Margin="0,0,10,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonX}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonX">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonX, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Buttons Y -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Width="120"
|
||||
Margin="0,0,10,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsButtonY}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonY">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonY, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<!-- Right DPad -->
|
||||
<Border
|
||||
Padding="10"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
IsVisible="{Binding IsRight}"
|
||||
Margin="0,5,0,0">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRStick}" />
|
||||
<!-- Right Joystick Keyboard -->
|
||||
<StackPanel Orientation="Vertical">
|
||||
<!-- Right Joystick Button -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickButton}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickButton">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickButton, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Joystick Up -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickUp}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickUp">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickUp, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Joystick Down -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickDown}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickDown">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickDown, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Joystick Left -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickLeft}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickLeft">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickLeft, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<!-- Right Joystick Right -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,4"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Width="120"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsStickRight}"
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickRight">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickRight, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</UserControl>
|
210
src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs
Normal file
210
src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,9 +3,10 @@
|
||||
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"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView"
|
||||
x:DataType="viewModels:MotionInputViewModel"
|
||||
@ -23,11 +24,11 @@
|
||||
Margin="0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsMotionGyroSensitivity}" />
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Margin="0,-5,0,-5"
|
||||
Width="150"
|
||||
MaxWidth="150"
|
||||
TickFrequency="0.01"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
SmallChange="0.01"
|
||||
Maximum="100"
|
||||
@ -45,11 +46,11 @@
|
||||
Margin="0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsMotionGyroDeadzone}" />
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Margin="0,-5,0,-5"
|
||||
Width="150"
|
||||
MaxWidth="150"
|
||||
TickFrequency="0.01"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
SmallChange="0.01"
|
||||
Maximum="100"
|
||||
@ -167,4 +168,4 @@
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -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<GamepadInputId, StickInputId>;
|
||||
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<GamepadInputId, StickInputId>;
|
||||
var config = viewModel.Config;
|
||||
config.Slot = content._viewModel.Slot;
|
||||
config.Sensitivity = content._viewModel.Sensitivity;
|
||||
config.GyroDeadzone = content._viewModel.GyroDeadzone;
|
||||
|
@ -1,10 +1,11 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns: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"
|
||||
@ -21,7 +22,7 @@
|
||||
TextWrapping="WrapWithOverflow"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRumbleStrongMultiplier}" />
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Margin="0,-5,0,-5"
|
||||
Width="200"
|
||||
TickFrequency="0.01"
|
||||
@ -41,7 +42,7 @@
|
||||
TextWrapping="WrapWithOverflow"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{locale:Locale ControllerSettingsRumbleWeakMultiplier}" />
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Margin="0,-5,0,-5"
|
||||
Width="200"
|
||||
MaxWidth="200"
|
||||
@ -58,4 +59,4 @@
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -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<GamepadInputId, StickInputId>;
|
||||
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<GamepadInputId, StickInputId>;
|
||||
var config = viewModel.Config;
|
||||
config.StrongRumble = content._viewModel.StrongRumble;
|
||||
config.WeakRumble = content._viewModel.WeakRumble;
|
||||
};
|
||||
|
@ -3,9 +3,11 @@
|
||||
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"
|
||||
xmlns:config="clr-namespace:Ryujinx.Common.Configuration;assembly=Ryujinx.Common"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView"
|
||||
x:DataType="viewModels:MainWindowViewModel">
|
||||
@ -112,15 +114,52 @@
|
||||
Background="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
<SplitButton
|
||||
Name="AspectRatioStatus"
|
||||
Margin="5,0,5,0"
|
||||
Padding="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerReleased="AspectRatioStatus_PointerReleased"
|
||||
Text="{Binding AspectRatioStatusText}"
|
||||
TextAlignment="Left" />
|
||||
Content="{Binding AspectRatioStatusText}"
|
||||
Click="AspectRatioStatus_OnClick"
|
||||
ToolTip.Tip="{locale:Locale AspectRatioTooltip}">
|
||||
<SplitButton.Styles>
|
||||
<Style Selector="Border#SeparatorBorder">
|
||||
<Setter Property="Opacity" Value="0" />
|
||||
</Style>
|
||||
</SplitButton.Styles>
|
||||
<SplitButton.Flyout>
|
||||
<MenuFlyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||
<MenuItem
|
||||
Header="{locale:Locale SettingsTabGraphicsAspectRatio4x3}"
|
||||
Command="{Binding SetAspectRatio}"
|
||||
CommandParameter="{x:Static config:AspectRatio.Fixed4x3}"/>
|
||||
<MenuItem
|
||||
Header="{locale:Locale SettingsTabGraphicsAspectRatio16x9}"
|
||||
Command="{Binding SetAspectRatio}"
|
||||
CommandParameter="{x:Static config:AspectRatio.Fixed16x9}"/>
|
||||
<MenuItem
|
||||
Header="{locale:Locale SettingsTabGraphicsAspectRatio16x10}"
|
||||
Command="{Binding SetAspectRatio}"
|
||||
CommandParameter="{x:Static config:AspectRatio.Fixed16x10}"/>
|
||||
<MenuItem
|
||||
Header="{locale:Locale SettingsTabGraphicsAspectRatio21x9}"
|
||||
Command="{Binding SetAspectRatio}"
|
||||
CommandParameter="{x:Static config:AspectRatio.Fixed21x9}"/>
|
||||
<MenuItem
|
||||
Header="{locale:Locale SettingsTabGraphicsAspectRatio32x9}"
|
||||
Command="{Binding SetAspectRatio}"
|
||||
CommandParameter="{x:Static config:AspectRatio.Fixed32x9}"/>
|
||||
<MenuItem
|
||||
Header="{locale:Locale SettingsTabGraphicsAspectRatioStretch}"
|
||||
Command="{Binding SetAspectRatio}"
|
||||
CommandParameter="{x:Static config:AspectRatio.Stretched}"/>
|
||||
</MenuFlyout>
|
||||
</SplitButton.Flyout>
|
||||
</SplitButton>
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
@ -138,6 +177,7 @@
|
||||
Content="{Binding VolumeStatusText}"
|
||||
IsChecked="{Binding VolumeMuted}"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
PointerWheelChanged="VolumeStatus_OnPointerWheelChanged"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0">
|
||||
@ -154,7 +194,7 @@
|
||||
<ToggleSplitButton.Flyout>
|
||||
<Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||
<Grid Margin="0">
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
MaxHeight="40"
|
||||
Width="150"
|
||||
Margin="0"
|
||||
|
@ -43,10 +43,9 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
|
||||
}
|
||||
|
||||
private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
private void AspectRatioStatus_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
|
||||
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
|
||||
}
|
||||
|
||||
@ -54,5 +53,20 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
Window.LoadApplications();
|
||||
}
|
||||
|
||||
private void VolumeStatus_OnPointerWheelChanged(object sender, PointerWheelEventArgs e)
|
||||
{
|
||||
// Change the volume by 5% at a time
|
||||
float newValue = Window.ViewModel.Volume + (float)e.Delta.Y * 0.05f;
|
||||
|
||||
Window.ViewModel.Volume = newValue switch
|
||||
{
|
||||
< 0 => 0,
|
||||
> 1 => 1,
|
||||
_ => newValue,
|
||||
};
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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}" />
|
||||
<Slider
|
||||
<controls:SliderScroll
|
||||
Width="150"
|
||||
Height="35"
|
||||
Margin="5,-10,5,0"
|
||||
@ -173,4 +174,4 @@
|
||||
DockPanel.Dock="Right"
|
||||
Text="{locale:Locale CommonSort}" />
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -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" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<Slider Value="{Binding Volume}"
|
||||
<controls:SliderScroll Value="{Binding Volume}"
|
||||
Margin="250,0,0,0"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
SmallChange="5"
|
||||
TickFrequency="5"
|
||||
SmallChange="1"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
LargeChange="10"
|
||||
Width="350" />
|
||||
@ -77,4 +78,4 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -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"
|
||||
@ -73,6 +74,7 @@
|
||||
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableColorSpacePassthrough}"
|
||||
IsVisible="{Binding ColorSpacePassthroughAvailable}"
|
||||
ToolTip.Tip="{locale:Locale SettingsEnableColorSpacePassthroughTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsEnableColorSpacePassthrough}" />
|
||||
</CheckBox>
|
||||
@ -172,7 +174,7 @@
|
||||
<TextBlock Text="FSR" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<Slider Value="{Binding ScalingFilterLevel}"
|
||||
<controls:SliderScroll Value="{Binding ScalingFilterLevel}"
|
||||
ToolTip.Tip="{locale:Locale GraphicsScalingFilterLevelTooltip}"
|
||||
MinWidth="150"
|
||||
Margin="10,-3,0,0"
|
||||
@ -296,4 +298,4 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
@ -27,9 +27,9 @@
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<views:ControllerInputView
|
||||
<views:InputView
|
||||
Grid.Row="0"
|
||||
Name="ControllerSettings" />
|
||||
Name="InputView" />
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
Grid.Row="2">
|
||||
|
@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ControllerSettings.Dispose();
|
||||
InputView.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -756,6 +756,18 @@ namespace Ryujinx.Common.Memory
|
||||
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
|
||||
}
|
||||
|
||||
public struct Array96<T> : IArray<T> where T : unmanaged
|
||||
{
|
||||
T _e0;
|
||||
Array64<T> _other;
|
||||
Array31<T> _other2;
|
||||
public readonly int Length => 96;
|
||||
public ref T this[int index] => ref AsSpan()[index];
|
||||
|
||||
[Pure]
|
||||
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
|
||||
}
|
||||
|
||||
public struct Array127<T> : IArray<T> where T : unmanaged
|
||||
{
|
||||
T _e0;
|
||||
|
@ -5,7 +5,7 @@ namespace Ryujinx.Common
|
||||
{
|
||||
public class ReactiveObject<T>
|
||||
{
|
||||
private readonly ReaderWriterLock _readerWriterLock = new();
|
||||
private readonly ReaderWriterLockSlim _readerWriterLock = new();
|
||||
private bool _isInitialized;
|
||||
private T _value;
|
||||
|
||||
@ -15,15 +15,15 @@ namespace Ryujinx.Common
|
||||
{
|
||||
get
|
||||
{
|
||||
_readerWriterLock.AcquireReaderLock(Timeout.Infinite);
|
||||
_readerWriterLock.EnterReadLock();
|
||||
T value = _value;
|
||||
_readerWriterLock.ReleaseReaderLock();
|
||||
_readerWriterLock.ExitReadLock();
|
||||
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_readerWriterLock.AcquireWriterLock(Timeout.Infinite);
|
||||
_readerWriterLock.EnterWriteLock();
|
||||
|
||||
T oldValue = _value;
|
||||
|
||||
@ -32,7 +32,7 @@ namespace Ryujinx.Common
|
||||
_isInitialized = true;
|
||||
_value = value;
|
||||
|
||||
_readerWriterLock.ReleaseWriterLock();
|
||||
_readerWriterLock.ExitWriteLock();
|
||||
|
||||
if (!oldIsInitialized || oldValue == null || !oldValue.Equals(_value))
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -32,6 +32,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||
/// </summary>
|
||||
public ref TState State => ref _state.State;
|
||||
|
||||
/// <summary>
|
||||
/// Current shadow state.
|
||||
/// </summary>
|
||||
public ref TState ShadowState => ref _shadowState.State;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the device state, with shadow state.
|
||||
/// </summary>
|
||||
|
@ -1,7 +1,10 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.Device;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@ -15,9 +18,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
private const int ColorLayerCountOffset = 0x818;
|
||||
private const int ColorStructSize = 0x40;
|
||||
private const int ZetaLayerCountOffset = 0x1230;
|
||||
private const int UniformBufferBindVertexOffset = 0x2410;
|
||||
private const int FirstVertexOffset = 0x1434;
|
||||
|
||||
private const int IndirectIndexedDataEntrySize = 0x14;
|
||||
|
||||
private const int LogicOpOffset = 0x19c4;
|
||||
private const int ShaderIdScratchOffset = 0x3470;
|
||||
private const int ShaderAddressScratchOffset = 0x3488;
|
||||
private const int UpdateConstantBufferAddressesBase = 0x34a8;
|
||||
private const int UpdateConstantBufferSizesBase = 0x34bc;
|
||||
private const int UpdateConstantBufferAddressCbu = 0x3460;
|
||||
|
||||
private readonly GPFifoProcessor _processor;
|
||||
private readonly MacroHLEFunctionName _functionName;
|
||||
|
||||
@ -49,6 +61,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
{
|
||||
switch (_functionName)
|
||||
{
|
||||
case MacroHLEFunctionName.BindShaderProgram:
|
||||
BindShaderProgram(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.ClearColor:
|
||||
ClearColor(state, arg0);
|
||||
break;
|
||||
@ -58,6 +73,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
case MacroHLEFunctionName.DrawArraysInstanced:
|
||||
DrawArraysInstanced(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.DrawElements:
|
||||
DrawElements(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.DrawElementsInstanced:
|
||||
DrawElementsInstanced(state, arg0);
|
||||
break;
|
||||
@ -67,6 +85,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
|
||||
MultiDrawElementsIndirectCount(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.UpdateBlendState:
|
||||
UpdateBlendState(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.UpdateColorMasks:
|
||||
UpdateColorMasks(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.UpdateUniformBufferState:
|
||||
UpdateUniformBufferState(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.UpdateUniformBufferStateCbu:
|
||||
UpdateUniformBufferStateCbu(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.UpdateUniformBufferStateCbuV2:
|
||||
UpdateUniformBufferStateCbuV2(state, arg0);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(_functionName.ToString());
|
||||
}
|
||||
@ -75,6 +108,149 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
Fifo.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a shader program with the index in arg0.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void BindShaderProgram(IDeviceState state, int arg0)
|
||||
{
|
||||
int scratchOffset = ShaderIdScratchOffset + arg0 * 4;
|
||||
|
||||
int lastId = state.Read(scratchOffset);
|
||||
int id = FetchParam().Word;
|
||||
int offset = FetchParam().Word;
|
||||
|
||||
if (lastId == id)
|
||||
{
|
||||
FetchParam();
|
||||
FetchParam();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_processor.ThreedClass.SetShaderOffset(arg0, (uint)offset);
|
||||
|
||||
// Removes overflow on the method address into the increment portion.
|
||||
// Present in the original macro.
|
||||
int addrMask = unchecked((int)0xfffc0fff) << 2;
|
||||
|
||||
state.Write(scratchOffset & addrMask, id);
|
||||
state.Write((ShaderAddressScratchOffset + arg0 * 4) & addrMask, offset);
|
||||
|
||||
int stage = FetchParam().Word;
|
||||
uint cbAddress = (uint)FetchParam().Word;
|
||||
|
||||
_processor.ThreedClass.UpdateUniformBufferState(65536, cbAddress >> 24, cbAddress << 8);
|
||||
|
||||
int stageOffset = (stage & 0x7f) << 3;
|
||||
|
||||
state.Write((UniformBufferBindVertexOffset + stageOffset * 4) & addrMask, 17);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates uniform buffer state for update or bind.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void UpdateUniformBufferState(IDeviceState state, int arg0)
|
||||
{
|
||||
uint address = (uint)state.Read(UpdateConstantBufferAddressesBase + arg0 * 4);
|
||||
int size = state.Read(UpdateConstantBufferSizesBase + arg0 * 4);
|
||||
|
||||
_processor.ThreedClass.UpdateUniformBufferState(size, address >> 24, address << 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates uniform buffer state for update.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void UpdateUniformBufferStateCbu(IDeviceState state, int arg0)
|
||||
{
|
||||
uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
|
||||
|
||||
UniformBufferState ubState = new()
|
||||
{
|
||||
Address = new()
|
||||
{
|
||||
High = address >> 24,
|
||||
Low = address << 8
|
||||
},
|
||||
Size = 24320,
|
||||
Offset = arg0 << 2
|
||||
};
|
||||
|
||||
_processor.ThreedClass.UpdateUniformBufferState(ubState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates uniform buffer state for update.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void UpdateUniformBufferStateCbuV2(IDeviceState state, int arg0)
|
||||
{
|
||||
uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
|
||||
|
||||
UniformBufferState ubState = new()
|
||||
{
|
||||
Address = new()
|
||||
{
|
||||
High = address >> 24,
|
||||
Low = address << 8
|
||||
},
|
||||
Size = 28672,
|
||||
Offset = arg0 << 2
|
||||
};
|
||||
|
||||
_processor.ThreedClass.UpdateUniformBufferState(ubState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates blend enable using the given argument.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void UpdateBlendState(IDeviceState state, int arg0)
|
||||
{
|
||||
state.Write(LogicOpOffset, 0);
|
||||
|
||||
Array8<Boolean32> enable = new();
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
enable[i] = new Boolean32((uint)(arg0 >> (i + 8)) & 1);
|
||||
}
|
||||
|
||||
_processor.ThreedClass.UpdateBlendEnable(ref enable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates color masks using the given argument and three pushed arguments.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void UpdateColorMasks(IDeviceState state, int arg0)
|
||||
{
|
||||
Array8<RtColorMask> masks = new();
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
masks[index++] = new RtColorMask((uint)arg0 & 0x1fff);
|
||||
masks[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff);
|
||||
|
||||
if (i != 3)
|
||||
{
|
||||
arg0 = FetchParam().Word;
|
||||
}
|
||||
}
|
||||
|
||||
_processor.ThreedClass.UpdateColorMasks(ref masks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears one bound color target.
|
||||
/// </summary>
|
||||
@ -129,6 +305,36 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
indexed: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void DrawElements(IDeviceState state, int arg0)
|
||||
{
|
||||
var topology = (PrimitiveTopology)arg0;
|
||||
|
||||
var indexAddressHigh = FetchParam();
|
||||
var indexAddressLow = FetchParam();
|
||||
var indexType = FetchParam();
|
||||
var firstIndex = 0;
|
||||
var indexCount = FetchParam();
|
||||
|
||||
_processor.ThreedClass.UpdateIndexBuffer(
|
||||
(uint)indexAddressHigh.Word,
|
||||
(uint)indexAddressLow.Word,
|
||||
(IndexType)indexType.Word);
|
||||
|
||||
_processor.ThreedClass.Draw(
|
||||
topology,
|
||||
indexCount.Word,
|
||||
1,
|
||||
firstIndex,
|
||||
state.Read(FirstVertexOffset),
|
||||
0,
|
||||
indexed: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw.
|
||||
/// </summary>
|
||||
|
@ -6,11 +6,19 @@
|
||||
enum MacroHLEFunctionName
|
||||
{
|
||||
None,
|
||||
BindShaderProgram,
|
||||
ClearColor,
|
||||
ClearDepthStencil,
|
||||
DrawArraysInstanced,
|
||||
DrawElements,
|
||||
DrawElementsInstanced,
|
||||
DrawElementsIndirect,
|
||||
MultiDrawElementsIndirectCount,
|
||||
|
||||
UpdateBlendState,
|
||||
UpdateColorMasks,
|
||||
UpdateUniformBufferState,
|
||||
UpdateUniformBufferStateCbu,
|
||||
UpdateUniformBufferStateCbuV2
|
||||
}
|
||||
}
|
||||
|
@ -46,12 +46,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
|
||||
private static readonly TableEntry[] _table = new TableEntry[]
|
||||
{
|
||||
new(MacroHLEFunctionName.BindShaderProgram, new Hash128(0x5d5efb912369f60b, 0x69131ed5019f08ef), 0x68),
|
||||
new(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28),
|
||||
new(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24),
|
||||
new(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48),
|
||||
new(MacroHLEFunctionName.DrawElements, new Hash128(0x3D7F32AE6C2702A7, 0x9353C9F41C1A244D), 0x20),
|
||||
new(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c),
|
||||
new(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c),
|
||||
new(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C),
|
||||
new(MacroHLEFunctionName.UpdateBlendState, new Hash128(0x40F6D4E7B08D7640, 0x82167BEEAECB959F), 0x28),
|
||||
new(MacroHLEFunctionName.UpdateColorMasks, new Hash128(0x9EE32420B8441DFD, 0x6E7724759A57333E), 0x24),
|
||||
new(MacroHLEFunctionName.UpdateUniformBufferState, new Hash128(0x8EE66706049CB0B0, 0x51C1CF906EC86F7C), 0x20),
|
||||
new(MacroHLEFunctionName.UpdateUniformBufferStateCbu, new Hash128(0xA4592676A3E581A0, 0xA39E77FE19FE04AC), 0x18),
|
||||
new(MacroHLEFunctionName.UpdateUniformBufferStateCbuV2, new Hash128(0x392FA750489983D4, 0x35BACE455155D2C3), 0x18)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@ -62,18 +69,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
/// <returns>True if the host supports the HLE macro, false otherwise</returns>
|
||||
private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name)
|
||||
{
|
||||
if (name == MacroHLEFunctionName.ClearColor ||
|
||||
name == MacroHLEFunctionName.ClearDepthStencil ||
|
||||
name == MacroHLEFunctionName.DrawArraysInstanced ||
|
||||
name == MacroHLEFunctionName.DrawElementsInstanced ||
|
||||
name == MacroHLEFunctionName.DrawElementsIndirect)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
|
||||
if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
|
||||
{
|
||||
return caps.SupportsIndirectParameters;
|
||||
}
|
||||
else if (name != MacroHLEFunctionName.None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -10,4 +10,22 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||
MethodPassthrough = 2,
|
||||
MethodReplay = 3,
|
||||
}
|
||||
|
||||
static class SetMmeShadowRamControlModeExtensions
|
||||
{
|
||||
public static bool IsTrack(this SetMmeShadowRamControlMode mode)
|
||||
{
|
||||
return mode == SetMmeShadowRamControlMode.MethodTrack || mode == SetMmeShadowRamControlMode.MethodTrackWithFilter;
|
||||
}
|
||||
|
||||
public static bool IsPassthrough(this SetMmeShadowRamControlMode mode)
|
||||
{
|
||||
return mode == SetMmeShadowRamControlMode.MethodPassthrough;
|
||||
}
|
||||
|
||||
public static bool IsReplay(this SetMmeShadowRamControlMode mode)
|
||||
{
|
||||
return mode == SetMmeShadowRamControlMode.MethodReplay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
class StateUpdater
|
||||
{
|
||||
public const int ShaderStateIndex = 26;
|
||||
public const int RtColorMaskIndex = 14;
|
||||
public const int RasterizerStateIndex = 15;
|
||||
public const int ScissorStateIndex = 16;
|
||||
public const int VertexBufferStateIndex = 0;
|
||||
public const int BlendStateIndex = 2;
|
||||
public const int IndexBufferStateIndex = 23;
|
||||
public const int PrimitiveRestartStateIndex = 12;
|
||||
public const int RenderTargetStateIndex = 27;
|
||||
|
@ -1,12 +1,15 @@
|
||||
using Ryujinx.Graphics.Device;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.Device;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
||||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
@ -26,6 +29,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
private readonly ConstantBufferUpdater _cbUpdater;
|
||||
private readonly StateUpdater _stateUpdater;
|
||||
|
||||
private SetMmeShadowRamControlMode ShadowMode => _state.State.SetMmeShadowRamControlMode;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the 3D engine class.
|
||||
/// </summary>
|
||||
@ -228,6 +233,206 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
_cbUpdater.Update(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if two 32 byte structs are equal.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the 32-byte struct</typeparam>
|
||||
/// <param name="lhs">First struct</param>
|
||||
/// <param name="rhs">Second struct</param>
|
||||
/// <returns>True if equal, false otherwise</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool UnsafeEquals32Byte<T>(ref T lhs, ref T rhs) where T : unmanaged
|
||||
{
|
||||
if (Vector256.IsHardwareAccelerated)
|
||||
{
|
||||
return Vector256.EqualsAll(
|
||||
Unsafe.As<T, Vector256<uint>>(ref lhs),
|
||||
Unsafe.As<T, Vector256<uint>>(ref rhs)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref var lhsVec = ref Unsafe.As<T, Vector128<uint>>(ref lhs);
|
||||
ref var rhsVec = ref Unsafe.As<T, Vector128<uint>>(ref rhs);
|
||||
|
||||
return Vector128.EqualsAll(lhsVec, rhsVec) &&
|
||||
Vector128.EqualsAll(Unsafe.Add(ref lhsVec, 1), Unsafe.Add(ref rhsVec, 1));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates blend enable. Respects current shadow mode.
|
||||
/// </summary>
|
||||
/// <param name="masks">Blend enable</param>
|
||||
public void UpdateBlendEnable(ref Array8<Boolean32> enable)
|
||||
{
|
||||
var shadow = ShadowMode;
|
||||
ref var state = ref _state.State.BlendEnable;
|
||||
|
||||
if (shadow.IsReplay())
|
||||
{
|
||||
enable = _state.ShadowState.BlendEnable;
|
||||
}
|
||||
|
||||
if (!UnsafeEquals32Byte(ref enable, ref state))
|
||||
{
|
||||
state = enable;
|
||||
|
||||
_stateUpdater.ForceDirty(StateUpdater.BlendStateIndex);
|
||||
}
|
||||
|
||||
if (shadow.IsTrack())
|
||||
{
|
||||
_state.ShadowState.BlendEnable = enable;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates color masks. Respects current shadow mode.
|
||||
/// </summary>
|
||||
/// <param name="masks">Color masks</param>
|
||||
public void UpdateColorMasks(ref Array8<RtColorMask> masks)
|
||||
{
|
||||
var shadow = ShadowMode;
|
||||
ref var state = ref _state.State.RtColorMask;
|
||||
|
||||
if (shadow.IsReplay())
|
||||
{
|
||||
masks = _state.ShadowState.RtColorMask;
|
||||
}
|
||||
|
||||
if (!UnsafeEquals32Byte(ref masks, ref state))
|
||||
{
|
||||
state = masks;
|
||||
|
||||
_stateUpdater.ForceDirty(StateUpdater.RtColorMaskIndex);
|
||||
}
|
||||
|
||||
if (shadow.IsTrack())
|
||||
{
|
||||
_state.ShadowState.RtColorMask = masks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates index buffer state for an indexed draw. Respects current shadow mode.
|
||||
/// </summary>
|
||||
/// <param name="addrHigh">High part of the address</param>
|
||||
/// <param name="addrLow">Low part of the address</param>
|
||||
/// <param name="type">Type of the binding</param>
|
||||
public void UpdateIndexBuffer(uint addrHigh, uint addrLow, IndexType type)
|
||||
{
|
||||
var shadow = ShadowMode;
|
||||
ref var state = ref _state.State.IndexBufferState;
|
||||
|
||||
if (shadow.IsReplay())
|
||||
{
|
||||
ref var shadowState = ref _state.ShadowState.IndexBufferState;
|
||||
addrHigh = shadowState.Address.High;
|
||||
addrLow = shadowState.Address.Low;
|
||||
type = shadowState.Type;
|
||||
}
|
||||
|
||||
if (state.Address.High != addrHigh || state.Address.Low != addrLow || state.Type != type)
|
||||
{
|
||||
state.Address.High = addrHigh;
|
||||
state.Address.Low = addrLow;
|
||||
state.Type = type;
|
||||
|
||||
_stateUpdater.ForceDirty(StateUpdater.IndexBufferStateIndex);
|
||||
}
|
||||
|
||||
if (shadow.IsTrack())
|
||||
{
|
||||
ref var shadowState = ref _state.ShadowState.IndexBufferState;
|
||||
shadowState.Address.High = addrHigh;
|
||||
shadowState.Address.Low = addrLow;
|
||||
shadowState.Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates uniform buffer state for update or bind. Respects current shadow mode.
|
||||
/// </summary>
|
||||
/// <param name="size">Size of the binding</param>
|
||||
/// <param name="addrHigh">High part of the addrsss</param>
|
||||
/// <param name="addrLow">Low part of the address</param>
|
||||
public void UpdateUniformBufferState(int size, uint addrHigh, uint addrLow)
|
||||
{
|
||||
var shadow = ShadowMode;
|
||||
ref var state = ref _state.State.UniformBufferState;
|
||||
|
||||
if (shadow.IsReplay())
|
||||
{
|
||||
ref var shadowState = ref _state.ShadowState.UniformBufferState;
|
||||
size = shadowState.Size;
|
||||
addrHigh = shadowState.Address.High;
|
||||
addrLow = shadowState.Address.Low;
|
||||
}
|
||||
|
||||
state.Size = size;
|
||||
state.Address.High = addrHigh;
|
||||
state.Address.Low = addrLow;
|
||||
|
||||
if (shadow.IsTrack())
|
||||
{
|
||||
ref var shadowState = ref _state.ShadowState.UniformBufferState;
|
||||
shadowState.Size = size;
|
||||
shadowState.Address.High = addrHigh;
|
||||
shadowState.Address.Low = addrLow;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a shader offset. Respects current shadow mode.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the shader to update</param>
|
||||
/// <param name="offset">Offset to update with</param>
|
||||
public void SetShaderOffset(int index, uint offset)
|
||||
{
|
||||
var shadow = ShadowMode;
|
||||
ref var shaderState = ref _state.State.ShaderState[index];
|
||||
|
||||
if (shadow.IsReplay())
|
||||
{
|
||||
offset = _state.ShadowState.ShaderState[index].Offset;
|
||||
}
|
||||
|
||||
if (shaderState.Offset != offset)
|
||||
{
|
||||
shaderState.Offset = offset;
|
||||
|
||||
_stateUpdater.ForceDirty(StateUpdater.ShaderStateIndex);
|
||||
}
|
||||
|
||||
if (shadow.IsTrack())
|
||||
{
|
||||
_state.ShadowState.ShaderState[index].Offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates uniform buffer state for update. Respects current shadow mode.
|
||||
/// </summary>
|
||||
/// <param name="ubState">Uniform buffer state</param>
|
||||
public void UpdateUniformBufferState(UniformBufferState ubState)
|
||||
{
|
||||
var shadow = ShadowMode;
|
||||
ref var state = ref _state.State.UniformBufferState;
|
||||
|
||||
if (shadow.IsReplay())
|
||||
{
|
||||
ubState = _state.ShadowState.UniformBufferState;
|
||||
}
|
||||
|
||||
state = ubState;
|
||||
|
||||
if (shadow.IsTrack())
|
||||
{
|
||||
_state.ShadowState.UniformBufferState = ubState;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Launches the Inline-to-Memory DMA copy operation.
|
||||
/// </summary>
|
||||
|
@ -590,9 +590,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// </summary>
|
||||
struct RtColorMask
|
||||
{
|
||||
#pragma warning disable CS0649 // Field is never assigned to
|
||||
public uint Packed;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
public RtColorMask(uint packed)
|
||||
{
|
||||
Packed = packed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks red channel enable.
|
||||
|
@ -5,9 +5,12 @@
|
||||
/// </summary>
|
||||
readonly struct Boolean32
|
||||
{
|
||||
#pragma warning disable CS0649 // Field is never assigned to
|
||||
private readonly uint _value;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
public Boolean32(uint value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public static implicit operator bool(Boolean32 value)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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 = 5682;
|
||||
private const uint CodeGenVersion = 5791;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -92,14 +92,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
private static string GetIndentation(int level)
|
||||
{
|
||||
string indentation = string.Empty;
|
||||
StringBuilder indentationBuilder = new();
|
||||
|
||||
for (int index = 0; index < level; index++)
|
||||
{
|
||||
indentation += Tab;
|
||||
indentationBuilder.Append(Tab);
|
||||
}
|
||||
|
||||
return indentation;
|
||||
return indentationBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenBallot;
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenCall;
|
||||
@ -67,11 +68,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
|
||||
int arity = (int)(info.Type & InstType.ArityMask);
|
||||
|
||||
string args = string.Empty;
|
||||
StringBuilder builder = new();
|
||||
|
||||
if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory))
|
||||
{
|
||||
args = GenerateLoadOrStore(context, operation, isStore: false);
|
||||
builder.Append(GenerateLoadOrStore(context, operation, isStore: false));
|
||||
|
||||
AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32
|
||||
? AggregateType.S32
|
||||
@ -79,7 +80,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
|
||||
for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++)
|
||||
{
|
||||
args += ", " + GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||
builder.Append($", {GetSoureExpr(context, operation.GetSource(argIndex), dstType)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -88,16 +89,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
{
|
||||
if (argIndex != 0)
|
||||
{
|
||||
args += ", ";
|
||||
builder.Append(", ");
|
||||
}
|
||||
|
||||
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
||||
|
||||
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||
builder.Append(GetSoureExpr(context, operation.GetSource(argIndex), dstType));
|
||||
}
|
||||
}
|
||||
|
||||
return info.OpName + '(' + args + ')';
|
||||
return $"{info.OpName}({builder})";
|
||||
}
|
||||
else if ((info.Type & InstType.Op) != 0)
|
||||
{
|
||||
@ -184,8 +185,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
case Instruction.TextureSample:
|
||||
return TextureSample(context, operation);
|
||||
|
||||
case Instruction.TextureSize:
|
||||
return TextureSize(context, operation);
|
||||
case Instruction.TextureQuerySamples:
|
||||
return TextureQuerySamples(context, operation);
|
||||
|
||||
case Instruction.TextureQuerySize:
|
||||
return TextureQuerySize(context, operation);
|
||||
|
||||
case Instruction.UnpackDouble2x32:
|
||||
return UnpackDouble2x32(context, operation);
|
||||
|
@ -118,7 +118,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
Add(Instruction.Subtract, InstType.OpBinary, "-", 2);
|
||||
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
||||
Add(Instruction.TextureSample, InstType.Special);
|
||||
Add(Instruction.TextureSize, InstType.Special);
|
||||
Add(Instruction.TextureQuerySamples, InstType.Special);
|
||||
Add(Instruction.TextureQuerySize, InstType.Special);
|
||||
Add(Instruction.Truncate, InstType.CallUnary, "trunc");
|
||||
Add(Instruction.UnpackDouble2x32, InstType.Special);
|
||||
Add(Instruction.UnpackHalf2x16, InstType.Special);
|
||||
|
@ -517,7 +517,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
return texCall;
|
||||
}
|
||||
|
||||
public static string TextureSize(CodeGenContext context, AstOperation operation)
|
||||
public static string TextureQuerySamples(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
|
||||
// TODO: Bindless texture support. For now we just return 0.
|
||||
if (isBindless)
|
||||
{
|
||||
return NumberFormatter.FormatInt(0);
|
||||
}
|
||||
|
||||
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||
|
||||
string indexExpr = null;
|
||||
|
||||
if (isIndexed)
|
||||
{
|
||||
indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
|
||||
}
|
||||
|
||||
string samplerName = GetSamplerName(context.Properties, texOp, indexExpr);
|
||||
|
||||
return $"textureSamples({samplerName})";
|
||||
}
|
||||
|
||||
public static string TextureQuerySize(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
@ -753,17 +779,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
|
||||
private static string GetMaskMultiDest(int mask)
|
||||
{
|
||||
string swizzle = ".";
|
||||
StringBuilder swizzleBuilder = new();
|
||||
swizzleBuilder.Append('.');
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if ((mask & (1 << i)) != 0)
|
||||
{
|
||||
swizzle += "xyzw"[i];
|
||||
swizzleBuilder.Append("xyzw"[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return swizzle;
|
||||
return swizzleBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
||||
public StructuredFunction CurrentFunction { get; set; }
|
||||
private readonly Dictionary<AstOperand, Instruction> _locals = new();
|
||||
private readonly Dictionary<int, Instruction[]> _localForArgs = new();
|
||||
private readonly Dictionary<int, Instruction> _funcArgs = new();
|
||||
private readonly Dictionary<int, (StructuredFunction, Instruction)> _functions = new();
|
||||
|
||||
@ -112,7 +111,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
IsMainFunction = isMainFunction;
|
||||
MayHaveReturned = false;
|
||||
_locals.Clear();
|
||||
_localForArgs.Clear();
|
||||
_funcArgs.Clear();
|
||||
}
|
||||
|
||||
@ -169,11 +167,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
_locals.Add(local, spvLocal);
|
||||
}
|
||||
|
||||
public void DeclareLocalForArgs(int funcIndex, Instruction[] spvLocals)
|
||||
{
|
||||
_localForArgs.Add(funcIndex, spvLocals);
|
||||
}
|
||||
|
||||
public void DeclareArgument(int argIndex, Instruction spvLocal)
|
||||
{
|
||||
_funcArgs.Add(argIndex, spvLocal);
|
||||
@ -278,11 +271,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
return _locals[local];
|
||||
}
|
||||
|
||||
public Instruction[] GetLocalForArgsPointers(int funcIndex)
|
||||
{
|
||||
return _localForArgs[funcIndex];
|
||||
}
|
||||
|
||||
public Instruction GetArgumentPointer(AstOperand funcArg)
|
||||
{
|
||||
return _funcArgs[funcArg.Value];
|
||||
|
@ -41,28 +41,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions)
|
||||
{
|
||||
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
||||
{
|
||||
StructuredFunction function = functions[funcIndex];
|
||||
SpvInstruction[] locals = new SpvInstruction[function.InArguments.Length];
|
||||
|
||||
for (int i = 0; i < function.InArguments.Length; i++)
|
||||
{
|
||||
var type = function.GetArgumentType(i);
|
||||
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
|
||||
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
|
||||
|
||||
context.AddLocalVariable(spvLocal);
|
||||
|
||||
locals[i] = spvLocal;
|
||||
}
|
||||
|
||||
context.DeclareLocalForArgs(funcIndex, locals);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
DeclareConstantBuffers(context, context.Properties.ConstantBuffers.Values);
|
||||
|
@ -134,7 +134,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
Add(Instruction.Subtract, GenerateSubtract);
|
||||
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
||||
Add(Instruction.TextureSample, GenerateTextureSample);
|
||||
Add(Instruction.TextureSize, GenerateTextureSize);
|
||||
Add(Instruction.TextureQuerySamples, GenerateTextureQuerySamples);
|
||||
Add(Instruction.TextureQuerySize, GenerateTextureQuerySize);
|
||||
Add(Instruction.Truncate, GenerateTruncate);
|
||||
Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32);
|
||||
Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16);
|
||||
@ -310,26 +311,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
var (function, spvFunc) = context.GetFunction(funcId.Value);
|
||||
|
||||
var args = new SpvInstruction[operation.SourcesCount - 1];
|
||||
var spvLocals = context.GetLocalForArgsPointers(funcId.Value);
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
var operand = operation.GetSource(i + 1);
|
||||
|
||||
if (i >= function.InArguments.Length)
|
||||
{
|
||||
args[i] = context.GetLocalPointer((AstOperand)operand);
|
||||
}
|
||||
else
|
||||
{
|
||||
var type = function.GetArgumentType(i);
|
||||
var value = context.Get(type, operand);
|
||||
var spvLocal = spvLocals[i];
|
||||
|
||||
context.Store(spvLocal, value);
|
||||
|
||||
args[i] = spvLocal;
|
||||
}
|
||||
AstOperand local = (AstOperand)operand;
|
||||
Debug.Assert(local.Type == OperandType.LocalVariable);
|
||||
args[i] = context.GetLocalPointer(local);
|
||||
}
|
||||
|
||||
var retType = function.ReturnType;
|
||||
@ -1492,7 +1481,36 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
return new OperationResult(swizzledResultType, result);
|
||||
}
|
||||
|
||||
private static OperationResult GenerateTextureSize(CodeGenContext context, AstOperation operation)
|
||||
private static OperationResult GenerateTextureQuerySamples(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
|
||||
// TODO: Bindless texture support. For now we just return 0.
|
||||
if (isBindless)
|
||||
{
|
||||
return new OperationResult(AggregateType.S32, context.Constant(context.TypeS32(), 0));
|
||||
}
|
||||
|
||||
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||
|
||||
if (isIndexed)
|
||||
{
|
||||
context.GetS32(texOp.GetSource(0));
|
||||
}
|
||||
|
||||
(var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
|
||||
|
||||
var image = context.Load(sampledImageType, sampledImageVariable);
|
||||
image = context.Image(imageType, image);
|
||||
|
||||
SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image);
|
||||
|
||||
return new OperationResult(AggregateType.S32, result);
|
||||
}
|
||||
|
||||
private static OperationResult GenerateTextureQuerySize(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
|
@ -161,7 +161,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
context.EnterBlock(function.MainBlock);
|
||||
|
||||
Declarations.DeclareLocals(context, function);
|
||||
Declarations.DeclareLocalForArgs(context, info.Functions);
|
||||
|
||||
Generate(context, function.MainBlock);
|
||||
|
||||
|
@ -339,6 +339,15 @@ namespace Ryujinx.Graphics.Shader
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host GPU texture gather with multiple offsets support.
|
||||
/// </summary>
|
||||
/// <returns>True if the GPU and driver supports texture gather offsets, false otherwise</returns>
|
||||
bool QueryHostSupportsTextureGatherOffsets()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host GPU texture shadow LOD support.
|
||||
/// </summary>
|
||||
|
@ -1094,7 +1094,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
|
||||
if (isBindless)
|
||||
{
|
||||
type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
|
||||
if (query == TexQuery.TexHeaderTextureType)
|
||||
{
|
||||
type = SamplerType.Texture2D | SamplerType.Multisample;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1102,31 +1109,69 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
}
|
||||
|
||||
TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
|
||||
int binding;
|
||||
|
||||
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.TextureSize,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
flags,
|
||||
TextureOperation.DefaultCbufSlot,
|
||||
imm);
|
||||
|
||||
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||
switch (query)
|
||||
{
|
||||
if ((compMask & 1) != 0)
|
||||
{
|
||||
Operand d = GetDest();
|
||||
case TexQuery.TexHeaderDimension:
|
||||
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.TextureQuerySize,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
flags,
|
||||
TextureOperation.DefaultCbufSlot,
|
||||
imm);
|
||||
|
||||
if (d == null)
|
||||
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||
{
|
||||
break;
|
||||
if ((compMask & 1) != 0)
|
||||
{
|
||||
Operand d = GetDest();
|
||||
|
||||
if (d == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
context.Copy(d, context.TextureQuerySize(type, flags, binding, compIndex, sources));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// TODO: Validate and use query parameter.
|
||||
Operand res = context.TextureSize(type, flags, binding, compIndex, sources);
|
||||
case TexQuery.TexHeaderTextureType:
|
||||
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.TextureQuerySamples,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
flags,
|
||||
TextureOperation.DefaultCbufSlot,
|
||||
imm);
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
if ((componentMask & 4) != 0)
|
||||
{
|
||||
// Skip first 2 components if necessary.
|
||||
if ((componentMask & 1) != 0)
|
||||
{
|
||||
GetDest();
|
||||
}
|
||||
|
||||
if ((componentMask & 2) != 0)
|
||||
{
|
||||
GetDest();
|
||||
}
|
||||
|
||||
Operand d = GetDest();
|
||||
|
||||
if (d != null)
|
||||
{
|
||||
context.Copy(d, context.TextureQuerySamples(type, flags, binding, sources));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
context.TranslatorContext.GpuAccessor.Log($"Invalid or unsupported query type \"{query}\".");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||
{
|
||||
[Flags]
|
||||
[SuppressMessage("Design", "CA1069: Enums values should not be duplicated")]
|
||||
enum Instruction
|
||||
{
|
||||
Absolute = 1,
|
||||
@ -118,7 +116,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||
Subtract,
|
||||
SwizzleAdd,
|
||||
TextureSample,
|
||||
TextureSize,
|
||||
TextureQuerySamples,
|
||||
TextureQuerySize,
|
||||
Truncate,
|
||||
UnpackDouble2x32,
|
||||
UnpackHalf2x16,
|
||||
@ -160,7 +159,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||
public static bool IsTextureQuery(this Instruction inst)
|
||||
{
|
||||
inst &= Instruction.Mask;
|
||||
return inst == Instruction.Lod || inst == Instruction.TextureSize;
|
||||
return inst == Instruction.Lod || inst == Instruction.TextureQuerySamples || inst == Instruction.TextureQuerySize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +124,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
|
||||
Add(Instruction.TextureSample, AggregateType.FP32);
|
||||
Add(Instruction.TextureSize, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
||||
Add(Instruction.TextureQuerySamples, AggregateType.S32, AggregateType.S32);
|
||||
Add(Instruction.TextureQuerySize, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
||||
Add(Instruction.Truncate, AggregateType.Scalar, AggregateType.Scalar);
|
||||
Add(Instruction.UnpackDouble2x32, AggregateType.U32, AggregateType.FP64);
|
||||
Add(Instruction.UnpackHalf2x16, AggregateType.FP32, AggregateType.U32);
|
||||
|
@ -2,17 +2,22 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
{
|
||||
static class StructuredProgram
|
||||
{
|
||||
// TODO: Eventually it should be possible to specify the parameter types for the function instead of using S32 for everything.
|
||||
private const AggregateType FuncParameterType = AggregateType.S32;
|
||||
|
||||
public static StructuredProgramInfo MakeStructuredProgram(
|
||||
IReadOnlyList<Function> functions,
|
||||
AttributeUsage attributeUsage,
|
||||
ShaderDefinitions definitions,
|
||||
ResourceManager resourceManager,
|
||||
TargetLanguage targetLanguage,
|
||||
bool debugMode)
|
||||
{
|
||||
StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, debugMode);
|
||||
@ -23,19 +28,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
|
||||
BasicBlock[] blocks = function.Blocks;
|
||||
|
||||
AggregateType returnType = function.ReturnsValue ? AggregateType.S32 : AggregateType.Void;
|
||||
AggregateType returnType = function.ReturnsValue ? FuncParameterType : AggregateType.Void;
|
||||
|
||||
AggregateType[] inArguments = new AggregateType[function.InArgumentsCount];
|
||||
AggregateType[] outArguments = new AggregateType[function.OutArgumentsCount];
|
||||
|
||||
for (int i = 0; i < inArguments.Length; i++)
|
||||
{
|
||||
inArguments[i] = AggregateType.S32;
|
||||
inArguments[i] = FuncParameterType;
|
||||
}
|
||||
|
||||
for (int i = 0; i < outArguments.Length; i++)
|
||||
{
|
||||
outArguments[i] = AggregateType.S32;
|
||||
outArguments[i] = FuncParameterType;
|
||||
}
|
||||
|
||||
context.EnterFunction(blocks.Length, function.Name, returnType, inArguments, outArguments);
|
||||
@ -58,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
}
|
||||
else
|
||||
{
|
||||
AddOperation(context, operation);
|
||||
AddOperation(context, operation, targetLanguage, functions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
return context.Info;
|
||||
}
|
||||
|
||||
private static void AddOperation(StructuredProgramContext context, Operation operation)
|
||||
private static void AddOperation(StructuredProgramContext context, Operation operation, TargetLanguage targetLanguage, IReadOnlyList<Function> functions)
|
||||
{
|
||||
Instruction inst = operation.Inst;
|
||||
StorageKind storageKind = operation.StorageKind;
|
||||
@ -114,9 +119,43 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
|
||||
IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount];
|
||||
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
if (inst == Instruction.Call && targetLanguage == TargetLanguage.Spirv)
|
||||
{
|
||||
sources[index] = context.GetOperandOrCbLoad(operation.GetSource(index));
|
||||
// SPIR-V requires that all function parameters are copied to a local variable before the call
|
||||
// (or at least that's what the Khronos compiler does).
|
||||
|
||||
// First one is the function index.
|
||||
Operand funcIndexOperand = operation.GetSource(0);
|
||||
Debug.Assert(funcIndexOperand.Type == OperandType.Constant);
|
||||
int funcIndex = funcIndexOperand.Value;
|
||||
|
||||
sources[0] = new AstOperand(OperandType.Constant, funcIndex);
|
||||
|
||||
int inArgsCount = functions[funcIndex].InArgumentsCount;
|
||||
|
||||
// Remaining ones are parameters, copy them to a temp local variable.
|
||||
for (int index = 1; index < operation.SourcesCount; index++)
|
||||
{
|
||||
IAstNode source = context.GetOperandOrCbLoad(operation.GetSource(index));
|
||||
|
||||
if (index - 1 < inArgsCount)
|
||||
{
|
||||
AstOperand argTemp = context.NewTemp(FuncParameterType);
|
||||
context.AddNode(new AstAssignment(argTemp, source));
|
||||
sources[index] = argTemp;
|
||||
}
|
||||
else
|
||||
{
|
||||
sources[index] = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
sources[index] = context.GetOperandOrCbLoad(operation.GetSource(index));
|
||||
}
|
||||
}
|
||||
|
||||
for (int index = 0; index < outDestsCount; index++)
|
||||
|
@ -897,7 +897,21 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources));
|
||||
}
|
||||
|
||||
public static Operand TextureSize(
|
||||
public static Operand TextureQuerySamples(
|
||||
this EmitterContext context,
|
||||
SamplerType type,
|
||||
TextureFlags flags,
|
||||
int binding,
|
||||
Operand[] sources)
|
||||
{
|
||||
Operand dest = Local();
|
||||
|
||||
context.Add(new TextureOperation(Instruction.TextureQuerySamples, type, TextureFormat.Unknown, flags, binding, 0, new[] { dest }, sources));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
public static Operand TextureQuerySize(
|
||||
this EmitterContext context,
|
||||
SamplerType type,
|
||||
TextureFlags flags,
|
||||
@ -907,7 +921,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
Operand dest = Local();
|
||||
|
||||
context.Add(new TextureOperation(Instruction.TextureSize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
|
||||
context.Add(new TextureOperation(Instruction.TextureQuerySize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
@ -27,9 +27,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
continue;
|
||||
}
|
||||
|
||||
if (texOp.Inst == Instruction.Lod ||
|
||||
texOp.Inst == Instruction.TextureSample ||
|
||||
texOp.Inst == Instruction.TextureSize)
|
||||
if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
|
||||
{
|
||||
Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block);
|
||||
|
||||
@ -40,7 +38,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
// as long bindless elimination is successful and we know where the texture descriptor is located.
|
||||
bool rewriteSamplerType =
|
||||
texOp.Type == SamplerType.TextureBuffer ||
|
||||
texOp.Inst == Instruction.TextureSize;
|
||||
texOp.Inst == Instruction.TextureQuerySamples ||
|
||||
texOp.Inst == Instruction.TextureQuerySize;
|
||||
|
||||
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
@ -785,30 +786,31 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
|
||||
private static string GetFunctionName(Operation baseOp, bool isMultiTarget, IReadOnlyList<uint> targetCbs)
|
||||
{
|
||||
string name = baseOp.Inst.ToString();
|
||||
StringBuilder nameBuilder = new();
|
||||
nameBuilder.Append(baseOp.Inst.ToString());
|
||||
|
||||
name += baseOp.StorageKind switch
|
||||
nameBuilder.Append(baseOp.StorageKind switch
|
||||
{
|
||||
StorageKind.GlobalMemoryS8 => "S8",
|
||||
StorageKind.GlobalMemoryS16 => "S16",
|
||||
StorageKind.GlobalMemoryU8 => "U8",
|
||||
StorageKind.GlobalMemoryU16 => "U16",
|
||||
_ => string.Empty,
|
||||
};
|
||||
});
|
||||
|
||||
if (isMultiTarget)
|
||||
{
|
||||
name += "Multi";
|
||||
nameBuilder.Append("Multi");
|
||||
}
|
||||
|
||||
foreach (uint targetCb in targetCbs)
|
||||
{
|
||||
(int sbCbSlot, int sbCbOffset) = UnpackCbSlotAndOffset(targetCb);
|
||||
|
||||
name += $"_c{sbCbSlot}o{sbCbOffset}";
|
||||
nameBuilder.Append($"_c{sbCbSlot}o{sbCbOffset}");
|
||||
}
|
||||
|
||||
return name;
|
||||
return nameBuilder.ToString();
|
||||
}
|
||||
|
||||
private static bool TryGenerateStorageOp(
|
||||
|
@ -232,8 +232,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
inst &= Instruction.Mask;
|
||||
bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
||||
bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
||||
bool accurateType = inst != Instruction.Lod && inst != Instruction.TextureSize;
|
||||
bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize;
|
||||
bool accurateType = !inst.IsTextureQuery();
|
||||
bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureQuerySize;
|
||||
bool coherent = flags.HasFlag(TextureFlags.Coherent);
|
||||
|
||||
if (!isImage)
|
||||
|
@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
{
|
||||
node = InsertCoordNormalization(context.Hfm, node, context.ResourceManager, context.GpuAccessor, context.Stage);
|
||||
node = InsertCoordGatherBias(node, context.ResourceManager, context.GpuAccessor);
|
||||
node = InsertConstOffsets(node, context.ResourceManager, context.GpuAccessor);
|
||||
node = InsertConstOffsets(node, context.GpuAccessor, context.Stage);
|
||||
|
||||
if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat())
|
||||
{
|
||||
@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||
|
||||
if (texOp.Inst == Instruction.TextureSize &&
|
||||
if (texOp.Inst == Instruction.TextureQuerySize &&
|
||||
texOp.Index < 2 &&
|
||||
!isBindless &&
|
||||
!isIndexed &&
|
||||
@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
}
|
||||
|
||||
LinkedListNode<INode> textureSizeNode = node.List.AddBefore(node, new TextureOperation(
|
||||
Instruction.TextureSize,
|
||||
Instruction.TextureQuerySize,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
@ -259,7 +259,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
}
|
||||
|
||||
node.List.AddBefore(node, new TextureOperation(
|
||||
Instruction.TextureSize,
|
||||
Instruction.TextureQuerySize,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
@ -287,7 +287,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
return node;
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor)
|
||||
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, IGpuAccessor gpuAccessor, ShaderStage stage)
|
||||
{
|
||||
// Non-constant texture offsets are not allowed (according to the spec),
|
||||
// however some GPUs does support that.
|
||||
@ -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<INode> 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);
|
||||
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);
|
||||
Operand[] texSizes = isGather
|
||||
? InsertTextureBaseSize(node, texOp, bindlessHandle, coordsCount)
|
||||
: InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage);
|
||||
|
||||
for (int index = 0; index < coordsCount; index++)
|
||||
{
|
||||
@ -549,26 +556,73 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
return node;
|
||||
}
|
||||
|
||||
private static Operand[] InsertTextureLod(
|
||||
private static Operand[] InsertTextureBaseSize(
|
||||
LinkedListNode<INode> node,
|
||||
TextureOperation texOp,
|
||||
Operand[] lodSources,
|
||||
Operand bindlessHandle,
|
||||
int coordsCount)
|
||||
{
|
||||
Operand[] texSizes = new Operand[coordsCount];
|
||||
|
||||
Operand lod = Local();
|
||||
for (int index = 0; index < coordsCount; index++)
|
||||
{
|
||||
texSizes[index] = Local();
|
||||
|
||||
node.List.AddBefore(node, new TextureOperation(
|
||||
Instruction.Lod,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Binding,
|
||||
0,
|
||||
new[] { lod },
|
||||
lodSources));
|
||||
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<INode> node,
|
||||
TextureOperation texOp,
|
||||
Operand[] lodSources,
|
||||
Operand bindlessHandle,
|
||||
int coordsCount,
|
||||
ShaderStage stage)
|
||||
{
|
||||
Operand[] texSizes = new Operand[coordsCount];
|
||||
|
||||
Operand lod;
|
||||
|
||||
if (stage == ShaderStage.Fragment)
|
||||
{
|
||||
lod = Local();
|
||||
|
||||
node.List.AddBefore(node, new TextureOperation(
|
||||
Instruction.Lod,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Binding,
|
||||
0,
|
||||
new[] { lod },
|
||||
lodSources));
|
||||
}
|
||||
else
|
||||
{
|
||||
lod = Const(0);
|
||||
}
|
||||
|
||||
for (int index = 0; index < coordsCount; index++)
|
||||
{
|
||||
@ -586,7 +640,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
}
|
||||
|
||||
node.List.AddBefore(node, new TextureOperation(
|
||||
Instruction.TextureSize,
|
||||
Instruction.TextureQuerySize,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
|
@ -329,6 +329,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
attributeUsage,
|
||||
definitions,
|
||||
resourceManager,
|
||||
Options.TargetLanguage,
|
||||
Options.Flags.HasFlag(TranslationFlags.DebugMode));
|
||||
|
||||
int geometryVerticesPerPrimitive = Definitions.OutputTopology switch
|
||||
|
@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private int _flushTemp;
|
||||
private int _lastFlushWrite = -1;
|
||||
|
||||
private readonly ReaderWriterLock _flushLock;
|
||||
private readonly ReaderWriterLockSlim _flushLock;
|
||||
private FenceHolder _flushFence;
|
||||
private int _flushWaiting;
|
||||
|
||||
@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_currentType = currentType;
|
||||
DesiredType = currentType;
|
||||
|
||||
_flushLock = new ReaderWriterLock();
|
||||
_flushLock = new ReaderWriterLockSlim();
|
||||
_useMirrors = gd.IsTBDR;
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_currentType = currentType;
|
||||
DesiredType = currentType;
|
||||
|
||||
_flushLock = new ReaderWriterLock();
|
||||
_flushLock = new ReaderWriterLockSlim();
|
||||
}
|
||||
|
||||
public bool TryBackingSwap(ref CommandBufferScoped? cbs)
|
||||
@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
// Only swap if the buffer is not used in any queued command buffer.
|
||||
bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool);
|
||||
|
||||
if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReaderLockHeld && (_pendingData == null || cbs != null))
|
||||
if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReadLockHeld && (_pendingData == null || cbs != null))
|
||||
{
|
||||
var currentAllocation = _allocationAuto;
|
||||
var currentBuffer = _buffer;
|
||||
@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
ClearMirrors(cbs.Value, 0, Size);
|
||||
}
|
||||
|
||||
_flushLock.AcquireWriterLock(Timeout.Infinite);
|
||||
_flushLock.EnterWriteLock();
|
||||
|
||||
ClearFlushFence();
|
||||
|
||||
@ -185,7 +185,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_gd.PipelineInternal.SwapBuffer(currentBuffer, _buffer);
|
||||
|
||||
_flushLock.ReleaseWriterLock();
|
||||
_flushLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
_swapQueued = false;
|
||||
@ -548,42 +548,44 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private void WaitForFlushFence()
|
||||
{
|
||||
// Assumes the _flushLock is held as reader, returns in same state.
|
||||
if (_flushFence == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If storage has changed, make sure the fence has been reached so that the data is in place.
|
||||
_flushLock.ExitReadLock();
|
||||
_flushLock.EnterWriteLock();
|
||||
|
||||
if (_flushFence != null)
|
||||
{
|
||||
// If storage has changed, make sure the fence has been reached so that the data is in place.
|
||||
var fence = _flushFence;
|
||||
Interlocked.Increment(ref _flushWaiting);
|
||||
|
||||
var cookie = _flushLock.UpgradeToWriterLock(Timeout.Infinite);
|
||||
// Don't wait in the lock.
|
||||
|
||||
if (_flushFence != null)
|
||||
_flushLock.ExitWriteLock();
|
||||
|
||||
fence.Wait();
|
||||
|
||||
_flushLock.EnterWriteLock();
|
||||
|
||||
if (Interlocked.Decrement(ref _flushWaiting) == 0)
|
||||
{
|
||||
var fence = _flushFence;
|
||||
Interlocked.Increment(ref _flushWaiting);
|
||||
|
||||
// Don't wait in the lock.
|
||||
|
||||
var restoreCookie = _flushLock.ReleaseLock();
|
||||
|
||||
fence.Wait();
|
||||
|
||||
_flushLock.RestoreLock(ref restoreCookie);
|
||||
|
||||
if (Interlocked.Decrement(ref _flushWaiting) == 0)
|
||||
{
|
||||
fence.Put();
|
||||
}
|
||||
|
||||
_flushFence = null;
|
||||
fence.Put();
|
||||
}
|
||||
|
||||
_flushLock.DowngradeFromWriterLock(ref cookie);
|
||||
_flushFence = null;
|
||||
}
|
||||
|
||||
// Assumes the _flushLock is held as reader, returns in same state.
|
||||
_flushLock.ExitWriteLock();
|
||||
_flushLock.EnterReadLock();
|
||||
}
|
||||
|
||||
public PinnedSpan<byte> GetData(int offset, int size)
|
||||
{
|
||||
_flushLock.AcquireReaderLock(Timeout.Infinite);
|
||||
_flushLock.EnterReadLock();
|
||||
|
||||
WaitForFlushFence();
|
||||
|
||||
@ -603,7 +605,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
// Need to be careful here, the buffer can't be unmapped while the data is being used.
|
||||
_buffer.IncrementReferenceCount();
|
||||
|
||||
_flushLock.ReleaseReaderLock();
|
||||
_flushLock.ExitReadLock();
|
||||
|
||||
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
|
||||
}
|
||||
@ -621,7 +623,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
result = resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size);
|
||||
}
|
||||
|
||||
_flushLock.ReleaseReaderLock();
|
||||
_flushLock.ExitReadLock();
|
||||
|
||||
// Flush buffer is pinned until the next GetBufferData on the thread, which is fine for current uses.
|
||||
return PinnedSpan<byte>.UnsafeFromSpan(result);
|
||||
@ -1073,11 +1075,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_allocationAuto.Dispose();
|
||||
}
|
||||
|
||||
_flushLock.AcquireWriterLock(Timeout.Infinite);
|
||||
_flushLock.EnterWriteLock();
|
||||
|
||||
ClearFlushFence();
|
||||
|
||||
_flushLock.ReleaseWriterLock();
|
||||
_flushLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -19,6 +19,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace Ryujinx.HLE.FileSystem
|
||||
@ -817,13 +818,13 @@ namespace Ryujinx.HLE.FileSystem
|
||||
|
||||
if (updateNcas.Count > 0)
|
||||
{
|
||||
string extraNcas = string.Empty;
|
||||
StringBuilder extraNcas = new();
|
||||
|
||||
foreach (var entry in updateNcas)
|
||||
{
|
||||
foreach (var (type, path) in entry.Value)
|
||||
{
|
||||
extraNcas += path + Environment.NewLine;
|
||||
extraNcas.AppendLine(path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -954,13 +955,13 @@ namespace Ryujinx.HLE.FileSystem
|
||||
|
||||
if (updateNcas.Count > 0)
|
||||
{
|
||||
string extraNcas = string.Empty;
|
||||
StringBuilder extraNcas = new();
|
||||
|
||||
foreach (var entry in updateNcas)
|
||||
{
|
||||
foreach (var (type, path) in entry.Value)
|
||||
{
|
||||
extraNcas += path + Environment.NewLine;
|
||||
extraNcas.AppendLine(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -436,14 +436,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
uint nameIndex = sym.NameOffset;
|
||||
|
||||
string name = string.Empty;
|
||||
StringBuilder nameBuilder = new();
|
||||
|
||||
for (int chr; (chr = memory.Read<byte>(strTblAddr + nameIndex++)) != 0;)
|
||||
{
|
||||
name += (char)chr;
|
||||
nameBuilder.Append((char)chr);
|
||||
}
|
||||
|
||||
return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
|
||||
return new ElfSymbol(nameBuilder.ToString(), sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
|
||||
}
|
||||
|
||||
private static ElfSymbol GetSymbol32(IVirtualMemoryManager memory, ulong address, ulong strTblAddr)
|
||||
@ -452,14 +452,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
uint nameIndex = sym.NameOffset;
|
||||
|
||||
string name = string.Empty;
|
||||
StringBuilder nameBuilder = new();
|
||||
|
||||
for (int chr; (chr = memory.Read<byte>(strTblAddr + nameIndex++)) != 0;)
|
||||
{
|
||||
name += (char)chr;
|
||||
nameBuilder.Append((char)chr);
|
||||
}
|
||||
|
||||
return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
|
||||
return new ElfSymbol(nameBuilder.ToString(), sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
|
||||
_completionEvent.WritableEvent.Signal();
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(completionEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
@ -187,6 +189,20 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10420)]
|
||||
// nn::friends::CheckBlockedUserListAvailability(nn::account::Uid userId) -> bool
|
||||
public ResultCode CheckBlockedUserListAvailability(ServiceCtx context)
|
||||
{
|
||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
||||
|
||||
// Yes, it is available.
|
||||
context.ResponseData.Write(true);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10600)]
|
||||
// nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid userId)
|
||||
public ResultCode DeclareOpenOnlinePlaySession(ServiceCtx context)
|
||||
|
@ -1,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Ins
|
||||
{
|
||||
[Service("ins:r")]
|
||||
class IReceiverManager : IpcService
|
||||
{
|
||||
public IReceiverManager(ServiceCtx context) { }
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Ins
|
||||
{
|
||||
[Service("ins:s")]
|
||||
class ISenderManager : IpcService
|
||||
{
|
||||
public ISenderManager(ServiceCtx context) { }
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
||||
private const bool IsDevelopment = false;
|
||||
|
||||
private readonly KEvent _stateChangeEvent;
|
||||
private int _stateChangeEventHandle;
|
||||
|
||||
private NetworkState _state;
|
||||
private DisconnectReason _disconnectReason;
|
||||
@ -277,12 +278,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
||||
// AttachStateChangeEvent() -> handle<copy>
|
||||
public ResultCode AttachStateChangeEvent(ServiceCtx context)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out int stateChangeEventHandle) != Result.Success)
|
||||
if (_stateChangeEventHandle == 0 && context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out _stateChangeEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(stateChangeEventHandle);
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangeEventHandle);
|
||||
|
||||
// Returns ResultCode.InvalidArgument if handle is null, doesn't occur in our case since we already throw an Exception.
|
||||
|
||||
@ -964,6 +965,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
||||
SetDisconnectReason(DisconnectReason.None);
|
||||
}
|
||||
|
||||
if (_stateChangeEventHandle != 0)
|
||||
{
|
||||
context.Process.HandleTable.CloseHandle(_stateChangeEventHandle);
|
||||
_stateChangeEventHandle = 0;
|
||||
}
|
||||
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
@ -1021,7 +1028,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
||||
|
||||
SetState(NetworkState.None);
|
||||
|
||||
NetworkClient?.DisconnectAndStop();
|
||||
NetworkClient?.Dispose();
|
||||
NetworkClient = null;
|
||||
|
||||
return ResultCode.Success;
|
||||
@ -1072,7 +1079,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: Service returns differents ResultCode here related to the nifm ResultCode.
|
||||
// NOTE: Service returns different ResultCode here related to the nifm ResultCode.
|
||||
resultCode = ResultCode.DeviceDisabled;
|
||||
_nifmResultCode = resultCode;
|
||||
}
|
||||
@ -1084,14 +1091,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (NetworkClient != null)
|
||||
{
|
||||
_station?.Dispose();
|
||||
_accessPoint?.Dispose();
|
||||
_station?.Dispose();
|
||||
_station = null;
|
||||
|
||||
NetworkClient.DisconnectAndStop();
|
||||
}
|
||||
_accessPoint?.Dispose();
|
||||
_accessPoint = null;
|
||||
|
||||
NetworkClient?.Dispose();
|
||||
NetworkClient = null;
|
||||
}
|
||||
}
|
||||
|
@ -290,7 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
||||
{
|
||||
coreData = new CoreData();
|
||||
|
||||
if (charInfo.IsValid())
|
||||
if (!charInfo.IsValid())
|
||||
{
|
||||
return ResultCode.InvalidCharInfo;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Ryujinx.HLE.HOS.Services.Mii.Types.RandomMiiConstants;
|
||||
@ -10,9 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||
{
|
||||
public const int Size = 0x30;
|
||||
|
||||
private byte _storage;
|
||||
private Array48<byte> _storage;
|
||||
|
||||
public Span<byte> Storage => MemoryMarshal.CreateSpan(ref _storage, Size);
|
||||
public Span<byte> Storage => _storage.AsSpan();
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x18)]
|
||||
public struct ElementInfo
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
@ -10,12 +11,12 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||
public const int CharCount = 10;
|
||||
private const int SizeConst = (CharCount + 1) * 2;
|
||||
|
||||
private byte _storage;
|
||||
private Array22<byte> _storage;
|
||||
|
||||
public static Nickname Default => FromString("no name");
|
||||
public static Nickname Question => FromString("???");
|
||||
|
||||
public Span<byte> Raw => MemoryMarshal.CreateSpan(ref _storage, SizeConst);
|
||||
public Span<byte> Raw => _storage.AsSpan();
|
||||
|
||||
private ReadOnlySpan<ushort> Characters => MemoryMarshal.Cast<byte, ushort>(Raw);
|
||||
|
||||
|
@ -62,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||
|
||||
private ushort CalculateDataCrc()
|
||||
{
|
||||
return Helper.CalculateCrc16(AsSpanWithoutDeviceCrc(), 0, true);
|
||||
return Helper.CalculateCrc16(AsSpanWithoutCrcs(), 0, true);
|
||||
}
|
||||
|
||||
private ushort CalculateDeviceCrc()
|
||||
@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||
|
||||
ushort deviceIdCrc16 = Helper.CalculateCrc16(SpanHelpers.AsByteSpan(ref deviceId), 0, false);
|
||||
|
||||
return Helper.CalculateCrc16(AsSpan(), deviceIdCrc16, true);
|
||||
return Helper.CalculateCrc16(AsSpanWithoutDeviceCrc(), deviceIdCrc16, true);
|
||||
}
|
||||
|
||||
private ReadOnlySpan<byte> AsSpan()
|
||||
@ -84,6 +84,11 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||
return AsSpan()[..(Size - 2)];
|
||||
}
|
||||
|
||||
private ReadOnlySpan<byte> AsSpanWithoutCrcs()
|
||||
{
|
||||
return AsSpan()[..(Size - 4)];
|
||||
}
|
||||
|
||||
public static StoreData BuildDefault(UtilityImpl utilImpl, uint index)
|
||||
{
|
||||
StoreData result = new()
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||
@ -8,9 +10,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||
{
|
||||
public const int Size = 0x60;
|
||||
|
||||
private byte _storage;
|
||||
private Array96<byte> _storage;
|
||||
|
||||
public Span<byte> Storage => MemoryMarshal.CreateSpan(ref _storage, Size);
|
||||
public Span<byte> Storage => _storage.AsSpan();
|
||||
|
||||
// TODO: define all getters/setters
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Ovln
|
||||
{
|
||||
[Service("ovln:rcv")]
|
||||
class IReceiverService : IpcService
|
||||
{
|
||||
public IReceiverService(ServiceCtx context) { }
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Ovln
|
||||
{
|
||||
[Service("ovln:snd")]
|
||||
class ISenderService : IpcService
|
||||
{
|
||||
public ISenderService(ServiceCtx context) { }
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Psc
|
||||
{
|
||||
[Service("psc:c")]
|
||||
class IPmControl : IpcService
|
||||
{
|
||||
public IPmControl(ServiceCtx context) { }
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Psc
|
||||
{
|
||||
[Service("psc:m")]
|
||||
class IPmService : IpcService
|
||||
{
|
||||
public IPmService(ServiceCtx context) { }
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Psc
|
||||
{
|
||||
[Service("psc:l")] // 9.0.0+
|
||||
class IPmUnknown : IpcService
|
||||
{
|
||||
public IPmUnknown(ServiceCtx context) { }
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sm
|
||||
{
|
||||
@ -235,7 +236,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||
|
||||
private static string ReadName(ServiceCtx context)
|
||||
{
|
||||
string name = string.Empty;
|
||||
StringBuilder nameBuilder = new();
|
||||
|
||||
for (int index = 0; index < 8 &&
|
||||
context.RequestData.BaseStream.Position <
|
||||
@ -245,11 +246,11 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||
|
||||
if (chr >= 0x20 && chr < 0x7f)
|
||||
{
|
||||
name += (char)chr;
|
||||
nameBuilder.Append((char)chr);
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
return nameBuilder.ToString();
|
||||
}
|
||||
|
||||
public override void DestroyAtExit()
|
||||
|
@ -1,9 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Srepo
|
||||
{
|
||||
[Service("srepo:a")] // 5.0.0+
|
||||
[Service("srepo:u")] // 5.0.0+
|
||||
class ISrepoService : IpcService
|
||||
{
|
||||
public ISrepoService(ServiceCtx context) { }
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user