feat(input): Implement SDL3 input and window management#2639
feat(input): Implement SDL3 input and window management#2639githubawn wants to merge 27 commits into
Conversation
a785545 to
7cc0861
Compare
|
| Filename | Overview |
|---|---|
| Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp | Core input manager, mouse, and keyboard implementations. Gamepad motion/button events now correctly set windowID from m_window. Inline if (d) bodies inside lambdas violate the project's debuggability style rule. |
| GeneralsMD/Code/Main/WinMain.cpp | SDL3 window/init path added with proper SDL_Init failure return. Two NULL → nullptr rule violations, and one inline-if style violation in the SDL3 block. |
| Core/GameEngineDevice/Source/SDL3GameEngine.cpp | SDL3GameEngine implements window integration, UTF-8 text input forwarding, and minimized-window throttle loop. GameEngine::init() is always called regardless of headless state. No issues found. |
| Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Cursor.cpp | ANI cursor loading delegated to SDL3_image's IMG_LoadAnimation_IO; prior m_frameCount out-of-bounds concern is no longer applicable. Bounds checks present in getCursor(). No issues found. |
| cmake/sdl3.cmake | Adds vcpkg/FetchContent fallback for SDL3 and SDL3_image with pinned commit for the ANI RIFF word-alignment fix. Static library alias and Windows system library linkage handled correctly. |
| vcpkg.json | Adds sdl3 and sdl3-image dependencies with version overrides matching vcpkg-lock.json (3.4.10 / 3.4.4). Lock file now updated to match. |
Sequence Diagram
sequenceDiagram
participant WinMain
participant SDL3GameEngine
participant SDL3InputManager
participant SDL3Mouse
participant SDL3Keyboard
participant GameEngine
WinMain->>SDL3GameEngine: CreateGameEngine()
WinMain->>WinMain: SDL_Init + SDL_CreateWindow → TheSDL3Window
SDL3GameEngine->>SDL3GameEngine: init()
SDL3GameEngine->>SDL3InputManager: new SDL3InputManager(m_SDLWindow)
SDL3GameEngine->>GameEngine: GameEngine::init()
GameEngine->>SDL3Mouse: createMouse() → new SDL3Mouse(TheSDL3Window)
GameEngine->>SDL3Keyboard: createKeyboard() → new SDL3Keyboard()
loop Game Loop
SDL3GameEngine->>SDL3GameEngine: update() → pollSDL3Events()
SDL3GameEngine->>SDL3InputManager: update()
SDL3InputManager->>SDL3InputManager: SDL_PollEvent (keyboard/mouse/window/gamepad)
SDL3InputManager->>SDL3InputManager: processGamepadInput()
Note over SDL3InputManager: synthetic mouse/key events injected with windowID set
SDL3InputManager-->>SDL3Mouse: addMouseSDLEvent (ring buffer)
SDL3InputManager-->>SDL3Keyboard: addKeyboardSDLEvent (ring buffer)
SDL3Mouse->>SDL3Mouse: getMouseEvent → translateEvent → scaleMouseCoordinates
SDL3Keyboard->>SDL3Keyboard: getKey → translateScanCodeToKeyVal
end
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
GeneralsMD/Code/Main/WinMain.cpp:899
**Inline `if` body on same line as condition**
`if (!TheGlobalData->m_windowed) flags |= SDL_WINDOW_FULLSCREEN;` places the body on the same line as the condition, which prevents targeted breakpoint placement. Per the project style, the body should be on its own line. The same pattern appears in `SDL3Input.cpp` inside the `processGamepadInput` lambdas: `if (d) TheMessageStream->appendMessage(...)` (three occurrences for NORTH, LEFT_STICK, and RIGHT_STICK buttons).
### Issue 2 of 2
GeneralsMD/Code/Main/WinMain.cpp:912-915
**`NULL` used instead of `nullptr` in SDL3 API calls**
Two SDL3 calls in this block pass `NULL` for pointer parameters: `SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL)` and `SDL_BlitSurfaceScaled(gLoadScreenSurface, NULL, screen, ...)`. Both `NULL` arguments are pointer-typed parameters and should be `nullptr` per the project's C++ coding standard.
Reviews (28): Last reviewed commit: "build: update vcpkg dependencies and fix..." | Re-trigger Greptile
| @@ -9,6 +9,16 @@ option(RTS_BUILD_OPTION_ASAN "Build code with Address Sanitizer." OFF) | |||
| option(RTS_BUILD_OPTION_VC6_FULL_DEBUG "Build VC6 with full debug info." OFF) | |||
| option(RTS_BUILD_OPTION_FFMPEG "Enable FFmpeg support" OFF) | |||
|
|
|||
| # Enable SDL3 by default for modern builds | |||
| if(NOT IS_VS6_BUILD) | |||
| option(SAGE_USE_SDL3 "Enable SDL3 input/window backend" ON) | |||
There was a problem hiding this comment.
Options should be on top of the file and be something like RTS_BUILD_OPTION_SDL3
|
|
||
| namespace { | ||
|
|
||
| Bool DecodeNextUtf8Codepoint(const char* text, size_t length, size_t& offset, UnsignedInt& outCodepoint) |
There was a problem hiding this comment.
Do we need this, maybe better placed in UnicodeString? Or some other helper if it's generic stuff
There was a problem hiding this comment.
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.
e365605 to
627ef23
Compare
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).
eb9908a to
534e694
Compare
xezon
left a comment
There was a problem hiding this comment.
This needs polishing. Not yet reviewed extensively until the obvious style issues are fixed.
|
|
||
| #if SAGE_USE_SDL3 | ||
| #include <SDL3/SDL.h> | ||
| #include "SDL3GameEngine.h" |
There was a problem hiding this comment.
This SDL code is added to a file called WinMain (for Windows). Is this intentional?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
|
|
||
| namespace { | ||
|
|
||
| Bool DecodeNextUtf8Codepoint(const char* text, size_t length, size_t& offset, UnsignedInt& outCodepoint) |
There was a problem hiding this comment.
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) |
| 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); }); |
|
Perhaps also check Fighter19's fork for SDL related implementations. As far as I am aware he has it all done. |
|
I have looked at all existing forks and attributed where possible. |
|
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. |
|
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>
- 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>
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.
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