-
Notifications
You must be signed in to change notification settings - Fork 517
Commit
- Loading branch information
There are no files selected for viewing
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) | ||
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / macOS Universal (Debug)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / macOS Universal (Debug)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / macOS Universal (Release)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / macOS Universal (Release)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-x64 (Debug)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-x64 (Debug)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-arm64 (Debug)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-arm64 (Debug)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / osx-x64 (Debug)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / osx-x64 (Debug)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-x64 (Release)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-x64 (Release)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-arm64 (Release)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-arm64 (Release)
Check failure on line 284 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / osx-x64 (Release)
|
||
{ | ||
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) | ||
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / macOS Universal (Debug)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / macOS Universal (Debug)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / macOS Universal (Release)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / macOS Universal (Release)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-x64 (Debug)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-x64 (Debug)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-arm64 (Debug)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-arm64 (Debug)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / osx-x64 (Debug)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / osx-x64 (Debug)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-x64 (Release)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-x64 (Release)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-arm64 (Release)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / linux-arm64 (Release)
Check failure on line 316 in src/Ryujinx/Headless/HeadlessRyujinx.Init.cs GitHub Actions / pr_build / osx-x64 (Release)
|
||
{ | ||
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); | ||
} | ||
} | ||
} |