-
Notifications
You must be signed in to change notification settings - Fork 563
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
382 additions
and
299 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,361 @@ | ||
using DiscordRPC; | ||
using LibHac.Tools.FsSystem; | ||
using Ryujinx.Audio.Backends.SDL2; | ||
using Ryujinx.Ava; | ||
using Ryujinx.Common.Configuration; | ||
using Ryujinx.Common.Configuration.Hid; | ||
using Ryujinx.Common.Configuration.Hid.Controller; | ||
using Ryujinx.Common.Configuration.Hid.Controller.Motion; | ||
using Ryujinx.Common.Configuration.Hid.Keyboard; | ||
using Ryujinx.Common.Logging; | ||
using Ryujinx.Common.Utilities; | ||
using Ryujinx.Graphics.GAL; | ||
using Ryujinx.Graphics.GAL.Multithreading; | ||
using Ryujinx.Graphics.OpenGL; | ||
using Ryujinx.Graphics.Vulkan; | ||
using Ryujinx.HLE; | ||
using Ryujinx.Input; | ||
using Ryujinx.UI.Common; | ||
using Ryujinx.UI.Common.Configuration; | ||
using Silk.NET.Vulkan; | ||
using System; | ||
using System.IO; | ||
using System.Text.Json; | ||
using System.Threading.Tasks; | ||
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; | ||
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; | ||
using Key = Ryujinx.Common.Configuration.Hid.Key; | ||
|
||
namespace Ryujinx.Headless | ||
{ | ||
public partial class HeadlessRyujinx | ||
{ | ||
public static void Initialize() | ||
{ | ||
// Ensure Discord presence timestamp begins at the absolute start of when Ryujinx is launched | ||
DiscordIntegrationModule.StartedAt = Timestamps.Now; | ||
|
||
// Delete backup files after updating. | ||
Task.Run(Updater.CleanupUpdate); | ||
|
||
// Hook unhandled exception and process exit events. | ||
AppDomain.CurrentDomain.UnhandledException += (sender, e) | ||
=> Program.ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating); | ||
AppDomain.CurrentDomain.ProcessExit += (_, _) => Program.Exit(); | ||
|
||
// Initialize the configuration. | ||
ConfigurationState.Initialize(); | ||
|
||
// Initialize Discord integration. | ||
DiscordIntegrationModule.Initialize(); | ||
|
||
// Logging system information. | ||
Program.PrintSystemInfo(); | ||
} | ||
|
||
private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) | ||
{ | ||
if (inputId == null) | ||
{ | ||
if (index == PlayerIndex.Player1) | ||
{ | ||
Logger.Info?.Print(LogClass.Application, $"{index} not configured, defaulting to default keyboard."); | ||
|
||
// Default to keyboard | ||
inputId = "0"; | ||
} | ||
else | ||
{ | ||
Logger.Info?.Print(LogClass.Application, $"{index} not configured"); | ||
|
||
return null; | ||
} | ||
} | ||
|
||
IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(inputId); | ||
|
||
bool isKeyboard = true; | ||
|
||
if (gamepad == null) | ||
{ | ||
gamepad = _inputManager.GamepadDriver.GetGamepad(inputId); | ||
isKeyboard = false; | ||
|
||
if (gamepad == null) | ||
{ | ||
Logger.Error?.Print(LogClass.Application, $"{index} gamepad not found (\"{inputId}\")"); | ||
|
||
return null; | ||
} | ||
} | ||
|
||
string gamepadName = gamepad.Name; | ||
|
||
gamepad.Dispose(); | ||
|
||
InputConfig config; | ||
|
||
if (inputProfileName == null || inputProfileName.Equals("default")) | ||
{ | ||
if (isKeyboard) | ||
{ | ||
config = new StandardKeyboardInputConfig | ||
{ | ||
Version = InputConfig.CurrentVersion, | ||
Backend = InputBackendType.WindowKeyboard, | ||
Id = null, | ||
ControllerType = ControllerType.JoyconPair, | ||
LeftJoycon = new LeftJoyconCommonConfig<Key> | ||
{ | ||
DpadUp = Key.Up, | ||
DpadDown = Key.Down, | ||
DpadLeft = Key.Left, | ||
DpadRight = Key.Right, | ||
ButtonMinus = Key.Minus, | ||
ButtonL = Key.E, | ||
ButtonZl = Key.Q, | ||
ButtonSl = Key.Unbound, | ||
ButtonSr = Key.Unbound, | ||
}, | ||
|
||
LeftJoyconStick = new JoyconConfigKeyboardStick<Key> | ||
{ | ||
StickUp = Key.W, | ||
StickDown = Key.S, | ||
StickLeft = Key.A, | ||
StickRight = Key.D, | ||
StickButton = Key.F, | ||
}, | ||
|
||
RightJoycon = new RightJoyconCommonConfig<Key> | ||
{ | ||
ButtonA = Key.Z, | ||
ButtonB = Key.X, | ||
ButtonX = Key.C, | ||
ButtonY = Key.V, | ||
ButtonPlus = Key.Plus, | ||
ButtonR = Key.U, | ||
ButtonZr = Key.O, | ||
ButtonSl = Key.Unbound, | ||
ButtonSr = Key.Unbound, | ||
}, | ||
|
||
RightJoyconStick = new JoyconConfigKeyboardStick<Key> | ||
{ | ||
StickUp = Key.I, | ||
StickDown = Key.K, | ||
StickLeft = Key.J, | ||
StickRight = Key.L, | ||
StickButton = Key.H, | ||
}, | ||
}; | ||
} | ||
else | ||
{ | ||
bool isNintendoStyle = gamepadName.Contains("Nintendo"); | ||
|
||
config = new StandardControllerInputConfig | ||
{ | ||
Version = InputConfig.CurrentVersion, | ||
Backend = InputBackendType.GamepadSDL2, | ||
Id = null, | ||
ControllerType = ControllerType.JoyconPair, | ||
DeadzoneLeft = 0.1f, | ||
DeadzoneRight = 0.1f, | ||
RangeLeft = 1.0f, | ||
RangeRight = 1.0f, | ||
TriggerThreshold = 0.5f, | ||
LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId> | ||
{ | ||
DpadUp = ConfigGamepadInputId.DpadUp, | ||
DpadDown = ConfigGamepadInputId.DpadDown, | ||
DpadLeft = ConfigGamepadInputId.DpadLeft, | ||
DpadRight = ConfigGamepadInputId.DpadRight, | ||
ButtonMinus = ConfigGamepadInputId.Minus, | ||
ButtonL = ConfigGamepadInputId.LeftShoulder, | ||
ButtonZl = ConfigGamepadInputId.LeftTrigger, | ||
ButtonSl = ConfigGamepadInputId.Unbound, | ||
ButtonSr = ConfigGamepadInputId.Unbound, | ||
}, | ||
|
||
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> | ||
{ | ||
Joystick = ConfigStickInputId.Left, | ||
StickButton = ConfigGamepadInputId.LeftStick, | ||
InvertStickX = false, | ||
InvertStickY = false, | ||
Rotate90CW = false, | ||
}, | ||
|
||
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> | ||
{ | ||
ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, | ||
ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, | ||
ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, | ||
ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, | ||
ButtonPlus = ConfigGamepadInputId.Plus, | ||
ButtonR = ConfigGamepadInputId.RightShoulder, | ||
ButtonZr = ConfigGamepadInputId.RightTrigger, | ||
ButtonSl = ConfigGamepadInputId.Unbound, | ||
ButtonSr = ConfigGamepadInputId.Unbound, | ||
}, | ||
|
||
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> | ||
{ | ||
Joystick = ConfigStickInputId.Right, | ||
StickButton = ConfigGamepadInputId.RightStick, | ||
InvertStickX = false, | ||
InvertStickY = false, | ||
Rotate90CW = false, | ||
}, | ||
|
||
Motion = new StandardMotionConfigController | ||
{ | ||
MotionBackend = MotionInputBackendType.GamepadDriver, | ||
EnableMotion = true, | ||
Sensitivity = 100, | ||
GyroDeadzone = 1, | ||
}, | ||
Rumble = new RumbleConfigController | ||
{ | ||
StrongRumble = 1f, | ||
WeakRumble = 1f, | ||
EnableRumble = false, | ||
}, | ||
}; | ||
} | ||
} | ||
else | ||
{ | ||
string profileBasePath; | ||
|
||
if (isKeyboard) | ||
{ | ||
profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "keyboard"); | ||
} | ||
else | ||
{ | ||
profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "controller"); | ||
} | ||
|
||
string path = Path.Combine(profileBasePath, inputProfileName + ".json"); | ||
|
||
if (!File.Exists(path)) | ||
{ | ||
Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" not found for \"{inputId}\""); | ||
|
||
return null; | ||
} | ||
|
||
try | ||
{ | ||
config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); | ||
} | ||
catch (JsonException) | ||
{ | ||
Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" parsing failed for \"{inputId}\""); | ||
|
||
return null; | ||
} | ||
} | ||
|
||
config.Id = inputId; | ||
config.PlayerIndex = index; | ||
|
||
string inputTypeName = isKeyboard ? "Keyboard" : "Gamepad"; | ||
|
||
Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} configured with {inputTypeName} \"{config.Id}\""); | ||
|
||
// If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0. | ||
if (config is StandardControllerInputConfig controllerConfig) | ||
{ | ||
if (controllerConfig.RangeLeft <= 0.0f && controllerConfig.RangeRight <= 0.0f) | ||
{ | ||
controllerConfig.RangeLeft = 1.0f; | ||
controllerConfig.RangeRight = 1.0f; | ||
|
||
Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration"); | ||
} | ||
} | ||
|
||
return config; | ||
} | ||
|
||
private static IRenderer CreateRenderer(Options options, WindowBase window) | ||
{ | ||
if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow) | ||
{ | ||
string preferredGpuId = string.Empty; | ||
Vk api = Vk.GetApi(); | ||
|
||
if (!string.IsNullOrEmpty(options.PreferredGPUVendor)) | ||
{ | ||
string preferredGpuVendor = options.PreferredGPUVendor.ToLowerInvariant(); | ||
var devices = VulkanRenderer.GetPhysicalDevices(api); | ||
|
||
foreach (var device in devices) | ||
{ | ||
if (device.Vendor.ToLowerInvariant() == preferredGpuVendor) | ||
{ | ||
preferredGpuId = device.Id; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
return new VulkanRenderer( | ||
api, | ||
(instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))), | ||
vulkanWindow.GetRequiredInstanceExtensions, | ||
preferredGpuId); | ||
} | ||
|
||
return new OpenGLRenderer(); | ||
} | ||
|
||
private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) | ||
{ | ||
BackendThreading threadingMode = options.BackendThreading; | ||
|
||
bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); | ||
|
||
if (threadedGAL) | ||
{ | ||
renderer = new ThreadedRenderer(renderer); | ||
} | ||
|
||
HLEConfiguration configuration = new(_virtualFileSystem, | ||
_libHacHorizonManager, | ||
_contentManager, | ||
_accountManager, | ||
_userChannelPersistence, | ||
renderer, | ||
new SDL2HardwareDeviceDriver(), | ||
options.DramSize, | ||
window, | ||
options.SystemLanguage, | ||
options.SystemRegion, | ||
options.VSyncMode, | ||
!options.DisableDockedMode, | ||
!options.DisablePTC, | ||
options.EnableInternetAccess, | ||
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, | ||
options.FsGlobalAccessLogMode, | ||
options.SystemTimeOffset, | ||
options.SystemTimeZone, | ||
options.MemoryManagerMode, | ||
options.IgnoreMissingServices, | ||
options.AspectRatio, | ||
options.AudioVolume, | ||
options.UseHypervisor ?? true, | ||
options.MultiplayerLanInterfaceId, | ||
Common.Configuration.Multiplayer.MultiplayerMode.Disabled, | ||
false, | ||
string.Empty, | ||
string.Empty, | ||
options.CustomVSyncInterval); | ||
|
||
return new Switch(configuration); | ||
} | ||
} | ||
} |
Oops, something went wrong.