Start testing gamepad controllers

This commit is contained in:
Adrian Conlon committed 2026-08-23 21:48:58 +01:00
1 parent c4b09e3010
commit 5c3159b847
4 files changed
+21 -78

No files matched your search

+8 -20
View File
@@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
public abstract class Game(bool verbose = false) : Device, IDisposable
@@ -19,7 +18,6 @@
private ulong _frameStartTime;
private ulong _frameEndTime;
private readonly SortedDictionary<uint, GameController> _gameControllers = [];
private readonly Dictionary<uint, uint> _mappedControllers = [];
private bool _disposed;
protected ScopedHandle Window { get; } = new(SDL.DestroyWindow);
@@ -68,8 +66,8 @@
this._vsync = this.UseVSYNC;
if (this._vsync)
{
float framesPerSecond = this.FramesPerSecond;
float? refreshRate = currentDisplayMode?.RefreshRate;
var framesPerSecond = this.FramesPerSecond;
var refreshRate = currentDisplayMode?.RefreshRate;
Debug.Assert(refreshRate.HasValue, "refresh rate is unavailable");
this._vsync = Math.Abs(framesPerSecond - refreshRate.Value) < 0.001;
if (this._vsync)
@@ -160,11 +158,11 @@
case SDL.EventType.GamepadButtonUp:
_ = this.HandleGamepadButtonUp(e.GButton);
break;
case SDL.EventType.JoystickAdded:
this.AddJoystick(e);
case SDL.EventType.GamepadAdded:
this.AddGampad(e);
break;
case SDL.EventType.JoystickRemoved:
this.RemoveJoystick(e);
case SDL.EventType.GamepadRemoved:
this.RemoveGamepad(e);
break;
}
}
@@ -208,26 +206,23 @@
SDL.LogWarn(SDL.LogCategory.Render, "Running slowly");
}
protected virtual void RemoveJoystick(SDL.Event e)
protected virtual void RemoveGamepad(SDL.Event e)
{
var which = e.JDevice.Which;
var found = this._gameControllers.TryGetValue(which, out var gameController);
Debug.Assert(found);
Debug.Assert(gameController != null, "controller is not null");
_ = this._mappedControllers.Remove(gameController.GetJoystickId());
_ = this._gameControllers.Remove(which);
SDL.LogInfo(SDL.LogCategory.Input, $"Joystick device {which} removed ({this._gameControllers.Count} controllers available)");
}
protected virtual void AddJoystick(SDL.Event e)
protected virtual void AddGampad(SDL.Event e)
{
var which = e.JDevice.Which;
var found = this._gameControllers.ContainsKey(which);
Debug.Assert(!found);
GameController gameController = new(which);
uint joystickId = gameController.GetJoystickId();
this._gameControllers[which] = gameController;
this._mappedControllers[joystickId] = which;
SDL.LogInfo(SDL.LogCategory.Input, $"Joystick device {which} address ({this._gameControllers.Count} controllers available)");
}
@@ -239,13 +234,6 @@
return gameController;
}
public uint MappedController(uint which)
{
var success = this._mappedControllers.TryGetValue(which, out var id);
Wrapper.MaybeThrowException(success, "Unknown joystick");
return id;
}
public uint ChooseControllerIndex(int who)
{
int count = this._gameControllers.Count;
+9 -57
View File
@@ -8,8 +8,6 @@
{
private readonly uint _index;
private readonly ScopedHandle _gamepad = new(SDL.CloseGamepad);
private readonly ScopedHandle _haptic = new(SDL.CloseHaptic);
private bool _hapticRumbleSupported;
private bool _disposed;
public GameController(uint index)
@@ -20,66 +18,20 @@
private void Open()
{
var joysticks = SDL.GetJoysticks(out var count);
Wrapper.MaybeThrowException(joysticks != null, "Unable to obtain joystick information");
Debug.Assert(count > 0, "count > 0");
Debug.Assert(this._index < count, "this._index < count");
if (SDL.IsGamepad(this._index))
{
this._gamepad.Handle = SDL.OpenGamepad(this._index);
Wrapper.MaybeThrowException(this._gamepad, "Unable to open gamepad");
this.OpenHaptic();
var gamepadName = SDL.GetGamepadName(this._gamepad);
Wrapper.MaybeThrowException(gamepadName != null, "Unable to obtain gamepad name");
SDL.LogInfo(SDL.LogCategory.Input, "Game controller name: " + gamepadName);
}
else
{
SDL.LogWarn(SDL.LogCategory.Input, "Joystick is not a game controller");
}
}
private void Close()
{
this._gamepad.Dispose();
this.CloseHaptic();
}
private void OpenHaptic()
{
this._haptic.Handle = SDL.OpenHaptic((int)this._index); // XXXX Possible SDL3-cs int cast bug
Wrapper.MaybeThrowException(this._haptic, "Unable to open haptic gamepad");
Wrapper.MaybeThrowException(SDL.InitHapticRumble(this._haptic), "Unable to initialise haptic gamepad");
this._hapticRumbleSupported = SDL.HapticRumbleSupported(this._haptic);
}
private void CloseHaptic()
{
this._haptic.Dispose();
this._hapticRumbleSupported = false;
Debug.Assert(SDL.IsGamepad(this._index));
this._gamepad.Handle = SDL.OpenGamepad(this._index);
Wrapper.MaybeThrowException(this._gamepad, "Unable to open gamepad");
var gamepadName = SDL.GetGamepadName(this._gamepad);
Wrapper.MaybeThrowException(gamepadName != null, "Unable to obtain gamepad name");
SDL.LogInfo(SDL.LogCategory.Input, "Game controller name: " + gamepadName);
}
public void StartRumble()
{
if (this._hapticRumbleSupported && !SDL.PlayHapticRumble(this._haptic, 1f, 1000U))
SDL.LogWarn(SDL.LogCategory.Input, "Unable to start haptic rumble: " + SDL.GetError());
var success = SDL.RumbleGamepad(this._gamepad, 0xFFFF, 0x0000, 1000);
Wrapper.MaybeThrowException(success, "Unable to start haptic rumble");
}
public void StopRumble()
{
if (this._hapticRumbleSupported && !SDL.StopHapticRumble(this._haptic))
SDL.LogWarn(SDL.LogCategory.Input, "Unable to stop haptic rumble: " + SDL.GetError());
}
public static uint BuildJoystickId(IntPtr gamepad)
{
var gamepadJoystick = SDL.GetGamepadJoystick(gamepad);
Wrapper.MaybeThrowException(gamepadJoystick, "Unable to obtain joystick from gamepad");
return SDL.GetJoystickID(gamepadJoystick);
}
public uint GetJoystickId() => GameController.BuildJoystickId((IntPtr) this._gamepad);
public void Dispose()
{
this.Dispose(true);
@@ -92,7 +44,7 @@
{
if (disposing)
{
this.Close();
this._gamepad.Dispose();
}
this._disposed = true;
}
+1
View File
@@ -5,6 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AnalysisLevel>latest-all</AnalysisLevel>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+3 -1
View File
@@ -9,6 +9,7 @@
private readonly Action<IntPtr> _deleter = deleter ?? throw new ArgumentNullException(nameof(deleter));
public override bool IsInvalid => this.handle == IntPtr.Zero;
public bool IsValid => !this.IsInvalid;
public IntPtr Handle
{
@@ -18,13 +19,14 @@
protected override bool ReleaseHandle()
{
if (!this.IsInvalid)
if (this.IsValid)
this._deleter(this.handle);
return true;
}
public static nint FromScopedHandle(ScopedHandle h)
{
// But it can still be invalid (i.e. h.IsInvalid == true)
Debug.Assert(h is not null);
return h.DangerousGetHandle();
}