Merge remote-tracking branch 'upstream/master' into validation-v2

This commit is contained in:
LotP1 2024-12-28 01:35:52 +01:00
commit 4f206d0e73
29 changed files with 150 additions and 95 deletions

View File

@ -21,9 +21,9 @@ env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.2" RYUJINX_BASE_VERSION: "1.2"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary" RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev" RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx" RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx-Canary" RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Canary-Releases"
RELEASE: 1 RELEASE: 1
jobs: jobs:

View File

@ -21,7 +21,7 @@ env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.2" RYUJINX_BASE_VERSION: "1.2"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release" RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev" RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx" RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
RELEASE: 1 RELEASE: 1

View File

@ -86,6 +86,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "s
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal.SharpMetalExtensions", "src/Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj", "{81EA598C-DBA1-40B0-8DA4-4796B78F2037}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
@ -95,8 +99,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\release.yml = .github\workflows\release.yml .github\workflows\release.yml = .github\workflows\release.yml
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -265,6 +267,10 @@ Global
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU {4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -1,3 +1,4 @@
using Ryujinx.Common.Configuration;
using System; using System;
namespace Ryujinx.Graphics.GAL namespace Ryujinx.Graphics.GAL

View File

@ -1,3 +1,4 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Window; using Ryujinx.Graphics.GAL.Multithreading.Commands.Window;
using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources; using Ryujinx.Graphics.GAL.Multithreading.Resources;

View File

@ -1,9 +0,0 @@
namespace Ryujinx.Graphics.GAL
{
public enum VSyncMode
{
Switch,
Unbounded,
Custom
}
}

View File

@ -0,0 +1,30 @@
using SharpMetal;
using SharpMetal.ObjectiveCCore;
using SharpMetal.QuartzCore;
using System.Runtime.Versioning;
// ReSharper disable InconsistentNaming
namespace Ryujinx.Graphics.Metal.SharpMetalExtensions
{
[SupportedOSPlatform("macOS")]
public static class CAMetalLayerExtensions
{
private static readonly Selector sel_displaySyncEnabled = "displaySyncEnabled";
private static readonly Selector sel_setDisplaySyncEnabled = "setDisplaySyncEnabled:";
private static readonly Selector sel_developerHUDProperties = "developerHUDProperties";
private static readonly Selector sel_setDeveloperHUDProperties = "setDeveloperHUDProperties:";
public static bool IsDisplaySyncEnabled(this CAMetalLayer metalLayer)
=> ObjectiveCRuntime.bool_objc_msgSend(metalLayer.NativePtr, sel_displaySyncEnabled);
public static void SetDisplaySyncEnabled(this CAMetalLayer metalLayer, bool enabled)
=> ObjectiveCRuntime.objc_msgSend(metalLayer.NativePtr, sel_setDisplaySyncEnabled, enabled);
public static nint GetDeveloperHudProperties(this CAMetalLayer metalLayer)
=> ObjectiveCRuntime.IntPtr_objc_msgSend(metalLayer.NativePtr, sel_developerHUDProperties);
public static void SetDeveloperHudProperties(this CAMetalLayer metalLayer, nint dictionaryPointer)
=> ObjectiveCRuntime.objc_msgSend(metalLayer.NativePtr, sel_setDeveloperHUDProperties, dictionaryPointer);
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpMetal" />
</ItemGroup>
</Project>

View File

@ -5,12 +5,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
</ItemGroup> <ProjectReference Include="..\Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj" />
<ItemGroup>
<PackageReference Include="SharpMetal" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,10 +1,14 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Metal.Effects; using Ryujinx.Graphics.Metal.Effects;
using Ryujinx.Graphics.Metal.SharpMetalExtensions;
using SharpMetal.ObjectiveCCore; using SharpMetal.ObjectiveCCore;
using SharpMetal.QuartzCore; using SharpMetal.QuartzCore;
using System; using System;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using AntiAliasing = Ryujinx.Graphics.GAL.AntiAliasing;
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
namespace Ryujinx.Graphics.Metal namespace Ryujinx.Graphics.Metal
{ {
@ -140,7 +144,15 @@ namespace Ryujinx.Graphics.Metal
public void ChangeVSyncMode(VSyncMode vSyncMode) public void ChangeVSyncMode(VSyncMode vSyncMode)
{ {
//_vSyncMode = vSyncMode; switch (vSyncMode)
{
case VSyncMode.Unbounded:
_metalLayer.SetDisplaySyncEnabled(false);
break;
case VSyncMode.Switch:
_metalLayer.SetDisplaySyncEnabled(true);
break;
}
} }
public void SetAntiAliasing(AntiAliasing effect) public void SetAntiAliasing(AntiAliasing effect)

View File

@ -1,9 +1,12 @@
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Effects; using Ryujinx.Graphics.OpenGL.Effects;
using Ryujinx.Graphics.OpenGL.Effects.Smaa; using Ryujinx.Graphics.OpenGL.Effects.Smaa;
using Ryujinx.Graphics.OpenGL.Image; using Ryujinx.Graphics.OpenGL.Image;
using System; using System;
using AntiAliasing = Ryujinx.Graphics.GAL.AntiAliasing;
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
namespace Ryujinx.Graphics.OpenGL namespace Ryujinx.Graphics.OpenGL
{ {

View File

@ -1,9 +1,12 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Vulkan.Effects; using Ryujinx.Graphics.Vulkan.Effects;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.KHR; using Silk.NET.Vulkan.Extensions.KHR;
using System; using System;
using System.Linq; using System.Linq;
using AntiAliasing = Ryujinx.Graphics.GAL.AntiAliasing;
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
using VkFormat = Silk.NET.Vulkan.Format; using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan

View File

@ -1,5 +1,8 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using System; using System;
using AntiAliasing = Ryujinx.Graphics.GAL.AntiAliasing;
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {

View File

@ -9,7 +9,6 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.UI; using Ryujinx.HLE.UI;
using System; using System;
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
namespace Ryujinx.HLE namespace Ryujinx.HLE
{ {

View File

@ -10,7 +10,6 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{ {

View File

@ -36,6 +36,8 @@ namespace Ryujinx.UI.App.Common
public string Path { get; set; } public string Path { get; set; }
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; } public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
public bool HasControlHolder => ControlHolder.ByteSpan.Length > 0;
public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed); public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed);
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n") ?? LocalizedNever(); public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n") ?? LocalizedNever();

View File

@ -789,16 +789,15 @@ namespace Ryujinx.UI.App.Common
using HttpClient httpClient = new HttpClient(); using HttpClient httpClient = new HttpClient();
string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games"); string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData); ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
var evt = new LdnGameDataReceivedEventArgs LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs
{ {
LdnData = ldnGameDataArray LdnData = ldnGameDataArray
}; });
LdnGameDataReceived?.Invoke(null, evt);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}"); Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}");
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs() LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs
{ {
LdnData = Array.Empty<LdnGameData>() LdnData = Array.Empty<LdnGameData>()
}); });
@ -806,7 +805,7 @@ namespace Ryujinx.UI.App.Common
} }
else else
{ {
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs() LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs
{ {
LdnData = Array.Empty<LdnGameData>() LdnData = Array.Empty<LdnGameData>()
}); });

View File

@ -0,0 +1,24 @@
using LibHac.Ns;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.UI.App.Common
{
public class LdnGameDataArray
{
private readonly LdnGameData[] _ldnDatas;
public LdnGameDataArray(IEnumerable<LdnGameData> receivedData, ref ApplicationControlProperty acp)
{
LibHac.Common.FixedArrays.Array8<ulong> communicationId = acp.LocalCommunicationId;
_ldnDatas = receivedData.Where(game =>
communicationId.Items.Contains(Convert.ToUInt64(game.TitleId, 16))
).ToArray();
}
public int PlayerCount => _ldnDatas.Sum(it => it.PlayerCount);
public int GameCount => _ldnDatas.Length;
}
}

View File

@ -311,7 +311,7 @@ namespace Ryujinx.Ava
Device.VSyncMode = e.NewValue; Device.VSyncMode = e.NewValue;
Device.UpdateVSyncInterval(); Device.UpdateVSyncInterval();
} }
_renderer.Window?.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)e.NewValue); _renderer.Window?.ChangeVSyncMode(e.NewValue);
_viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom); _viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom);
} }
@ -1074,7 +1074,7 @@ namespace Ryujinx.Ava
Device.Gpu.SetGpuThread(); Device.Gpu.SetGpuThread();
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
_renderer.Window.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)Device.VSyncMode); _renderer.Window.ChangeVSyncMode(Device.VSyncMode);
while (_isActive) while (_isActive)
{ {

View File

@ -33,11 +33,8 @@ namespace Ryujinx.Ava
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>() .ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>()
.MainWindow.Cast<MainWindow>(); .MainWindow.Cast<MainWindow>();
public static bool IsClipboardAvailable(out IClipboard clipboard) public static bool IsClipboardAvailable(out IClipboard clipboard)
{ => (clipboard = MainWindow.Clipboard) != null;
clipboard = MainWindow.Clipboard;
return clipboard != null;
}
public static void SetTaskbarProgress(TaskBarProgressBarState state) => MainWindow.PlatformFeatures.SetTaskBarProgressBarState(state); public static void SetTaskbarProgress(TaskBarProgressBarState state) => MainWindow.PlatformFeatures.SetTaskBarProgressBarState(state);
public static void SetTaskbarProgressValue(ulong current, ulong total) => MainWindow.PlatformFeatures.SetTaskBarProgressBarValue(current, total); public static void SetTaskbarProgressValue(ulong current, ulong total) => MainWindow.PlatformFeatures.SetTaskBarProgressBarValue(current, total);

View File

@ -19,7 +19,7 @@ namespace Ryujinx.Ava.UI.Helpers
AvaLogger.Sink = new LoggerAdapter(); AvaLogger.Sink = new LoggerAdapter();
} }
private static RyuLogger.Log? GetLog(AvaLogLevel level) private static RyuLogger.Log? GetLog(AvaLogLevel level, string area)
{ {
return level switch return level switch
{ {
@ -27,7 +27,7 @@ namespace Ryujinx.Ava.UI.Helpers
AvaLogLevel.Debug => RyuLogger.Debug, AvaLogLevel.Debug => RyuLogger.Debug,
AvaLogLevel.Information => RyuLogger.Debug, AvaLogLevel.Information => RyuLogger.Debug,
AvaLogLevel.Warning => RyuLogger.Debug, AvaLogLevel.Warning => RyuLogger.Debug,
AvaLogLevel.Error => RyuLogger.Error, AvaLogLevel.Error => area is "IME" ? RyuLogger.Debug : RyuLogger.Error,
AvaLogLevel.Fatal => RyuLogger.Error, AvaLogLevel.Fatal => RyuLogger.Error,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null), _ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
}; };
@ -35,17 +35,17 @@ namespace Ryujinx.Ava.UI.Helpers
public bool IsEnabled(AvaLogLevel level, string area) public bool IsEnabled(AvaLogLevel level, string area)
{ {
return GetLog(level) != null; return GetLog(level, area) != null;
} }
public void Log(AvaLogLevel level, string area, object source, string messageTemplate) public void Log(AvaLogLevel level, string area, object source, string messageTemplate)
{ {
GetLog(level)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, null)); GetLog(level, area)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, null));
} }
public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues) public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
{ {
GetLog(level)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, propertyValues)); GetLog(level, area)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, propertyValues));
} }
private static string Format(AvaLogLevel level, string area, string template, object source, object[] v) private static string Format(AvaLogLevel level, string area, string template, object source, object[] v)

View File

@ -1,3 +1,4 @@
using Gommon;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Ncm; using LibHac.Ncm;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
@ -47,7 +48,7 @@ namespace Ryujinx.Ava.UI.Models
TitleId = info.ProgramId; TitleId = info.ProgramId;
UserId = info.UserId; UserId = info.UserId;
var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.IdString.Equals(TitleIdString, StringComparison.OrdinalIgnoreCase)); var appData = RyujinxApp.MainWindow.ViewModel.Applications.FirstOrDefault(x => x.IdString.EqualsIgnoreCase(TitleIdString));
InGameList = appData != null; InGameList = appData != null;

View File

@ -9,6 +9,7 @@ using Avalonia.Threading;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Gommon;
using LibHac.Common; using LibHac.Common;
using LibHac.Ns; using LibHac.Ns;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
@ -109,13 +110,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool _areMimeTypesRegistered = FileAssociationHelper.AreMimeTypesRegistered; private bool _areMimeTypesRegistered = FileAssociationHelper.AreMimeTypesRegistered;
private bool _canUpdate = true; private bool _canUpdate = true;
private Cursor _cursor;
private string _title;
private ApplicationData _currentApplicationData; private ApplicationData _currentApplicationData;
private readonly AutoResetEvent _rendererWaitEvent; private readonly AutoResetEvent _rendererWaitEvent;
private WindowState _windowState;
private double _windowWidth;
private double _windowHeight;
private int _customVSyncInterval; private int _customVSyncInterval;
private int _customVSyncIntervalPercentageProxy; private int _customVSyncIntervalPercentageProxy;
@ -124,8 +120,9 @@ namespace Ryujinx.Ava.UI.ViewModels
public ApplicationData ListSelectedApplication; public ApplicationData ListSelectedApplication;
public ApplicationData GridSelectedApplication; public ApplicationData GridSelectedApplication;
public IEnumerable<LdnGameData> LastLdnGameData; // Key is Title ID
public SafeDictionary<string, LdnGameDataArray> LdnData = [];
// The UI specifically uses a thicker bordered variant of the icon to avoid crunching out the border at lower resolutions. // The UI specifically uses a thicker bordered variant of the icon to avoid crunching out the border at lower resolutions.
// For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left. // For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left.
@ -216,7 +213,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool CanUpdate public bool CanUpdate
{ {
get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate(false); get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate();
set set
{ {
_canUpdate = value; _canUpdate = value;
@ -226,12 +223,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public Cursor Cursor public Cursor Cursor
{ {
get => _cursor; get => Window.Cursor;
set set => Window.Cursor = value;
{
_cursor = value;
OnPropertyChanged();
}
} }
public ReadOnlyObservableCollection<ApplicationData> AppsObservableList public ReadOnlyObservableCollection<ApplicationData> AppsObservableList
@ -813,35 +806,23 @@ namespace Ryujinx.Ava.UI.ViewModels
public WindowState WindowState public WindowState WindowState
{ {
get => _windowState; get => Window.WindowState;
internal set internal set
{ {
_windowState = value; Window.WindowState = value;
OnPropertyChanged();
} }
} }
public double WindowWidth public double WindowWidth
{ {
get => _windowWidth; get => Window.Width;
set set => Window.Width = value;
{
_windowWidth = value;
OnPropertyChanged();
}
} }
public double WindowHeight public double WindowHeight
{ {
get => _windowHeight; get => Window.Height;
set set => Window.Height = value;
{
_windowHeight = value;
OnPropertyChanged();
}
} }
public bool IsGrid => Glyph == Glyph.Grid; public bool IsGrid => Glyph == Glyph.Grid;
@ -889,11 +870,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public string Title public string Title
{ {
get => _title; get => Window.Title;
set set
{ {
_title = value; Window.Title = value;
OnPropertyChanged(); OnPropertyChanged();
} }
} }

View File

@ -750,7 +750,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.ToFileFormat().SaveConfig(Program.ConfigurationPath); config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
MainWindow.UpdateGraphicsConfig(); MainWindow.UpdateGraphicsConfig();
MainWindow.MainWindowViewModel.VSyncModeSettingChanged(); RyujinxApp.MainWindow.ViewModel.VSyncModeSettingChanged();
SaveSettingsEvent?.Invoke(); SaveSettingsEvent?.Invoke();

View File

@ -17,8 +17,7 @@
Margin="7, 0" Margin="7, 0"
Height="25" Height="25"
Width="25" Width="25"
ToolTip.Tip="{Binding Title}" ToolTip.Tip="{Binding Title}" />
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx_AntiAlias.png?assembly=Ryujinx.UI.Common" />
<Menu <Menu
Name="Menu" Name="Menu"
Height="32" Height="32"

View File

@ -29,6 +29,7 @@ namespace Ryujinx.Ava.UI.Views.Main
InitializeComponent(); InitializeComponent();
RyuLogo.IsVisible = !ConfigurationState.Instance.ShowTitleBar; RyuLogo.IsVisible = !ConfigurationState.Instance.ShowTitleBar;
RyuLogo.Source = MainWindowViewModel.IconBitmap;
ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems(); ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems();
ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems(); ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems();

View File

@ -7,7 +7,6 @@
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:local="clr-namespace:Ryujinx.Ava.UI.Views.Main"
xmlns:config="clr-namespace:Ryujinx.Common.Configuration;assembly=Ryujinx.Common" xmlns:config="clr-namespace:Ryujinx.Common.Configuration;assembly=Ryujinx.Common"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView" x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView"

View File

@ -9,11 +9,6 @@
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:main="clr-namespace:Ryujinx.Ava.UI.Views.Main" xmlns:main="clr-namespace:Ryujinx.Ava.UI.Views.Main"
Cursor="{Binding Cursor}"
Title="{Binding Title}"
WindowState="{Binding WindowState}"
Width="{Binding WindowWidth}"
Height="{Binding WindowHeight}"
MinWidth="800" MinWidth="800"
MinHeight="500" MinHeight="500"
d:DesignHeight="720" d:DesignHeight="720"

View File

@ -39,8 +39,6 @@ namespace Ryujinx.Ava.UI.Windows
{ {
public partial class MainWindow : StyleableAppWindow public partial class MainWindow : StyleableAppWindow
{ {
internal static MainWindowViewModel MainWindowViewModel { get; private set; }
public MainWindowViewModel ViewModel { get; } public MainWindowViewModel ViewModel { get; }
internal readonly AvaHostUIHandler UiHandler; internal readonly AvaHostUIHandler UiHandler;
@ -76,7 +74,7 @@ namespace Ryujinx.Ava.UI.Windows
public MainWindow() public MainWindow()
{ {
DataContext = ViewModel = MainWindowViewModel = new MainWindowViewModel DataContext = ViewModel = new MainWindowViewModel
{ {
Window = this Window = this
}; };
@ -169,24 +167,28 @@ namespace Ryujinx.Ava.UI.Windows
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
var ldnGameDataArray = e.LdnData; var ldnGameDataArray = e.LdnData.ToList();
ViewModel.LastLdnGameData = ldnGameDataArray; ViewModel.LdnData.Clear();
foreach (var application in ViewModel.Applications) foreach (var application in ViewModel.Applications)
{ {
ViewModel.LdnData[application.IdString] = new LdnGameDataArray(
ldnGameDataArray,
ref application.ControlHolder.Value
);
UpdateApplicationWithLdnData(application); UpdateApplicationWithLdnData(application);
} }
ViewModel.RefreshView(); ViewModel.RefreshView();
}); });
} }
private void UpdateApplicationWithLdnData(ApplicationData application) private void UpdateApplicationWithLdnData(ApplicationData application)
{ {
if (application.ControlHolder.ByteSpan.Length > 0 && ViewModel.LastLdnGameData != null) if (application.HasControlHolder && ViewModel.LdnData.TryGetValue(application.IdString, out var ldnGameDatas))
{ {
IEnumerable<LdnGameData> ldnGameData = ViewModel.LastLdnGameData.Where(game => application.ControlHolder.Value.LocalCommunicationId.Items.Contains(Convert.ToUInt64(game.TitleId, 16))); application.PlayerCount = ldnGameDatas.PlayerCount;
application.GameCount = ldnGameDatas.GameCount;
application.PlayerCount = ldnGameData.Sum(game => game.PlayerCount);
application.GameCount = ldnGameData.Count();
} }
else else
{ {