Skip to content

feat(input): Implement SDL3 input and window management#2639

Open
githubawn wants to merge 29 commits into
TheSuperHackers:mainfrom
githubawn:feature/sdl3-input-backport
Open

feat(input): Implement SDL3 input and window management#2639
githubawn wants to merge 29 commits into
TheSuperHackers:mainfrom
githubawn:feature/sdl3-input-backport

Conversation

@githubawn

@githubawn githubawn commented Apr 20, 2026

Copy link
Copy Markdown

SDL3 Input Backend
This PR implements an SDL3-based input and windowing backend as a modern alternative to DirectInput and Win32 window creation. By utilizing SDL3, we bypass DirectInput emulation layers on modern systems, providing a lower-latency pipeline for Windows 11 and Wine/Linux users.

Input handling
Unified event manager that centralizes keyboard, mouse, and gamepad events into a thread-safe buffer

Gamepad Support (v0.1)
This is an initial baseline implementation focused on providing functional out-of-the-box playability. Feedback is encouraged regarding the ergonomics and logic of these default mappings.

Input Action
Left Stick Virtual mouse cursor
Right Stick Camera pan (arrow keys)
LT (hold) Precision Mode
RT (hold) Force Attack (LCTRL)
LB Select All (Q)
RB Queue Orders (LSHIFT)
A Left click
B Right click
X Attack Move (A)
Y Stop (S)
D-Pad Control groups 1–4
Start Menu (ESC)
Back Last radar event (SPACE)
L3 Next idle worker
R3 Snap to command center

Scope Note: This implementation provides a hardcoded default layout to establish core functionality. Advanced features, such as input remapping, radial menus, adjustable deadzones, are currently out of scope for this PR and may be addressed in future iterations.

The foundation of this backend was built using the SDL3 input work from the generalsX fork by fbraz3.

Todo: replicate to generals

@githubawn githubawn force-pushed the feature/sdl3-input-backport branch from a785545 to 7cc0861 Compare April 20, 2026 15:00
@greptile-apps

greptile-apps Bot commented Apr 20, 2026

Copy link
Copy Markdown

Greptile Summary

Implements an SDL3-based input and window backend (SDL3GameEngine, SDL3Mouse, SDL3Keyboard, SDL3InputManager, SDL3CursorManager) as a compile-time opt-in (RTS_SDL3_ENABLE) alternative to the existing Win32/DirectInput path. The feature is gated entirely in #if RTS_SDL3_ENABLE blocks with no impact on existing builds.

  • SDL3InputManager centralises all event dispatching into two fixed-size ring buffers (256 slots each) using SDL_EVENT_FIRST (= 0) as a sentinel; mouse coordinate scaling handles letterbox/viewport modes via the new Display::getViewportRect hook.
  • Gamepad support translates left-stick movement into synthetic SDL_EVENT_MOUSE_MOTION events (with correct windowID set), maps face/shoulder buttons to virtual keypresses, and routes D-pad to control-group hotkeys.
  • vcpkg-lock.json has been regenerated to match the sdl3@3.4.10 and sdl3-image@3.4.4 overrides in vcpkg.json, and the old builtin-baseline was correctly migrated to vcpkg-configuration.json.

Confidence Score: 5/5

The SDL3 backend is fully gated behind RTS_SDL3_ENABLE; existing Win32/DirectInput builds are unaffected. All previously identified compile and runtime correctness issues have been addressed in this revision.

The new SDL3 backend is additive and flag-gated, meaning the existing build path carries zero risk. The ring-buffer event queue, coordinate scaling, and gamepad mapping are all logically correct. The vcpkg lock now matches the declared overrides. Remaining findings are style-only.

GeneralsMD/Code/Main/WinMain.cpp (NULL literals) and Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp (inline-if inside lambdas) have minor style issues worth cleaning up before merge.

Important Files Changed

Filename Overview
Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp New file: implements SDL3Mouse, SDL3Keyboard, and SDL3InputManager with ring-buffer event queuing and gamepad-to-keyboard/mouse mapping; inline if bodies inside lambdas violate the team's debuggability rule
Core/GameEngineDevice/Source/SDL3Device/Common/SDL3GameEngine.cpp New file: SDL3GameEngine lifecycle (init/reset/update/poll), text input forwarding with correct UTF-8 decode, headless guard properly deferred past GameEngine::init()
Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Cursor.cpp New file: ANI/animated cursor loading via SDL3_image; delegates animation lifetime to SDL3 (SDL_CreateAnimatedCursor), correct cleanup in AnimatedCursor destructor
GeneralsMD/Code/Main/WinMain.cpp SDL3 path added: window creation, splash screen blit, SDL3GameEngine factory; two new lines pass NULL instead of nullptr to SDL3 C++ APIs
GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DGameClient.h Conditional compile split: SDL3 path creates SDL3Mouse/SDL3Keyboard; Win32 path retains W3DMouse/DirectInputKeyboard; W3DSnow.h correctly moved outside the guard
Core/GameEngine/Include/GameClient/Display.h Adds getViewportRect() virtual stub to Display base class, guarded by RTS_SDL3_ENABLE, returns FALSE for the no-letterbox default path
cmake/sdl3.cmake New cmake module: vcpkg find_package with FetchContent fallback; SDL3 alias targets and static link helper added; SDL3_image lacks OVERRIDE_FIND_PACKAGE so mixed found/not-found states could redundantly trigger FetchContent for both libs
vcpkg.json Adds sdl3 and sdl3-image dependencies with overrides pinned to 3.4.10 and 3.4.4; builtin-baseline correctly migrated to vcpkg-configuration.json default-registry
vcpkg-lock.json Lock file regenerated: sdl3@3.4.10 and sdl3-image@3.4.4 now match vcpkg.json overrides; vcpkg-cmake-config entry added as expected new transitive dep
vcpkg-configuration.json New file: default-registry carries the previous builtin-baseline, secondary registry pins SDL3/SDL3_image to a baseline with 3.4.x packages

Sequence Diagram

sequenceDiagram
    participant WM as WinMain
    participant SGE as SDL3GameEngine
    participant SIM as SDL3InputManager
    participant SMouse as SDL3Mouse
    participant SKbd as SDL3Keyboard
    participant GP as Gamepad

    WM->>SGE: CreateGameEngine() → new SDL3GameEngine
    WM->>WM: SDL_Init + SDL_CreateWindow
    SGE->>SIM: new SDL3InputManager(m_SDLWindow)
    SIM->>SIM: openFirstGamepad()

    loop Game loop
        SGE->>SGE: update()
        SGE->>SIM: update()
        SIM->>SIM: SDL_PollEvent()
        SIM->>SIM: addMouseSDLEvent / addKeyboardSDLEvent
        SIM->>SIM: processGamepadInput()
        GP-->>SIM: axis/button state
        SIM->>SIM: virtualPulseKey / virtualPulseMouse
        note over SIM: synthetic events injected into ring buffers
        SMouse->>SIM: getNextMouseEvent()
        SKbd->>SIM: getNextKeyboardEvent()
        SMouse->>SMouse: translateEvent → scaleMouseCoordinates
        SKbd->>SKbd: translateScanCodeToKeyVal
    end
Loading

Reviews (30): Last reviewed commit: "Move SDL3GameEngine files to Common dire..." | Re-trigger Greptile

Comment thread Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp Outdated
Comment thread Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp Outdated
Comment thread Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp Outdated
Comment thread Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp
Comment thread cmake/sdl3.cmake Outdated
Comment thread cmake/sdl3.cmake
Comment thread GeneralsMD/Code/Main/WinMain.cpp
Comment thread cmake/config-build.cmake Outdated
Comment thread cmake/config-build.cmake Outdated

namespace {

Bool DecodeNextUtf8Codepoint(const char* text, size_t length, size_t& offset, UnsignedInt& outCodepoint)

@stephanmeesters stephanmeesters Apr 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this, maybe better placed in UnicodeString? Or some other helper if it's generic stuff

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

I looked at #2045 and #2528 which are working on UTF-8 infrastructure.
If one of those lands first, forwardTextInputEvent could use the bulk Utf8_To_Utf16Le conversion and iterate the resulting wchar_t string directly, making this function unnecessary.

Happy to refactor once there's a clear winner between those two. For now it's self-contained here.

@githubawn githubawn force-pushed the feature/sdl3-input-backport branch from e365605 to 627ef23 Compare April 20, 2026 18:24
@githubawn githubawn changed the title feat(input): Implement SDL3 input backend feat(input): Implement SDL3 input and window management Apr 20, 2026
Consolidated all work from the test/sdl3-backport branch into a single atomic commit:
- Centralized input management via SDL3InputManager.
- Hardened Ani/RIFF cursor loading with robust bounds checking.
- Native Gamepad support with analogue stick-to-mouse emulation and custom RTS mappings.
- Modernized focus and capture handling for better stability during Alt-Tab.
- Standardized and secured string operations throughout the SDL3 path.
Consolidated all work from the test/sdl3-backport branch into a single atomic commit:
- Centralized input management via SDL3InputManager.
- Hardened Ani/RIFF cursor loading with robust bounds checking.
- Native Gamepad support with analogue stick-to-mouse emulation and custom RTS mappings.
- Modernized focus and capture handling for better stability during Alt-Tab.
- Standardized and secured string operations throughout the SDL3 path.
- Updated credit attribution (fbraz3).
@githubawn githubawn force-pushed the feature/sdl3-input-backport branch from eb9908a to 534e694 Compare April 21, 2026 01:17
Comment thread GeneralsMD/Code/Main/WinMain.cpp Outdated
Comment thread Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp Outdated
Comment thread Core/GameEngineDevice/Source/SDL3GameEngine.cpp Outdated
Comment thread Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Cursor.cpp
@DevGeniusCode DevGeniusCode added this to the Linux support milestone Apr 29, 2026
@xezon xezon added Gen Relates to Generals ZH Relates to Zero Hour Platform Work towards platform support, such as Linux, MacOS Input labels May 12, 2026

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs polishing. Not yet reviewed extensively until the obvious style issues are fixed.

Comment thread vcpkg-lock.json
Comment thread CMakeLists.txt Outdated
Comment thread GeneralsMD/Code/Main/WinMain.cpp Outdated

#if SAGE_USE_SDL3
#include <SDL3/SDL.h>
#include "SDL3GameEngine.h"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This SDL code is added to a file called WinMain (for Windows). Is this intentional?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was intentional. The SDL3 input backend is designed with a future splitscreen skirmish feature in mind, supporting multiple simultaneous mice/keyboards so that 2–8 players can share a single machine rather than needing separate computers, either controlling the same or unique armies.

SDL3 input doesn't function without an SDL window, so creating the window here was the minimum viable approach to make the input backend functional. The Win32/DirectInput path doesn't lend itself to the multi-device model SDL3 enables, which is why SDL3 is the foundation for this direction.

@xezon xezon May 27, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we have a SDL3Main then? I have seen other forks do that.

Maybe make WinMain and SDL3Main share code if they do. But basically get rid of the ifdef approach.


ParticleSystemManager* SDL3GameEngine::createParticleSystemManager(Bool dummy)
{
(void)dummy;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not create dummy here?


namespace {

Bool DecodeNextUtf8Codepoint(const char* text, size_t length, size_t& offset, UnsignedInt& outCodepoint)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function looks out of place. Bobtista was also working on Utf8 decoding in another change. It would be good to have this as a utility somewhere accessible engine wide, and not specific to this file.

m_gamepad(nullptr),
m_precisionMode(FALSE),
m_lastUpdateTime(0),
m_isQuitting(FALSE)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or use false

Comment thread Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp Outdated
handleGamepadButton(SDL_GAMEPAD_BUTTON_DPAD_RIGHT, m_state.buttonState[SDL_GAMEPAD_BUTTON_DPAD_RIGHT], SDL_GetGamepadButton(m_gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT), [&](bool d){ virtualPulseKey(SDL_SCANCODE_3, d); });
handleGamepadButton(SDL_GAMEPAD_BUTTON_DPAD_DOWN, m_state.buttonState[SDL_GAMEPAD_BUTTON_DPAD_DOWN], SDL_GetGamepadButton(m_gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN), [&](bool d){ virtualPulseKey(SDL_SCANCODE_4, d); });
handleGamepadButton(SDL_GAMEPAD_BUTTON_LEFT_STICK, m_state.buttonState[SDL_GAMEPAD_BUTTON_LEFT_STICK], SDL_GetGamepadButton(m_gamepad, SDL_GAMEPAD_BUTTON_LEFT_STICK), [&](bool d){ if (d) TheMessageStream->appendMessage(GameMessage::MSG_META_SELECT_NEXT_IDLE_WORKER); });
handleGamepadButton(SDL_GAMEPAD_BUTTON_RIGHT_STICK, m_state.buttonState[SDL_GAMEPAD_BUTTON_RIGHT_STICK], SDL_GetGamepadButton(m_gamepad, SDL_GAMEPAD_BUTTON_RIGHT_STICK), [&](bool d){ if (d) TheMessageStream->appendMessage(GameMessage::MSG_META_VIEW_COMMAND_CENTER); });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines are awfully long.

Comment thread cmake/config-build.cmake Outdated
Comment thread cmake/config-build.cmake Outdated
@xezon

xezon commented May 12, 2026

Copy link
Copy Markdown

Perhaps also check Fighter19's fork for SDL related implementations. As far as I am aware he has it all done.

@githubawn

Copy link
Copy Markdown
Author

I have looked at all existing forks and attributed where possible.

Comment thread Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp
@bobtista

bobtista commented Jun 3, 2026

Copy link
Copy Markdown

How about changing display-mode? eg getDisplayModeCount() / getDisplayModeDescription() using SDL_GetFullscreenDisplayModes + desktop mode + resolution filtering. EDIT: Also account for mouse position which can sometimes not match clicks to cursor position.

@bobtista

bobtista commented Jun 3, 2026

Copy link
Copy Markdown

Is it better to use SDL3InputManager (+ SDL3CursorManager) or to split out SDL3Keyboard + SDL3Mouse that subclass the engine's existing Keyboard/Mouse base classes?

- Switched SDL3_image to the latest GitHub main branch commit to natively resolve the RIFF/ANI odd-chunk parsing bug.
- Removed the legacy cbSize header patching workaround in SDL3CursorManager::loadANI.
- Kept base SDL3 library pinned to stable release-3.4.10.
- Cleaned up transient code-level header comments.

Co-authored-by: fbraz3 <fbraz3@users.noreply.github.com>
Co-authored-by: Fighter19 <Fighter19@users.noreply.github.com>
Co-authored-by: feliwir <feliwir@users.noreply.github.com>
Comment thread vcpkg-lock.json
githubawn and others added 4 commits June 10, 2026 00:37
- Replaced (void) with empty parentheses () in SDL3Input and SDL3GameEngine declarations and definitions.

Co-authored-by: fbraz3 <fbraz3@users.noreply.github.com>
Co-authored-by: Fighter19 <Fighter19@users.noreply.github.com>
Co-authored-by: feliwir <feliwir@users.noreply.github.com>
- Updated vcpkg.json overrides and regenerated vcpkg-lock.json (sdl3 to 3.4.10, sdl3-image to 3.4.4).
- Normalized formatting on vcpkg.json using the official vcpkg format-manifest tool.
- Changed translateScanCodeToKeyVal parameter type to SDL_Scancode to prevent high-range media keys from wrapping into valid letter keycodes.
- Documented SDL3_image FetchContent Git pin reasoning in cmake/sdl3.cmake.

Co-authored-by: fbraz3 <fbraz3@users.noreply.github.com>
Co-authored-by: Fighter19 <Fighter19@users.noreply.github.com>
Co-authored-by: feliwir <feliwir@users.noreply.github.com>
Replace legacy block comment banners with line comments.
Group and sort header includes.
Break long gamepad input lines in SDL3Input.cpp with newlines.
Remove Sage mentions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Gen Relates to Generals Input Platform Work towards platform support, such as Linux, MacOS ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants