aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml6
-rw-r--r--ARMeilleure/ARMeilleure.csproj4
-rw-r--r--README.md2
-rw-r--r--Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj2
-rw-r--r--Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj2
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj2
-rw-r--r--Ryujinx.Audio.Backends/Ryujinx.Audio.Backends.csproj2
-rw-r--r--Ryujinx.Audio/Ryujinx.Audio.csproj2
-rw-r--r--Ryujinx.Common/Ryujinx.Common.csproj6
-rw-r--r--Ryujinx.Common/System/ForceDpiAware.cs15
-rw-r--r--Ryujinx.Cpu/Ryujinx.Cpu.csproj2
-rw-r--r--Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj2
-rw-r--r--Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj2
-rw-r--r--Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj6
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs24
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs38
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheMigration.cs46
-rw-r--r--Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj2
-rw-r--r--Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj2
-rw-r--r--Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj2
-rw-r--r--Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj2
-rw-r--r--Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj2
-rw-r--r--Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj2
-rw-r--r--Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj2
-rw-r--r--Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj2
-rw-r--r--Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj2
-rw-r--r--Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs4
-rw-r--r--Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs729
-rw-r--r--Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs585
-rw-r--r--Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs22
-rw-r--r--Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs3
-rw-r--r--Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs2
-rw-r--r--Ryujinx.HLE/Ryujinx.HLE.csproj3
-rw-r--r--Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs3
-rw-r--r--Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj2
-rw-r--r--Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj2
-rw-r--r--Ryujinx.Input/Ryujinx.Input.csproj2
-rw-r--r--Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj2
-rw-r--r--Ryujinx.Memory/Ryujinx.Memory.csproj4
-rw-r--r--Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj2
-rw-r--r--Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj2
-rw-r--r--Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj2
-rw-r--r--Ryujinx.Tests/Ryujinx.Tests.csproj2
-rw-r--r--Ryujinx/Modules/Updater/Updater.cs76
-rw-r--r--Ryujinx/Program.cs3
-rw-r--r--Ryujinx/Ryujinx.csproj5
-rw-r--r--Ryujinx/Ui/Windows/AmiiboWindow.cs6
-rw-r--r--appveyor.yml8
-rw-r--r--global.json4
51 files changed, 913 insertions, 762 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fcb8c2af..ca1e0f1d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -50,7 +50,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with:
- dotnet-version: 5.0.x
+ dotnet-version: 6.0.x
- name: Ensure NuGet Source
uses: fabriciomurta/ensure-nuget-source@v1
- name: Get git short hash
@@ -63,10 +63,10 @@ jobs:
- name: Test
run: dotnet test -c "${{ matrix.configuration }}"
- name: Publish Ryujinx
- run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx
+ run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
if: github.event_name == 'pull_request'
- name: Publish Ryujinx.Headless.SDL2
- run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2
+ run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
if: github.event_name == 'pull_request'
- name: Upload Ryujinx artifact
uses: actions/upload-artifact@v2
diff --git a/ARMeilleure/ARMeilleure.csproj b/ARMeilleure/ARMeilleure.csproj
index ebc4433a..1fd95ad0 100644
--- a/ARMeilleure/ARMeilleure.csproj
+++ b/ARMeilleure/ARMeilleure.csproj
@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
+ <PackageReference Include="Mono.Posix.NETStandard" Version="5.20.1-preview" />
</ItemGroup>
<ItemGroup>
diff --git a/README.md b/README.md
index fc1c7917..121c537e 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
If you wish to build the emulator yourself you will need to:
-**Step one:** Install the X64 version of [.NET 5.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/5.0).
+**Step one:** Install the X64 version of [.NET 6.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/6.0).
**Step two (choose one):**
**(Variant one)**
diff --git a/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj b/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj
index 4619d73d..fd648715 100644
--- a/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj
+++ b/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
diff --git a/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj b/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj
index 6619a500..fa70d341 100644
--- a/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj
+++ b/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj b/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj
index a9a2fe75..49d142aa 100644
--- a/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj
+++ b/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Audio.Backends/Ryujinx.Audio.Backends.csproj b/Ryujinx.Audio.Backends/Ryujinx.Audio.Backends.csproj
index 431187ed..83088f27 100644
--- a/Ryujinx.Audio.Backends/Ryujinx.Audio.Backends.csproj
+++ b/Ryujinx.Audio.Backends/Ryujinx.Audio.Backends.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj
index ccdeae3e..2499bb44 100644
--- a/Ryujinx.Audio/Ryujinx.Audio.csproj
+++ b/Ryujinx.Audio/Ryujinx.Audio.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj
index a7e9c66c..e0cc2d56 100644
--- a/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/Ryujinx.Common/Ryujinx.Common.csproj
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
- <PackageReference Include="System.Drawing.Common" Version="5.0.1" />
- <PackageReference Include="System.Management" Version="5.0.0" />
+ <PackageReference Include="System.Drawing.Common" Version="6.0.0" />
+ <PackageReference Include="System.Management" Version="6.0.0" />
</ItemGroup>
</Project>
diff --git a/Ryujinx.Common/System/ForceDpiAware.cs b/Ryujinx.Common/System/ForceDpiAware.cs
index 81c69376..dc513307 100644
--- a/Ryujinx.Common/System/ForceDpiAware.cs
+++ b/Ryujinx.Common/System/ForceDpiAware.cs
@@ -2,6 +2,7 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
namespace Ryujinx.Common.System
{
@@ -19,7 +20,7 @@ namespace Ryujinx.Common.System
public static void Windows()
{
// Make process DPI aware for proper window sizing on high-res screens.
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Major >= 6)
+ if (OperatingSystem.IsWindowsVersionAtLeast(6))
{
SetProcessDPIAware();
}
@@ -27,16 +28,22 @@ namespace Ryujinx.Common.System
public static double GetWindowScaleFactor()
{
- double userDpiScale;
+ double userDpiScale = 96.0;
try
{
- userDpiScale = Graphics.FromHwnd(IntPtr.Zero).DpiX;
+ if (OperatingSystem.IsWindows())
+ {
+ userDpiScale = Graphics.FromHwnd(IntPtr.Zero).DpiX;
+ }
+ else
+ {
+ // TODO: Linux support
+ }
}
catch (Exception e)
{
Logger.Warning?.Print(LogClass.Application, $"Couldn't determine monitor DPI: {e.Message}");
- userDpiScale = 96.0;
}
return Math.Min(userDpiScale / _standardDpiScale, _maxScaleFactor);
diff --git a/Ryujinx.Cpu/Ryujinx.Cpu.csproj b/Ryujinx.Cpu/Ryujinx.Cpu.csproj
index ef33dd18..84972af1 100644
--- a/Ryujinx.Cpu/Ryujinx.Cpu.csproj
+++ b/Ryujinx.Cpu/Ryujinx.Cpu.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj b/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj
index 2f002aa3..fff78129 100644
--- a/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj
+++ b/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>
diff --git a/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj b/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj
index e8b3f52d..725f48ea 100644
--- a/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj
+++ b/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
diff --git a/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj b/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj
index 01e8e235..e3645668 100644
--- a/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj
+++ b/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
@@ -14,4 +14,8 @@
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
</ItemGroup>
+ <ItemGroup>
+ <PackageReference Include="SharpZipLib" Version="1.3.3" />
+ </ItemGroup>
+
</Project>
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs
index 316e027f..a98531f6 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs
@@ -1,11 +1,11 @@
-using Ryujinx.Common;
+using ICSharpCode.SharpZipLib.Zip;
+using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-using System.IO.Compression;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
/// <summary>
/// Main storage of the cache collection.
/// </summary>
- private ZipArchive _cacheArchive;
+ private ZipFile _cacheArchive;
/// <summary>
/// Indicates if the cache collection supports modification.
@@ -324,7 +324,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
EnsureArchiveUpToDate();
// Open the zip in readonly to avoid anyone modifying/corrupting it during normal operations.
- _cacheArchive = ZipFile.Open(GetArchivePath(), ZipArchiveMode.Read);
+ _cacheArchive = new ZipFile(File.OpenRead(GetArchivePath()));
}
/// <summary>
@@ -336,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
// First close previous opened instance if found.
if (_cacheArchive != null)
{
- _cacheArchive.Dispose();
+ _cacheArchive.Close();
}
string archivePath = GetArchivePath();
@@ -355,8 +355,18 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
return;
}
+ if (!File.Exists(archivePath))
+ {
+ using (ZipFile newZip = ZipFile.Create(archivePath))
+ {
+ // Workaround for SharpZipLib issue #395
+ newZip.BeginUpdate();
+ newZip.CommitUpdate();
+ }
+ }
+
// Open the zip in read/write.
- _cacheArchive = ZipFile.Open(archivePath, ZipArchiveMode.Update);
+ _cacheArchive = new ZipFile(File.Open(archivePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None));
Logger.Info?.Print(LogClass.Gpu, $"Updating cache collection archive {archivePath}...");
@@ -366,7 +376,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
CacheHelper.EnsureArchiveUpToDate(_cacheDirectory, _cacheArchive, _hashTable);
// Close the instance to force a flush.
- _cacheArchive.Dispose();
+ _cacheArchive.Close();
_cacheArchive = null;
string cacheTempDataPath = GetCacheTempDataPath();
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
index 09107346..ee4e1265 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
@@ -1,4 +1,5 @@
-using Ryujinx.Common;
+using ICSharpCode.SharpZipLib.Zip;
+using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
@@ -9,7 +10,6 @@ using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.IO;
-using System.IO.Compression;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -192,19 +192,19 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
/// <param name="entry">The given hash</param>
/// <returns>The cached file if present or null</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static byte[] ReadFromArchive(ZipArchive archive, Hash128 entry)
+ public static byte[] ReadFromArchive(ZipFile archive, Hash128 entry)
{
if (archive != null)
{
- ZipArchiveEntry archiveEntry = archive.GetEntry($"{entry}");
+ ZipEntry archiveEntry = archive.GetEntry($"{entry}");
if (archiveEntry != null)
{
try
{
- byte[] result = new byte[archiveEntry.Length];
+ byte[] result = new byte[archiveEntry.Size];
- using (Stream archiveStream = archiveEntry.Open())
+ using (Stream archiveStream = archive.GetInputStream(archiveEntry))
{
archiveStream.Read(result);
@@ -538,8 +538,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
/// <param name="archive">The archive to use</param>
/// <param name="entries">The entries in the cache</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void EnsureArchiveUpToDate(string baseCacheDirectory, ZipArchive archive, HashSet<Hash128> entries)
+ public static void EnsureArchiveUpToDate(string baseCacheDirectory, ZipFile archive, HashSet<Hash128> entries)
{
+ List<string> filesToDelete = new List<string>();
+
+ archive.BeginUpdate();
+
foreach (Hash128 hash in entries)
{
string cacheTempFilePath = GenCacheTempFilePath(baseCacheDirectory, hash);
@@ -548,15 +552,25 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
{
string cacheHash = $"{hash}";
- ZipArchiveEntry entry = archive.GetEntry(cacheHash);
-
- entry?.Delete();
+ ZipEntry entry = archive.GetEntry(cacheHash);
- archive.CreateEntryFromFile(cacheTempFilePath, cacheHash);
+ if (entry != null)
+ {
+ archive.Delete(entry);
+ }
- File.Delete(cacheTempFilePath);
+ // We enforce deflate compression here to avoid possible incompatibilities on older version of Ryujinx that use System.IO.Compression.
+ archive.Add(new StaticDiskDataSource(cacheTempFilePath), cacheHash, CompressionMethod.Deflated);
+ filesToDelete.Add(cacheTempFilePath);
}
}
+
+ archive.CommitUpdate();
+
+ foreach (string filePath in filesToDelete)
+ {
+ File.Delete(filePath);
+ }
}
public static bool IsArchiveReadOnly(string archivePath)
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheMigration.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheMigration.cs
index e726bc2c..5b4a1713 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheMigration.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheMigration.cs
@@ -1,11 +1,11 @@
-using Ryujinx.Common;
+using ICSharpCode.SharpZipLib.Zip;
+using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
using System;
using System.Collections.Generic;
using System.IO;
-using System.IO.Compression;
namespace Ryujinx.Graphics.Gpu.Shader.Cache
{
@@ -35,27 +35,36 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
return false;
}
+ private class StreamZipEntryDataSource : IStaticDataSource
+ {
+ private readonly ZipFile Archive;
+ private readonly ZipEntry Entry;
+ public StreamZipEntryDataSource(ZipFile archive, ZipEntry entry)
+ {
+ Archive = archive;
+ Entry = entry;
+ }
+
+ public Stream GetSource()
+ {
+ return Archive.GetInputStream(Entry);
+ }
+ }
+
/// <summary>
/// Move a file with the name of a given hash to another in the cache archive.
/// </summary>
/// <param name="archive">The archive in use</param>
/// <param name="oldKey">The old key</param>
/// <param name="newKey">The new key</param>
- private static void MoveEntry(ZipArchive archive, Hash128 oldKey, Hash128 newKey)
+ private static void MoveEntry(ZipFile archive, Hash128 oldKey, Hash128 newKey)
{
- ZipArchiveEntry oldGuestEntry = archive.GetEntry($"{oldKey}");
+ ZipEntry oldGuestEntry = archive.GetEntry($"{oldKey}");
if (oldGuestEntry != null)
{
- ZipArchiveEntry newGuestEntry = archive.CreateEntry($"{newKey}");
-
- using (Stream oldStream = oldGuestEntry.Open())
- using (Stream newStream = newGuestEntry.Open())
- {
- oldStream.CopyTo(newStream);
- }
-
- oldGuestEntry.Delete();
+ archive.Add(new StreamZipEntryDataSource(archive, oldGuestEntry), $"{newKey}", CompressionMethod.Deflated);
+ archive.Delete(oldGuestEntry);
}
}
@@ -81,8 +90,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
string guestArchivePath = CacheHelper.GetArchivePath(guestBaseCacheDirectory);
string hostArchivePath = CacheHelper.GetArchivePath(hostBaseCacheDirectory);
- ZipArchive guestArchive = ZipFile.Open(guestArchivePath, ZipArchiveMode.Update);
- ZipArchive hostArchive = ZipFile.Open(hostArchivePath, ZipArchiveMode.Update);
+ ZipFile guestArchive = new ZipFile(File.Open(guestArchivePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None));
+ ZipFile hostArchive = new ZipFile(File.Open(hostArchivePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None));
CacheHelper.EnsureArchiveUpToDate(guestBaseCacheDirectory, guestArchive, guestEntries);
CacheHelper.EnsureArchiveUpToDate(hostBaseCacheDirectory, hostArchive, hostEntries);
@@ -129,8 +138,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
File.WriteAllBytes(guestManifestPath, newGuestManifestContent);
File.WriteAllBytes(hostManifestPath, newHostManifestContent);
- guestArchive.Dispose();
- hostArchive.Dispose();
+ guestArchive.CommitUpdate();
+ hostArchive.CommitUpdate();
+
+ guestArchive.Close();
+ hostArchive.Close();
}
}
diff --git a/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj b/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj
index 69b6103f..49ed1a5c 100644
--- a/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj
+++ b/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
diff --git a/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj b/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj
index b437f36e..4e90eb20 100644
--- a/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj
+++ b/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj b/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj
index 51e88025..b30e6aea 100644
--- a/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj
+++ b/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj b/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj
index 095e0e59..68c0c2af 100644
--- a/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj
+++ b/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj
index aff59d97..3f4bcdee 100644
--- a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj
+++ b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj
index d59efc2e..c604902f 100644
--- a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj
+++ b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
diff --git a/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj b/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj
index b74938c0..6af7e775 100644
--- a/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj
+++ b/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj b/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj
index fe9b834b..0e564d02 100644
--- a/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj
+++ b/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj b/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj
index a7f8f746..484b7177 100644
--- a/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj
+++ b/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
index 523fa5de..3cfd192c 100644
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
@@ -170,7 +170,9 @@ namespace Ryujinx.HLE.HOS.Applets
{
_npads?.Update();
- return _keyboardRenderer?.DrawTo(surfaceInfo, destination, position) ?? false;
+ _keyboardRenderer?.SetSurfaceInfo(surfaceInfo);
+
+ return _keyboardRenderer?.DrawTo(destination, position) ?? false;
}
private void ExecuteForegroundKeyboard()
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs
index dfd10925..c30ad11b 100644
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs
@@ -1,717 +1,164 @@
using Ryujinx.HLE.Ui;
using Ryujinx.Memory;
using System;
-using System.Buffers.Binary;
-using System.Diagnostics;
-using System.Drawing;
-using System.Drawing.Drawing2D;
-using System.Drawing.Imaging;
-using System.Drawing.Text;
-using System.IO;
-using System.Numerics;
-using System.Reflection;
-using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
/// <summary>
- /// Class that generates the graphics for the software keyboard applet during inline mode.
+ /// Class that manages the renderer base class and its state in a multithreaded context.
/// </summary>
internal class SoftwareKeyboardRenderer : IDisposable
{
- const int TextBoxBlinkThreshold = 8;
- const int TextBoxBlinkSleepMilliseconds = 100;
- const int TextBoxBlinkJoinWaitMilliseconds = 1000;
+ private const int TextBoxBlinkSleepMilliseconds = 100;
+ private const int RendererWaitTimeoutMilliseconds = 100;
- const string MessageText = "Please use the keyboard to input text";
- const string AcceptText = "Accept";
- const string CancelText = "Cancel";
- const string ControllerToggleText = "Toggle input";
+ private readonly object _stateLock = new object();
- private RenderingSurfaceInfo _surfaceInfo;
- private Bitmap _surface = null;
- private object _renderLock = new object();
+ private SoftwareKeyboardUiState _state = new SoftwareKeyboardUiState();
+ private SoftwareKeyboardRendererBase _renderer;
- private string _inputText = "";
- private int _cursorStart = 0;
- private int _cursorEnd = 0;
- private bool _acceptPressed = false;
- private bool _cancelPressed = false;
- private bool _overwriteMode = false;
- private bool _typingEnabled = true;
- private bool _controllerEnabled = true;
-
- private Image _ryujinxLogo = null;
- private Image _padAcceptIcon = null;
- private Image _padCancelIcon = null;
- private Image _keyModeIcon = null;
-
- private float _textBoxOutlineWidth;
- private float _padPressedPenWidth;
-
- private Brush _panelBrush;
- private Brush _disabledBrush;
- private Brush _textNormalBrush;
- private Brush _textSelectedBrush;
- private Brush _textOverCursorBrush;
- private Brush _cursorBrush;
- private Brush _selectionBoxBrush;
- private Brush _keyCapBrush;
- private Brush _keyProgressBrush;
-
- private Pen _gridSeparatorPen;
- private Pen _textBoxOutlinePen;
- private Pen _cursorPen;
- private Pen _selectionBoxPen;
- private Pen _padPressedPen;
-
- private int _inputTextFontSize;
- private int _padButtonFontSize;
- private Font _messageFont;
- private Font _inputTextFont;
- private Font _labelsTextFont;
- private Font _padSymbolFont;
- private Font _keyCapFont;
-
- private float _inputTextCalibrationHeight;
- private float _panelPositionY;
- private RectangleF _panelRectangle;
- private PointF _logoPosition;
- private float _messagePositionY;
-
- private TRef<int> _textBoxBlinkCounter = new TRef<int>(0);
private TimedAction _textBoxBlinkTimedAction = new TimedAction();
+ private TimedAction _renderAction = new TimedAction();
public SoftwareKeyboardRenderer(IHostUiTheme uiTheme)
{
- _surfaceInfo = new RenderingSurfaceInfo(0, 0, 0, 0, 0);
-
- string ryujinxLogoPath = "Ryujinx.Ui.Resources.Logo_Ryujinx.png";
- int ryujinxLogoSize = 32;
-
- _ryujinxLogo = LoadResource(Assembly.GetEntryAssembly(), ryujinxLogoPath, ryujinxLogoSize, ryujinxLogoSize);
-
- string padAcceptIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnA.png";
- string padCancelIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnB.png";
- string keyModeIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_KeyF6.png";
-
- _padAcceptIcon = LoadResource(Assembly.GetExecutingAssembly(), padAcceptIconPath , 0, 0);
- _padCancelIcon = LoadResource(Assembly.GetExecutingAssembly(), padCancelIconPath , 0, 0);
- _keyModeIcon = LoadResource(Assembly.GetExecutingAssembly(), keyModeIconPath , 0, 0);
-
- Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
- Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
- Color normalTextColor = ToColor(uiTheme.DefaultForegroundColor);
- Color invertedTextColor = ToColor(uiTheme.DefaultForegroundColor, null, true);
- Color selectedTextColor = ToColor(uiTheme.SelectionForegroundColor);
- Color borderColor = ToColor(uiTheme.DefaultBorderColor);
- Color selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor);
- Color gridSeparatorColor = Color.FromArgb(180, 255, 255, 255);
-
- float cursorWidth = 2;
-
- _textBoxOutlineWidth = 2;
- _padPressedPenWidth = 2;
-
- _panelBrush = new SolidBrush(panelColor);
- _disabledBrush = new SolidBrush(panelTransparentColor);
- _textNormalBrush = new SolidBrush(normalTextColor);
- _textSelectedBrush = new SolidBrush(selectedTextColor);
- _textOverCursorBrush = new SolidBrush(invertedTextColor);
- _cursorBrush = new SolidBrush(normalTextColor);
- _selectionBoxBrush = new SolidBrush(selectionBackgroundColor);
- _keyCapBrush = Brushes.White;
- _keyProgressBrush = new SolidBrush(borderColor);
+ _renderer = new SoftwareKeyboardRendererBase(uiTheme);
- _gridSeparatorPen = new Pen(gridSeparatorColor, 2);
- _textBoxOutlinePen = new Pen(borderColor, _textBoxOutlineWidth);
- _cursorPen = new Pen(normalTextColor, cursorWidth);
- _selectionBoxPen = new Pen(selectionBackgroundColor, cursorWidth);
- _padPressedPen = new Pen(borderColor, _padPressedPenWidth);
-
- _inputTextFontSize = 20;
- _padButtonFontSize = 24;
-
- string font = uiTheme.FontFamily;
-
- _messageFont = new Font(font, 26, FontStyle.Regular, GraphicsUnit.Pixel);
- _inputTextFont = new Font(font, _inputTextFontSize, FontStyle.Regular, GraphicsUnit.Pixel);
- _labelsTextFont = new Font(font, 24, FontStyle.Regular, GraphicsUnit.Pixel);
- _padSymbolFont = new Font(font, _padButtonFontSize, FontStyle.Regular, GraphicsUnit.Pixel);
- _keyCapFont = new Font(font, 15, FontStyle.Regular, GraphicsUnit.Pixel);
-
- // System.Drawing has serious problems measuring strings, so it requires a per-pixel calibration
- // to ensure we are rendering text inside the proper region
- _inputTextCalibrationHeight = CalibrateTextHeight(_inputTextFont);
-
- StartTextBoxBlinker(_textBoxBlinkTimedAction, _textBoxBlinkCounter);
+ StartTextBoxBlinker(_textBoxBlinkTimedAction, _state, _stateLock);
+ StartRenderer(_renderAction, _renderer, _state, _stateLock);
}
- private static void StartTextBoxBlinker(TimedAction timedAction, TRef<int> blinkerCounter)
+ private static void StartTextBoxBlinker(TimedAction timedAction, SoftwareKeyboardUiState state, object stateLock)
{
timedAction.Reset(() =>
{
- // The blinker is on falf of the time and events such as input
- // changes can reset the blinker.
- var value = Volatile.Read(ref blinkerCounter.Value);
- value = (value + 1) % (2 * TextBoxBlinkThreshold);
- Volatile.Write(ref blinkerCounter.Value, value);
-
- }, TextBoxBlinkSleepMilliseconds);
- }
-
- private Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
- {
- var a = (byte)(color.A * 255);
- var r = (byte)(color.R * 255);
- var g = (byte)(color.G * 255);
- var b = (byte)(color.B * 255);
-
- if (flipRgb)
- {
- r = (byte)(255 - r);
- g = (byte)(255 - g);
- b = (byte)(255 - b);
- }
-
- return Color.FromArgb(overrideAlpha.GetValueOrDefault(a), r, g, b);
- }
-
- private Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
- {
- Stream resourceStream = assembly.GetManifestResourceStream(resourcePath);
-
- Debug.Assert(resourceStream != null);
-
- var originalImage = Image.FromStream(resourceStream);
-
- if (newHeight == 0 || newWidth == 0)
- {
- return originalImage;
- }
-
- var newSize = new Rectangle(0, 0, newWidth, newHeight);
- var newImage = new Bitmap(newWidth, newHeight);
-
- using (var graphics = System.Drawing.Graphics.FromImage(newImage))
- using (var wrapMode = new ImageAttributes())
- {
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- graphics.CompositingMode = CompositingMode.SourceCopy;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.SmoothingMode = SmoothingMode.HighQuality;
-
- wrapMode.SetWrapMode(WrapMode.TileFlipXY);
- graphics.DrawImage(originalImage, newSize, 0, 0, originalImage.Width, originalImage.Height, GraphicsUnit.Pixel, wrapMode);
- }
-
- return newImage;
- }
-
-#pragma warning disable CS8632
- public void UpdateTextState(string? inputText, int? cursorStart, int? cursorEnd, bool? overwriteMode, bool? typingEnabled)
-#pragma warning restore CS8632
- {
- lock (_renderLock)
- {
- // Update the parameters that were provided.
- _inputText = inputText != null ? inputText : _inputText;
- _cursorStart = cursorStart.GetValueOrDefault(_cursorStart);
- _cursorEnd = cursorEnd.GetValueOrDefault(_cursorEnd);
- _overwriteMode = overwriteMode.GetValueOrDefault(_overwriteMode);
- _typingEnabled = typingEnabled.GetValueOrDefault(_typingEnabled);
-
- // Reset the cursor blink.
- Volatile.Write(ref _textBoxBlinkCounter.Value, 0);
- }
- }
-
- public void UpdateCommandState(bool? acceptPressed, bool? cancelPressed, bool? controllerEnabled)
- {
- lock (_renderLock)
- {
- // Update the parameters that were provided.
- _acceptPressed = acceptPressed.GetValueOrDefault(_acceptPressed);
- _cancelPressed = cancelPressed.GetValueOrDefault(_cancelPressed);
- _controllerEnabled = controllerEnabled.GetValueOrDefault(_controllerEnabled);
- }
- }
-
- private void Redraw()
- {
- if (_surface == null)
- {
- return;
- }
-
- using (var graphics = CreateGraphics())
- {
- var messageRectangle = MeasureString(graphics, MessageText, _messageFont);
- float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X;
- float messagePositionY = _messagePositionY - messageRectangle.Y;
- PointF messagePosition = new PointF(messagePositionX, messagePositionY);
-
- graphics.Clear(Color.Transparent);
- graphics.TranslateTransform(0, _panelPositionY);
- graphics.FillRectangle(_panelBrush, _panelRectangle);
- graphics.DrawImage(_ryujinxLogo, _logoPosition);
-
- DrawString(graphics, MessageText, _messageFont, _textNormalBrush, messagePosition);
-
- if (!_typingEnabled)
+ lock (stateLock)
{
- // Just draw a semi-transparent rectangle on top to fade the component with the background.
- // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
- graphics.FillRectangle(_disabledBrush, messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
- }
-
- DrawTextBox(graphics);
-
- float halfWidth = _panelRectangle.Width / 2;
-
- PointF acceptButtonPosition = new PointF(halfWidth - 180, 185);
- PointF cancelButtonPosition = new PointF(halfWidth , 185);
- PointF disableButtonPosition = new PointF(halfWidth + 180, 185);
-
- DrawPadButton (graphics, acceptButtonPosition , _padAcceptIcon, AcceptText, _acceptPressed, _controllerEnabled);
- DrawPadButton (graphics, cancelButtonPosition , _padCancelIcon, CancelText, _cancelPressed, _controllerEnabled);
- DrawControllerToggle(graphics, disableButtonPosition, _controllerEnabled);
- }
- }
-
- private void RecreateSurface()
- {
- Debug.Assert(_surfaceInfo.ColorFormat == Services.SurfaceFlinger.ColorFormat.A8B8G8R8);
-
- // Use the whole area of the image to draw, even the alignment, otherwise it may shear the final
- // image if the pitch is different.
- uint totalWidth = _surfaceInfo.Pitch / 4;
- uint totalHeight = _surfaceInfo.Size / _surfaceInfo.Pitch;
-
- Debug.Assert(_surfaceInfo.Width <= totalWidth);
- Debug.Assert(_surfaceInfo.Height <= totalHeight);
- Debug.Assert(_surfaceInfo.Pitch * _surfaceInfo.Height <= _surfaceInfo.Size);
-
- _surface = new Bitmap((int)totalWidth, (int)totalHeight, PixelFormat.Format32bppArgb);
- }
-
- private void RecomputeConstants()
- {
- float totalWidth = _surfaceInfo.Width;
- float totalHeight = _surfaceInfo.Height;
-
- float panelHeight = 240;
+ // The blinker is on half of the time and events such as input
+ // changes can reset the blinker.
+ state.TextBoxBlinkCounter = (state.TextBoxBlinkCounter + 1) % (2 * SoftwareKeyboardRendererBase.TextBoxBlinkThreshold);
- _panelPositionY = totalHeight - panelHeight;
- _panelRectangle = new RectangleF(0, 0, totalWidth, panelHeight);
-
- _messagePositionY = 60;
-
- float logoPositionX = (totalWidth - _ryujinxLogo.Width) / 2;
- float logoPositionY = 18;
-
- _logoPosition = new PointF(logoPositionX, logoPositionY);
- }
-
- private StringFormat CreateStringFormat(string text)
- {
- StringFormat format = new StringFormat(StringFormat.GenericTypographic);
- format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
- format.SetMeasurableCharacterRanges(new CharacterRange[] { new CharacterRange(0, text.Length) });
-
- return format;
- }
-
- private RectangleF MeasureString(System.Drawing.Graphics graphics, string text, System.Drawing.Font font)
- {
- bool isEmpty = false;
-
- if (string.IsNullOrEmpty(text))
- {
- isEmpty = true;
- text = " ";
- }
-
- var format = CreateStringFormat(text);
- var rectangle = new RectangleF(0, 0, float.PositiveInfinity, float.PositiveInfinity);
- var regions = graphics.MeasureCharacterRanges(text, font, rectangle, format);
-
- Debug.Assert(regions.Length == 1);
-
- rectangle = regions[0].GetBounds(graphics);
-
- if (isEmpty)
- {
- rectangle.Width = 0;
- }
- else
- {
- rectangle.Width += 1.0f;
- }
-
- return rectangle;
+ // Tell the render thread there is something new to render.
+ Monitor.PulseAll(stateLock);
+ }
+ }, TextBoxBlinkSleepMilliseconds);
}
- private float CalibrateTextHeight(Font font)
+ private static void StartRenderer(TimedAction timedAction, SoftwareKeyboardRendererBase renderer, SoftwareKeyboardUiState state, object stateLock)
{
- // This is a pixel-wise calibration that tests the offset of a reference character because Windows text measurement
- // is horrible when compared to other frameworks like Cairo and diverge across systems and fonts.
-
- Debug.Assert(font.Unit == GraphicsUnit.Pixel);
+ SoftwareKeyboardUiState internalState = new SoftwareKeyboardUiState();
- var surfaceSize = (int)Math.Ceiling(2 * font.Size);
+ bool canCreateSurface = false;
+ bool needsUpdate = true;
- string calibrationText = "|";
-
- using (var surface = new Bitmap(surfaceSize, surfaceSize, PixelFormat.Format32bppArgb))
- using (var graphics = CreateGraphics(surface))
+ timedAction.Reset(() =>
{
- var measuredRectangle = MeasureString(graphics, calibrationText, font);
-
- Debug.Assert(measuredRectangle.Right <= surfaceSize);
- Debug.Assert(measuredRectangle.Bottom <= surfaceSize);
-
- var textPosition = new PointF(0, 0);
-
- graphics.Clear(Color.Transparent);
- DrawString(graphics, calibrationText, font, Brushes.White, textPosition);
-
- var lockRectangle = new Rectangle(0, 0, surface.Width, surface.Height);
- var surfaceData = surface.LockBits(lockRectangle, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
- var surfaceBytes = new byte[surfaceData.Stride * surfaceData.Height];
-
- Marshal.Copy(surfaceData.Scan0, surfaceBytes, 0, surfaceBytes.Length);
-
- Point topLeft = new Point();
- Point bottomLeft = new Point();
-
- bool foundTopLeft = false;
-
- for (int y = 0; y < surfaceData.Height; y++)
+ lock (stateLock)
{
- for (int x = 0; x < surfaceData.Stride; x += 4)
+ if (!Monitor.Wait(stateLock, RendererWaitTimeoutMilliseconds))
{
- int position = y * surfaceData.Stride + x;
-
- if (surfaceBytes[position] != 0)
- {
- if (!foundTopLeft)
- {
- topLeft.X = x;
- topLeft.Y = y;
- foundTopLeft = true;
-
- break;
- }
- else
- {
- bottomLeft.X = x;
- bottomLeft.Y = y;
-
- break;
- }
- }
+ return;
}
- }
-
- return bottomLeft.Y - topLeft.Y;
- }
- }
-
- private void DrawString(System.Drawing.Graphics graphics, string text, Font font, Brush brush, PointF point)
- {
- var format = CreateStringFormat(text);
- graphics.DrawString(text, font, brush, point, format);
- }
-
- private System.Drawing.Graphics CreateGraphics()
- {
- return CreateGraphics(_surface);
- }
-
- private System.Drawing.Graphics CreateGraphics(Image surface)
- {
- var graphics = System.Drawing.Graphics.FromImage(surface);
-
- graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
- graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
- graphics.CompositingQuality = CompositingQuality.HighSpeed;
- graphics.CompositingMode = CompositingMode.SourceOver;
- graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
- graphics.SmoothingMode = SmoothingMode.HighSpeed;
-
- return graphics;
- }
-
- private void DrawTextBox(System.Drawing.Graphics graphics)
- {
- var inputTextRectangle = MeasureString(graphics, _inputText, _inputTextFont);
-
- float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8));
- float boxHeight = 32;
- float boxY = 110;
- float boxX = (int)((_panelRectangle.Width - boxWidth) / 2);
-
- graphics.DrawRectangle(_textBoxOutlinePen, boxX, boxY, boxWidth, boxHeight);
- float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.X;
- float inputTextY = boxY + boxHeight - inputTextRectangle.Bottom - 5;
+ needsUpdate = UpdateStateField(ref state.InputText, ref internalState.InputText);
+ needsUpdate |= UpdateStateField(ref state.CursorBegin, ref internalState.CursorBegin);
+ needsUpdate |= UpdateStateField(ref state.CursorEnd, ref internalState.CursorEnd);
+ needsUpdate |= UpdateStateField(ref state.AcceptPressed, ref internalState.AcceptPressed);
+ needsUpdate |= UpdateStateField(ref state.CancelPressed, ref internalState.CancelPressed);
+ needsUpdate |= UpdateStateField(ref state.OverwriteMode, ref internalState.OverwriteMode);
+ needsUpdate |= UpdateStateField(ref state.TypingEnabled, ref internalState.TypingEnabled);
+ needsUpdate |= UpdateStateField(ref state.ControllerEnabled, ref internalState.ControllerEnabled);
+ needsUpdate |= UpdateStateField(ref state.TextBoxBlinkCounter, ref internalState.TextBoxBlinkCounter);
- var inputTextPosition = new PointF(inputTextX, inputTextY);
+ canCreateSurface = state.SurfaceInfo != null && internalState.SurfaceInfo == null;
- DrawString(graphics, _inputText, _inputTextFont, _textNormalBrush, inputTextPosition);
-
- // Draw the cursor on top of the text and redraw the text with a different color if necessary.
-
- Brush cursorTextBrush;
- Brush cursorBrush;
- Pen cursorPen;
-
- float cursorPositionYBottom = inputTextY + inputTextRectangle.Bottom;
- float cursorPositionYTop = cursorPositionYBottom - _inputTextCalibrationHeight - 2;
- float cursorPositionXLeft;
- float cursorPositionXRight;
-
- bool cursorVisible = false;
-
- if (_cursorStart != _cursorEnd)
- {
- cursorTextBrush = _textSelectedBrush;
- cursorBrush = _selectionBoxBrush;
- cursorPen = _selectionBoxPen;
-
- string textUntilBegin = _inputText.Substring(0, _cursorStart);
- string textUntilEnd = _inputText.Substring(0, _cursorEnd);
-
- RectangleF selectionBeginRectangle = MeasureString(graphics, textUntilBegin, _inputTextFont);
- RectangleF selectionEndRectangle = MeasureString(graphics, textUntilEnd , _inputTextFont);
-
- cursorVisible = true;
- cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X;
- cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X;
- }
- else
- {
- cursorTextBrush = _textOverCursorBrush;
- cursorBrush = _cursorBrush;
- cursorPen = _cursorPen;
-
- if (Volatile.Read(ref _textBoxBlinkCounter.Value) < TextBoxBlinkThreshold)
- {
- // Show the blinking cursor.
-
- int cursorStart = Math.Min(_inputText.Length, _cursorStart);
- string textUntilCursor = _inputText.Substring(0, cursorStart);
- RectangleF cursorTextRectangle = MeasureString(graphics, textUntilCursor, _inputTextFont);
-
- cursorVisible = true;
- cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
-
- if (_overwriteMode)
+ if (canCreateSurface)
{
- // The blinking cursor is in overwrite mode so it takes the size of a character.
-
- if (_cursorStart < _inputText.Length)
- {
- textUntilCursor = _inputText.Substring(0, cursorStart + 1);
- cursorTextRectangle = MeasureString(graphics, textUntilCursor, _inputTextFont);
- cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
- }
- else
- {
- cursorPositionXRight = cursorPositionXLeft + _inputTextFontSize / 2;
- }
+ internalState.SurfaceInfo = state.SurfaceInfo;
}
- else
- {
- // The blinking cursor is in insert mode so it is only a line.
- cursorPositionXRight = cursorPositionXLeft;
- }
- }
- else
- {
- cursorPositionXLeft = inputTextX;
- cursorPositionXRight = inputTextX;
}
- }
-
- if (_typingEnabled && cursorVisible)
- {
- float cursorWidth = cursorPositionXRight - cursorPositionXLeft;
- float cursorHeight = cursorPositionYBottom - cursorPositionYTop;
- if (cursorWidth == 0)
+ if (canCreateSurface)
{
- graphics.DrawLine(cursorPen, cursorPositionXLeft, cursorPositionYTop, cursorPositionXLeft, cursorPositionYBottom);
+ renderer.CreateSurface(internalState.SurfaceInfo);
}
- else
- {
- graphics.DrawRectangle(cursorPen, cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
- graphics.FillRectangle(cursorBrush, cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
-
- var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
-
- var oldClip = graphics.Clip;
- graphics.Clip = new Region(cursorRectangle);
-
- DrawString(graphics, _inputText, _inputTextFont, cursorTextBrush, inputTextPosition);
- graphics.Clip = oldClip;
+ if (needsUpdate)
+ {
+ renderer.DrawMutableElements(internalState);
+ renderer.CopyImageToBuffer();
+ needsUpdate = false;
}
- }
- else if (!_typingEnabled)
- {
- // Just draw a semi-transparent rectangle on top to fade the component with the background.
- // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
- graphics.FillRectangle(_disabledBrush, boxX - _textBoxOutlineWidth, boxY - _textBoxOutlineWidth,
- boxWidth + 2* _textBoxOutlineWidth, boxHeight + 2* _textBoxOutlineWidth);
- }
+ });
}
- private void DrawPadButton(System.Drawing.Graphics graphics, PointF point, Image icon, string label, bool pressed, bool enabled)
+ private static bool UpdateStateField<T>(ref T source, ref T destination) where T : IEquatable<T>
{
- // Use relative positions so we can center the the entire drawing later.
-
- float iconX = 0;
- float iconY = 0;
- float iconWidth = icon.Width;
- float iconHeight = icon.Height;
-
- var labelRectangle = MeasureString(graphics, label, _labelsTextFont);
-
- float labelPositionX = iconWidth + 8 - labelRectangle.X;
- float labelPositionY = (iconHeight - labelRectangle.Height) / 2 - labelRectangle.Y - 1;
-
- float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X;
- float fullHeight = iconHeight;
-
- // Convert all relative positions into absolute.
-
- float originX = (int)(point.X - fullWidth / 2);
- float originY = (int)(point.Y - fullHeight / 2);
-
- iconX += originX;
- iconY += originY;
-
- var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
-
- graphics.DrawImageUnscaled(icon, (int)iconX, (int)iconY);
-
- DrawString(graphics, label, _labelsTextFont, _textNormalBrush, labelPosition);
-
- GraphicsPath frame = new GraphicsPath();
- frame.AddRectangle(new RectangleF(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth,
- fullWidth + 4 * _padPressedPenWidth, fullHeight + 4 * _padPressedPenWidth));
-
- if (enabled)
+ if (!source.Equals(destination))
{
- if (pressed)
- {
- graphics.DrawPath(_padPressedPen, frame);
- }
+ destination = source;
+ return true;
}
- else
- {
- // Just draw a semi-transparent rectangle on top to fade the component with the background.
- // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
- graphics.FillPath(_disabledBrush, frame);
- }
- }
-
- private void DrawControllerToggle(System.Drawing.Graphics graphics, PointF point, bool enabled)
- {
- var labelRectangle = MeasureString(graphics, ControllerToggleText, _labelsTextFont);
-
- // Use relative positions so we can center the the entire drawing later.
-
- float keyWidth = _keyModeIcon.Width;
- float keyHeight = _keyModeIcon.Height;
-
- float labelPositionX = keyWidth + 8 - labelRectangle.X;
- float labelPositionY = -labelRectangle.Y - 1;
-
- float keyX = 0;
- float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2);
-
- float fullWidth = labelPositionX + labelRectangle.Width;
- float fullHeight = Math.Max(labelPositionY + labelRectangle.Height, keyHeight);
-
- // Convert all relative positions into absolute.
- float originX = (int)(point.X - fullWidth / 2);
- float originY = (int)(point.Y - fullHeight / 2);
-
- keyX += originX;
- keyY += originY;
-
- var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
- var overlayPosition = new Point((int)keyX, (int)keyY);
-
- graphics.DrawImageUnscaled(_keyModeIcon, overlayPosition);
-
- DrawString(graphics, ControllerToggleText, _labelsTextFont, _textNormalBrush, labelPosition);
+ return false;
}
- private bool TryCopyTo(IVirtualMemoryManager destination, ulong position)
+#pragma warning disable CS8632
+ public void UpdateTextState(string? inputText, int? cursorBegin, int? cursorEnd, bool? overwriteMode, bool? typingEnabled)
+#pragma warning restore CS8632
{
- if (_surface == null)
+ lock (_stateLock)
{
- return false;
- }
-
- Rectangle lockRectangle = new Rectangle(0, 0, _surface.Width, _surface.Height);
- BitmapData surfaceData = _surface.LockBits(lockRectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
-
- Debug.Assert(surfaceData.Stride == _surfaceInfo.Pitch);
- Debug.Assert(surfaceData.Stride * surfaceData.Height == _surfaceInfo.Size);
-
- // Convert the pixel format used in System.Drawing to the one required by a Switch Surface.
- int dataLength = surfaceData.Stride * surfaceData.Height;
-
- byte[] data = new byte[dataLength];
- Span<uint> dataConvert = MemoryMarshal.Cast<byte, uint>(data);
+ // Update the parameters that were provided.
+ _state.InputText = inputText != null ? inputText : _state.InputText;
+ _state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin);
+ _state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd);
+ _state.OverwriteMode = overwriteMode.GetValueOrDefault(_state.OverwriteMode);
+ _state.TypingEnabled = typingEnabled.GetValueOrDefault(_state.TypingEnabled);
- Marshal.Copy(surfaceData.Scan0, data, 0, dataLength);
+ // Reset the cursor blink.
+ _state.TextBoxBlinkCounter = 0;
- for (int i = 0; i < dataConvert.Length; i++)
- {
- dataConvert[i] = BitOperations.RotateRight(BinaryPrimitives.ReverseEndianness(dataConvert[i]), 8);
+ // Tell the render thread there is something new to render.
+ Monitor.PulseAll(_stateLock);
}
+ }
- try
- {
- destination.Write(position, data);
- }
- finally
+ public void UpdateCommandState(bool? acceptPressed, bool? cancelPressed, bool? controllerEnabled)
+ {
+ lock (_stateLock)
{
- _surface.UnlockBits(surfaceData);
- }
+ // Update the parameters that were provided.
+ _state.AcceptPressed = acceptPressed.GetValueOrDefault(_state.AcceptPressed);
+ _state.CancelPressed = cancelPressed.GetValueOrDefault(_state.CancelPressed);
+ _state.ControllerEnabled = controllerEnabled.GetValueOrDefault(_state.ControllerEnabled);
- return true;
+ // Tell the render thread there is something new to render.
+ Monitor.PulseAll(_stateLock);
+ }
}
- internal bool DrawTo(RenderingSurfaceInfo surfaceInfo, IVirtualMemoryManager destination, ulong position)
+ public void SetSurfaceInfo(RenderingSurfaceInfo surfaceInfo)
{
- lock (_renderLock)
+ lock (_stateLock)
{
- if (!_surfaceInfo.Equals(surfaceInfo))
- {
- _surfaceInfo = surfaceInfo;
- RecreateSurface();
- RecomputeConstants();
- }
-
- Redraw();
+ _state.SurfaceInfo = surfaceInfo;
- return TryCopyTo(destination, position);
+ // Tell the render thread there is something new to render.
+ Monitor.PulseAll(_stateLock);
}
}
+ internal bool DrawTo(IVirtualMemoryManager destination, ulong position)
+ {
+ return _renderer.WriteBufferToMemory(destination, position);
+ }
+
public void Dispose()
{
_textBoxBlinkTimedAction.RequestCancel();
+ _renderAction.RequestCancel();
}
}
}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
new file mode 100644
index 00000000..b059200d
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
@@ -0,0 +1,585 @@
+using Ryujinx.HLE.Ui;
+using Ryujinx.Memory;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Processing;
+using SixLabors.ImageSharp.Drawing.Processing;
+using SixLabors.Fonts;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Numerics;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ /// <summary>
+ /// Base class that generates the graphics for the software keyboard applet during inline mode.
+ /// </summary>
+ internal class SoftwareKeyboardRendererBase
+ {
+ public const int TextBoxBlinkThreshold = 8;
+
+ const string MessageText = "Please use the keyboard to input text";
+ const string AcceptText = "Accept";
+ const string CancelText = "Cancel";
+ const string ControllerToggleText = "Toggle input";
+
+ private readonly object _bufferLock = new object();
+
+ private RenderingSurfaceInfo _surfaceInfo = null;
+ private Image<Argb32> _surface = null;
+ private byte[] _bufferData = null;
+
+ private Image _ryujinxLogo = null;
+ private Image _padAcceptIcon = null;
+ private Image _padCancelIcon = null;
+ private Image _keyModeIcon = null;
+
+ private float _textBoxOutlineWidth;
+ private float _padPressedPenWidth;
+
+ private Color _textNormalColor;
+ private Color _textSelectedColor;
+ private Color _textOverCursorColor;
+
+ private IBrush _panelBrush;
+ private IBrush _disabledBrush;
+ private IBrush _cursorBrush;
+ private IBrush _selectionBoxBrush;
+
+ private Pen _textBoxOutlinePen;
+ private Pen _cursorPen;
+ private Pen _selectionBoxPen;
+ private Pen _padPressedPen;
+
+ private int _inputTextFontSize;
+ private Font _messageFont;
+ private Font _inputTextFont;
+ private Font _labelsTextFont;
+
+ private RectangleF _panelRectangle;
+ private Point _logoPosition;
+ private float _messagePositionY;
+
+ public SoftwareKeyboardRendererBase(IHostUiTheme uiTheme)
+ {
+ string ryujinxLogoPath = "Ryujinx.Ui.Resources.Logo_Ryujinx.png";
+ int ryujinxLogoSize = 32;
+
+ _ryujinxLogo = LoadResource(Assembly.GetEntryAssembly(), ryujinxLogoPath, ryujinxLogoSize, ryujinxLogoSize);
+
+ string padAcceptIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnA.png";
+ string padCancelIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnB.png";
+ string keyModeIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_KeyF6.png";
+
+ _padAcceptIcon = LoadResource(Assembly.GetExecutingAssembly(), padAcceptIconPath , 0, 0);
+ _padCancelIcon = LoadResource(Assembly.GetExecutingAssembly(), padCancelIconPath , 0, 0);
+ _keyModeIcon = LoadResource(Assembly.GetExecutingAssembly(), keyModeIconPath , 0, 0);
+
+ Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
+ Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
+ Color borderColor = ToColor(uiTheme.DefaultBorderColor);
+ Color selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor);
+
+ _textNormalColor = ToColor(uiTheme.DefaultForegroundColor);
+ _textSelectedColor = ToColor(uiTheme.SelectionForegroundColor);
+ _textOverCursorColor = ToColor(uiTheme.DefaultForegroundColor, null, true);
+
+ float cursorWidth = 2;
+
+ _textBoxOutlineWidth = 2;
+ _padPressedPenWidth = 2;
+
+ _panelBrush = new SolidBrush(panelColor);
+ _disabledBrush = new SolidBrush(panelTransparentColor);
+ _cursorBrush = new SolidBrush(_textNormalColor);
+ _selectionBoxBrush = new SolidBrush(selectionBackgroundColor);
+
+ _textBoxOutlinePen = new Pen(borderColor, _textBoxOutlineWidth);
+ _cursorPen = new Pen(_textNormalColor, cursorWidth);
+ _selectionBoxPen = new Pen(selectionBackgroundColor, cursorWidth);
+ _padPressedPen = new Pen(borderColor, _padPressedPenWidth);
+
+ _inputTextFontSize = 20;
+
+ CreateFonts(uiTheme.FontFamily);
+ }
+
+ private void CreateFonts(string uiThemeFontFamily)
+ {
+ // Try a list of fonts in case any of them is not available in the system.
+
+ string[] availableFonts = new string[]
+ {
+ uiThemeFontFamily,
+ "Liberation Sans",
+ "FreeSans",
+ "DejaVu Sans"
+ };
+
+ foreach (string fontFamily in availableFonts)
+ {
+ try
+ {
+ _messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular);
+ _inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular);
+ _labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular);
+
+ return;
+ }
+ catch
+ {
+ }
+ }
+
+ throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!");
+ }
+
+ private Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
+ {
+ var a = (byte)(color.A * 255);
+ var r = (byte)(color.R * 255);
+ var g = (byte)(color.G * 255);
+ var b = (byte)(color.B * 255);
+
+ if (flipRgb)
+ {
+ r = (byte)(255 - r);
+ g = (byte)(255 - g);
+ b = (byte)(255 - b);
+ }
+
+ return Color.FromRgba(r, g, b, overrideAlpha.GetValueOrDefault(a));
+ }
+
+ private Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
+ {
+ Stream resourceStream = assembly.GetManifestResourceStream(resourcePath);
+
+ Debug.Assert(resourceStream != null);
+
+ var image = Image.Load(resourceStream);
+
+ if (newHeight != 0 && newWidth != 0)
+ {
+ image.Mutate(x => x.Resize(newWidth, newHeight, KnownResamplers.Lanczos3));
+ }
+
+ return image;
+ }
+
+ private void SetGraphicsOptions(IImageProcessingContext context)
+ {
+ context.GetGraphicsOptions().Antialias = true;
+ context.GetShapeGraphicsOptions().GraphicsOptions.Antialias = true;
+ }
+
+ private void DrawImmutableElements()
+ {
+ if (_surface == null)
+ {
+ return;
+ }
+
+ _surface.Mutate(context =>
+ {
+ SetGraphicsOptions(context);
+
+ context.Clear(Color.Transparent);
+ context.Fill(_panelBrush, _panelRectangle);
+ context.DrawImage(_ryujinxLogo, _logoPosition, 1);
+
+ float halfWidth = _panelRectangle.Width / 2;
+ float buttonsY = _panelRectangle.Y + 185;
+
+ PointF disableButtonPosition = new PointF(halfWidth + 180, buttonsY);
+
+ DrawControllerToggle(context, disableButtonPosition);
+ });
+ }
+
+ public void DrawMutableElements(SoftwareKeyboardUiState state)
+ {
+ if (_surface == null)
+ {
+ return;
+ }
+
+ _surface.Mutate(context =>
+ {
+ var messageRectangle = MeasureString(MessageText, _messageFont);
+ float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X;
+ float messagePositionY = _messagePositionY - messageRectangle.Y;
+ var messagePosition = new PointF(messagePositionX, messagePositionY);
+ var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
+
+ SetGraphicsOptions(context);
+
+ context.Fill(_panelBrush, messageBoundRectangle);
+
+ context.DrawText(MessageText, _messageFont, _textNormalColor, messagePosition);
+
+ if (!state.TypingEnabled)
+ {
+ // Just draw a semi-transparent rectangle on top to fade the component with the background.
+ // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
+
+ context.Fill(_disabledBrush, messageBoundRectangle);
+ }
+
+ DrawTextBox(context, state);
+
+ float halfWidth = _panelRectangle.Width / 2;
+ float buttonsY = _panelRectangle.Y + 185;
+
+ PointF acceptButtonPosition = new PointF(halfWidth - 180, buttonsY);
+ PointF cancelButtonPosition = new PointF(halfWidth , buttonsY);
+ PointF disableButtonPosition = new PointF(halfWidth + 180, buttonsY);
+
+ DrawPadButton(context, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled);
+ DrawPadButton(context, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled);
+ });
+ }
+
+ public void CreateSurface(RenderingSurfaceInfo surfaceInfo)
+ {
+ if (_surfaceInfo != null)
+ {
+ return;
+ }
+
+ _surfaceInfo = surfaceInfo;
+
+ Debug.Assert(_surfaceInfo.ColorFormat == Services.SurfaceFlinger.ColorFormat.A8B8G8R8);
+
+ // Use the whole area of the image to draw, even the alignment, otherwise it may shear the final
+ // image if the pitch is different.
+ uint totalWidth = _surfaceInfo.Pitch / 4;
+ uint totalHeight = _surfaceInfo.Size / _surfaceInfo.Pitch;
+
+ Debug.Assert(_surfaceInfo.Width <= totalWidth);
+ Debug.Assert(_surfaceInfo.Height <= totalHeight);
+ Debug.Assert(_surfaceInfo.Pitch * _surfaceInfo.Height <= _surfaceInfo.Size);
+
+ _surface = new Image<Argb32>((int)totalWidth, (int)totalHeight);
+
+ ComputeConstants();
+ DrawImmutableElements();
+ }
+
+ private void ComputeConstants()
+ {
+ int totalWidth = (int)_surfaceInfo.Width;
+ int totalHeight = (int)_surfaceInfo.Height;
+
+ int panelHeight = 240;
+ int panelPositionY = totalHeight - panelHeight;
+
+ _panelRectangle = new RectangleF(0, panelPositionY, totalWidth, panelHeight);
+
+ _messagePositionY = panelPositionY + 60;
+
+ int logoPositionX = (totalWidth - _ryujinxLogo.Width) / 2;
+ int logoPositionY = panelPositionY + 18;
+
+ _logoPosition = new Point(logoPositionX, logoPositionY);
+ }
+
+ private RectangleF MeasureString(string text, Font font)
+ {
+ RendererOptions options = new RendererOptions(font);
+ FontRectangle rectangle = TextMeasurer.Measure(text == "" ? " " : text, options);
+
+ if (text == "")
+ {
+ return new RectangleF(0, rectangle.Y, 0, rectangle.Height);
+ }
+ else
+ {
+ return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
+ }
+ }
+
+ private void DrawTextBox(IImageProcessingContext context, SoftwareKeyboardUiState state)
+ {
+ var inputTextRectangle = MeasureString(state.InputText, _inputTextFont);
+
+ float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8));
+ float boxHeight = 32;
+ float boxY = _panelRectangle.Y + 110;
+ float boxX = (int)((_panelRectangle.Width - boxWidth) / 2);
+
+ RectangleF boxRectangle = new RectangleF(boxX, boxY, boxWidth, boxHeight);
+
+ RectangleF boundRectangle = new RectangleF(_panelRectangle.X, boxY - _textBoxOutlineWidth,
+ _panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth);
+
+ context.Fill(_panelBrush, boundRectangle);
+
+ context.Draw(_textBoxOutlinePen, boxRectangle);
+
+ float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.X;
+ float inputTextY = boxY + 5;
+
+ var inputTextPosition = new PointF(inputTextX, inputTextY);
+
+ context.DrawText(state.InputText, _inputTextFont, _textNormalColor, inputTextPosition);
+
+ // Draw the cursor on top of the text and redraw the text with a different color if necessary.
+
+ Color cursorTextColor;
+ IBrush cursorBrush;
+ Pen cursorPen;
+
+ float cursorPositionYTop = inputTextY + 1;
+ float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1;
+ float cursorPositionXLeft;
+ float cursorPositionXRight;
+
+ bool cursorVisible = false;
+
+ if (state.CursorBegin != state.CursorEnd)
+ {
+ Debug.Assert(state.InputText.Length > 0);
+
+ cursorTextColor = _textSelectedColor;
+ cursorBrush = _selectionBoxBrush;
+ cursorPen = _selectionBoxPen;
+
+ string textUntilBegin = state.InputText.Substring(0, state.CursorBegin);
+ string textUntilEnd = state.InputText.Substring(0, state.CursorEnd);
+
+ var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont);
+ var selectionEndRectangle = MeasureString(textUntilEnd , _inputTextFont);
+
+ cursorVisible = true;
+ cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X;
+ cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X;
+ }
+ else
+ {
+ cursorTextColor = _textOverCursorColor;
+ cursorBrush = _cursorBrush;
+ cursorPen = _cursorPen;
+
+ if (state.TextBoxBlinkCounter < TextBoxBlinkThreshold)
+ {
+ // Show the blinking cursor.
+
+ int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin);
+ string textUntilCursor = state.InputText.Substring(0, cursorBegin);
+ var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
+
+ cursorVisible = true;
+ cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
+
+ if (state.OverwriteMode)
+ {
+ // The blinking cursor is in overwrite mode so it takes the size of a character.
+
+ if (state.CursorBegin < state.InputText.Length)
+ {
+ textUntilCursor = state.InputText.Substring(0, cursorBegin + 1);
+ cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
+ cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
+ }
+ else
+ {
+ cursorPositionXRight = cursorPositionXLeft + _inputTextFontSize / 2;
+ }
+ }
+ else
+ {
+ // The blinking cursor is in insert mode so it is only a line.
+ cursorPositionXRight = cursorPositionXLeft;
+ }
+ }
+ else
+ {
+ cursorPositionXLeft = inputTextX;
+ cursorPositionXRight = inputTextX;
+ }
+ }
+
+ if (state.TypingEnabled && cursorVisible)
+ {
+ float cursorWidth = cursorPositionXRight - cursorPositionXLeft;
+ float cursorHeight = cursorPositionYBottom - cursorPositionYTop;
+
+ if (cursorWidth == 0)
+ {
+ PointF[] points = new PointF[]
+ {
+ new PointF(cursorPositionXLeft, cursorPositionYTop),
+ new PointF(cursorPositionXLeft, cursorPositionYBottom),
+ };
+
+ context.DrawLines(cursorPen, points);
+ }
+ else
+ {
+ var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
+
+ context.Draw(cursorPen , cursorRectangle);
+ context.Fill(cursorBrush, cursorRectangle);
+
+ Image<Argb32> textOverCursor = new Image<Argb32>((int)cursorRectangle.Width, (int)cursorRectangle.Height);
+ textOverCursor.Mutate(context =>
+ {
+ var textRelativePosition = new PointF(inputTextPosition.X - cursorRectangle.X, inputTextPosition.Y - cursorRectangle.Y);
+ context.DrawText(state.InputText, _inputTextFont, cursorTextColor, textRelativePosition);
+ });
+
+ var cursorPosition = new Point((int)cursorRectangle.X, (int)cursorRectangle.Y);
+ context.DrawImage(textOverCursor, cursorPosition, 1);
+ }
+ }
+ else if (!state.TypingEnabled)
+ {
+ // Just draw a semi-transparent rectangle on top to fade the component with the background.
+ // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
+
+ context.Fill(_disabledBrush, boundRectangle);
+ }
+ }
+
+ private void DrawPadButton(IImageProcessingContext context, PointF point, Image icon, string label, bool pressed, bool enabled)
+ {
+ // Use relative positions so we can center the the entire drawing later.
+
+ float iconX = 0;
+ float iconY = 0;
+ float iconWidth = icon.Width;
+ float iconHeight = icon.Height;
+
+ var labelRectangle = MeasureString(label, _labelsTextFont);
+
+ float labelPositionX = iconWidth + 8 - labelRectangle.X;
+ float labelPositionY = 3;
+
+ float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X;
+ float fullHeight = iconHeight;
+
+ // Convert all relative positions into absolute.
+
+ float originX = (int)(point.X - fullWidth / 2);
+ float originY = (int)(point.Y - fullHeight / 2);
+
+ iconX += originX;
+ iconY += originY;
+
+ var iconPosition = new Point((int)iconX, (int)iconY);
+ var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
+
+ var selectedRectangle = new RectangleF(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth,
+ fullWidth + 4 * _padPressedPenWidth, fullHeight + 4 * _padPressedPenWidth);
+
+ var boundRectangle = new RectangleF(originX, originY, fullWidth, fullHeight);
+ boundRectangle.Inflate(4 * _padPressedPenWidth, 4 * _padPressedPenWidth);
+
+ context.Fill(_panelBrush, boundRectangle);
+ context.DrawImage(icon, iconPosition, 1);
+ context.DrawText(label, _labelsTextFont, _textNormalColor, labelPosition);
+
+ if (enabled)
+ {
+ if (pressed)
+ {
+ context.Draw(_padPressedPen, selectedRectangle);
+ }
+ }
+ else
+ {
+ // Just draw a semi-transparent rectangle on top to fade the component with the background.
+ // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
+
+ context.Fill(_disabledBrush, boundRectangle);
+ }
+ }
+
+ private void DrawControllerToggle(IImageProcessingContext context, PointF point)
+ {
+ var labelRectangle = MeasureString(ControllerToggleText, _labelsTextFont);
+
+ // Use relative positions so we can center the the entire drawing later.
+
+ float keyWidth = _keyModeIcon.Width;
+ float keyHeight = _keyModeIcon.Height;
+
+ float labelPositionX = keyWidth + 8 - labelRectangle.X;
+ float labelPositionY = -labelRectangle.Y - 1;
+
+ float keyX = 0;
+ float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2);
+
+ float fullWidth = labelPositionX + labelRectangle.Width;
+ float fullHeight = Math.Max(labelPositionY + labelRectangle.Height, keyHeight);
+
+ // Convert all relative positions into absolute.
+
+ float originX = (int)(point.X - fullWidth / 2);
+ float originY = (int)(point.Y - fullHeight / 2);
+
+ keyX += originX;
+ keyY += originY;
+
+ var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
+ var overlayPosition = new Point((int)keyX, (int)keyY);
+
+ context.DrawImage(_keyModeIcon, overlayPosition, 1);
+ context.DrawText(ControllerToggleText, _labelsTextFont, _textNormalColor, labelPosition);
+ }
+
+ public void CopyImageToBuffer()
+ {
+ lock (_bufferLock)
+ {
+ if (_surface == null)
+ {
+ return;
+ }
+
+ // Convert the pixel format used in the image to the one used in the Switch surface.
+
+ if (!_surface.TryGetSinglePixelSpan(out Span<Argb32> pixels))
+ {
+ return;
+ }
+
+ _bufferData = MemoryMarshal.AsBytes(pixels).ToArray();
+ Span<uint> dataConvert = MemoryMarshal.Cast<byte, uint>(_bufferData);
+
+ Debug.Assert(_bufferData.Length == _surfaceInfo.Size);
+
+ for (int i = 0; i < dataConvert.Length; i++)
+ {
+ dataConvert[i] = BitOperations.RotateRight(dataConvert[i], 8);
+ }
+ }
+ }
+
+ public bool WriteBufferToMemory(IVirtualMemoryManager destination, ulong position)
+ {
+ lock (_bufferLock)
+ {
+ if (_bufferData == null)
+ {
+ return false;
+ }
+
+ try
+ {
+ destination.Write(position, _bufferData);
+ }
+ catch
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs
new file mode 100644
index 00000000..e6131e62
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiState.cs
@@ -0,0 +1,22 @@
+using Ryujinx.HLE.Ui;
+
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ /// <summary>
+ /// TODO
+ /// </summary>
+ internal class SoftwareKeyboardUiState
+ {
+ public string InputText = "";
+ public int CursorBegin = 0;
+ public int CursorEnd = 0;
+ public bool AcceptPressed = false;
+ public bool CancelPressed = false;
+ public bool OverwriteMode = false;
+ public bool TypingEnabled = true;
+ public bool ControllerEnabled = true;
+ public int TextBoxBlinkCounter = 0;
+
+ public RenderingSurfaceInfo SurfaceInfo = null;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs
index 8884bdcf..0de78a0e 100644
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs
@@ -144,6 +144,20 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
}), cancelled);
}
+ public void Reset(Action action)
+ {
+ // Create a dedicated cancel token for each task.
+ var cancelled = new TRef<bool>(false);
+
+ Reset(new Thread(() =>
+ {
+ while (!Volatile.Read(ref cancelled.Value))
+ {
+ action();
+ }
+ }), cancelled);
+ }
+
private static bool SleepWithSubstep(SleepSubstepData substepData, TRef<bool> cancelled)
{
for (int i = 0; i < substepData.SleepCount; i++)
diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs
index 9510ef4c..3eb88950 100644
--- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs
@@ -1,5 +1,6 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Memory;
+using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS.Services.Mii;
using Ryujinx.HLE.HOS.Services.Mii.Types;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
@@ -172,7 +173,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
if (File.Exists(filePath))
{
- virtualAmiiboFile = JsonSerializer.Deserialize<VirtualAmiiboFile>(File.ReadAllText(filePath));
+ virtualAmiiboFile = JsonHelper.DeserializeFromFile<VirtualAmiiboFile>(filePath);
}
else
{
diff --git a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs
index 0f38e685..3de746ac 100644
--- a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs
@@ -1,18 +1,17 @@
-using System;
-using System.Security.Cryptography;
+using System.Security.Cryptography;
namespace Ryujinx.HLE.HOS.Services.Spl
{
[Service("csrng")]
class IRandomInterface : DisposableIpcService
{
- private RNGCryptoServiceProvider _rng;
+ private RandomNumberGenerator _rng;
private object _lock = new object();
public IRandomInterface(ServiceCtx context)
{
- _rng = new RNGCryptoServiceProvider();
+ _rng = RandomNumberGenerator.Create();
}
[CommandHipc(0)]
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
index 267548dd..49e6614b 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
@@ -396,7 +396,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
if (!applet.DrawTo(surfaceInfo, context.Memory, layerBuffPosition))
{
- Logger.Error?.Print(LogClass.ServiceVi, $"Applet did not draw on indirect layer handle {layerHandle}");
+ Logger.Warning?.Print(LogClass.ServiceVi, $"Applet did not draw on indirect layer handle {layerHandle}");
return ResultCode.Success;
}
diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj
index 76e934bb..22555df7 100644
--- a/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
@@ -21,6 +21,7 @@
<PackageReference Include="Concentus" Version="1.1.7" />
<PackageReference Include="LibHac" Version="0.13.3" />
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
+ <PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
</ItemGroup>
diff --git a/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs b/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs
index 0903ffdd..0ba116ad 100644
--- a/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs
+++ b/Ryujinx.HLE/Ui/RenderingSurfaceInfo.cs
@@ -1,11 +1,12 @@
using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
+using System;
namespace Ryujinx.HLE.Ui
{
/// <summary>
/// Information about the indirect layer that is being drawn to.
/// </summary>
- class RenderingSurfaceInfo
+ class RenderingSurfaceInfo : IEquatable<RenderingSurfaceInfo>
{
public ColorFormat ColorFormat { get; }
public uint Width { get; }
diff --git a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
index a40e23cb..0c92a227 100644
--- a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
+++ b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
diff --git a/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj b/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj
index 2d61dfb8..e93c02ee 100644
--- a/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj
+++ b/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Input/Ryujinx.Input.csproj b/Ryujinx.Input/Ryujinx.Input.csproj
index b2de3ac2..c7c76abc 100644
--- a/Ryujinx.Input/Ryujinx.Input.csproj
+++ b/Ryujinx.Input/Ryujinx.Input.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
diff --git a/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj b/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj
index 38a241b9..90a7e54a 100644
--- a/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj
+++ b/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
diff --git a/Ryujinx.Memory/Ryujinx.Memory.csproj b/Ryujinx.Memory/Ryujinx.Memory.csproj
index f6d19b99..5ece9322 100644
--- a/Ryujinx.Memory/Ryujinx.Memory.csproj
+++ b/Ryujinx.Memory/Ryujinx.Memory.csproj
@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
+ <PackageReference Include="Mono.Posix.NETStandard" Version="5.20.1-preview" />
</ItemGroup>
<ItemGroup>
diff --git a/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj
index 16eb3b3c..de9f7cf7 100644
--- a/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj
+++ b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
diff --git a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj
index e11559d5..803129c8 100644
--- a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj
+++ b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<Configurations>Debug;Release</Configurations>
diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
index 55cb85cd..2005e4fb 100644
--- a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
+++ b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configurations>Debug;Release</Configurations>
</PropertyGroup>
diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj
index 58b11f57..82047ca0 100644
--- a/Ryujinx.Tests/Ryujinx.Tests.csproj
+++ b/Ryujinx.Tests/Ryujinx.Tests.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs
index 524060d4..320928e4 100644
--- a/Ryujinx/Modules/Updater/Updater.cs
+++ b/Ryujinx/Modules/Updater/Updater.cs
@@ -4,7 +4,6 @@ using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip;
using Mono.Unix;
using Newtonsoft.Json.Linq;
-using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Ui;
using Ryujinx.Ui.Widgets;
@@ -13,6 +12,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text;
@@ -92,10 +92,10 @@ namespace Ryujinx.Modules
// Get latest version number from Appveyor
try
{
- using (WebClient jsonClient = new WebClient())
+ using (HttpClient jsonClient = new HttpClient())
{
// Fetch latest build information
- string fetchedJson = await jsonClient.DownloadStringTaskAsync($"{AppveyorApiUrl}/projects/gdkchan/ryujinx/branch/master");
+ string fetchedJson = await jsonClient.GetStringAsync($"{AppveyorApiUrl}/projects/gdkchan/ryujinx/branch/master");
JObject jsonRoot = JObject.Parse(fetchedJson);
JToken buildToken = jsonRoot["build"];
@@ -149,15 +149,15 @@ namespace Ryujinx.Modules
}
// Fetch build size information to learn chunk sizes.
- using (WebClient buildSizeClient = new WebClient())
- {
+ using (HttpClient buildSizeClient = new HttpClient())
+ {
try
{
- buildSizeClient.Headers.Add("Range", "bytes=0-0");
- await buildSizeClient.DownloadDataTaskAsync(new Uri(_buildUrl));
+ buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
+
+ HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
- string contentRange = buildSizeClient.ResponseHeaders["Content-Range"];
- _buildSize = long.Parse(contentRange.Substring(contentRange.IndexOf('/') + 1));
+ _buildSize = message.Content.Headers.ContentRange.Length.Value;
}
catch (Exception ex)
{
@@ -220,7 +220,10 @@ namespace Ryujinx.Modules
for (int i = 0; i < ConnectionCount; i++)
{
+#pragma warning disable SYSLIB0014
+ // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
using (WebClient client = new WebClient())
+#pragma warning restore SYSLIB0014
{
webClients.Add(client);
@@ -307,31 +310,56 @@ namespace Ryujinx.Modules
}
}
- private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile)
+ private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile)
{
- // Single-Threaded Updater
- using (WebClient client = new WebClient())
+ using (HttpClient client = new HttpClient())
{
- client.DownloadProgressChanged += (_, args) =>
- {
- updateDialog.ProgressBar.Value = args.ProgressPercentage;
- };
+ // We do not want to timeout while downloading
+ client.Timeout = TimeSpan.FromDays(1);
- client.DownloadDataCompleted += (_, args) =>
+ using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result)
+ using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result)
{
- File.WriteAllBytes(updateFile, args.Result);
- InstallUpdate(updateDialog, updateFile);
- };
+ using (Stream updateFileStream = File.Open(updateFile, FileMode.Create))
+ {
+ long totalBytes = response.Content.Headers.ContentLength.Value;
+ long byteWritten = 0;
+
+ byte[] buffer = new byte[32 * 1024];
+
+ while (true)
+ {
+ int readSize = remoteFileStream.Read(buffer);
+
+ if (readSize == 0)
+ {
+ break;
+ }
- client.DownloadDataAsync(new Uri(downloadUrl));
+ byteWritten += readSize;
+
+ updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100;
+ updateFileStream.Write(buffer, 0, readSize);
+ }
+ }
+ }
+
+ InstallUpdate(updateDialog, updateFile);
}
}
-
+
+ private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile)
+ {
+ Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile));
+ worker.Name = "Updater.SingleThreadWorker";
+ worker.Start();
+ }
+
private static void SetUnixPermissions()
{
- string ryuBin = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
+ string ryuBin = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (!OperatingSystem.IsWindows())
{
UnixFileInfo unixFileInfo = new UnixFileInfo(ryuBin);
unixFileInfo.FileAccessPermissions |= FileAccessPermissions.UserExecute;
diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs
index 1e0fdd3a..5b67e2b9 100644
--- a/Ryujinx/Program.cs
+++ b/Ryujinx/Program.cs
@@ -61,6 +61,9 @@ namespace Ryujinx
}
}
+ // Enforce loading of Mono.Posix.NETStandard to avoid .NET runtime lazy loading it during an update.
+ Assembly.Load("Mono.Posix.NETStandard");
+
// Make process DPI aware for proper window sizing on high-res screens.
ForceDpiAware.Windows();
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index aba9b53c..e747dc5b 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -24,7 +24,8 @@
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
<PackageReference Include="OpenTK.Graphics" Version="4.5.0" />
<PackageReference Include="SPB" Version="0.0.3-build15" />
- <PackageReference Include="SharpZipLib" Version="1.3.0" />
+ <PackageReference Include="SharpZipLib" Version="1.3.3" />
+ <PackageReference Include="Mono.Posix.NETStandard" Version="5.20.1-preview" />
</ItemGroup>
<ItemGroup>
diff --git a/Ryujinx/Ui/Windows/AmiiboWindow.cs b/Ryujinx/Ui/Windows/AmiiboWindow.cs
index c949d220..7b2a61b9 100644
--- a/Ryujinx/Ui/Windows/AmiiboWindow.cs
+++ b/Ryujinx/Ui/Windows/AmiiboWindow.cs
@@ -1,6 +1,7 @@
using Gtk;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Widgets;
using System;
using System.Collections.Generic;
@@ -9,7 +10,6 @@ using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
-using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
@@ -128,7 +128,7 @@ namespace Ryujinx.Ui.Windows
{
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
- if (await NeedsUpdate(JsonSerializer.Deserialize<AmiiboJson>(amiiboJsonString).LastUpdated))
+ if (await NeedsUpdate(JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).LastUpdated))
{
amiiboJsonString = await DownloadAmiiboJson();
}
@@ -147,7 +147,7 @@ namespace Ryujinx.Ui.Windows
}
}
- _amiiboList = JsonSerializer.Deserialize<AmiiboJson>(amiiboJsonString).Amiibo;
+ _amiiboList = JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).Amiibo;
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
if (LastScannedAmiiboShowAll)
diff --git a/appveyor.yml b/appveyor.yml
index 4ccb0337..b56a36d0 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,9 +2,9 @@ version: 1.0.{build}
branches:
only:
- master
-image: Visual Studio 2019
+image: Visual Studio 2022
environment:
- appveyor_dotnet_runtime: net5.0
+ appveyor_dotnet_runtime: net6.0
matrix:
- config: Release
config_name: '-'
@@ -12,9 +12,9 @@ build_script:
- ps: >-
dotnet --version
- dotnet publish -c $env:config -r win-x64 /p:Version=$env:APPVEYOR_BUILD_VERSION /p:DebugType=embedded
+ dotnet publish -c $env:config -r win-x64 /p:Version=$env:APPVEYOR_BUILD_VERSION /p:DebugType=embedded --self-contained
- dotnet publish -c $env:config -r linux-x64 /p:Version=$env:APPVEYOR_BUILD_VERSION /p:DebugType=embedded
+ dotnet publish -c $env:config -r linux-x64 /p:Version=$env:APPVEYOR_BUILD_VERSION /p:DebugType=embedded --self-contained
7z a ryujinx$env:config_name$env:APPVEYOR_BUILD_VERSION-win_x64.zip $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\$env:config\$env:appveyor_dotnet_runtime\win-x64\publish\
diff --git a/global.json b/global.json
index d129334e..d6c2c37f 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "5.0.100",
- "rollForward": "latestFeature"
+ "version": "6.0.100",
+ "rollForward": "latestFeature"
}
} \ No newline at end of file