forked from Mirror/Ryujinx
[Headless] Add missing arguments & Fix typos (#4193)
* headless: Fix typos in command line options * Remove nullable from command line options Add EnableMacroHLE option Add HideCursorOnIdle option * headless: Adjust enable-ptc help text * headless: Use switch statement instead of if-else chain * headless: Improve formatting for long constructors * headless: Remove discards from SDL_ShowCursor() * headless: Add window icon * Fix hiding cursor on idle At least on Wayland, SDL2 doesn't produce any mouse motion events. * Add new command line args: BaseDataDir and UserProfile * headless: Read icon from embedded resource * headless: Skip SetWindowIcon() on Windows if dll isn't present * headless: Fix division by zero * headless: Fix command line options not working correctly * headless: Fix crash when viewing command line options * headless: Load window icon bmp from memory * Add comment to the workaround for SDL_LoadBMP_RW * headless: Enable logging to file by default * headless: Add 3 options for --hide-cursor Replaces --disable-hide-cursor-on-idle
This commit is contained in:
parent
610eecc1c1
commit
51b3953cfc
10 changed files with 248 additions and 102 deletions
9
Ryujinx.Headless.SDL2/HideCursor.cs
Normal file
9
Ryujinx.Headless.SDL2/HideCursor.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.Headless.SDL2
|
||||||
|
{
|
||||||
|
public enum HideCursor
|
||||||
|
{
|
||||||
|
Never,
|
||||||
|
OnIdle,
|
||||||
|
Always
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.OpenGL;
|
using Ryujinx.Graphics.OpenGL;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
using static SDL2.SDL;
|
using static SDL2.SDL;
|
||||||
|
|
||||||
namespace Ryujinx.Headless.SDL2.OpenGL
|
namespace Ryujinx.Headless.SDL2.OpenGL
|
||||||
|
@ -103,7 +102,13 @@ namespace Ryujinx.Headless.SDL2.OpenGL
|
||||||
private GraphicsDebugLevel _glLogLevel;
|
private GraphicsDebugLevel _glLogLevel;
|
||||||
private SDL2OpenGLContext _openGLContext;
|
private SDL2OpenGLContext _openGLContext;
|
||||||
|
|
||||||
public OpenGLWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse)
|
public OpenGLWindow(
|
||||||
|
InputManager inputManager,
|
||||||
|
GraphicsDebugLevel glLogLevel,
|
||||||
|
AspectRatio aspectRatio,
|
||||||
|
bool enableMouse,
|
||||||
|
HideCursor hideCursor)
|
||||||
|
: base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor)
|
||||||
{
|
{
|
||||||
_glLogLevel = glLogLevel;
|
_glLogLevel = glLogLevel;
|
||||||
}
|
}
|
||||||
|
@ -161,4 +166,4 @@ namespace Ryujinx.Headless.SDL2.OpenGL
|
||||||
SDL_GL_SwapWindow(WindowHandle);
|
SDL_GL_SwapWindow(WindowHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,14 @@ namespace Ryujinx.Headless.SDL2
|
||||||
{
|
{
|
||||||
public class Options
|
public class Options
|
||||||
{
|
{
|
||||||
|
// General
|
||||||
|
|
||||||
|
[Option("root-data-dir", Required = false, HelpText = "Set the custom folder path for Ryujinx data.")]
|
||||||
|
public string BaseDataDir { get; set; }
|
||||||
|
|
||||||
|
[Option("profile", Required = false, HelpText = "Set the user profile to launch the game with.")]
|
||||||
|
public string UserProfile { get; set; }
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
|
|
||||||
[Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")]
|
[Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")]
|
||||||
|
@ -23,7 +31,7 @@ namespace Ryujinx.Headless.SDL2
|
||||||
[Option("input-profile-5", Required = false, HelpText = "Set the input profile in use for Player 5.")]
|
[Option("input-profile-5", Required = false, HelpText = "Set the input profile in use for Player 5.")]
|
||||||
public string InputProfile5Name { get; set; }
|
public string InputProfile5Name { get; set; }
|
||||||
|
|
||||||
[Option("input-profile-6", Required = false, HelpText = "Set the input profile in use for Player 5.")]
|
[Option("input-profile-6", Required = false, HelpText = "Set the input profile in use for Player 6.")]
|
||||||
public string InputProfile6Name { get; set; }
|
public string InputProfile6Name { get; set; }
|
||||||
|
|
||||||
[Option("input-profile-7", Required = false, HelpText = "Set the input profile in use for Player 7.")]
|
[Option("input-profile-7", Required = false, HelpText = "Set the input profile in use for Player 7.")]
|
||||||
|
@ -63,42 +71,45 @@ namespace Ryujinx.Headless.SDL2
|
||||||
public string InputIdHandheld { get; set; }
|
public string InputIdHandheld { get; set; }
|
||||||
|
|
||||||
[Option("enable-keyboard", Required = false, Default = false, HelpText = "Enable or disable keyboard support (Independent from controllers binding).")]
|
[Option("enable-keyboard", Required = false, Default = false, HelpText = "Enable or disable keyboard support (Independent from controllers binding).")]
|
||||||
public bool? EnableKeyboard { get; set; }
|
public bool EnableKeyboard { get; set; }
|
||||||
|
|
||||||
[Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")]
|
[Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")]
|
||||||
public bool? EnableMouse { get; set; }
|
public bool EnableMouse { get; set; }
|
||||||
|
|
||||||
|
[Option("hide-cursor", Required = false, Default = HideCursor.OnIdle, HelpText = "Change when the cursor gets hidden.")]
|
||||||
|
public HideCursor HideCursor { get; set; }
|
||||||
|
|
||||||
[Option("list-input-profiles", Required = false, HelpText = "List inputs profiles.")]
|
[Option("list-input-profiles", Required = false, HelpText = "List inputs profiles.")]
|
||||||
public bool? ListInputProfiles { get; set; }
|
public bool ListInputProfiles { get; set; }
|
||||||
|
|
||||||
[Option("list-inputs-ids", Required = false, HelpText = "List inputs ids.")]
|
[Option("list-inputs-ids", Required = false, HelpText = "List inputs ids.")]
|
||||||
public bool ListInputIds { get; set; }
|
public bool ListInputIds { get; set; }
|
||||||
|
|
||||||
// System
|
// System
|
||||||
|
|
||||||
[Option("enable-ptc", Required = false, Default = true, HelpText = "Enables profiled translation cache persistency.")]
|
[Option("disable-ptc", Required = false, HelpText = "Disables profiled persistent translation cache.")]
|
||||||
public bool? EnablePtc { get; set; }
|
public bool DisablePtc { get; set; }
|
||||||
|
|
||||||
[Option("enable-internet-connection", Required = false, Default = false, HelpText = "Enables guest Internet connection.")]
|
[Option("enable-internet-connection", Required = false, Default = false, HelpText = "Enables guest Internet connection.")]
|
||||||
public bool? EnableInternetAccess { get; set; }
|
public bool EnableInternetAccess { get; set; }
|
||||||
|
|
||||||
[Option("enable-fs-integrity-checks", Required = false, Default = true, HelpText = "Enables integrity checks on Game content files.")]
|
[Option("disable-fs-integrity-checks", Required = false, HelpText = "Disables integrity checks on Game content files.")]
|
||||||
public bool? EnableFsIntegrityChecks { get; set; }
|
public bool DisableFsIntegrityChecks { get; set; }
|
||||||
|
|
||||||
[Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")]
|
[Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")]
|
||||||
public int FsGlobalAccessLogMode { get; set; }
|
public int FsGlobalAccessLogMode { get; set; }
|
||||||
|
|
||||||
[Option("enable-vsync", Required = false, Default = true, HelpText = "Enables Vertical Sync.")]
|
[Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync.")]
|
||||||
public bool? EnableVsync { get; set; }
|
public bool DisableVsync { get; set; }
|
||||||
|
|
||||||
[Option("enable-shader-cache", Required = false, Default = true, HelpText = "Enables Shader cache.")]
|
[Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")]
|
||||||
public bool? EnableShaderCache { get; set; }
|
public bool DisableShaderCache { get; set; }
|
||||||
|
|
||||||
[Option("enable-texture-recompression", Required = false, Default = false, HelpText = "Enables Texture recompression.")]
|
[Option("enable-texture-recompression", Required = false, Default = false, HelpText = "Enables Texture recompression.")]
|
||||||
public bool? EnableTextureRecompression { get; set; }
|
public bool EnableTextureRecompression { get; set; }
|
||||||
|
|
||||||
[Option("enable-docked-mode", Required = false, Default = true, HelpText = "Enables Docked Mode.")]
|
[Option("disable-docked-mode", Required = false, HelpText = "Disables Docked Mode.")]
|
||||||
public bool? EnableDockedMode { get; set; }
|
public bool DisableDockedMode { get; set; }
|
||||||
|
|
||||||
[Option("system-language", Required = false, Default = SystemLanguage.AmericanEnglish, HelpText = "Change System Language.")]
|
[Option("system-language", Required = false, Default = SystemLanguage.AmericanEnglish, HelpText = "Change System Language.")]
|
||||||
public SystemLanguage SystemLanguage { get; set; }
|
public SystemLanguage SystemLanguage { get; set; }
|
||||||
|
@ -120,32 +131,32 @@ namespace Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
|
|
||||||
[Option("enable-file-logging", Required = false, Default = false, HelpText = "Enables logging to a file on disk.")]
|
[Option("disable-file-logging", Required = false, Default = false, HelpText = "Disables logging to a file on disk.")]
|
||||||
public bool? EnableFileLog { get; set; }
|
public bool DisableFileLog { get; set; }
|
||||||
|
|
||||||
[Option("enable-debug-logs", Required = false, Default = false, HelpText = "Enables printing debug log messages.")]
|
[Option("enable-debug-logs", Required = false, Default = false, HelpText = "Enables printing debug log messages.")]
|
||||||
public bool? LoggingEnableDebug { get; set; }
|
public bool LoggingEnableDebug { get; set; }
|
||||||
|
|
||||||
[Option("enable-stub-logs", Required = false, Default = true, HelpText = "Enables printing stub log messages.")]
|
[Option("disable-stub-logs", Required = false, HelpText = "Disables printing stub log messages.")]
|
||||||
public bool? LoggingEnableStub { get; set; }
|
public bool LoggingDisableStub { get; set; }
|
||||||
|
|
||||||
[Option("enable-info-logs", Required = false, Default = true, HelpText = "Enables printing info log messages.")]
|
[Option("disable-info-logs", Required = false, HelpText = "Disables printing info log messages.")]
|
||||||
public bool? LoggingEnableInfo { get; set; }
|
public bool LoggingDisableInfo { get; set; }
|
||||||
|
|
||||||
[Option("enable-warning-logs", Required = false, Default = true, HelpText = "Enables printing warning log messages.")]
|
[Option("disable-warning-logs", Required = false, HelpText = "Disables printing warning log messages.")]
|
||||||
public bool? LoggingEnableWarning { get; set; }
|
public bool LoggingDisableWarning { get; set; }
|
||||||
|
|
||||||
[Option("enable-error-logs", Required = false, Default = true, HelpText = "Enables printing error log messages.")]
|
[Option("disable-error-logs", Required = false, HelpText = "Disables printing error log messages.")]
|
||||||
public bool? LoggingEnableError { get; set; }
|
public bool LoggingEnableError { get; set; }
|
||||||
|
|
||||||
[Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")]
|
[Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")]
|
||||||
public bool? LoggingEnableTrace { get; set; }
|
public bool LoggingEnableTrace { get; set; }
|
||||||
|
|
||||||
[Option("enable-guest-logs", Required = false, Default = true, HelpText = "Enables printing guest log messages.")]
|
[Option("disable-guest-logs", Required = false, HelpText = "Disables printing guest log messages.")]
|
||||||
public bool? LoggingEnableGuest { get; set; }
|
public bool LoggingDisableGuest { get; set; }
|
||||||
|
|
||||||
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")]
|
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")]
|
||||||
public bool? LoggingEnableFsAccessLog { get; set; }
|
public bool LoggingEnableFsAccessLog { get; set; }
|
||||||
|
|
||||||
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")]
|
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")]
|
||||||
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
|
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
|
||||||
|
@ -164,6 +175,9 @@ namespace Ryujinx.Headless.SDL2
|
||||||
[Option("backend-threading", Required = false, Default = BackendThreading.Auto, HelpText = "Whether or not backend threading is enabled. The \"Auto\" setting will determine whether threading should be enabled at runtime.")]
|
[Option("backend-threading", Required = false, Default = BackendThreading.Auto, HelpText = "Whether or not backend threading is enabled. The \"Auto\" setting will determine whether threading should be enabled at runtime.")]
|
||||||
public BackendThreading BackendThreading { get; set; }
|
public BackendThreading BackendThreading { get; set; }
|
||||||
|
|
||||||
|
[Option("disable-macro-hle", Required= false, HelpText = "Disables high-level emulation of Macro code. Leaving this enabled improves performance but may cause graphical glitches in some games.")]
|
||||||
|
public bool DisableMacroHLE { get; set; }
|
||||||
|
|
||||||
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")]
|
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")]
|
||||||
public string GraphicsShadersDumpPath { get; set; }
|
public string GraphicsShadersDumpPath { get; set; }
|
||||||
|
|
||||||
|
@ -176,14 +190,14 @@ namespace Ryujinx.Headless.SDL2
|
||||||
// Hacks
|
// Hacks
|
||||||
|
|
||||||
[Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")]
|
[Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")]
|
||||||
public bool? ExpandRam { get; set; }
|
public bool ExpandRam { get; set; }
|
||||||
|
|
||||||
[Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")]
|
[Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")]
|
||||||
public bool? IgnoreMissingServices { get; set; }
|
public bool IgnoreMissingServices { get; set; }
|
||||||
|
|
||||||
// Values
|
// Values
|
||||||
|
|
||||||
[Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)]
|
[Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)]
|
||||||
public string InputPath { get; set; }
|
public string InputPath { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -33,7 +33,6 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
|
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
|
||||||
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
|
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
|
||||||
using Key = Ryujinx.Common.Configuration.Hid.Key;
|
using Key = Ryujinx.Common.Configuration.Hid.Key;
|
||||||
|
@ -63,20 +62,6 @@ namespace Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";
|
Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";
|
||||||
|
|
||||||
AppDataManager.Initialize(null);
|
|
||||||
|
|
||||||
_virtualFileSystem = VirtualFileSystem.CreateInstance();
|
|
||||||
_libHacHorizonManager = new LibHacHorizonManager();
|
|
||||||
|
|
||||||
_libHacHorizonManager.InitializeFsServer(_virtualFileSystem);
|
|
||||||
_libHacHorizonManager.InitializeArpServer();
|
|
||||||
_libHacHorizonManager.InitializeBcatServer();
|
|
||||||
_libHacHorizonManager.InitializeSystemClients();
|
|
||||||
|
|
||||||
_contentManager = new ContentManager(_virtualFileSystem);
|
|
||||||
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient);
|
|
||||||
_userChannelPersistence = new UserChannelPersistence();
|
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
|
if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
|
||||||
{
|
{
|
||||||
AutoResetEvent invoked = new AutoResetEvent(false);
|
AutoResetEvent invoked = new AutoResetEvent(false);
|
||||||
|
@ -97,15 +82,9 @@ namespace Ryujinx.Headless.SDL2
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
|
||||||
|
|
||||||
GraphicsConfig.EnableShaderCache = true;
|
|
||||||
|
|
||||||
Parser.Default.ParseArguments<Options>(args)
|
Parser.Default.ParseArguments<Options>(args)
|
||||||
.WithParsed(options => Load(options))
|
.WithParsed(Load)
|
||||||
.WithNotParsed(errors => errors.Output());
|
.WithNotParsed(errors => errors.Output());
|
||||||
|
|
||||||
_inputManager.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
||||||
|
@ -343,6 +322,24 @@ namespace Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
static void Load(Options option)
|
static void Load(Options option)
|
||||||
{
|
{
|
||||||
|
AppDataManager.Initialize(option.BaseDataDir);
|
||||||
|
|
||||||
|
_virtualFileSystem = VirtualFileSystem.CreateInstance();
|
||||||
|
_libHacHorizonManager = new LibHacHorizonManager();
|
||||||
|
|
||||||
|
_libHacHorizonManager.InitializeFsServer(_virtualFileSystem);
|
||||||
|
_libHacHorizonManager.InitializeArpServer();
|
||||||
|
_libHacHorizonManager.InitializeBcatServer();
|
||||||
|
_libHacHorizonManager.InitializeSystemClients();
|
||||||
|
|
||||||
|
_contentManager = new ContentManager(_virtualFileSystem);
|
||||||
|
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile);
|
||||||
|
_userChannelPersistence = new UserChannelPersistence();
|
||||||
|
|
||||||
|
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||||
|
|
||||||
|
GraphicsConfig.EnableShaderCache = true;
|
||||||
|
|
||||||
IGamepad gamepad;
|
IGamepad gamepad;
|
||||||
|
|
||||||
if (option.ListInputIds)
|
if (option.ListInputIds)
|
||||||
|
@ -378,8 +375,8 @@ namespace Ryujinx.Headless.SDL2
|
||||||
}
|
}
|
||||||
|
|
||||||
_inputConfiguration = new List<InputConfig>();
|
_inputConfiguration = new List<InputConfig>();
|
||||||
_enableKeyboard = (bool)option.EnableKeyboard;
|
_enableKeyboard = option.EnableKeyboard;
|
||||||
_enableMouse = (bool)option.EnableMouse;
|
_enableMouse = option.EnableMouse;
|
||||||
|
|
||||||
void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
||||||
{
|
{
|
||||||
|
@ -407,16 +404,16 @@ namespace Ryujinx.Headless.SDL2
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup logging level
|
// Setup logging level
|
||||||
Logger.SetEnable(LogLevel.Debug, (bool)option.LoggingEnableDebug);
|
Logger.SetEnable(LogLevel.Debug, option.LoggingEnableDebug);
|
||||||
Logger.SetEnable(LogLevel.Stub, (bool)option.LoggingEnableStub);
|
Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub);
|
||||||
Logger.SetEnable(LogLevel.Info, (bool)option.LoggingEnableInfo);
|
Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo);
|
||||||
Logger.SetEnable(LogLevel.Warning, (bool)option.LoggingEnableWarning);
|
Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning);
|
||||||
Logger.SetEnable(LogLevel.Error, (bool)option.LoggingEnableError);
|
Logger.SetEnable(LogLevel.Error, option.LoggingEnableError);
|
||||||
Logger.SetEnable(LogLevel.Trace, (bool)option.LoggingEnableTrace);
|
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
|
||||||
Logger.SetEnable(LogLevel.Guest, (bool)option.LoggingEnableGuest);
|
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
|
||||||
Logger.SetEnable(LogLevel.AccessLog, (bool)option.LoggingEnableFsAccessLog);
|
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
||||||
|
|
||||||
if ((bool)option.EnableFileLog)
|
if (!option.DisableFileLog)
|
||||||
{
|
{
|
||||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||||
new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"),
|
new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"),
|
||||||
|
@ -426,11 +423,12 @@ namespace Ryujinx.Headless.SDL2
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup graphics configuration
|
// Setup graphics configuration
|
||||||
GraphicsConfig.EnableShaderCache = (bool)option.EnableShaderCache;
|
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
||||||
GraphicsConfig.EnableTextureRecompression = (bool)option.EnableTextureRecompression;
|
GraphicsConfig.EnableTextureRecompression = option.EnableTextureRecompression;
|
||||||
GraphicsConfig.ResScale = option.ResScale;
|
GraphicsConfig.ResScale = option.ResScale;
|
||||||
GraphicsConfig.MaxAnisotropy = option.MaxAnisotropy;
|
GraphicsConfig.MaxAnisotropy = option.MaxAnisotropy;
|
||||||
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
|
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
|
||||||
|
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -443,6 +441,8 @@ namespace Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
_userChannelPersistence.ShouldRestart = false;
|
_userChannelPersistence.ShouldRestart = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_inputManager.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetupProgressHandler()
|
private static void SetupProgressHandler()
|
||||||
|
@ -479,8 +479,8 @@ namespace Ryujinx.Headless.SDL2
|
||||||
private static WindowBase CreateWindow(Options options)
|
private static WindowBase CreateWindow(Options options)
|
||||||
{
|
{
|
||||||
return options.GraphicsBackend == GraphicsBackend.Vulkan
|
return options.GraphicsBackend == GraphicsBackend.Vulkan
|
||||||
? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse)
|
? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor)
|
||||||
: new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse);
|
: new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IRenderer CreateRenderer(Options options, WindowBase window)
|
private static IRenderer CreateRenderer(Options options, WindowBase window)
|
||||||
|
@ -533,20 +533,20 @@ namespace Ryujinx.Headless.SDL2
|
||||||
_userChannelPersistence,
|
_userChannelPersistence,
|
||||||
renderer,
|
renderer,
|
||||||
new SDL2HardwareDeviceDriver(),
|
new SDL2HardwareDeviceDriver(),
|
||||||
(bool)options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB,
|
options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB,
|
||||||
window,
|
window,
|
||||||
options.SystemLanguage,
|
options.SystemLanguage,
|
||||||
options.SystemRegion,
|
options.SystemRegion,
|
||||||
(bool)options.EnableVsync,
|
!options.DisableVsync,
|
||||||
(bool)options.EnableDockedMode,
|
!options.DisableDockedMode,
|
||||||
(bool)options.EnablePtc,
|
!options.DisablePtc,
|
||||||
(bool)options.EnableInternetAccess,
|
options.EnableInternetAccess,
|
||||||
(bool)options.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||||
options.FsGlobalAccessLogMode,
|
options.FsGlobalAccessLogMode,
|
||||||
options.SystemTimeOffset,
|
options.SystemTimeOffset,
|
||||||
options.SystemTimeZone,
|
options.SystemTimeZone,
|
||||||
options.MemoryManagerMode,
|
options.MemoryManagerMode,
|
||||||
(bool)options.IgnoreMissingServices,
|
options.IgnoreMissingServices,
|
||||||
options.AspectRatio,
|
options.AspectRatio,
|
||||||
options.AudioVolume);
|
options.AudioVolume);
|
||||||
|
|
||||||
|
@ -649,7 +649,7 @@ namespace Ryujinx.Headless.SDL2
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
Logger.Warning?.Print(LogClass.Application, $"Couldn't load '{options.InputPath}'. Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
||||||
|
|
||||||
_emulationContext.Dispose();
|
_emulationContext.Dispose();
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,10 @@
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Ryujinx.bmp" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Due to .net core 3.1 embedded resource loading -->
|
<!-- Due to .net core 3.1 embedded resource loading -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseDependentUponConvention>
|
<EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseDependentUponConvention>
|
||||||
|
|
BIN
Ryujinx.Headless.SDL2/Ryujinx.bmp
Normal file
BIN
Ryujinx.Headless.SDL2/Ryujinx.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
|
@ -10,7 +10,12 @@ namespace Ryujinx.Headless.SDL2
|
||||||
{
|
{
|
||||||
class SDL2MouseDriver : IGamepadDriver
|
class SDL2MouseDriver : IGamepadDriver
|
||||||
{
|
{
|
||||||
|
private const int CursorHideIdleTime = 8; // seconds
|
||||||
|
|
||||||
private bool _isDisposed;
|
private bool _isDisposed;
|
||||||
|
private HideCursor _hideCursor;
|
||||||
|
private bool _isHidden;
|
||||||
|
private long _lastCursorMoveTime;
|
||||||
|
|
||||||
public bool[] PressedButtons { get; }
|
public bool[] PressedButtons { get; }
|
||||||
|
|
||||||
|
@ -18,9 +23,16 @@ namespace Ryujinx.Headless.SDL2
|
||||||
public Vector2 Scroll { get; private set; }
|
public Vector2 Scroll { get; private set; }
|
||||||
public Size _clientSize;
|
public Size _clientSize;
|
||||||
|
|
||||||
public SDL2MouseDriver()
|
public SDL2MouseDriver(HideCursor hideCursor)
|
||||||
{
|
{
|
||||||
PressedButtons = new bool[(int)MouseButton.Count];
|
PressedButtons = new bool[(int)MouseButton.Count];
|
||||||
|
_hideCursor = hideCursor;
|
||||||
|
|
||||||
|
if (_hideCursor == HideCursor.Always)
|
||||||
|
{
|
||||||
|
SDL_ShowCursor(SDL_DISABLE);
|
||||||
|
_isHidden = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
@ -31,26 +43,75 @@ namespace Ryujinx.Headless.SDL2
|
||||||
return (MouseButton)(rawButton - 1);
|
return (MouseButton)(rawButton - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(SDL_Event evnt)
|
public void UpdatePosition()
|
||||||
{
|
{
|
||||||
if (evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN || evnt.type == SDL_EventType.SDL_MOUSEBUTTONUP)
|
SDL_GetMouseState(out int posX, out int posY);
|
||||||
|
Vector2 position = new(posX, posY);
|
||||||
|
|
||||||
|
if (CurrentPosition != position)
|
||||||
{
|
{
|
||||||
uint rawButton = evnt.button.button;
|
CurrentPosition = position;
|
||||||
|
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
if (rawButton > 0 && rawButton <= (int)MouseButton.Count)
|
CheckIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckIdle()
|
||||||
|
{
|
||||||
|
if (_hideCursor != HideCursor.OnIdle)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime;
|
||||||
|
|
||||||
|
if (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency)
|
||||||
|
{
|
||||||
|
if (!_isHidden)
|
||||||
{
|
{
|
||||||
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;
|
SDL_ShowCursor(SDL_DISABLE);
|
||||||
|
_isHidden = true;
|
||||||
CurrentPosition = new Vector2(evnt.button.x, evnt.button.y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (evnt.type == SDL_EventType.SDL_MOUSEMOTION)
|
else
|
||||||
{
|
{
|
||||||
CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y);
|
if (_isHidden)
|
||||||
|
{
|
||||||
|
SDL_ShowCursor(SDL_ENABLE);
|
||||||
|
_isHidden = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (evnt.type == SDL_EventType.SDL_MOUSEWHEEL)
|
}
|
||||||
|
|
||||||
|
public void Update(SDL_Event evnt)
|
||||||
|
{
|
||||||
|
switch (evnt.type)
|
||||||
{
|
{
|
||||||
Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y);
|
case SDL_EventType.SDL_MOUSEBUTTONDOWN:
|
||||||
|
case SDL_EventType.SDL_MOUSEBUTTONUP:
|
||||||
|
uint rawButton = evnt.button.button;
|
||||||
|
|
||||||
|
if (rawButton > 0 && rawButton <= (int)MouseButton.Count)
|
||||||
|
{
|
||||||
|
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;
|
||||||
|
|
||||||
|
CurrentPosition = new Vector2(evnt.button.x, evnt.button.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// NOTE: On Linux using Wayland mouse motion events won't be received at all.
|
||||||
|
case SDL_EventType.SDL_MOUSEMOTION:
|
||||||
|
CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y);
|
||||||
|
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_EventType.SDL_MOUSEWHEEL:
|
||||||
|
Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,4 +161,4 @@ namespace Ryujinx.Headless.SDL2
|
||||||
_isDisposed = true;
|
_isDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,13 @@ namespace Ryujinx.Headless.SDL2.Vulkan
|
||||||
{
|
{
|
||||||
private GraphicsDebugLevel _glLogLevel;
|
private GraphicsDebugLevel _glLogLevel;
|
||||||
|
|
||||||
public VulkanWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse)
|
public VulkanWindow(
|
||||||
|
InputManager inputManager,
|
||||||
|
GraphicsDebugLevel glLogLevel,
|
||||||
|
AspectRatio aspectRatio,
|
||||||
|
bool enableMouse,
|
||||||
|
HideCursor hideCursor)
|
||||||
|
: base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor)
|
||||||
{
|
{
|
||||||
_glLogLevel = glLogLevel;
|
_glLogLevel = glLogLevel;
|
||||||
}
|
}
|
||||||
|
@ -95,4 +101,4 @@ namespace Ryujinx.Headless.SDL2.Vulkan
|
||||||
|
|
||||||
protected override void SwapBuffers() { }
|
protected override void SwapBuffers() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,13 +14,16 @@ using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using static SDL2.SDL;
|
using static SDL2.SDL;
|
||||||
using Switch = Ryujinx.HLE.Switch;
|
using Switch = Ryujinx.HLE.Switch;
|
||||||
|
|
||||||
namespace Ryujinx.Headless.SDL2
|
namespace Ryujinx.Headless.SDL2
|
||||||
{
|
{
|
||||||
abstract class WindowBase : IHostUiHandler, IDisposable
|
abstract partial class WindowBase : IHostUiHandler, IDisposable
|
||||||
{
|
{
|
||||||
protected const int DefaultWidth = 1280;
|
protected const int DefaultWidth = 1280;
|
||||||
protected const int DefaultHeight = 720;
|
protected const int DefaultHeight = 720;
|
||||||
|
@ -29,6 +32,10 @@ namespace Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
private static ConcurrentQueue<Action> MainThreadActions = new ConcurrentQueue<Action>();
|
private static ConcurrentQueue<Action> MainThreadActions = new ConcurrentQueue<Action>();
|
||||||
|
|
||||||
|
[LibraryImport("SDL2")]
|
||||||
|
// TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly
|
||||||
|
private static partial IntPtr SDL_LoadBMP_RW(IntPtr src, int freesrc);
|
||||||
|
|
||||||
public static void QueueMainThreadAction(Action action)
|
public static void QueueMainThreadAction(Action action)
|
||||||
{
|
{
|
||||||
MainThreadActions.Enqueue(action);
|
MainThreadActions.Enqueue(action);
|
||||||
|
@ -66,9 +73,14 @@ namespace Ryujinx.Headless.SDL2
|
||||||
private AspectRatio _aspectRatio;
|
private AspectRatio _aspectRatio;
|
||||||
private bool _enableMouse;
|
private bool _enableMouse;
|
||||||
|
|
||||||
public WindowBase(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse)
|
public WindowBase(
|
||||||
|
InputManager inputManager,
|
||||||
|
GraphicsDebugLevel glLogLevel,
|
||||||
|
AspectRatio aspectRatio,
|
||||||
|
bool enableMouse,
|
||||||
|
HideCursor hideCursor)
|
||||||
{
|
{
|
||||||
MouseDriver = new SDL2MouseDriver();
|
MouseDriver = new SDL2MouseDriver(hideCursor);
|
||||||
_inputManager = inputManager;
|
_inputManager = inputManager;
|
||||||
_inputManager.SetMouseDriver(MouseDriver);
|
_inputManager.SetMouseDriver(MouseDriver);
|
||||||
NpadManager = _inputManager.CreateNpadManager();
|
NpadManager = _inputManager.CreateNpadManager();
|
||||||
|
@ -103,6 +115,34 @@ namespace Ryujinx.Headless.SDL2
|
||||||
TouchScreenManager.Initialize(device);
|
TouchScreenManager.Initialize(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetWindowIcon()
|
||||||
|
{
|
||||||
|
Stream iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.Headless.SDL2.Ryujinx.bmp");
|
||||||
|
byte[] iconBytes = new byte[iconStream!.Length];
|
||||||
|
|
||||||
|
if (iconStream.Read(iconBytes, 0, iconBytes.Length) != iconBytes.Length)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, "Failed to read icon to byte array.");
|
||||||
|
iconStream.Close();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iconStream.Close();
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* iconPtr = iconBytes)
|
||||||
|
{
|
||||||
|
IntPtr rwOpsStruct = SDL_RWFromConstMem((IntPtr)iconPtr, iconBytes.Length);
|
||||||
|
IntPtr iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1);
|
||||||
|
|
||||||
|
SDL_SetWindowIcon(WindowHandle, iconHandle);
|
||||||
|
SDL_FreeSurface(iconHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void InitializeWindow()
|
private void InitializeWindow()
|
||||||
{
|
{
|
||||||
string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty
|
string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty
|
||||||
|
@ -127,6 +167,8 @@ namespace Ryujinx.Headless.SDL2
|
||||||
throw new Exception(errorMessage);
|
throw new Exception(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetWindowIcon();
|
||||||
|
|
||||||
_windowId = SDL_GetWindowID(WindowHandle);
|
_windowId = SDL_GetWindowID(WindowHandle);
|
||||||
SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
|
SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
|
||||||
|
|
||||||
|
@ -146,9 +188,11 @@ namespace Ryujinx.Headless.SDL2
|
||||||
Renderer?.Window.SetSize(Width, Height);
|
Renderer?.Window.SetSize(Width, Height);
|
||||||
MouseDriver.SetClientSize(Width, Height);
|
MouseDriver.SetClientSize(Width, Height);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE:
|
case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE:
|
||||||
Exit();
|
Exit();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -331,6 +375,9 @@ namespace Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
Device.Hid.DebugPad.Update();
|
Device.Hid.DebugPad.Update();
|
||||||
|
|
||||||
|
// TODO: Replace this with MouseDriver.CheckIdle() when mouse motion events are received on every supported platform.
|
||||||
|
MouseDriver.UpdatePosition();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,4 +498,4 @@ namespace Ryujinx.Headless.SDL2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -163,7 +163,7 @@ namespace Ryujinx.Input.HLE
|
||||||
ReloadConfiguration(inputConfig, enableKeyboard, enableMouse);
|
ReloadConfiguration(inputConfig, enableKeyboard, enableMouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(float aspectRatio = 0)
|
public void Update(float aspectRatio = 1)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue