Compare commits

..

16 Commits

Author SHA1 Message Date
Evan Husted
66ceecd493 Merge 849fd0199e into 31de0bf8c6 2025-01-26 19:45:50 -06:00
Evan Husted
849fd0199e Merge branch 'master' into xeyes 2025-01-26 17:33:58 -06:00
Evan Husted
57e26114e8 Merge branch 'master' into xeyes 2025-01-25 21:56:40 -06:00
Evan Husted
6a283190b3 Add the controller image back 2025-01-24 22:24:02 -06:00
MutantAura
85547874c8 Consolidate most logic into StickVisualizer. 2025-01-24 20:08:58 -06:00
MutantAura
16ca8e5005 Move most logic into new StickVisualizer class and add keyboard support. 2025-01-24 20:01:36 -06:00
MutantAura
cfa5ad0757 Simplfy clamping and fix bug on window close. 2025-01-24 19:58:17 -06:00
MutantAura
43ece083b2 Move some backing fields to match others.
formatting pt.2
2025-01-24 19:57:11 -06:00
MutantAura
e1f5c501b0 Fix some issues with stick magnitude. 2025-01-24 19:56:21 -06:00
MutantAura
ffe366d953 Cleanup AXAML and hide sticks when only one is present on guest controller. 2025-01-24 19:52:57 -06:00
MutantAura
75c7a29278 style and analyzers 2025-01-24 19:52:52 -06:00
MutantAura
3a0d9c1435 whitespace 2025-01-24 19:52:47 -06:00
MutantAura
93d1476a2a Remove unused grid definitions. 2025-01-24 19:52:39 -06:00
MutantAura
ce13830063 Clean up some magic numbers between code and axaml. 2025-01-24 19:52:24 -06:00
MutantAura
aa3f2824e0 Polish the aesthetic and include deadzone visualization. 2025-01-24 19:51:44 -06:00
MutantAura
d2bb580aea Initial implementation of analog stick visualization. 2025-01-24 19:50:09 -06:00
73 changed files with 793 additions and 715 deletions

View File

@@ -11,7 +11,7 @@
[![Latest release](https://img.shields.io/github/v/release/GreemDev/Ryujinx)](https://github.com/Ryubing/Ryujinx/releases/latest) [![Latest release](https://img.shields.io/github/v/release/GreemDev/Ryujinx)](https://github.com/Ryubing/Ryujinx/releases/latest)
<br> <br>
[![Canary workflow](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml/badge.svg)](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml) [![Canary workflow](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml/badge.svg)](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml)
[![Latest canary release](https://img.shields.io/github/v/release/Ryubing/Canary-Releases?label=canary)](https://github.com/Ryubing/Canary-Releases/releases/latest) [![Latest canary release](https://img.shields.io/github/v/release/GreemDev/Ryujinx-Canary?label=canary)](https://github.com/Ryubing/Ryujinx-Canary/releases/latest)
</td> </td>
</tr> </tr>
</table> </table>
@@ -64,7 +64,7 @@ Canary builds are compiled automatically for each commit on the `master` branch.
While we strive to ensure optimal stability and performance prior to pushing an update, these builds **may be unstable or completely broken**. While we strive to ensure optimal stability and performance prior to pushing an update, these builds **may be unstable or completely broken**.
These canary builds are only recommended for experienced users. These canary builds are only recommended for experienced users.
You can find the latest canary release [here](https://github.com/Ryubing/Canary-Releases/releases/latest). You can find the latest canary release [here](https://github.com/Ryubing/Ryujinx-Canary/releases/latest).
## Documentation ## Documentation

View File

@@ -3,8 +3,6 @@ using ARMeilleure.CodeGen.Linking;
using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Common; using ARMeilleure.Common;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using ARMeilleure.State;
using Humanizer;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
@@ -32,8 +30,8 @@ namespace ARMeilleure.Translation.PTC
{ {
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 7007; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 6998; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0"; private const string ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";
@@ -186,36 +184,6 @@ namespace ARMeilleure.Translation.PTC
InitializeCarriers(); InitializeCarriers();
} }
private bool ContainsBlacklistedFunctions()
{
List<ulong> blacklist = Profiler.GetBlacklistedFunctions();
bool containsBlacklistedFunctions = false;
_infosStream.Seek(0L, SeekOrigin.Begin);
bool foundBadFunction = false;
for (int index = 0; index < GetEntriesCount(); index++)
{
InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
foreach (ulong address in blacklist)
{
if (infoEntry.Address == address)
{
containsBlacklistedFunctions = true;
Logger.Warning?.Print(LogClass.Ptc, "PPTC cache invalidated: Found blacklisted functions in PPTC cache");
foundBadFunction = true;
break;
}
}
if (foundBadFunction)
{
break;
}
}
return containsBlacklistedFunctions;
}
private void PreLoad() private void PreLoad()
{ {
string fileNameActual = $"{CachePathActual}.cache"; string fileNameActual = $"{CachePathActual}.cache";
@@ -564,7 +532,7 @@ namespace ARMeilleure.Translation.PTC
public void LoadTranslations(Translator translator) public void LoadTranslations(Translator translator)
{ {
if (AreCarriersEmpty() || ContainsBlacklistedFunctions()) if (AreCarriersEmpty())
{ {
return; return;
} }
@@ -867,18 +835,10 @@ namespace ARMeilleure.Translation.PTC
while (profiledFuncsToTranslate.TryDequeue(out (ulong address, PtcProfiler.FuncProfile funcProfile) item)) while (profiledFuncsToTranslate.TryDequeue(out (ulong address, PtcProfiler.FuncProfile funcProfile) item))
{ {
ulong address = item.address; ulong address = item.address;
ExecutionMode executionMode = item.funcProfile.Mode;
bool highCq = item.funcProfile.HighCq;
Debug.Assert(Profiler.IsAddressInStaticCodeRange(address)); Debug.Assert(Profiler.IsAddressInStaticCodeRange(address));
TranslatedFunction func = translator.Translate(address, executionMode, highCq); TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
if (func == null)
{
Profiler.UpdateEntry(address, executionMode, true, true);
continue;
}
bool isAddressUnique = translator.Functions.TryAdd(address, func.GuestSize, func); bool isAddressUnique = translator.Functions.TryAdd(address, func.GuestSize, func);
@@ -924,11 +884,8 @@ namespace ARMeilleure.Translation.PTC
sw.Stop(); sw.Stop();
PtcStateChanged?.Invoke(PtcLoadingState.Loaded, _translateCount, _translateTotalCount); PtcStateChanged?.Invoke(PtcLoadingState.Loaded, _translateCount, _translateTotalCount);
Logger.Info?.Print(LogClass.Ptc, Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism} in {sw.Elapsed.TotalSeconds} s");
$"{_translateCount} of {_translateTotalCount} functions translated in {sw.Elapsed.TotalSeconds} seconds " +
$"| {"function".ToQuantity(_translateTotalCount - _translateCount)} blacklisted " +
$"| Thread count: {degreeOfParallelism}");
Thread preSaveThread = new(PreSave) Thread preSaveThread = new(PreSave)
{ {

View File

@@ -24,12 +24,11 @@ namespace ARMeilleure.Translation.PTC
{ {
private const string OuterHeaderMagicString = "Pohd\0\0\0\0"; private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
private const uint InternalVersion = 7007; //! Not to be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 5518; //! Not to be incremented manually for each change to the ARMeilleure project.
private static readonly uint[] _migrateInternalVersions = private static readonly uint[] _migrateInternalVersions =
[ [
1866, 1866
5518,
]; ];
private const int SaveInterval = 30; // Seconds. private const int SaveInterval = 30; // Seconds.
@@ -78,30 +77,20 @@ namespace ARMeilleure.Translation.PTC
private void TimerElapsed(object _, ElapsedEventArgs __) private void TimerElapsed(object _, ElapsedEventArgs __)
=> new Thread(PreSave) { Name = "Ptc.DiskWriter" }.Start(); => new Thread(PreSave) { Name = "Ptc.DiskWriter" }.Start();
public void AddEntry(ulong address, ExecutionMode mode, bool highCq, bool blacklist = false) public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
{ {
if (IsAddressInStaticCodeRange(address)) if (IsAddressInStaticCodeRange(address))
{ {
Debug.Assert(!highCq); Debug.Assert(!highCq);
if (blacklist) lock (_lock)
{ {
lock (_lock) ProfiledFuncs.TryAdd(address, new FuncProfile(mode, highCq: false));
{
ProfiledFuncs[address] = new FuncProfile(mode, highCq: false, true);
}
}
else
{
lock (_lock)
{
ProfiledFuncs.TryAdd(address, new FuncProfile(mode, highCq: false, false));
}
} }
} }
} }
public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq, bool? blacklist = null) public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
{ {
if (IsAddressInStaticCodeRange(address)) if (IsAddressInStaticCodeRange(address))
{ {
@@ -111,7 +100,7 @@ namespace ARMeilleure.Translation.PTC
{ {
Debug.Assert(ProfiledFuncs.ContainsKey(address)); Debug.Assert(ProfiledFuncs.ContainsKey(address));
ProfiledFuncs[address] = new FuncProfile(mode, highCq: true, blacklist ?? ProfiledFuncs[address].Blacklist); ProfiledFuncs[address] = new FuncProfile(mode, highCq: true);
} }
} }
} }
@@ -127,7 +116,7 @@ namespace ARMeilleure.Translation.PTC
foreach (KeyValuePair<ulong, FuncProfile> profiledFunc in ProfiledFuncs) foreach (KeyValuePair<ulong, FuncProfile> profiledFunc in ProfiledFuncs)
{ {
if (!funcs.ContainsKey(profiledFunc.Key) && !profiledFunc.Value.Blacklist) if (!funcs.ContainsKey(profiledFunc.Key))
{ {
profiledFuncsToTranslate.Enqueue((profiledFunc.Key, profiledFunc.Value)); profiledFuncsToTranslate.Enqueue((profiledFunc.Key, profiledFunc.Value));
} }
@@ -142,24 +131,6 @@ namespace ARMeilleure.Translation.PTC
ProfiledFuncs.TrimExcess(); ProfiledFuncs.TrimExcess();
} }
public List<ulong> GetBlacklistedFunctions()
{
List<ulong> funcs = new List<ulong>();
foreach (var profiledFunc in ProfiledFuncs)
{
if (profiledFunc.Value.Blacklist)
{
if (!funcs.Contains(profiledFunc.Key))
{
funcs.Add(profiledFunc.Key);
}
}
}
return funcs;
}
public void PreLoad() public void PreLoad()
{ {
_lastHash = default; _lastHash = default;
@@ -250,18 +221,13 @@ namespace ARMeilleure.Translation.PTC
return false; return false;
} }
Func<ulong, FuncProfile, (ulong, FuncProfile)> migrateEntryFunc = null;
switch (outerHeader.InfoFileVersion) switch (outerHeader.InfoFileVersion)
{ {
case InternalVersion: case InternalVersion:
ProfiledFuncs = Deserialize(stream); ProfiledFuncs = Deserialize(stream);
break; break;
case 1866: case 1866:
migrateEntryFunc = (address, profile) => (address + 0x500000UL, profile); ProfiledFuncs = Deserialize(stream, (address, profile) => (address + 0x500000UL, profile));
goto case 5518;
case 5518:
ProfiledFuncs = DeserializeAddBlacklist(stream, migrateEntryFunc);
break; break;
default: default:
Logger.Error?.Print(LogClass.Ptc, $"No migration path for {nameof(outerHeader.InfoFileVersion)} '{outerHeader.InfoFileVersion}'. Discarding cache."); Logger.Error?.Print(LogClass.Ptc, $"No migration path for {nameof(outerHeader.InfoFileVersion)} '{outerHeader.InfoFileVersion}'. Discarding cache.");
@@ -291,16 +257,6 @@ namespace ARMeilleure.Translation.PTC
return DeserializeDictionary<ulong, FuncProfile>(stream, DeserializeStructure<FuncProfile>); return DeserializeDictionary<ulong, FuncProfile>(stream, DeserializeStructure<FuncProfile>);
} }
private static Dictionary<ulong, FuncProfile> DeserializeAddBlacklist(Stream stream, Func<ulong, FuncProfile, (ulong, FuncProfile)> migrateEntryFunc = null)
{
if (migrateEntryFunc != null)
{
return DeserializeAndUpdateDictionary(stream, (Stream stream) => { return new FuncProfile(DeserializeStructure<FuncProfilePreBlacklist>(stream)); }, migrateEntryFunc);
}
return DeserializeDictionary<ulong, FuncProfile>(stream, (Stream stream) => { return new FuncProfile(DeserializeStructure<FuncProfilePreBlacklist>(stream)); });
}
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream) private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
{ {
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position); return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
@@ -432,35 +388,13 @@ namespace ARMeilleure.Translation.PTC
} }
} }
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 6*/)] [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
public struct FuncProfile public struct FuncProfile
{ {
public ExecutionMode Mode; public ExecutionMode Mode;
public bool HighCq; public bool HighCq;
public bool Blacklist;
public FuncProfile(ExecutionMode mode, bool highCq, bool blacklist) public FuncProfile(ExecutionMode mode, bool highCq)
{
Mode = mode;
HighCq = highCq;
Blacklist = blacklist;
}
public FuncProfile(FuncProfilePreBlacklist fp)
{
Mode = fp.Mode;
HighCq = fp.HighCq;
Blacklist = false;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
public struct FuncProfilePreBlacklist
{
public ExecutionMode Mode;
public bool HighCq;
public FuncProfilePreBlacklist(ExecutionMode mode, bool highCq)
{ {
Mode = mode; Mode = mode;
HighCq = highCq; HighCq = highCq;

View File

@@ -249,11 +249,6 @@ namespace ARMeilleure.Translation
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter); ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter);
if (cfg == null)
{
return null;
}
ulong funcSize = funcRange.End - funcRange.Start; ulong funcSize = funcRange.End - funcRange.Start;
Logger.EndPass(PassName.Translation, cfg); Logger.EndPass(PassName.Translation, cfg);
@@ -412,11 +407,6 @@ namespace ARMeilleure.Translation
if (opCode.Instruction.Emitter != null) if (opCode.Instruction.Emitter != null)
{ {
opCode.Instruction.Emitter(context); opCode.Instruction.Emitter(context);
if (opCode.Instruction.Name == InstName.Und && blkIndex == 0)
{
range = new Range(rangeStart, rangeEnd);
return null;
}
} }
else else
{ {

View File

@@ -6,16 +6,4 @@ namespace Ryujinx.Common.Configuration
Unbounded, Unbounded,
Custom Custom
} }
public static class VSyncModeExtensions
{
public static VSyncMode Next(this VSyncMode vsync, bool customEnabled = false) =>
vsync switch
{
VSyncMode.Switch => customEnabled ? VSyncMode.Custom : VSyncMode.Unbounded,
VSyncMode.Unbounded => VSyncMode.Switch,
VSyncMode.Custom => VSyncMode.Unbounded,
_ => VSyncMode.Switch
};
}
} }

View File

@@ -1,58 +0,0 @@
using Gommon;
using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Common.Helper
{
public class RefEvent<T>
{
public delegate void Handler(ref T arg);
private readonly Lock _subLock = new();
private readonly List<Handler> _subscriptions = [];
public bool HasSubscribers
{
get
{
lock (_subLock)
return _subscriptions.Count != 0;
}
}
public IReadOnlyList<Handler> Subscriptions
{
get
{
lock (_subLock)
return _subscriptions;
}
}
public void Add(Handler subscriber)
{
Guard.Require(subscriber, nameof(subscriber));
lock (_subLock)
_subscriptions.Add(subscriber);
}
public void Remove(Handler subscriber)
{
Guard.Require(subscriber, nameof(subscriber));
lock (_subLock)
_subscriptions.Remove(subscriber);
}
public void Clear()
{
lock (_subLock)
_subscriptions.Clear();
}
public void Call(ref T arg)
{
foreach (Handler subscription in Subscriptions)
subscription(ref arg);
}
}
}

View File

@@ -30,11 +30,10 @@ namespace Ryujinx.Common
public static readonly string[] GreatMetalTitles = public static readonly string[] GreatMetalTitles =
[ [
"01009b500007c000", // ARMS "010076f0049a2000", // Bayonetta
"0100a5c00d162000", // Cuphead "0100a5c00d162000", // Cuphead
"010023800d64a000", // Deltarune "010023800d64a000", // Deltarune
"01003a30012c0000", // LEGO City Undercover "01003a30012c0000", // LEGO City Undercover
"010048701995e000", // Luigi's Manion 2 HD
"010028600EBDA000", // Mario 3D World "010028600EBDA000", // Mario 3D World
"0100152000022000", // Mario Kart 8 Deluxe "0100152000022000", // Mario Kart 8 Deluxe
"010075a016a3a000", // Persona 4 Arena Ultimax "010075a016a3a000", // Persona 4 Arena Ultimax
@@ -49,14 +48,10 @@ namespace Ryujinx.Common
"01009bf0072d4000", // Captain Toad: Treasure Tracker "01009bf0072d4000", // Captain Toad: Treasure Tracker
"01009510001ca000", // Fast RMX "01009510001ca000", // Fast RMX
"01005CA01580E000", // Persona 5 Royale "01005CA01580E000", // Persona 5 Royale
"010015100b514000", // Super Mario Bros. Wonder
"0100000000010000", // Super Mario Odyssey "0100000000010000", // Super Mario Odyssey
// Further testing is appreciated, I did not test the entire game: //Isaac claims it has a issue in level 2, but I am not able to replicate it on my M3. More testing would be appreciated:
"01007300020fa000", // Astral Chain "010015100b514000", // Super Mario Bros. Wonder
"010076f0049a2000", // Bayonetta
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
"0100f4300bf2c000", // New Pokemon Snap
]; ];
public static string GetDiscordGameAsset(string titleId) public static string GetDiscordGameAsset(string titleId)

View File

@@ -1,5 +1,4 @@
using Gommon; using Gommon;
using Ryujinx.Common.Helper;
using System; using System;
using System.Drawing; using System.Drawing;
using System.Threading; using System.Threading;
@@ -56,7 +55,7 @@ namespace Ryujinx.Common.Utilities
{ {
_color = HsbToRgb((_color.GetHue() + Speed) / 360); _color = HsbToRgb((_color.GetHue() + Speed) / 360);
_updatedHandler.Call(ref _color); _updatedHandler.Call(_color.ToArgb());
} }
} }
@@ -68,13 +67,13 @@ namespace Ryujinx.Common.Utilities
_color = Color.Blue; _color = Color.Blue;
} }
public static event RefEvent<Color>.Handler Updated public static event Action<int> Updated
{ {
add => _updatedHandler.Add(value); add => _updatedHandler.Add(value);
remove => _updatedHandler.Remove(value); remove => _updatedHandler.Remove(value);
} }
private static readonly RefEvent<Color> _updatedHandler = new(); private static readonly Event<int> _updatedHandler = new();
private static Color HsbToRgb(float hue, float saturation = 1, float brightness = 1) private static Color HsbToRgb(float hue, float saturation = 1, float brightness = 1)
{ {

View File

@@ -76,7 +76,7 @@ namespace Ryujinx.Graphics.Metal
return model; return model;
} }
return string.Empty; return "";
} }
} }
} }

View File

@@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
foreach (MemoryDefinition memory in memories) foreach (MemoryDefinition memory in memories)
{ {
string arraySize = string.Empty; string arraySize = "";
if ((memory.Type & AggregateType.Array) != 0) if ((memory.Type & AggregateType.Array) != 0)
{ {
arraySize = $"[{memory.ArrayLength}]"; arraySize = $"[{memory.ArrayLength}]";
@@ -240,7 +240,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
BufferDefinition buffer = buffers[i]; BufferDefinition buffer = buffers[i];
bool needsPadding = buffer.Layout == BufferLayout.Std140; bool needsPadding = buffer.Layout == BufferLayout.Std140;
string fsiSuffix = !constant && fsi ? " [[raster_order_group(0)]]" : string.Empty; string fsiSuffix = !constant && fsi ? " [[raster_order_group(0)]]" : "";
bufferDec[i] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};"; bufferDec[i] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};";
@@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
type &= ~AggregateType.Array; type &= ~AggregateType.Array;
string typeName = GetVarTypeName(type); string typeName = GetVarTypeName(type);
string arraySuffix = string.Empty; string arraySuffix = "";
if (field.Type.HasFlag(AggregateType.Array)) if (field.Type.HasFlag(AggregateType.Array))
{ {
@@ -353,7 +353,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
imageTypeName = $"array<{imageTypeName}, {image.ArrayLength}>"; imageTypeName = $"array<{imageTypeName}, {image.ArrayLength}>";
} }
string fsiSuffix = fsi ? " [[raster_order_group(0)]]" : string.Empty; string fsiSuffix = fsi ? " [[raster_order_group(0)]]" : "";
imageDec[i] = $"{imageTypeName} {image.Name}{fsiSuffix};"; imageDec[i] = $"{imageTypeName} {image.Name}{fsiSuffix};";
} }
@@ -454,7 +454,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
IoVariable.VertexIndex => "[[vertex_id]]", IoVariable.VertexIndex => "[[vertex_id]]",
// IoVariable.PointCoord => "[[point_coord]]", // IoVariable.PointCoord => "[[point_coord]]",
IoVariable.UserDefined => context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{ioDefinition.Location})]]" : $"[[attribute({ioDefinition.Location})]]", IoVariable.UserDefined => context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{ioDefinition.Location})]]" : $"[[attribute({ioDefinition.Location})]]",
_ => string.Empty _ => ""
}; };
context.AppendLine($"{type} {name} {iq}{suffix};"); context.AppendLine($"{type} {name} {iq}{suffix};");
@@ -545,7 +545,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]",
IoVariable.FragmentOutputDepth => "[[depth(any)]]", IoVariable.FragmentOutputDepth => "[[depth(any)]]",
IoVariable.ClipDistance => $"[[clip_distance]][{Defaults.TotalClipDistances}]", IoVariable.ClipDistance => $"[[clip_distance]][{Defaults.TotalClipDistances}]",
_ => string.Empty _ => ""
}; };
context.AppendLine($"{type} {name} {suffix};"); context.AppendLine($"{type} {name} {suffix};");

View File

@@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions
inputsCount--; inputsCount--;
} }
string fieldName = string.Empty; string fieldName = "";
switch (storageKind) switch (storageKind)
{ {
case StorageKind.ConstantBuffer: case StorageKind.ConstantBuffer:
@@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions
} }
} }
varName += fieldName; varName += fieldName;
varName += fieldHasPadding ? ".x" : string.Empty; varName += fieldHasPadding ? ".x" : "";
if (isStore) if (isStore)
{ {
@@ -434,7 +434,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions
string prefix = intCoords ? "uint" : "float"; string prefix = intCoords ? "uint" : "float";
return prefix + (count > 1 ? count : string.Empty) + "(" + coords + ")"; return prefix + (count > 1 ? count : "") + "(" + coords + ")";
} }
Append(AssemblePVector(pCount)); Append(AssemblePVector(pCount));
@@ -504,7 +504,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions
} }
texCallBuilder.Append(')'); texCallBuilder.Append(')');
texCallBuilder.Append(colorIsVector ? GetMaskMultiDest(texOp.Index) : string.Empty); texCallBuilder.Append(colorIsVector ? GetMaskMultiDest(texOp.Index) : "");
return texCallBuilder.ToString(); return texCallBuilder.ToString();
} }
@@ -558,7 +558,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions
{ {
if (mask == 0x0) if (mask == 0x0)
{ {
return string.Empty; return "";
} }
string swizzle = "."; string swizzle = ".";

View File

@@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
if (parameters.Definitions.Stage is not (ShaderStage.Vertex or ShaderStage.Fragment or ShaderStage.Compute)) if (parameters.Definitions.Stage is not (ShaderStage.Vertex or ShaderStage.Fragment or ShaderStage.Compute))
{ {
Logger.Warning?.Print(LogClass.Gpu, $"Attempted to generate unsupported shader type {parameters.Definitions.Stage}!"); Logger.Warning?.Print(LogClass.Gpu, $"Attempted to generate unsupported shader type {parameters.Definitions.Stage}!");
return string.Empty; return "";
} }
CodeGenContext context = new(info, parameters); CodeGenContext context = new(info, parameters);

View File

@@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Shader
_ => "float" _ => "float"
}; };
return $"{typeName}<{format}{(image ? ", access::read_write" : string.Empty)}>"; return $"{typeName}<{format}{(image ? ", access::read_write" : "")}>";
} }
} }
} }

View File

@@ -192,7 +192,6 @@ namespace Ryujinx.HLE
/// <summary> /// <summary>
/// The desired hacky workarounds. /// The desired hacky workarounds.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
public EnabledDirtyHack[] Hacks { internal get; set; } public EnabledDirtyHack[] Hacks { internal get; set; }
public HLEConfiguration(VirtualFileSystem virtualFileSystem, public HLEConfiguration(VirtualFileSystem virtualFileSystem,

View File

@@ -20,7 +20,6 @@ namespace Ryujinx.HLE.HOS
private readonly string _titleIdText; private readonly string _titleIdText;
private readonly string _displayVersion; private readonly string _displayVersion;
private readonly bool _diskCacheEnabled; private readonly bool _diskCacheEnabled;
private readonly string _diskCacheSelector;
private readonly ulong _codeAddress; private readonly ulong _codeAddress;
private readonly ulong _codeSize; private readonly ulong _codeSize;
@@ -32,7 +31,6 @@ namespace Ryujinx.HLE.HOS
string titleIdText, string titleIdText,
string displayVersion, string displayVersion,
bool diskCacheEnabled, bool diskCacheEnabled,
string diskCacheSelector,
ulong codeAddress, ulong codeAddress,
ulong codeSize) ulong codeSize)
{ {
@@ -41,7 +39,6 @@ namespace Ryujinx.HLE.HOS
_titleIdText = titleIdText; _titleIdText = titleIdText;
_displayVersion = displayVersion; _displayVersion = displayVersion;
_diskCacheEnabled = diskCacheEnabled; _diskCacheEnabled = diskCacheEnabled;
_diskCacheSelector = diskCacheSelector;
_codeAddress = codeAddress; _codeAddress = codeAddress;
_codeSize = codeSize; _codeSize = codeSize;
} }
@@ -117,7 +114,7 @@ namespace Ryujinx.HLE.HOS
} }
} }
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, _diskCacheSelector ?? "default"); DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, "default"); //Ready for exefs profiles
return processContext; return processContext;
} }

View File

@@ -6,7 +6,6 @@ using LibHac.Loader;
using LibHac.Tools.Fs; using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.RomFs; using LibHac.Tools.FsSystem.RomFs;
using LibHac.Util;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
@@ -20,7 +19,6 @@ using System.Collections.Specialized;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using LazyFile = Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy.LazyFile; using LazyFile = Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy.LazyFile;
using Path = System.IO.Path; using Path = System.IO.Path;
@@ -296,7 +294,7 @@ namespace Ryujinx.HLE.HOS
AddModsFromDirectory(mods, applicationDir, modMetadata); AddModsFromDirectory(mods, applicationDir, modMetadata);
} }
public static void QueryContentsDir(ModCache mods, DirectoryInfo contentsDir, ulong applicationId, ulong[] installedDlcs) public static void QueryContentsDir(ModCache mods, DirectoryInfo contentsDir, ulong applicationId)
{ {
if (!contentsDir.Exists) if (!contentsDir.Exists)
{ {
@@ -311,16 +309,6 @@ namespace Ryujinx.HLE.HOS
{ {
QueryApplicationDir(mods, applicationDir, applicationId); QueryApplicationDir(mods, applicationDir, applicationId);
} }
foreach (ulong installedDlcId in installedDlcs)
{
DirectoryInfo dlcModDir = FindApplicationDir(contentsDir, $"{installedDlcId:x16}");
if (dlcModDir != null)
{
QueryApplicationDir(mods, dlcModDir, applicationId);
}
}
} }
private static int QueryCheatsDir(ModCache mods, DirectoryInfo cheatsDir) private static int QueryCheatsDir(ModCache mods, DirectoryInfo cheatsDir)
@@ -427,7 +415,7 @@ namespace Ryujinx.HLE.HOS
{ {
foreach ((ulong applicationId, ModCache cache) in modCaches) foreach ((ulong applicationId, ModCache cache) in modCaches)
{ {
QueryContentsDir(cache, searchDir, applicationId, Array.Empty<ulong>()); QueryContentsDir(cache, searchDir, applicationId);
} }
return true; return true;
@@ -593,7 +581,6 @@ namespace Ryujinx.HLE.HOS
public BitVector32 Stubs; public BitVector32 Stubs;
public BitVector32 Replaces; public BitVector32 Replaces;
public MetaLoader Npdm; public MetaLoader Npdm;
public string Hash;
public bool Modified => (Stubs.Data | Replaces.Data) != 0; public bool Modified => (Stubs.Data | Replaces.Data) != 0;
} }
@@ -604,11 +591,8 @@ namespace Ryujinx.HLE.HOS
{ {
Stubs = new BitVector32(), Stubs = new BitVector32(),
Replaces = new BitVector32(), Replaces = new BitVector32(),
Hash = null,
}; };
string tempHash = string.Empty;
if (!_appMods.TryGetValue(applicationId, out ModCache mods) || mods.ExefsDirs.Count == 0) if (!_appMods.TryGetValue(applicationId, out ModCache mods) || mods.ExefsDirs.Count == 0)
{ {
return modLoadResult; return modLoadResult;
@@ -644,16 +628,8 @@ namespace Ryujinx.HLE.HOS
modLoadResult.Replaces[1 << i] = true; modLoadResult.Replaces[1 << i] = true;
using (FileStream stream = nsoFile.OpenRead()) nsos[i] = new NsoExecutable(nsoFile.OpenRead().AsStorage(), nsoName);
{ Logger.Info?.Print(LogClass.ModLoader, $"NSO '{nsoName}' replaced");
nsos[i] = new NsoExecutable(stream.AsStorage(), nsoName);
Logger.Info?.Print(LogClass.ModLoader, $"NSO '{nsoName}' replaced");
using (MD5 md5 = MD5.Create())
{
stream.Seek(0, SeekOrigin.Begin);
tempHash += BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "").ToLowerInvariant();
}
}
} }
modLoadResult.Stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension)); modLoadResult.Stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension));
@@ -685,14 +661,6 @@ namespace Ryujinx.HLE.HOS
} }
} }
if (!string.IsNullOrEmpty(tempHash))
{
using (MD5 md5 = MD5.Create())
{
modLoadResult.Hash += BitConverter.ToString(md5.ComputeHash(tempHash.ToBytes())).Replace("-", string.Empty).ToLowerInvariant();
}
}
return modLoadResult; return modLoadResult;
} }

View File

@@ -121,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
private void UpdatePassphraseIfNeeded() private void UpdatePassphraseIfNeeded()
{ {
string passphrase = _config.MultiplayerLdnPassphrase ?? string.Empty; string passphrase = _config.MultiplayerLdnPassphrase ?? "";
if (passphrase != _passphrase) if (passphrase != _passphrase)
{ {
_passphrase = passphrase; _passphrase = passphrase;

View File

@@ -84,6 +84,13 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
// Apply Nsos patches. // Apply Nsos patches.
device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(programId, nsoExecutables); device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(programId, nsoExecutables);
// Don't use PTC if ExeFS files have been replaced.
bool enablePtc = device.System.EnablePtc && !modLoadResult.Modified;
if (!enablePtc)
{
Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled.");
}
string programName = string.Empty; string programName = string.Empty;
if (!isHomebrew && programId > 0x010000000000FFFF) if (!isHomebrew && programId > 0x010000000000FFFF)
@@ -110,8 +117,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
device.System.KernelContext, device.System.KernelContext,
metaLoader, metaLoader,
nacpData, nacpData,
device.System.EnablePtc, enablePtc,
modLoadResult.Hash,
true, true,
programName, programName,
metaLoader.GetProgramId(), metaLoader.GetProgramId(),

View File

@@ -235,7 +235,6 @@ namespace Ryujinx.HLE.Loaders.Processes
dummyExeFs.GetNpdm(), dummyExeFs.GetNpdm(),
nacpData, nacpData,
diskCacheEnabled: false, diskCacheEnabled: false,
diskCacheSelector: null,
allowCodeMemoryForJit: true, allowCodeMemoryForJit: true,
programName, programName,
programId, programId,

View File

@@ -186,7 +186,6 @@ namespace Ryujinx.HLE.Loaders.Processes
string.Empty, string.Empty,
string.Empty, string.Empty,
false, false,
null,
codeAddress, codeAddress,
codeSize); codeSize);
@@ -227,7 +226,6 @@ namespace Ryujinx.HLE.Loaders.Processes
MetaLoader metaLoader, MetaLoader metaLoader,
BlitStruct<ApplicationControlProperty> applicationControlProperties, BlitStruct<ApplicationControlProperty> applicationControlProperties,
bool diskCacheEnabled, bool diskCacheEnabled,
string diskCacheSelector,
bool allowCodeMemoryForJit, bool allowCodeMemoryForJit,
string name, string name,
ulong programId, ulong programId,
@@ -381,7 +379,6 @@ namespace Ryujinx.HLE.Loaders.Processes
$"{programId:x16}", $"{programId:x16}",
displayVersion, displayVersion,
diskCacheEnabled, diskCacheEnabled,
diskCacheSelector,
codeStart, codeStart,
codeSize); codeSize);

View File

@@ -88,7 +88,7 @@ namespace Ryujinx.HLE.Loaders.Processes
bool isFirmwareApplication = ProgramId <= 0x0100000000007FFF; bool isFirmwareApplication = ProgramId <= 0x0100000000007FFF;
string name = !isFirmware string name = !isFirmware
? (isFirmwareApplication ? "Firmware Application " : string.Empty) + (!string.IsNullOrWhiteSpace(Name) ? Name : "<Unknown Name>") ? (isFirmwareApplication ? "Firmware Application " : "") + (!string.IsNullOrWhiteSpace(Name) ? Name : "<Unknown Name>")
: "Firmware"; : "Firmware";
// TODO: LibHac npdm currently doesn't support version field. // TODO: LibHac npdm currently doesn't support version field.

View File

@@ -2,6 +2,8 @@ using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS.Services.Hid;
using SDL2;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
@@ -10,7 +12,7 @@ using static SDL2.SDL;
namespace Ryujinx.Input.SDL2 namespace Ryujinx.Input.SDL2
{ {
public class SDL2Gamepad : IGamepad class SDL2Gamepad : IGamepad
{ {
private bool HasConfiguration => _configuration != null; private bool HasConfiguration => _configuration != null;
@@ -111,7 +113,7 @@ namespace Ryujinx.Input.SDL2
byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0; byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0;
if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0) if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0)
Logger.Error?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting."); Logger.Error?.Print(LogClass.Hid, "LED is not supported on this game controller.");
} }
private GamepadFeaturesFlag GetFeaturesFlag() private GamepadFeaturesFlag GetFeaturesFlag()

View File

@@ -319,12 +319,31 @@ namespace Ryujinx.Ava
public void VSyncModeToggle() public void VSyncModeToggle()
{ {
VSyncMode oldVSyncMode = Device.VSyncMode; VSyncMode oldVSyncMode = Device.VSyncMode;
VSyncMode newVSyncMode = VSyncMode.Switch;
bool customVSyncIntervalEnabled = ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Value; bool customVSyncIntervalEnabled = ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Value;
UpdateVSyncMode(this, new ReactiveEventArgs<VSyncMode>( switch (oldVSyncMode)
oldVSyncMode, {
oldVSyncMode.Next(customVSyncIntervalEnabled)) case VSyncMode.Switch:
); newVSyncMode = VSyncMode.Unbounded;
break;
case VSyncMode.Unbounded:
if (customVSyncIntervalEnabled)
{
newVSyncMode = VSyncMode.Custom;
}
else
{
newVSyncMode = VSyncMode.Switch;
}
break;
case VSyncMode.Custom:
newVSyncMode = VSyncMode.Switch;
break;
}
UpdateVSyncMode(this, new ReactiveEventArgs<VSyncMode>(oldVSyncMode, newVSyncMode));
} }
private void UpdateCustomVSyncIntervalValue(object sender, ReactiveEventArgs<int> e) private void UpdateCustomVSyncIntervalValue(object sender, ReactiveEventArgs<int> e)

View File

@@ -0,0 +1,13 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="MenuItem.withCheckbox Viewbox#PART_IconPresenter">
<Setter Property="MaxHeight" Value="36" />
<Setter Property="MinHeight" Value="36" />
<Setter Property="MaxWidth" Value="36" />
<Setter Property="MinWidth" Value="36" />
</Style>
<Style Selector="MenuItem.withCheckbox ContentPresenter#PART_HeaderPresenter">
<Setter Property="Padding" Value="-10,0,0,0" />
</Style>
</Styles>

View File

@@ -218,15 +218,6 @@
<Setter Property="BorderBrush" <Setter Property="BorderBrush"
Value="{DynamicResource ThemeControlBorderColor}" /> Value="{DynamicResource ThemeControlBorderColor}" />
</Style> </Style>
<Style Selector="MenuItem.withCheckbox Viewbox#PART_IconPresenter">
<Setter Property="MaxHeight" Value="36" />
<Setter Property="MinHeight" Value="36" />
<Setter Property="MaxWidth" Value="36" />
<Setter Property="MinWidth" Value="36" />
</Style>
<Style Selector="MenuItem.withCheckbox ContentPresenter#PART_HeaderPresenter">
<Setter Property="Padding" Value="-10,0,0,0" />
</Style>
<Style Selector="TabItem > ScrollViewer"> <Style Selector="TabItem > ScrollViewer">
<Setter Property="Background" <Setter Property="Background"
Value="{DynamicResource ThemeBackgroundColor}" /> Value="{DynamicResource ThemeBackgroundColor}" />

View File

@@ -2022,56 +2022,6 @@
"zh_TW": "下一次啟動遊戲時,觸發 PPTC 進行重建" "zh_TW": "下一次啟動遊戲時,觸發 PPTC 進行重建"
} }
}, },
{
"ID": "GameListContextMenuCacheManagementNukePptc",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Purge PPTC cache",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "GameListContextMenuCacheManagementNukePptcToolTip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Deletes all PPTC cache files for the Application",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "GameListContextMenuCacheManagementPurgeShaderCache", "ID": "GameListContextMenuCacheManagementPurgeShaderCache",
"Translations": { "Translations": {
@@ -7747,31 +7697,6 @@
"zh_TW": "" "zh_TW": ""
} }
}, },
{
"ID": "ControllerSettingsLedColorRainbowSpeed",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Rainbow Speed",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "ControllerSettingsLedColor", "ID": "ControllerSettingsLedColor",
"Translations": { "Translations": {
@@ -7793,7 +7718,7 @@
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
"zh_CN": "颜色", "zh_CN": "",
"zh_TW": "" "zh_TW": ""
} }
}, },
@@ -13022,31 +12947,6 @@
"zh_TW": "在 {0} 清除 PPTC 快取時出錯: {1}" "zh_TW": "在 {0} 清除 PPTC 快取時出錯: {1}"
} }
}, },
{
"ID": "DialogPPTCNukeMessage",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "DialogShaderDeletionMessage", "ID": "DialogShaderDeletionMessage",
"Translations": { "Translations": {
@@ -19043,7 +18943,7 @@
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
"zh_CN": "LED 设置", "zh_CN": "",
"zh_TW": "" "zh_TW": ""
} }
}, },

View File

@@ -5,7 +5,6 @@ using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Logging.Targets; using Ryujinx.Common.Logging.Targets;
@@ -27,7 +26,6 @@ using Ryujinx.SDL2.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
namespace Ryujinx.Headless namespace Ryujinx.Headless
@@ -288,9 +286,6 @@ namespace Ryujinx.Headless
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE; GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off); DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
if (_inputConfiguration.OfType<StandardControllerInputConfig>().Any(ic => ic.Led.UseRainbow))
Rainbow.Enable();
while (true) while (true)
{ {

View File

@@ -112,11 +112,7 @@ namespace Ryujinx.Ava
// Hook unhandled exception and process exit events. // Hook unhandled exception and process exit events.
AppDomain.CurrentDomain.UnhandledException += (sender, e) AppDomain.CurrentDomain.UnhandledException += (sender, e)
=> ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating); => ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating);
TaskScheduler.UnobservedTaskException += (sender, e)
=> ProcessUnhandledException(sender, e.Exception, false);
AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit(); AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit();
// Setup base data directory. // Setup base data directory.
AppDataManager.Initialize(CommandLineState.BaseDirPathArg); AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
@@ -210,16 +206,6 @@ namespace Ryujinx.Ava
_ => ConfigurationState.Instance.Graphics.GraphicsBackend _ => ConfigurationState.Instance.Graphics.GraphicsBackend
}; };
// Check if backend threading was overridden
if (CommandLineState.OverrideBackendThreading is not null)
ConfigurationState.Instance.Graphics.BackendThreading.Value = CommandLineState.OverrideBackendThreading.ToLower() switch
{
"auto" => BackendThreading.Auto,
"off" => BackendThreading.Off,
"on" => BackendThreading.On,
_ => ConfigurationState.Instance.Graphics.BackendThreading
};
// Check if docked mode was overriden. // Check if docked mode was overriden.
if (CommandLineState.OverrideDockedMode.HasValue) if (CommandLineState.OverrideDockedMode.HasValue)
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
@@ -286,7 +272,9 @@ namespace Ryujinx.Ava
log.PrintMsg(LogClass.Application, message); log.PrintMsg(LogClass.Application, message);
} }
if (isTerminating) if (isTerminating)
Exit(); Exit();
} }

View File

@@ -123,11 +123,13 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</AvaloniaResource> </AvaloniaResource>
<AvaloniaResource Include="Assets\Styles\Styles.xaml" /> <AvaloniaResource Include="Assets\Styles\Styles.xaml" />
<AvaloniaResource Include="Assets\Styles\CheckboxMenuItemStyle.axaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="Assets\locales.json" /> <None Remove="Assets\locales.json" />
<None Remove="Assets\Styles\Themes.xaml" /> <None Remove="Assets\Styles\Themes.xaml" />
<None Remove="Assets\Styles\CheckboxMenuItemStyle.xaml" />
<None Remove="Assets\Icons\Controller_JoyConLeft.svg" /> <None Remove="Assets\Icons\Controller_JoyConLeft.svg" />
<None Remove="Assets\Icons\Controller_JoyConPair.svg" /> <None Remove="Assets\Icons\Controller_JoyConPair.svg" />
<None Remove="Assets\Icons\Controller_JoyConRight.svg" /> <None Remove="Assets\Icons\Controller_JoyConRight.svg" />
@@ -149,6 +151,7 @@
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Assets\locales.json" /> <EmbeddedResource Include="Assets\locales.json" />
<EmbeddedResource Include="Assets\Styles\Styles.xaml" /> <EmbeddedResource Include="Assets\Styles\Styles.xaml" />
<EmbeddedResource Include="Assets\Styles\CheckboxMenuItemStyle.axaml" />
<EmbeddedResource Include="Assets\Icons\Controller_JoyConLeft.svg" /> <EmbeddedResource Include="Assets\Icons\Controller_JoyConLeft.svg" />
<EmbeddedResource Include="Assets\Icons\Controller_JoyConPair.svg" /> <EmbeddedResource Include="Assets\Icons\Controller_JoyConPair.svg" />
<EmbeddedResource Include="Assets\Icons\Controller_JoyConRight.svg" /> <EmbeddedResource Include="Assets\Icons\Controller_JoyConRight.svg" />

View File

@@ -16,5 +16,6 @@
<Application.Styles> <Application.Styles>
<sty:FluentAvaloniaTheme PreferUserAccentColor="True" PreferSystemTheme="False" /> <sty:FluentAvaloniaTheme PreferUserAccentColor="True" PreferSystemTheme="False" />
<StyleInclude Source="/Assets/Styles/Styles.xaml" /> <StyleInclude Source="/Assets/Styles/Styles.xaml" />
<StyleInclude Source="/Assets/Styles/CheckboxMenuItemStyle.axaml"/>
</Application.Styles> </Application.Styles>
</Application> </Application>

View File

@@ -13,6 +13,11 @@
mc:Ignorable="d" mc:Ignorable="d"
Focusable="True" Focusable="True"
x:DataType="viewModels:UserSelectorDialogViewModel"> x:DataType="viewModels:UserSelectorDialogViewModel">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Design.DataContext> <Design.DataContext>
<viewModels:UserSelectorDialogViewModel /> <viewModels:UserSelectorDialogViewModel />
</Design.DataContext> </Design.DataContext>
@@ -75,7 +80,7 @@
Height="96" Height="96"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Top"
Source="{Binding Image, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" /> Source="{Binding Image, Converter={StaticResource ByteImage}}" />
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
MaxWidth="90" MaxWidth="90"
@@ -105,5 +110,12 @@
</ListBox> </ListBox>
</Border> </Border>
<StackPanel
Grid.Row="1"
Margin="0 24 0 0"
HorizontalAlignment="Left"
Orientation="Horizontal"
Spacing="10">
</StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -81,11 +81,6 @@
Header="{ext:Locale GameListContextMenuCacheManagementPurgePptc}" Header="{ext:Locale GameListContextMenuCacheManagementPurgePptc}"
Icon="{ext:Icon mdi-refresh}" Icon="{ext:Icon mdi-refresh}"
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" /> ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
<MenuItem
Click="NukePtcCache_Click"
Header="{ext:Locale GameListContextMenuCacheManagementNukePptc}"
Icon="{ext:Icon mdi-delete-alert}"
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementNukePptcToolTip}" />
<MenuItem <MenuItem
Click="PurgeShaderCache_Click" Click="PurgeShaderCache_Click"
Header="{ext:Locale GameListContextMenuCacheManagementPurgeShaderCache}" Header="{ext:Locale GameListContextMenuCacheManagementPurgeShaderCache}"

View File

@@ -128,11 +128,7 @@ namespace Ryujinx.Ava.UI.Controls
public async void OpenModManager_Click(object sender, RoutedEventArgs args) public async void OpenModManager_Click(object sender, RoutedEventArgs args)
{ {
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel }) if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
await ModManagerWindow.Show( await ModManagerWindow.Show(viewModel.SelectedApplication.Id, viewModel.SelectedApplication.Name);
viewModel.SelectedApplication.Id,
viewModel.SelectedApplication.IdBase,
viewModel.ApplicationLibrary,
viewModel.SelectedApplication.Name);
} }
public async void PurgePtcCache_Click(object sender, RoutedEventArgs args) public async void PurgePtcCache_Click(object sender, RoutedEventArgs args)
@@ -179,52 +175,6 @@ namespace Ryujinx.Ava.UI.Controls
} }
} }
public async void NukePtcCache_Click(object sender, RoutedEventArgs args)
{
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
return;
UserResult result = await ContentDialogHelper.CreateLocalizedConfirmationDialog(
LocaleManager.Instance[LocaleKeys.DialogWarning],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCNukeMessage, viewModel.SelectedApplication.Name)
);
if (result == UserResult.Yes)
{
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "0"));
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "1"));
List<FileInfo> cacheFiles = new();
if (mainDir.Exists)
{
cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache"));
cacheFiles.AddRange(mainDir.EnumerateFiles("*.info"));
}
if (backupDir.Exists)
{
cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache"));
cacheFiles.AddRange(mainDir.EnumerateFiles("*.info"));
}
if (cacheFiles.Count > 0)
{
foreach (FileInfo file in cacheFiles)
{
try
{
file.Delete();
}
catch (Exception ex)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, ex));
}
}
}
}
}
public async void PurgeShaderCache_Click(object sender, RoutedEventArgs args) public async void PurgeShaderCache_Click(object sender, RoutedEventArgs args)
{ {
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel }) if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })

View File

@@ -13,6 +13,9 @@
mc:Ignorable="d" mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel"> x:DataType="viewModels:MainWindowViewModel">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -65,7 +68,7 @@
Grid.Row="0" Grid.Row="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Top"
Source="{Binding Icon, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" /> Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<Panel <Panel
Grid.Row="1" Grid.Row="1"
Height="50" Height="50"

View File

@@ -12,6 +12,9 @@
mc:Ignorable="d" mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel"> x:DataType="viewModels:MainWindowViewModel">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -59,7 +62,7 @@
Classes.large="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridLarge}" Classes.large="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridMedium}" Classes.normal="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridMedium}"
Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}" Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}"
Source="{Binding Icon, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" /> Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<Border <Border
Grid.Column="2" Grid.Column="2"
Margin="0,0,5,0" Margin="0,0,5,0"

View File

@@ -9,7 +9,7 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
internal class BitmapArrayValueConverter : IValueConverter internal class BitmapArrayValueConverter : IValueConverter
{ {
public static readonly BitmapArrayValueConverter Instance = new(); public static BitmapArrayValueConverter Instance = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ {

View File

@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
internal class DownloadableContentLabelConverter : IMultiValueConverter internal class DownloadableContentLabelConverter : IMultiValueConverter
{ {
public static readonly DownloadableContentLabelConverter Instance = new(); public static DownloadableContentLabelConverter Instance = new();
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture) public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
{ {

View File

@@ -10,7 +10,7 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
internal class KeyValueConverter : IValueConverter internal class KeyValueConverter : IValueConverter
{ {
public static readonly KeyValueConverter Instance = new(); public static KeyValueConverter Instance = new();
private static readonly Dictionary<Key, LocaleKeys> _keysMap = new() private static readonly Dictionary<Key, LocaleKeys> _keysMap = new()
{ {

View File

@@ -1,5 +1,6 @@
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System; using System;
using System.Globalization; using System.Globalization;
@@ -18,10 +19,15 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
return $"Hosted Games: {applicationData.GameCount}\nOnline Players: {applicationData.PlayerCount}"; return $"Hosted Games: {applicationData.GameCount}\nOnline Players: {applicationData.PlayerCount}";
} }
else
{
return "";
}
}
else
{
return "";
} }
return "";
} }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

View File

@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
internal class TitleUpdateLabelConverter : IMultiValueConverter internal class TitleUpdateLabelConverter : IMultiValueConverter
{ {
public static readonly TitleUpdateLabelConverter Instance = new(); public static TitleUpdateLabelConverter Instance = new();
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture) public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
{ {

View File

@@ -12,7 +12,7 @@ namespace Ryujinx.Ava.UI.Helpers
internal class XCITrimmerFileSpaceSavingsConverter : IValueConverter internal class XCITrimmerFileSpaceSavingsConverter : IValueConverter
{ {
private const long _bytesPerMB = 1024 * 1024; private const long _bytesPerMB = 1024 * 1024;
public static readonly XCITrimmerFileSpaceSavingsConverter Instance = new(); public static XCITrimmerFileSpaceSavingsConverter Instance = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ {

View File

@@ -9,7 +9,7 @@ namespace Ryujinx.Ava.UI.Models
{ {
private bool _isEnabled = false; private bool _isEnabled = false;
public ObservableCollection<CheatNode> SubNodes { get; } = []; public ObservableCollection<CheatNode> SubNodes { get; } = [];
public string CleanName => Name.Length > 0 ? Name[1..^7] : Name; public string CleanName => Name[1..^7];
public string BuildIdKey => $"{BuildId}-{Name}"; public string BuildIdKey => $"{BuildId}-{Name}";
public bool IsRootNode { get; } public bool IsRootNode { get; }
public string Name { get; } public string Name { get; }

View File

@@ -0,0 +1,260 @@
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Input;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Models.Input
{
public class StickVisualizer : BaseModel, IDisposable
{
public const int DrawStickPollRate = 50; // Milliseconds per poll.
public const int DrawStickCircumference = 5;
public const float DrawStickScaleFactor = DrawStickCanvasCenter;
public const int DrawStickCanvasSize = 100;
public const int DrawStickBorderSize = DrawStickCanvasSize + 5;
public const float DrawStickCanvasCenter = (DrawStickCanvasSize - DrawStickCircumference) / 2;
public const float MaxVectorLength = DrawStickCanvasSize / 2;
public CancellationTokenSource PollTokenSource;
public CancellationToken PollToken;
private static float _vectorLength;
private static float _vectorMultiplier;
private bool disposedValue;
private DeviceType _type;
public DeviceType Type
{
get => _type;
set
{
_type = value;
OnPropertyChanged();
}
}
private GamepadInputConfig _gamepadConfig;
public GamepadInputConfig GamepadConfig
{
get => _gamepadConfig;
set
{
_gamepadConfig = value;
OnPropertyChanged();
}
}
private KeyboardInputConfig _keyboardConfig;
public KeyboardInputConfig KeyboardConfig
{
get => _keyboardConfig;
set
{
_keyboardConfig = value;
OnPropertyChanged();
}
}
private (float, float) _uiStickLeft;
public (float, float) UiStickLeft
{
get => (_uiStickLeft.Item1 * DrawStickScaleFactor, _uiStickLeft.Item2 * DrawStickScaleFactor);
set
{
_uiStickLeft = value;
OnPropertyChanged();
OnPropertyChanged(nameof(UiStickRightX));
OnPropertyChanged(nameof(UiStickRightY));
OnPropertyChanged(nameof(UiDeadzoneRight));
}
}
private (float, float) _uiStickRight;
public (float, float) UiStickRight
{
get => (_uiStickRight.Item1 * DrawStickScaleFactor, _uiStickRight.Item2 * DrawStickScaleFactor);
set
{
_uiStickRight = value;
OnPropertyChanged();
OnPropertyChanged(nameof(UiStickLeftX));
OnPropertyChanged(nameof(UiStickLeftY));
OnPropertyChanged(nameof(UiDeadzoneLeft));
}
}
public float UiStickLeftX => ClampVector(UiStickLeft).Item1;
public float UiStickLeftY => ClampVector(UiStickLeft).Item2;
public float UiStickRightX => ClampVector(UiStickRight).Item1;
public float UiStickRightY => ClampVector(UiStickRight).Item2;
public int UiStickCircumference => DrawStickCircumference;
public int UiCanvasSize => DrawStickCanvasSize;
public int UiStickBorderSize => DrawStickBorderSize;
public float? UiDeadzoneLeft => _gamepadConfig?.DeadzoneLeft * DrawStickCanvasSize - DrawStickCircumference;
public float? UiDeadzoneRight => _gamepadConfig?.DeadzoneRight * DrawStickCanvasSize - DrawStickCircumference;
private InputViewModel Parent;
public StickVisualizer(InputViewModel parent)
{
Parent = parent;
PollTokenSource = new CancellationTokenSource();
PollToken = PollTokenSource.Token;
Task.Run(Initialize, PollToken);
}
public void UpdateConfig(object config)
{
if (config is ControllerInputViewModel padConfig)
{
GamepadConfig = padConfig.Config;
Type = DeviceType.Controller;
return;
}
else if (config is KeyboardInputViewModel keyConfig)
{
KeyboardConfig = keyConfig.Config;
Type = DeviceType.Keyboard;
return;
}
Type = DeviceType.None;
}
public async Task Initialize()
{
(float, float) leftBuffer;
(float, float) rightBuffer;
while (!PollToken.IsCancellationRequested)
{
leftBuffer = (0f, 0f);
rightBuffer = (0f, 0f);
switch (Type)
{
case DeviceType.Keyboard:
IKeyboard keyboard = (IKeyboard)Parent.AvaloniaKeyboardDriver.GetGamepad("0");
if (keyboard != null)
{
KeyboardStateSnapshot snapshot = keyboard.GetKeyboardStateSnapshot();
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickRight))
{
leftBuffer.Item1 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickLeft))
{
leftBuffer.Item1 -= 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickUp))
{
leftBuffer.Item2 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickDown))
{
leftBuffer.Item2 -= 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickRight))
{
rightBuffer.Item1 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickLeft))
{
rightBuffer.Item1 -= 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickUp))
{
rightBuffer.Item2 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickDown))
{
rightBuffer.Item2 -= 1;
}
UiStickLeft = leftBuffer;
UiStickRight = rightBuffer;
}
break;
case DeviceType.Controller:
IGamepad controller = Parent.SelectedGamepad;
if (controller != null)
{
leftBuffer = controller.GetStick((StickInputId)GamepadConfig.LeftJoystick);
rightBuffer = controller.GetStick((StickInputId)GamepadConfig.RightJoystick);
}
break;
case DeviceType.None:
break;
default:
throw new ArgumentException($"Unable to poll device type \"{Type}\"");
}
UiStickLeft = leftBuffer;
UiStickRight = rightBuffer;
await Task.Delay(DrawStickPollRate, PollToken);
}
PollTokenSource.Dispose();
}
public static (float, float) ClampVector((float, float) vect)
{
_vectorMultiplier = 1;
_vectorLength = MathF.Sqrt((vect.Item1 * vect.Item1) + (vect.Item2 * vect.Item2));
if (_vectorLength > MaxVectorLength)
{
_vectorMultiplier = MaxVectorLength / _vectorLength;
}
vect.Item1 = vect.Item1 * _vectorMultiplier + DrawStickCanvasCenter;
vect.Item2 = vect.Item2 * _vectorMultiplier + DrawStickCanvasCenter;
return vect;
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
PollTokenSource.Cancel();
}
KeyboardConfig = null;
GamepadConfig = null;
Parent = null;
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,17 +1,41 @@
using Avalonia.Svg.Skia; using Avalonia.Svg.Skia;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.Views.Input; using Ryujinx.Ava.UI.Views.Input;
using Ryujinx.Common.Utilities;
using Ryujinx.UI.Views.Input; using Ryujinx.UI.Views.Input;
using System.Drawing;
namespace Ryujinx.Ava.UI.ViewModels.Input namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
public partial class ControllerInputViewModel : BaseModel public partial class ControllerInputViewModel : BaseModel
{ {
[ObservableProperty] private GamepadInputConfig _config; private GamepadInputConfig _config;
public GamepadInputConfig Config
{
get => _config;
set
{
_config = value;
OnPropertyChanged();
}
}
private StickVisualizer _visualizer;
public StickVisualizer Visualizer
{
get => _visualizer;
set
{
_visualizer = value;
OnPropertyChanged();
}
}
private bool _isLeft; private bool _isLeft;
public bool IsLeft public bool IsLeft
{ {
@@ -37,33 +61,17 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
public bool HasSides => IsLeft ^ IsRight; public bool HasSides => IsLeft ^ IsRight;
[ObservableProperty] private SvgImage _image; [ObservableProperty] private SvgImage _image;
public InputViewModel ParentModel { get; } public InputViewModel ParentModel { get; }
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config) public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config, StickVisualizer visualizer)
{ {
ParentModel = model; ParentModel = model;
Visualizer = visualizer;
model.NotifyChangesEvent += OnParentModelChanged; model.NotifyChangesEvent += OnParentModelChanged;
OnParentModelChanged(); OnParentModelChanged();
config.PropertyChanged += (_, args) =>
{
if (args.PropertyName is nameof(Config.UseRainbowLed))
{
if (Config is { UseRainbowLed: true, TurnOffLed: false, EnableLedChanging: true })
Rainbow.Updated += (ref Color color) => ParentModel.SelectedGamepad.SetLed((uint)color.ToArgb());
else
{
Rainbow.Reset();
if (Config.TurnOffLed)
ParentModel.SelectedGamepad.ClearLed();
else
ParentModel.SelectedGamepad.SetLed(Config.LedColor.ToUInt32());
}
}
};
Config = config; Config = config;
} }

View File

@@ -23,7 +23,6 @@ using Ryujinx.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
@@ -49,7 +48,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private int _controller; private int _controller;
private string _controllerImage; private string _controllerImage;
private int _device; private int _device;
[ObservableProperty] private object _configViewModel; private object _configViewModel;
[ObservableProperty] private string _profileName; [ObservableProperty] private string _profileName;
private bool _isLoaded; private bool _isLoaded;
@@ -64,16 +63,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
get => _selectedGamepad; get => _selectedGamepad;
private set private set
{ {
Rainbow.Reset();
_selectedGamepad = value; _selectedGamepad = value;
if (ConfigViewModel is ControllerInputViewModel { Config.UseRainbowLed: true })
Rainbow.Updated += (ref Color color) => _selectedGamepad.SetLed((uint)color.ToArgb());
OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed)); OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed));
} }
} }
public StickVisualizer VisualStick { get; private set; }
public ObservableCollection<PlayerModel> PlayerIndexes { get; set; } public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; } public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
@@ -94,6 +88,19 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool IsModified { get; set; } public bool IsModified { get; set; }
public event Action NotifyChangesEvent; public event Action NotifyChangesEvent;
public object ConfigViewModel
{
get => _configViewModel;
set
{
_configViewModel = value;
VisualStick.UpdateConfig(value);
OnPropertyChanged();
}
}
public PlayerIndex PlayerIdChoose public PlayerIndex PlayerIdChoose
{ {
get => _playerIdChoose; get => _playerIdChoose;
@@ -269,6 +276,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
Devices = []; Devices = [];
ProfilesList = []; ProfilesList = [];
DeviceList = []; DeviceList = [];
VisualStick = new StickVisualizer(this);
ControllerImage = ProControllerResource; ControllerImage = ProControllerResource;
@@ -289,12 +297,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
if (Config is StandardKeyboardInputConfig keyboardInputConfig) if (Config is StandardKeyboardInputConfig keyboardInputConfig)
{ {
ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig)); ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig), VisualStick);
} }
if (Config is StandardControllerInputConfig controllerInputConfig) if (Config is StandardControllerInputConfig controllerInputConfig)
{ {
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig)); ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick);
} }
} }
@@ -893,6 +901,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates(); _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
VisualStick.Dispose();
SelectedGamepad?.Dispose(); SelectedGamepad?.Dispose();
AvaloniaKeyboardDriver.Dispose(); AvaloniaKeyboardDriver.Dispose();

View File

@@ -6,7 +6,29 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
public partial class KeyboardInputViewModel : BaseModel public partial class KeyboardInputViewModel : BaseModel
{ {
[ObservableProperty] private KeyboardInputConfig _config; private KeyboardInputConfig _config;
public KeyboardInputConfig Config
{
get => _config;
set
{
_config = value;
OnPropertyChanged();
}
}
private StickVisualizer _visualizer;
public StickVisualizer Visualizer
{
get => _visualizer;
set
{
_visualizer = value;
OnPropertyChanged();
}
}
private bool _isLeft; private bool _isLeft;
public bool IsLeft public bool IsLeft
@@ -38,9 +60,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public readonly InputViewModel ParentModel; public readonly InputViewModel ParentModel;
public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config) public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config, StickVisualizer visualizer)
{ {
ParentModel = model; ParentModel = model;
Visualizer = visualizer;
model.NotifyChangesEvent += OnParentModelChanged; model.NotifyChangesEvent += OnParentModelChanged;
OnParentModelChanged(); OnParentModelChanged();
Config = config; Config = config;

View File

@@ -1,13 +1,7 @@
using Avalonia.Media; using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Gommon;
using Humanizer;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities.Configuration;
using System;
using System.Globalization;
using System.Linq;
namespace Ryujinx.Ava.UI.ViewModels.Input namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
@@ -27,19 +21,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
[ObservableProperty] private bool _enableLedChanging; [ObservableProperty] private bool _enableLedChanging;
[ObservableProperty] private Color _ledColor; [ObservableProperty] private Color _ledColor;
public string RainbowSpeedText => RainbowSpeed.ToString(CultureInfo.CurrentCulture).Truncate(4, string.Empty);
public float RainbowSpeed
{
get => ConfigurationState.Instance.Hid.RainbowSpeed;
set
{
ConfigurationState.Instance.Hid.RainbowSpeed.Value = value;
OnPropertyChanged();
OnPropertyChanged(nameof(RainbowSpeedText));
}
}
public bool ShowLedColorPicker => !TurnOffLed && !UseRainbowLed; public bool ShowLedColorPicker => !TurnOffLed && !UseRainbowLed;

View File

@@ -7,7 +7,6 @@ using Gommon;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
@@ -30,7 +29,6 @@ namespace Ryujinx.Ava.UI.ViewModels
private string _search; private string _search;
private readonly ulong _applicationId; private readonly ulong _applicationId;
private readonly ulong[] _installedDlcIds;
private readonly IStorageProvider _storageProvider; private readonly IStorageProvider _storageProvider;
private static readonly ModMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly ModMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
@@ -63,23 +61,18 @@ namespace Ryujinx.Ava.UI.ViewModels
get => string.Format(LocaleManager.Instance[LocaleKeys.ModWindowHeading], Mods.Count); get => string.Format(LocaleManager.Instance[LocaleKeys.ModWindowHeading], Mods.Count);
} }
public ModManagerViewModel(ulong applicationId, ulong applicationIdBase, ApplicationLibrary appLibrary) public ModManagerViewModel(ulong applicationId)
{ {
_applicationId = applicationId; _applicationId = applicationId;
_installedDlcIds = appLibrary.DownloadableContents.Keys
.Where(x => x.TitleIdBase == applicationIdBase)
.Select(x => x.TitleId)
.ToArray();
_modJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationId.ToString("x16"), "mods.json"); _modJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationId.ToString("x16"), "mods.json");
_storageProvider = RyujinxApp.MainWindow.StorageProvider; _storageProvider = RyujinxApp.MainWindow.StorageProvider;
LoadMods(applicationId, _installedDlcIds); LoadMods(applicationId);
} }
private void LoadMods(ulong applicationId, ulong[] installedDlcIds) private void LoadMods(ulong applicationId)
{ {
Mods.Clear(); Mods.Clear();
SelectedMods.Clear(); SelectedMods.Clear();
@@ -91,7 +84,7 @@ namespace Ryujinx.Ava.UI.ViewModels
bool inSd = path == ModLoader.GetSdModsBasePath(); bool inSd = path == ModLoader.GetSdModsBasePath();
ModLoader.ModCache modCache = new(); ModLoader.ModCache modCache = new();
ModLoader.QueryContentsDir(modCache, new DirectoryInfo(Path.Combine(path, "contents")), applicationId, _installedDlcIds); ModLoader.QueryContentsDir(modCache, new DirectoryInfo(Path.Combine(path, "contents")), applicationId);
foreach (ModLoader.Mod<DirectoryInfo> mod in modCache.RomfsDirs) foreach (ModLoader.Mod<DirectoryInfo> mod in modCache.RomfsDirs)
{ {
@@ -285,7 +278,7 @@ namespace Ryujinx.Ava.UI.ViewModels
File.Copy(file, file.Replace(directory.Parent.ToString(), destinationDir), true); File.Copy(file, file.Replace(directory.Parent.ToString(), destinationDir), true);
} }
LoadMods(_applicationId, _installedDlcIds); LoadMods(_applicationId);
} }
public async void Add() public async void Add()

View File

@@ -20,6 +20,9 @@
<Design.DataContext> <Design.DataContext>
<viewModels:ControllerInputViewModel /> <viewModels:ControllerInputViewModel />
</Design.DataContext> </Design.DataContext>
<UserControl.Resources>
<helpers:KeyValueConverter x:Key="Key" />
</UserControl.Resources>
<UserControl.Styles> <UserControl.Styles>
<Style Selector="ToggleButton"> <Style Selector="ToggleButton">
<Setter Property="Width" Value="90" /> <Setter Property="Width" Value="90" />
@@ -75,7 +78,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonZl"> <ToggleButton Name="ButtonZl">
<TextBlock <TextBlock
Text="{Binding Config.ButtonZl, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonZl, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -91,7 +94,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonL"> <ToggleButton Name="ButtonL">
<TextBlock <TextBlock
Text="{Binding Config.ButtonL, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonL, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -107,7 +110,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonMinus"> <ToggleButton Name="ButtonMinus">
<TextBlock <TextBlock
Text="{Binding Config.ButtonMinus, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonMinus, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -141,7 +144,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftStickButton"> <ToggleButton Name="LeftStickButton">
<TextBlock <TextBlock
Text="{Binding Config.LeftStickButton, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftStickButton, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -158,7 +161,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftJoystick" Tag="stick"> <ToggleButton Name="LeftJoystick" Tag="stick">
<TextBlock <TextBlock
Text="{Binding Config.LeftJoystick, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftJoystick, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -251,7 +254,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="DpadUp"> <ToggleButton Name="DpadUp">
<TextBlock <TextBlock
Text="{Binding Config.DpadUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.DpadUp, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -268,7 +271,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="DpadDown"> <ToggleButton Name="DpadDown">
<TextBlock <TextBlock
Text="{Binding Config.DpadDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.DpadDown, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -285,7 +288,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="DpadLeft"> <ToggleButton Name="DpadLeft">
<TextBlock <TextBlock
Text="{Binding Config.DpadLeft, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.DpadLeft, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -302,7 +305,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="DpadRight"> <ToggleButton Name="DpadRight">
<TextBlock <TextBlock
Text="{Binding Config.DpadRight, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.DpadRight, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -316,17 +319,103 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<!-- Controller Picture --> <!-- Controller Picture -->
<Image
Margin="0,10"
MaxHeight="300"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Source="{Binding Image}" />
<Border <Border
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
CornerRadius="5" CornerRadius="5"
Margin="0,10"
MinHeight="90"> MinHeight="90">
<StackPanel Orientation="Vertical">
<Image
Margin="5,10"
MaxHeight="300"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Source="{Binding Image}" />
<StackPanel
Margin="10"
Orientation="Horizontal"
Spacing="20"
HorizontalAlignment="Center">
<Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Height="{Binding Visualizer.UiStickBorderSize}"
Width="{Binding Visualizer.UiStickBorderSize}"
IsVisible="{Binding IsLeft}">
<Canvas
Background="{DynamicResource ThemeBackgroundColor}"
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}">
<Grid
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}"
Background="{DynamicResource ThemeBackgroundColor}">
<Ellipse
HorizontalAlignment="Center"
Stroke="Black"
StrokeThickness="1"
Width="{Binding Visualizer.UiCanvasSize}"
Height="{Binding Visualizer.UiCanvasSize}" />
<Ellipse
HorizontalAlignment="Center"
Fill="Gray"
Opacity="100"
Height="{Binding Visualizer.UiDeadzoneLeft}"
Width="{Binding Visualizer.UiDeadzoneLeft}" />
</Grid>
<Ellipse
Fill="Red"
Width="{Binding Visualizer.UiStickCircumference}"
Height="{Binding Visualizer.UiStickCircumference}"
Canvas.Bottom="{Binding Visualizer.UiStickLeftY}"
Canvas.Left="{Binding Visualizer.UiStickLeftX}" />
</Canvas>
</Border>
<Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Height="{Binding Visualizer.UiStickBorderSize}"
Width="{Binding Visualizer.UiStickBorderSize}"
IsVisible="{Binding IsRight}">
<Canvas
Background="{DynamicResource ThemeBackgroundColor}"
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}">
<Grid
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}"
Background="{DynamicResource ThemeBackgroundColor}">
<Ellipse
HorizontalAlignment="Center"
Stroke="Black"
StrokeThickness="1"
Width="{Binding Visualizer.UiCanvasSize}"
Height="{Binding Visualizer.UiCanvasSize}" />
<Ellipse
HorizontalAlignment="Center"
Fill="Gray"
Opacity="100"
Height="{Binding Visualizer.UiDeadzoneRight}"
Width="{Binding Visualizer.UiDeadzoneRight}" />
</Grid>
<Ellipse
Fill="Red"
Width="{Binding Visualizer.UiStickCircumference}"
Height="{Binding Visualizer.UiStickCircumference}"
Canvas.Bottom="{Binding Visualizer.UiStickRightY}"
Canvas.Left="{Binding Visualizer.UiStickRightX}" />
</Canvas>
</Border>
</StackPanel>
</StackPanel>
</Border>
<Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5">
<StackPanel <StackPanel
Margin="8" Margin="8"
Orientation="Vertical"> Orientation="Vertical">
@@ -345,8 +434,8 @@
Minimum="0" Minimum="0"
Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" /> Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" />
<TextBlock <TextBlock
Width="25" Width="25"
Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" /> Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Orientation="Vertical" Orientation="Vertical"
@@ -365,7 +454,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftButtonSr"> <ToggleButton Name="LeftButtonSr">
<TextBlock <TextBlock
Text="{Binding Config.LeftButtonSr, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftButtonSr, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -383,7 +472,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftButtonSl"> <ToggleButton Name="LeftButtonSl">
<TextBlock <TextBlock
Text="{Binding Config.LeftButtonSl, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftButtonSl, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -401,7 +490,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightButtonSr"> <ToggleButton Name="RightButtonSr">
<TextBlock <TextBlock
Text="{Binding Config.RightButtonSr, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightButtonSr, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -419,7 +508,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightButtonSl"> <ToggleButton Name="RightButtonSl">
<TextBlock <TextBlock
Text="{Binding Config.RightButtonSl, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightButtonSl, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -547,7 +636,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonZr"> <ToggleButton Name="ButtonZr">
<TextBlock <TextBlock
Text="{Binding Config.ButtonZr, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonZr, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -565,7 +654,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonR"> <ToggleButton Name="ButtonR">
<TextBlock <TextBlock
Text="{Binding Config.ButtonR, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonR, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -583,7 +672,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonPlus"> <ToggleButton Name="ButtonPlus">
<TextBlock <TextBlock
Text="{Binding Config.ButtonPlus, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonPlus, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -618,7 +707,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonA"> <ToggleButton Name="ButtonA">
<TextBlock <TextBlock
Text="{Binding Config.ButtonA, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonA, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -635,7 +724,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonB"> <ToggleButton Name="ButtonB">
<TextBlock <TextBlock
Text="{Binding Config.ButtonB, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonB, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -652,7 +741,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonX"> <ToggleButton Name="ButtonX">
<TextBlock <TextBlock
Text="{Binding Config.ButtonX, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonX, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -669,7 +758,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonY"> <ToggleButton Name="ButtonY">
<TextBlock <TextBlock
Text="{Binding Config.ButtonY, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonY, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -703,7 +792,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightStickButton"> <ToggleButton Name="RightStickButton">
<TextBlock <TextBlock
Text="{Binding Config.RightStickButton, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightStickButton, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -721,7 +810,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightJoystick" Tag="stick"> <ToggleButton Name="RightJoystick" Tag="stick">
<TextBlock <TextBlock
Text="{Binding Config.RightJoystick, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightJoystick, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>

View File

@@ -18,6 +18,9 @@
<Design.DataContext> <Design.DataContext>
<viewModels:KeyboardInputViewModel /> <viewModels:KeyboardInputViewModel />
</Design.DataContext> </Design.DataContext>
<UserControl.Resources>
<helpers:KeyValueConverter x:Key="Key" />
</UserControl.Resources>
<UserControl.Styles> <UserControl.Styles>
<Style Selector="ToggleButton"> <Style Selector="ToggleButton">
<Setter Property="Width" Value="90" /> <Setter Property="Width" Value="90" />
@@ -73,7 +76,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonZl"> <ToggleButton Name="ButtonZl">
<TextBlock <TextBlock
Text="{Binding Config.ButtonZl, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonZl, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -89,7 +92,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonL"> <ToggleButton Name="ButtonL">
<TextBlock <TextBlock
Text="{Binding Config.ButtonL, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonL, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -105,7 +108,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonMinus"> <ToggleButton Name="ButtonMinus">
<TextBlock <TextBlock
Text="{Binding Config.ButtonMinus, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonMinus, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -140,7 +143,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftStickButton"> <ToggleButton Name="LeftStickButton">
<TextBlock <TextBlock
Text="{Binding Config.LeftStickButton, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftStickButton, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -157,7 +160,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftStickUp"> <ToggleButton Name="LeftStickUp">
<TextBlock <TextBlock
Text="{Binding Config.LeftStickUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftStickUp, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -174,7 +177,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftStickDown"> <ToggleButton Name="LeftStickDown">
<TextBlock <TextBlock
Text="{Binding Config.LeftStickDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftStickDown, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -191,7 +194,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftStickLeft"> <ToggleButton Name="LeftStickLeft">
<TextBlock <TextBlock
Text="{Binding Config.LeftStickLeft, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftStickLeft, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -208,7 +211,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftStickRight"> <ToggleButton Name="LeftStickRight">
<TextBlock <TextBlock
Text="{Binding Config.LeftStickRight, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftStickRight, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -244,7 +247,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="DpadUp"> <ToggleButton Name="DpadUp">
<TextBlock <TextBlock
Text="{Binding Config.DpadUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.DpadUp, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -261,7 +264,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="DpadDown"> <ToggleButton Name="DpadDown">
<TextBlock <TextBlock
Text="{Binding Config.DpadDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.DpadDown, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -278,7 +281,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="DpadLeft"> <ToggleButton Name="DpadLeft">
<TextBlock <TextBlock
Text="{Binding Config.DpadLeft, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.DpadLeft, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -295,7 +298,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="DpadRight"> <ToggleButton Name="DpadRight">
<TextBlock <TextBlock
Text="{Binding Config.DpadRight, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.DpadRight, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -309,12 +312,79 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<!-- Controller Picture --> <!-- Controller Picture -->
<Image <Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Margin="0,10" Margin="0,10"
MaxHeight="300" MinHeight="90">
HorizontalAlignment="Stretch" <StackPanel
VerticalAlignment="Stretch" Margin="10"
Source="{Binding Image}" /> Orientation="Horizontal"
Spacing="20"
HorizontalAlignment="Center">
<Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Height="{Binding Visualizer.UiStickBorderSize}"
Width="{Binding Visualizer.UiStickBorderSize}"
IsVisible="{Binding IsLeft}">
<Canvas
Background="{DynamicResource ThemeBackgroundColor}"
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}">
<Grid
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}"
Background="{DynamicResource ThemeBackgroundColor}">
<Ellipse
HorizontalAlignment="Center"
Stroke="Black"
StrokeThickness="1"
Width="{Binding Visualizer.UiCanvasSize}"
Height="{Binding Visualizer.UiCanvasSize}"/>
</Grid>
<Ellipse
Fill="Red"
Width="{Binding Visualizer.UiStickCircumference}"
Height="{Binding Visualizer.UiStickCircumference}"
Canvas.Bottom="{Binding Visualizer.UiStickLeftY}"
Canvas.Left="{Binding Visualizer.UiStickLeftX}" />
</Canvas>
</Border>
<Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Height="{Binding Visualizer.UiStickBorderSize}"
Width="{Binding Visualizer.UiStickBorderSize}"
IsVisible="{Binding IsRight}">
<Canvas
Background="{DynamicResource ThemeBackgroundColor}"
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}">
<Grid
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}"
Background="{DynamicResource ThemeBackgroundColor}">
<Ellipse
HorizontalAlignment="Center"
Stroke="Black"
StrokeThickness="1"
Width="{Binding Visualizer.UiCanvasSize}"
Height="{Binding Visualizer.UiCanvasSize}"/>
</Grid>
<Ellipse
Fill="Red"
Width="{Binding Visualizer.UiStickCircumference}"
Height="{Binding Visualizer.UiStickCircumference}"
Canvas.Bottom="{Binding Visualizer.UiStickRightY}"
Canvas.Left="{Binding Visualizer.UiStickRightX}" />
</Canvas>
</Border>
</StackPanel>
</Border>
<Border <Border
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
@@ -338,7 +408,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftButtonSr"> <ToggleButton Name="LeftButtonSr">
<TextBlock <TextBlock
Text="{Binding Config.LeftButtonSr, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftButtonSr, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -356,7 +426,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="LeftButtonSl"> <ToggleButton Name="LeftButtonSl">
<TextBlock <TextBlock
Text="{Binding Config.LeftButtonSl, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.LeftButtonSl, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -374,7 +444,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightButtonSr"> <ToggleButton Name="RightButtonSr">
<TextBlock <TextBlock
Text="{Binding Config.RightButtonSr, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightButtonSr, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -392,7 +462,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightButtonSl"> <ToggleButton Name="RightButtonSl">
<TextBlock <TextBlock
Text="{Binding Config.RightButtonSl, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightButtonSl, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -434,7 +504,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonZr"> <ToggleButton Name="ButtonZr">
<TextBlock <TextBlock
Text="{Binding Config.ButtonZr, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonZr, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -452,7 +522,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonR"> <ToggleButton Name="ButtonR">
<TextBlock <TextBlock
Text="{Binding Config.ButtonR, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonR, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -470,7 +540,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonPlus"> <ToggleButton Name="ButtonPlus">
<TextBlock <TextBlock
Text="{Binding Config.ButtonPlus, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonPlus, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -505,7 +575,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonA"> <ToggleButton Name="ButtonA">
<TextBlock <TextBlock
Text="{Binding Config.ButtonA, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonA, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -522,7 +592,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonB"> <ToggleButton Name="ButtonB">
<TextBlock <TextBlock
Text="{Binding Config.ButtonB, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonB, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -539,7 +609,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonX"> <ToggleButton Name="ButtonX">
<TextBlock <TextBlock
Text="{Binding Config.ButtonX, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonX, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -556,7 +626,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="ButtonY"> <ToggleButton Name="ButtonY">
<TextBlock <TextBlock
Text="{Binding Config.ButtonY, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.ButtonY, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -591,7 +661,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightStickButton"> <ToggleButton Name="RightStickButton">
<TextBlock <TextBlock
Text="{Binding Config.RightStickButton, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightStickButton, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -608,7 +678,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightStickUp"> <ToggleButton Name="RightStickUp">
<TextBlock <TextBlock
Text="{Binding Config.RightStickUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightStickUp, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -625,7 +695,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightStickDown"> <ToggleButton Name="RightStickDown">
<TextBlock <TextBlock
Text="{Binding Config.RightStickDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightStickDown, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -642,7 +712,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightStickLeft"> <ToggleButton Name="RightStickLeft">
<TextBlock <TextBlock
Text="{Binding Config.RightStickLeft, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightStickLeft, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
@@ -659,7 +729,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton Name="RightStickRight"> <ToggleButton Name="RightStickRight">
<TextBlock <TextBlock
Text="{Binding Config.RightStickRight, Converter={x:Static helpers:KeyValueConverter.Instance}}" Text="{Binding Config.RightStickRight, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>

View File

@@ -11,7 +11,7 @@
x:Class="Ryujinx.UI.Views.Input.LedInputView"> x:Class="Ryujinx.UI.Views.Input.LedInputView">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center"> <StackPanel Orientation="Vertical" HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal" IsVisible="{Binding ParentModel.CanClearLed}"> <StackPanel Orientation="Horizontal" IsVisible="{Binding ParentModel.CanClearLed}">
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColorDisable}" /> <TextBlock MinWidth="75" MaxWidth="150" Text="{ext:Locale ControllerSettingsLedColorDisable}" />
<CheckBox <CheckBox
Margin="5" Margin="5"
MinWidth="0" MinWidth="0"
@@ -20,33 +20,15 @@
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" IsEnabled="{Binding !TurnOffLed}"> <StackPanel Orientation="Horizontal" IsEnabled="{Binding !TurnOffLed}">
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColorRainbow}" /> <TextBlock MinWidth="75" MaxWidth="150" Text="{ext:Locale ControllerSettingsLedColorRainbow}" />
<CheckBox <CheckBox
Margin="5" Margin="5"
MinWidth="0" MinWidth="0"
IsChecked="{Binding UseRainbowLed, Mode=TwoWay}"> IsChecked="{Binding UseRainbowLed, Mode=TwoWay}">
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" IsEnabled="{Binding !TurnOffLed}">
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColorRainbowSpeed}" />
<Slider HorizontalAlignment="Center"
Value="{Binding RainbowSpeed}"
Width="175"
Margin="0,-3,0,0"
Height="32"
Padding="0,-5"
TickFrequency="0.25"
LargeChange="1"
SmallChange="0.25"
VerticalAlignment="Center"
Minimum="1"
Maximum="10" />
<TextBlock Margin="5,0"
MinWidth="75"
Text="{Binding RainbowSpeedText}" />
</StackPanel>
<StackPanel Orientation="Horizontal" IsEnabled="{Binding ShowLedColorPicker}"> <StackPanel Orientation="Horizontal" IsEnabled="{Binding ShowLedColorPicker}">
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColor}" /> <TextBlock MinWidth="75" MaxWidth="150" Text="{ext:Locale ControllerSettingsLedColor}" />
<ui:ColorPickerButton <ui:ColorPickerButton
Margin="5" Margin="5"
IsMoreButtonVisible="False" IsMoreButtonVisible="False"

View File

@@ -1,9 +1,11 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Ava.UI.Views.Input;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.UI.Views.Input namespace Ryujinx.UI.Views.Input

View File

@@ -134,12 +134,7 @@ namespace Ryujinx.Ava.UI.Views.Main
{ {
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager); Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
Rainbow.Enable();
await Window.SettingsWindow.ShowDialog(Window); await Window.SettingsWindow.ShowDialog(Window);
Rainbow.Disable();
Rainbow.Reset();
Window.SettingsWindow = null; Window.SettingsWindow = null;

View File

@@ -14,6 +14,9 @@
<Design.DataContext> <Design.DataContext>
<viewModels:SettingsViewModel /> <viewModels:SettingsViewModel />
</Design.DataContext> </Design.DataContext>
<UserControl.Resources>
<helpers:KeyValueConverter x:Key="Key" />
</UserControl.Resources>
<UserControl.Styles> <UserControl.Styles>
<Style Selector="StackPanel > StackPanel"> <Style Selector="StackPanel > StackPanel">
<Setter Property="Margin" Value="10, 0, 0, 0" /> <Setter Property="Margin" Value="10, 0, 0, 0" />
@@ -49,67 +52,67 @@
<StackPanel> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" />
<ToggleButton Name="ToggleVSyncMode"> <ToggleButton Name="ToggleVSyncMode">
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" />
<ToggleButton Name="Screenshot"> <ToggleButton Name="Screenshot">
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" />
<ToggleButton Name="ShowUI"> <ToggleButton Name="ShowUI">
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" />
<ToggleButton Name="Pause"> <ToggleButton Name="Pause">
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" />
<ToggleButton Name="ToggleMute"> <ToggleButton Name="ToggleMute">
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" />
<ToggleButton Name="ResScaleUp"> <ToggleButton Name="ResScaleUp">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" />
<ToggleButton Name="ResScaleDown"> <ToggleButton Name="ResScaleDown">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" />
<ToggleButton Name="VolumeUp"> <ToggleButton Name="VolumeUp">
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" />
<ToggleButton Name="VolumeDown"> <ToggleButton Name="VolumeDown">
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal"> <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalIncrement"> <ToggleButton Name="CustomVSyncIntervalIncrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal"> <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" /> <TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalDecrement"> <ToggleButton Name="CustomVSyncIntervalDecrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> <TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

View File

@@ -7,26 +7,23 @@ namespace Ryujinx.Ava.UI.Views.Settings
{ {
public partial class SettingsNetworkView : UserControl public partial class SettingsNetworkView : UserControl
{ {
private readonly Random _random;
public SettingsViewModel ViewModel; public SettingsViewModel ViewModel;
public SettingsNetworkView() public SettingsNetworkView()
{ {
_random = new Random();
InitializeComponent(); InitializeComponent();
} }
private void GenLdnPassButton_OnClick(object sender, RoutedEventArgs e) private void GenLdnPassButton_OnClick(object sender, RoutedEventArgs e)
{ {
byte[] code = new byte[4]; byte[] code = new byte[4];
_random.NextBytes(code); new Random().NextBytes(code);
ViewModel.LdnPassphrase = $"Ryujinx-{BitConverter.ToUInt32(code):x8}"; ViewModel.LdnPassphrase = $"Ryujinx-{BitConverter.ToUInt32(code):x8}";
} }
private void ClearLdnPassButton_OnClick(object sender, RoutedEventArgs e) private void ClearLdnPassButton_OnClick(object sender, RoutedEventArgs e)
{ {
ViewModel.LdnPassphrase = string.Empty; ViewModel.LdnPassphrase = "";
} }
} }
} }

View File

@@ -10,6 +10,9 @@
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
mc:Ignorable="d" mc:Ignorable="d"
x:DataType="viewModels:SettingsViewModel"> x:DataType="viewModels:SettingsViewModel">
<UserControl.Resources>
<helpers:TimeZoneConverter x:Key="TimeZone" />
</UserControl.Resources>
<Design.DataContext> <Design.DataContext>
<viewModels:SettingsViewModel /> <viewModels:SettingsViewModel />
</Design.DataContext> </Design.DataContext>
@@ -159,7 +162,7 @@
Text="{Binding Path=TimeZone, Mode=OneWay}" Text="{Binding Path=TimeZone, Mode=OneWay}"
TextChanged="TimeZoneBox_OnTextChanged" TextChanged="TimeZoneBox_OnTextChanged"
ToolTip.Tip="{ext:Locale TimezoneTooltip}" ToolTip.Tip="{ext:Locale TimezoneTooltip}"
ValueMemberBinding="{Binding Mode=OneWay, Converter={x:Static helpers:TimeZoneConverter.Instance}}" /> ValueMemberBinding="{Binding Mode=OneWay, Converter={StaticResource TimeZone}}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Margin="0,0,0,10" Margin="0,0,0,10"

View File

@@ -14,6 +14,9 @@
mc:Ignorable="d" mc:Ignorable="d"
Focusable="True" Focusable="True"
x:DataType="models:TempProfile"> x:DataType="models:TempProfile">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Grid Margin="0"> <Grid Margin="0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@@ -71,7 +74,7 @@
Margin="0" Margin="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Top"
Source="{Binding Image, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" /> Source="{Binding Image, Converter={StaticResource ByteImage}}" />
</Panel> </Panel>
</Border> </Border>
</StackPanel> </StackPanel>

View File

@@ -17,6 +17,9 @@
<Design.DataContext> <Design.DataContext>
<viewModels:UserFirmwareAvatarSelectorViewModel /> <viewModels:UserFirmwareAvatarSelectorViewModel />
</Design.DataContext> </Design.DataContext>
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Grid <Grid
Margin="0" Margin="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
@@ -59,7 +62,7 @@
<Panel <Panel
Background="{Binding BackgroundColor}" Background="{Binding BackgroundColor}"
Margin="5"> Margin="5">
<Image Source="{Binding Data, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" /> <Image Source="{Binding Data, Converter={StaticResource ByteImage}}" />
</Panel> </Panel>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>

View File

@@ -19,6 +19,9 @@
<Design.DataContext> <Design.DataContext>
<viewModels:UserSaveManagerViewModel /> <viewModels:UserSaveManagerViewModel />
</Design.DataContext> </Design.DataContext>
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -144,7 +147,7 @@
IsVisible="{Binding InGameList}" IsVisible="{Binding InGameList}"
Width="42" Width="42"
Height="42" Height="42"
Source="{Binding Icon, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" /> Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<TextBlock <TextBlock
MaxLines="3" MaxLines="3"
Width="320" Width="320"

View File

@@ -15,6 +15,9 @@
mc:Ignorable="d" mc:Ignorable="d"
Focusable="True" Focusable="True"
x:DataType="viewModels:UserProfileViewModel"> x:DataType="viewModels:UserProfileViewModel">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Design.DataContext> <Design.DataContext>
<viewModels:UserProfileViewModel /> <viewModels:UserProfileViewModel />
</Design.DataContext> </Design.DataContext>
@@ -71,7 +74,7 @@
Height="96" Height="96"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Top"
Source="{Binding Image, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" /> Source="{Binding Image, Converter={StaticResource ByteImage}}" />
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
MaxWidth="90" MaxWidth="90"

View File

@@ -6,8 +6,8 @@
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows" xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
Width="600" Width="500"
Height="750" Height="500"
MinWidth="500" MinWidth="500"
MinHeight="500" MinHeight="500"
x:DataType="window:CheatWindow" x:DataType="window:CheatWindow"

View File

@@ -34,9 +34,6 @@ namespace Ryujinx.Ava.UI.Windows
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath) public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
{ {
MinWidth = 500;
MinHeight = 650;
LoadedCheats = []; LoadedCheats = [];
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid ? IntegrityCheckLevel.ErrorOnInvalid
@@ -64,7 +61,7 @@ namespace Ryujinx.Ava.UI.Windows
ModLoader.ModCache mods = new(); ModLoader.ModCache mods = new();
ModLoader.QueryContentsDir(mods, new DirectoryInfo(Path.Combine(modsBasePath, "contents")), titleIdValue, []); ModLoader.QueryContentsDir(mods, new DirectoryInfo(Path.Combine(modsBasePath, "contents")), titleIdValue);
string currentCheatFile = string.Empty; string currentCheatFile = string.Empty;
string buildId = string.Empty; string buildId = string.Empty;

View File

@@ -30,6 +30,9 @@
<Design.DataContext> <Design.DataContext>
<viewModels:MainWindowViewModel /> <viewModels:MainWindowViewModel />
</Design.DataContext> </Design.DataContext>
<Window.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</Window.Resources>
<Window.KeyBindings> <Window.KeyBindings>
<KeyBinding Gesture="Alt+Return" Command="{Binding ToggleFullscreen}" /> <KeyBinding Gesture="Alt+Return" Command="{Binding ToggleFullscreen}" />
<KeyBinding Gesture="F11" Command="{Binding ToggleFullscreen}" /> <KeyBinding Gesture="F11" Command="{Binding ToggleFullscreen}" />
@@ -118,7 +121,7 @@
Width="256" Width="256"
Height="256" Height="256"
IsVisible="{Binding ShowLoadProgress}" IsVisible="{Binding ShowLoadProgress}"
Source="{Binding SelectedIcon, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" /> Source="{Binding SelectedIcon, Converter={StaticResource ByteImage}}" />
</Border> </Border>
<Grid <Grid
Grid.Column="1" Grid.Column="1"

View File

@@ -6,7 +6,6 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using System.Threading.Tasks; using System.Threading.Tasks;
using Button = Avalonia.Controls.Button; using Button = Avalonia.Controls.Button;
@@ -24,21 +23,21 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent(); InitializeComponent();
} }
public ModManagerWindow(ulong titleId, ulong titleIdBase, ApplicationLibrary applicationLibrary) public ModManagerWindow(ulong titleId)
{ {
DataContext = ViewModel = new ModManagerViewModel(titleId, titleIdBase, applicationLibrary); DataContext = ViewModel = new ModManagerViewModel(titleId);
InitializeComponent(); InitializeComponent();
} }
public static async Task Show(ulong titleId, ulong titleIdBase, ApplicationLibrary appLibrary, string titleName) public static async Task Show(ulong titleId, string titleName)
{ {
ContentDialog contentDialog = new() ContentDialog contentDialog = new()
{ {
PrimaryButtonText = string.Empty, PrimaryButtonText = string.Empty,
SecondaryButtonText = string.Empty, SecondaryButtonText = string.Empty,
CloseButtonText = string.Empty, CloseButtonText = string.Empty,
Content = new ModManagerWindow(titleId, titleIdBase, appLibrary), Content = new ModManagerWindow(titleId),
Title = string.Format(LocaleManager.Instance[LocaleKeys.ModWindowTitle], titleName, titleId.ToString("X16")), Title = string.Format(LocaleManager.Instance[LocaleKeys.ModWindowTitle], titleName, titleId.ToString("X16")),
}; };

View File

@@ -11,7 +11,7 @@
xmlns:settings="clr-namespace:Ryujinx.Ava.UI.Views.Settings" xmlns:settings="clr-namespace:Ryujinx.Ava.UI.Views.Settings"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
Width="1100" Width="1100"
Height="768" Height="850"
MinWidth="800" MinWidth="800"
MinHeight="480" MinHeight="480"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"

View File

@@ -34,6 +34,7 @@ namespace Ryujinx.Ava.UI.Windows
#if DEBUG #if DEBUG
this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Alt)); this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Alt));
#endif #endif
} }
public SettingsWindow() public SettingsWindow()

View File

@@ -14,6 +14,9 @@
mc:Ignorable="d" mc:Ignorable="d"
x:DataType="viewModels:TitleUpdateViewModel" x:DataType="viewModels:TitleUpdateViewModel"
Focusable="True"> Focusable="True">
<UserControl.Resources>
<helpers:TitleUpdateLabelConverter x:Key="TitleUpdateLabel" />
</UserControl.Resources>
<Grid RowDefinitions="Auto,*,Auto"> <Grid RowDefinitions="Auto,*,Auto">
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
@@ -54,7 +57,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
TextWrapping="Wrap"> TextWrapping="Wrap">
<TextBlock.Text> <TextBlock.Text>
<MultiBinding Converter="{x:Static helpers:TitleUpdateLabelConverter.Instance}"> <MultiBinding Converter="{StaticResource TitleUpdateLabel}">
<Binding Path="DisplayVersion" /> <Binding Path="DisplayVersion" />
<Binding Path="IsBundled" /> <Binding Path="IsBundled" />
</MultiBinding> </MultiBinding>

View File

@@ -13,6 +13,11 @@
x:DataType="viewModels:XCITrimmerViewModel" x:DataType="viewModels:XCITrimmerViewModel"
Focusable="True" Focusable="True"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources>
<helpers:XCITrimmerFileStatusConverter x:Key="StatusLabel" />
<helpers:XCITrimmerFileStatusDetailConverter x:Key="StatusDetailLabel" />
<helpers:XCITrimmerFileSpaceSavingsConverter x:Key="SpaceSavingsLabel" />
</UserControl.Resources>
<Grid Margin="20 0 20 0"> <Grid Margin="20 0 20 0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -181,7 +186,7 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
MaxLines="1" MaxLines="1"
Text="{Binding ., Converter={x:Static helpers:XCITrimmerFileStatusConverter.Instance}}"> Text="{Binding ., Converter={StaticResource StatusLabel}}">
<ToolTip.Tip> <ToolTip.Tip>
<StackPanel <StackPanel
IsVisible="{Binding IsFailed}"> IsVisible="{Binding IsFailed}">
@@ -189,7 +194,7 @@
Classes="h1" Classes="h1"
Text="{ext:Locale XCITrimmerTitleStatusFailed}" /> Text="{ext:Locale XCITrimmerTitleStatusFailed}" />
<TextBlock <TextBlock
Text="{Binding ., Converter={x:Static helpers:XCITrimmerFileStatusDetailConverter.Instance}}" Text="{Binding ., Converter={StaticResource StatusDetailLabel}}"
MaxLines="5" MaxLines="5"
MaxWidth="200" MaxWidth="200"
MaxHeight="100" MaxHeight="100"
@@ -204,7 +209,7 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
MaxLines="1" MaxLines="1"
Text="{Binding ., Converter={x:Static helpers:XCITrimmerFileSpaceSavingsConverter.Instance}}">> Text="{Binding ., Converter={StaticResource SpaceSavingsLabel}}">>
</TextBlock> </TextBlock>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -10,7 +10,6 @@ namespace Ryujinx.Ava.Utilities
public static bool? OverrideDockedMode { get; private set; } public static bool? OverrideDockedMode { get; private set; }
public static bool? OverrideHardwareAcceleration { get; private set; } public static bool? OverrideHardwareAcceleration { get; private set; }
public static string OverrideGraphicsBackend { get; private set; } public static string OverrideGraphicsBackend { get; private set; }
public static string OverrideBackendThreading { get; private set; }
public static string OverrideHideCursor { get; private set; } public static string OverrideHideCursor { get; private set; }
public static string BaseDirPathArg { get; private set; } public static string BaseDirPathArg { get; private set; }
public static string Profile { get; private set; } public static string Profile { get; private set; }
@@ -75,16 +74,6 @@ namespace Ryujinx.Ava.Utilities
OverrideGraphicsBackend = args[++i]; OverrideGraphicsBackend = args[++i];
break; break;
case "--backend-threading":
if (i + 1 >= args.Length)
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
OverrideBackendThreading = args[++i];
break;
case "-i": case "-i":
case "--application-id": case "--application-id":
LaunchApplicationId = args[++i]; LaunchApplicationId = args[++i];

View File

@@ -1,5 +1,6 @@
using Ryujinx.Ava.Utilities.Configuration.System; using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI; using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Configuration.Multiplayer;
@@ -7,6 +8,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE; using Ryujinx.HLE;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Nodes;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.Ava.Utilities.Configuration
{ {
@@ -15,7 +17,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 62; public const int CurrentVersion = 61;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format
@@ -374,15 +376,24 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// </summary> /// </summary>
public KeyboardHotkeys Hotkeys { get; set; } public KeyboardHotkeys Hotkeys { get; set; }
/// <summary>
/// Legacy keyboard control bindings
/// </summary>
/// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks>
/// TODO: Remove this when those older versions aren't in use anymore.
public List<JsonObject> KeyboardConfig { get; set; }
/// <summary>
/// Legacy controller control bindings
/// </summary>
/// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks>
/// TODO: Remove this when those older versions aren't in use anymore.
public List<JsonObject> ControllerConfig { get; set; }
/// <summary> /// <summary>
/// Input configurations /// Input configurations
/// </summary> /// </summary>
public List<InputConfig> InputConfig { get; set; } public List<InputConfig> InputConfig { get; set; }
/// <summary>
/// The speed of spectrum cycling for the Rainbow LED feature.
/// </summary>
public float RainbowSpeed { get; set; }
/// <summary> /// <summary>
/// Graphics backend /// Graphics backend

View File

@@ -140,7 +140,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
Hid.EnableMouse.Value = cff.EnableMouse; Hid.EnableMouse.Value = cff.EnableMouse;
Hid.Hotkeys.Value = cff.Hotkeys; Hid.Hotkeys.Value = cff.Hotkeys;
Hid.InputConfig.Value = cff.InputConfig ?? []; Hid.InputConfig.Value = cff.InputConfig ?? [];
Hid.RainbowSpeed.Value = cff.RainbowSpeed;
Multiplayer.LanInterfaceId.Value = cff.MultiplayerLanInterfaceId; Multiplayer.LanInterfaceId.Value = cff.MultiplayerLanInterfaceId;
Multiplayer.Mode.Value = cff.MultiplayerMode; Multiplayer.Mode.Value = cff.MultiplayerMode;
@@ -428,8 +427,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
LedColor = new Color(255, 5, 1, 253).ToUInt32() LedColor = new Color(255, 5, 1, 253).ToUInt32()
}; };
} }
}), })
(62, static cff => cff.RainbowSpeed = 1f)
); );
} }
} }

View File

@@ -7,7 +7,6 @@ using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE; using Ryujinx.HLE;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -445,11 +444,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// TODO: Implement a ReactiveList class. /// TODO: Implement a ReactiveList class.
/// </summary> /// </summary>
public ReactiveObject<List<InputConfig>> InputConfig { get; private set; } public ReactiveObject<List<InputConfig>> InputConfig { get; private set; }
/// <summary>
/// The speed of spectrum cycling for the Rainbow LED feature.
/// </summary>
public ReactiveObject<float> RainbowSpeed { get; }
public HidSection() public HidSection()
{ {
@@ -457,8 +451,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
EnableMouse = new ReactiveObject<bool>(); EnableMouse = new ReactiveObject<bool>();
Hotkeys = new ReactiveObject<KeyboardHotkeys>(); Hotkeys = new ReactiveObject<KeyboardHotkeys>();
InputConfig = new ReactiveObject<List<InputConfig>>(); InputConfig = new ReactiveObject<List<InputConfig>>();
RainbowSpeed = new ReactiveObject<float>();
RainbowSpeed.Event += (_, args) => Rainbow.Speed = args.NewValue;
} }
} }

View File

@@ -130,8 +130,9 @@ namespace Ryujinx.Ava.Utilities.Configuration
EnableKeyboard = Hid.EnableKeyboard, EnableKeyboard = Hid.EnableKeyboard,
EnableMouse = Hid.EnableMouse, EnableMouse = Hid.EnableMouse,
Hotkeys = Hid.Hotkeys, Hotkeys = Hid.Hotkeys,
KeyboardConfig = [],
ControllerConfig = [],
InputConfig = Hid.InputConfig, InputConfig = Hid.InputConfig,
RainbowSpeed = Hid.RainbowSpeed,
GraphicsBackend = Graphics.GraphicsBackend, GraphicsBackend = Graphics.GraphicsBackend,
PreferredGpu = Graphics.PreferredGpu, PreferredGpu = Graphics.PreferredGpu,
MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId, MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId,
@@ -203,8 +204,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
Multiplayer.LanInterfaceId.Value = "0"; Multiplayer.LanInterfaceId.Value = "0";
Multiplayer.Mode.Value = MultiplayerMode.Disabled; Multiplayer.Mode.Value = MultiplayerMode.Disabled;
Multiplayer.DisableP2p.Value = false; Multiplayer.DisableP2p.Value = false;
Multiplayer.LdnPassphrase.Value = string.Empty; Multiplayer.LdnPassphrase.Value = "";
Multiplayer.LdnServer.Value = string.Empty; Multiplayer.LdnServer.Value = "";
UI.GuiColumns.FavColumn.Value = true; UI.GuiColumns.FavColumn.Value = true;
UI.GuiColumns.IconColumn.Value = true; UI.GuiColumns.IconColumn.Value = true;
UI.GuiColumns.AppColumn.Value = true; UI.GuiColumns.AppColumn.Value = true;
@@ -254,7 +255,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
VolumeUp = Key.Unbound, VolumeUp = Key.Unbound,
VolumeDown = Key.Unbound, VolumeDown = Key.Unbound,
}; };
Hid.RainbowSpeed.Value = 1f;
Hid.InputConfig.Value = Hid.InputConfig.Value =
[ [
new StandardKeyboardInputConfig new StandardKeyboardInputConfig