diff --git a/src/Ryujinx.Input.SDL3/SDL3Gamepad.cs b/src/Ryujinx.Input.SDL3/SDL3Gamepad.cs index 2b006147d..4985d8eea 100644 --- a/src/Ryujinx.Input.SDL3/SDL3Gamepad.cs +++ b/src/Ryujinx.Input.SDL3/SDL3Gamepad.cs @@ -151,7 +151,9 @@ namespace Ryujinx.Input.SDL3 result |= GamepadFeaturesFlag.Led; } SDL_UnlockProperties(propID); - SDL_DestroyProperties(propID); + + // NOTE: Do not call SDL_DestroyProperties here. These properties are owned + // internally by SDL and are freed when SDL_CloseGamepad is called (in Dispose). return result; } diff --git a/src/Ryujinx.Input.SDL3/SDL3GamepadDriver.cs b/src/Ryujinx.Input.SDL3/SDL3GamepadDriver.cs index 897966689..0e2a63579 100644 --- a/src/Ryujinx.Input.SDL3/SDL3GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL3/SDL3GamepadDriver.cs @@ -331,28 +331,18 @@ namespace Ryujinx.Input.SDL3 public IEnumerable GetGamepads() { - lock (_gamepadsIds) + string[] ids; + lock (_lock) { - foreach (var gamepad in _gamepadsIds) - { - yield return GetGamepad(gamepad.Value); - } + ids = _gamepadsIds.Values + .Concat(_joyConsIds.Values) + .Concat(_linkedJoyConsIds.Values) + .ToArray(); } - lock (_joyConsIds) + foreach (string id in ids) { - foreach (var gamepad in _joyConsIds) - { - yield return GetGamepad(gamepad.Value); - } - } - - lock (_linkedJoyConsIds) - { - foreach (var gamepad in _linkedJoyConsIds) - { - yield return GetGamepad(gamepad.Value); - } + yield return GetGamepad(id); } } } diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs index 84f9e89ab..29bc973f6 100644 --- a/src/Ryujinx.Input/HLE/NpadController.cs +++ b/src/Ryujinx.Input/HLE/NpadController.cs @@ -563,7 +563,7 @@ namespace Ryujinx.Input.HLE float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble)); float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble)); - _gamepad.Rumble(low, high, uint.MaxValue); + _gamepad?.Rumble(low, high, uint.MaxValue); Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " + $"L.low.amp={leftVibrationValue.AmplitudeLow}, " + diff --git a/src/Ryujinx.SDL3.Common/SDL3Driver.cs b/src/Ryujinx.SDL3.Common/SDL3Driver.cs index 529a846e0..557c461c5 100644 --- a/src/Ryujinx.SDL3.Common/SDL3Driver.cs +++ b/src/Ryujinx.SDL3.Common/SDL3Driver.cs @@ -61,6 +61,14 @@ namespace Ryujinx.SDL3.Common SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); + // When hid_nintendo is loaded, it creates separate evdev devices for the gamepad + // and IMU which SDL3's evdev backend combines via UNIQ matching. Using HIDAPI + // instead conflicts with the kernel driver and breaks motion and hotplug. + if (OperatingSystem.IsLinux() && Directory.Exists("/sys/module/hid_nintendo")) + { + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); + } + // NOTE: As of SDL3 2.24.0, joycons are combined by default but the motion source only come from one of them. // We disable this behavior for now. SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");