2023-12-04 14:17:13 +01:00
|
|
|
using Ryujinx.SDL2.Common;
|
2021-05-05 23:37:09 +02:00
|
|
|
using System;
|
2021-04-14 12:28:43 +02:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using static SDL2.SDL;
|
|
|
|
|
|
|
|
namespace Ryujinx.Input.SDL2
|
|
|
|
{
|
|
|
|
public class SDL2GamepadDriver : IGamepadDriver
|
|
|
|
{
|
2023-06-26 03:55:25 +02:00
|
|
|
private readonly Dictionary<int, string> _gamepadsInstanceIdsMapping;
|
|
|
|
private readonly List<string> _gamepadsIds;
|
2024-10-11 01:17:46 +02:00
|
|
|
private readonly object _lock = new();
|
2021-04-14 12:28:43 +02:00
|
|
|
|
2024-01-22 21:02:44 +01:00
|
|
|
public ReadOnlySpan<string> GamepadsIds
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
lock (_lock)
|
|
|
|
{
|
|
|
|
return _gamepadsIds.ToArray();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-14 12:28:43 +02:00
|
|
|
|
|
|
|
public string DriverName => "SDL2";
|
|
|
|
|
|
|
|
public event Action<string> OnGamepadConnected;
|
|
|
|
public event Action<string> OnGamepadDisconnected;
|
|
|
|
|
|
|
|
public SDL2GamepadDriver()
|
|
|
|
{
|
|
|
|
_gamepadsInstanceIdsMapping = new Dictionary<int, string>();
|
|
|
|
_gamepadsIds = new List<string>();
|
|
|
|
|
|
|
|
SDL2Driver.Instance.Initialize();
|
|
|
|
SDL2Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected;
|
|
|
|
SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected;
|
|
|
|
|
|
|
|
// Add already connected gamepads
|
2023-03-11 21:05:48 +01:00
|
|
|
int numJoysticks = SDL_NumJoysticks();
|
|
|
|
|
|
|
|
for (int joystickIndex = 0; joystickIndex < numJoysticks; joystickIndex++)
|
2021-04-14 12:28:43 +02:00
|
|
|
{
|
|
|
|
HandleJoyStickConnected(joystickIndex, SDL_JoystickGetDeviceInstanceID(joystickIndex));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-22 21:02:44 +01:00
|
|
|
private string GenerateGamepadId(int joystickIndex)
|
2021-04-14 12:28:43 +02:00
|
|
|
{
|
|
|
|
Guid guid = SDL_JoystickGetDeviceGUID(joystickIndex);
|
|
|
|
|
2024-01-22 21:02:44 +01:00
|
|
|
// Add a unique identifier to the start of the GUID in case of duplicates.
|
|
|
|
|
2021-04-14 12:28:43 +02:00
|
|
|
if (guid == Guid.Empty)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2024-01-22 21:02:44 +01:00
|
|
|
string id;
|
|
|
|
|
|
|
|
lock (_lock)
|
|
|
|
{
|
|
|
|
int guidIndex = 0;
|
|
|
|
id = guidIndex + "-" + guid;
|
|
|
|
|
|
|
|
while (_gamepadsIds.Contains(id))
|
|
|
|
{
|
|
|
|
id = (++guidIndex) + "-" + guid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return id;
|
2021-04-14 12:28:43 +02:00
|
|
|
}
|
|
|
|
|
2024-01-22 21:02:44 +01:00
|
|
|
private int GetJoystickIndexByGamepadId(string id)
|
2021-04-14 12:28:43 +02:00
|
|
|
{
|
2024-01-22 21:02:44 +01:00
|
|
|
lock (_lock)
|
2021-04-14 12:28:43 +02:00
|
|
|
{
|
2024-01-22 21:02:44 +01:00
|
|
|
return _gamepadsIds.IndexOf(id);
|
2021-04-14 12:28:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void HandleJoyStickDisconnected(int joystickInstanceId)
|
|
|
|
{
|
2024-10-11 01:17:46 +02:00
|
|
|
if (!_gamepadsInstanceIdsMapping.Remove(joystickInstanceId, out string id))
|
|
|
|
return;
|
2021-04-14 12:28:43 +02:00
|
|
|
|
2024-10-11 01:17:46 +02:00
|
|
|
lock (_lock)
|
|
|
|
{
|
|
|
|
_gamepadsIds.Remove(id);
|
2021-04-14 12:28:43 +02:00
|
|
|
}
|
2024-10-11 01:17:46 +02:00
|
|
|
|
|
|
|
OnGamepadDisconnected?.Invoke(id);
|
2021-04-14 12:28:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId)
|
|
|
|
{
|
|
|
|
if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE)
|
|
|
|
{
|
2024-01-22 21:02:44 +01:00
|
|
|
if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId))
|
2021-04-14 12:28:43 +02:00
|
|
|
{
|
2024-01-22 21:02:44 +01:00
|
|
|
// Sometimes a JoyStick connected event fires after the app starts even though it was connected before
|
|
|
|
// so it is rejected to avoid doubling the entries.
|
2021-04-14 12:28:43 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-01-22 21:02:44 +01:00
|
|
|
string id = GenerateGamepadId(joystickDeviceId);
|
|
|
|
|
|
|
|
if (id == null)
|
2022-06-20 19:01:55 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-04-14 12:28:43 +02:00
|
|
|
if (_gamepadsInstanceIdsMapping.TryAdd(joystickInstanceId, id))
|
|
|
|
{
|
2024-01-22 21:02:44 +01:00
|
|
|
lock (_lock)
|
|
|
|
{
|
2024-11-17 07:57:56 +01:00
|
|
|
if (joystickDeviceId <= _gamepadsIds.FindLastIndex(_ => true))
|
|
|
|
_gamepadsIds.Insert(joystickDeviceId, id);
|
|
|
|
else
|
|
|
|
_gamepadsIds.Add(id);
|
2024-01-22 21:02:44 +01:00
|
|
|
}
|
2021-04-14 12:28:43 +02:00
|
|
|
|
|
|
|
OnGamepadConnected?.Invoke(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
{
|
|
|
|
if (disposing)
|
|
|
|
{
|
|
|
|
SDL2Driver.Instance.OnJoyStickConnected -= HandleJoyStickConnected;
|
|
|
|
SDL2Driver.Instance.OnJoystickDisconnected -= HandleJoyStickDisconnected;
|
|
|
|
|
|
|
|
// Simulate a full disconnect when disposing
|
|
|
|
foreach (string id in _gamepadsIds)
|
|
|
|
{
|
|
|
|
OnGamepadDisconnected?.Invoke(id);
|
|
|
|
}
|
|
|
|
|
2024-01-22 21:02:44 +01:00
|
|
|
lock (_lock)
|
|
|
|
{
|
|
|
|
_gamepadsIds.Clear();
|
|
|
|
}
|
2021-04-14 12:28:43 +02:00
|
|
|
|
|
|
|
SDL2Driver.Instance.Dispose();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
2023-06-26 03:55:25 +02:00
|
|
|
GC.SuppressFinalize(this);
|
2021-04-14 12:28:43 +02:00
|
|
|
Dispose(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public IGamepad GetGamepad(string id)
|
|
|
|
{
|
|
|
|
int joystickIndex = GetJoystickIndexByGamepadId(id);
|
|
|
|
|
|
|
|
if (joystickIndex == -1)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2024-10-26 15:46:41 +02:00
|
|
|
nint gamepadHandle = SDL_GameControllerOpen(joystickIndex);
|
2021-04-14 12:28:43 +02:00
|
|
|
|
2024-10-26 15:46:41 +02:00
|
|
|
if (gamepadHandle == nint.Zero)
|
2021-04-14 12:28:43 +02:00
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new SDL2Gamepad(gamepadHandle, id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|