From f2c41b82c5ed425a44b187887b093cff624edf98 Mon Sep 17 00:00:00 2001 From: Victor Chelaru Date: Sun, 26 Jul 2026 18:04:06 -0600 Subject: [PATCH 1/2] Fix SdlGamepad button/trigger state going stale with no event subscribers The array assignment (_buttons[i] = new Button(...), _triggers[i] = new Trigger(...)) was written inside the argument list of the ButtonDown/ButtonUp/TriggerMoved ?.Invoke(...) call. C#'s null-conditional operator short-circuits the whole call, including evaluating its arguments, when the event has no subscribers. So with nobody subscribed, the internal arrays never actually got updated, and polling Buttons/Triggers read permanently stale state even with real button presses. Moves the assignment to its own statement before the event fires, so it always runs regardless of subscribers. See issue #2604 for the full writeup and repro. --- src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs b/src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs index 7453d3d5d3..42684dcb02 100644 --- a/src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs +++ b/src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs @@ -128,14 +128,14 @@ public void DoEvent(Event @event) } case GameControllerAxis.ControllerAxisTriggerleft: { - TriggerMoved?.Invoke - (this, _triggers[0] = new Trigger(0, (float) @event.Caxis.Value / short.MaxValue)); + var trigger0 = _triggers[0] = new Trigger(0, (float) @event.Caxis.Value / short.MaxValue); + TriggerMoved?.Invoke(this, trigger0); break; } case GameControllerAxis.ControllerAxisTriggerright: { - TriggerMoved?.Invoke - (this, _triggers[1] = new Trigger(1, (float) @event.Caxis.Value / short.MaxValue)); + var trigger1 = _triggers[1] = new Trigger(1, (float) @event.Caxis.Value / short.MaxValue); + TriggerMoved?.Invoke(this, trigger1); break; } } @@ -145,15 +145,15 @@ public void DoEvent(Event @event) case EventType.Controllerbuttondown: { var ogBtn = _buttons[@event.Cbutton.Button]; - ButtonDown?.Invoke - (this, _buttons[@event.Cbutton.Button] = new Button(ogBtn.Name, ogBtn.Index, true)); + var newBtn = _buttons[@event.Cbutton.Button] = new Button(ogBtn.Name, ogBtn.Index, true); + ButtonDown?.Invoke(this, newBtn); break; } case EventType.Controllerbuttonup: { var ogBtn = _buttons[@event.Cbutton.Button]; - ButtonUp?.Invoke - (this, _buttons[@event.Cbutton.Button] = new Button(ogBtn.Name, ogBtn.Index, false)); + var newBtn = _buttons[@event.Cbutton.Button] = new Button(ogBtn.Name, ogBtn.Index, false); + ButtonUp?.Invoke(this, newBtn); break; } case EventType.Controllerdeviceadded: From 7109300acfa7fbd478a66f48bc3438f222fd7d37 Mon Sep 17 00:00:00 2001 From: Victor Chelaru Date: Sun, 26 Jul 2026 18:47:42 -0600 Subject: [PATCH 2/2] Also fix SdlJoystick, drop unneeded locals in SdlGamepad SdlJoystick.cs had the exact same bug for AxisMoved, HatMoved, ButtonDown, and ButtonUp: the array assignment was inside the ?.Invoke(...) argument list, so it never ran with zero subscribers. Same fix, moved to its own statement before the event fires. Also dropped the local variables from the SdlGamepad fix in favor of just re-reading the array (matches how ThumbstickMoved, the one case that was already correct, does it). --- src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs | 16 ++++---- src/Input/Silk.NET.Input.Sdl/SdlJoystick.cs | 42 ++++++++------------- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs b/src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs index 42684dcb02..68a17943c5 100644 --- a/src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs +++ b/src/Input/Silk.NET.Input.Sdl/SdlGamepad.cs @@ -128,14 +128,14 @@ public void DoEvent(Event @event) } case GameControllerAxis.ControllerAxisTriggerleft: { - var trigger0 = _triggers[0] = new Trigger(0, (float) @event.Caxis.Value / short.MaxValue); - TriggerMoved?.Invoke(this, trigger0); + _triggers[0] = new Trigger(0, (float) @event.Caxis.Value / short.MaxValue); + TriggerMoved?.Invoke(this, _triggers[0]); break; } case GameControllerAxis.ControllerAxisTriggerright: { - var trigger1 = _triggers[1] = new Trigger(1, (float) @event.Caxis.Value / short.MaxValue); - TriggerMoved?.Invoke(this, trigger1); + _triggers[1] = new Trigger(1, (float) @event.Caxis.Value / short.MaxValue); + TriggerMoved?.Invoke(this, _triggers[1]); break; } } @@ -145,15 +145,15 @@ public void DoEvent(Event @event) case EventType.Controllerbuttondown: { var ogBtn = _buttons[@event.Cbutton.Button]; - var newBtn = _buttons[@event.Cbutton.Button] = new Button(ogBtn.Name, ogBtn.Index, true); - ButtonDown?.Invoke(this, newBtn); + _buttons[@event.Cbutton.Button] = new Button(ogBtn.Name, ogBtn.Index, true); + ButtonDown?.Invoke(this, _buttons[@event.Cbutton.Button]); break; } case EventType.Controllerbuttonup: { var ogBtn = _buttons[@event.Cbutton.Button]; - var newBtn = _buttons[@event.Cbutton.Button] = new Button(ogBtn.Name, ogBtn.Index, false); - ButtonUp?.Invoke(this, newBtn); + _buttons[@event.Cbutton.Button] = new Button(ogBtn.Name, ogBtn.Index, false); + ButtonUp?.Invoke(this, _buttons[@event.Cbutton.Button]); break; } case EventType.Controllerdeviceadded: diff --git a/src/Input/Silk.NET.Input.Sdl/SdlJoystick.cs b/src/Input/Silk.NET.Input.Sdl/SdlJoystick.cs index 79493f2081..b25137a796 100644 --- a/src/Input/Silk.NET.Input.Sdl/SdlJoystick.cs +++ b/src/Input/Silk.NET.Input.Sdl/SdlJoystick.cs @@ -52,12 +52,9 @@ public void DoEvent(Event @event) Array.Resize(ref _axes, @event.Jaxis.Axis + 1); } - AxisMoved?.Invoke - ( - this, - _axes[@event.Jaxis.Axis] = new Axis - (@event.Jaxis.Axis, (float) @event.Jaxis.Value / short.MaxValue) - ); + _axes[@event.Jaxis.Axis] = new Axis + (@event.Jaxis.Axis, (float) @event.Jaxis.Value / short.MaxValue); + AxisMoved?.Invoke(this, _axes[@event.Jaxis.Axis]); break; } case EventType.Joyballmotion: @@ -73,17 +70,14 @@ public void DoEvent(Event @event) } var val = @event.Jhat.Value; - HatMoved?.Invoke + _hats[@event.Jhat.Hat] = new Hat ( - this, - _hats[@event.Jhat.Hat] = new Hat - ( - @event.Jhat.Hat, (Position2D) ((val & 0x01) * (int) Position2D.Up + - (val & 0x02) * (int) Position2D.Right + - (val & 0x04) * (int) Position2D.Down + - (val & 0x08) * (int) Position2D.Left) - ) + @event.Jhat.Hat, (Position2D) ((val & 0x01) * (int) Position2D.Up + + (val & 0x02) * (int) Position2D.Right + + (val & 0x04) * (int) Position2D.Down + + (val & 0x08) * (int) Position2D.Left) ); + HatMoved?.Invoke(this, _hats[@event.Jhat.Hat]); break; } case EventType.Joybuttondown: @@ -93,12 +87,9 @@ public void DoEvent(Event @event) Array.Resize(ref _buttons, @event.Jbutton.Button + 1); } - ButtonDown?.Invoke - ( - this, - _buttons[@event.Jbutton.Button] = new Button - ((ButtonName) @event.Jbutton.Button, @event.Jbutton.Button, true) - ); + _buttons[@event.Jbutton.Button] = new Button + ((ButtonName) @event.Jbutton.Button, @event.Jbutton.Button, true); + ButtonDown?.Invoke(this, _buttons[@event.Jbutton.Button]); break; } case EventType.Joybuttonup: @@ -108,12 +99,9 @@ public void DoEvent(Event @event) Array.Resize(ref _buttons, @event.Jbutton.Button + 1); } - ButtonUp?.Invoke - ( - this, - _buttons[@event.Jbutton.Button] = new Button - ((ButtonName) @event.Jbutton.Button, @event.Jbutton.Button, false) - ); + _buttons[@event.Jbutton.Button] = new Button + ((ButtonName) @event.Jbutton.Button, @event.Jbutton.Button, false); + ButtonUp?.Invoke(this, _buttons[@event.Jbutton.Button]); break; } case EventType.Joydeviceadded: