Repository navigation
Conversation
a785545 to
7cc0861
Compare
[Medium impact] The SDL3 backend reaches the runtime window and input paths for both games on modern builds. Fix closing the minimized game and satisfy the required statement format before merging. Findings
|
e365605 to
627ef23
Compare
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.
|
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? |
|
I've started cleaning up the style and structural issues in the latest commits. Regarding SDL3Main: The main benefit of the current approach is that we keep the game's native Win32 WinMain entry point and startup sequence intact. To get rid of the #ifdef blocks in WinMain while minimizing duplication, I can move the window initialization out into SDL3Window. Regarding splitting out subclasses: I don't think splitting the input handling into isolated SDL3Keyboard and SDL3Mouse subclasses is the right way. It runs contrary to how SDL3 tries to be a unified way; it relies on a single event pump where all keyboard, mouse, and gamepad signals flow together. |
But this is how you currently have set this up. What is the plan here? |
Co-authored-by: Bobby Battista <bobtista@gmail.com>
SDL3 loads .ani cursors via TheFileSystem rather than Win32's LoadCursorFromFile, enabling cursor loading from .BIG archives and virtual filesystems. Add a non-positive size check before allocating memory buffers to handle empty or invalid files safely.
- Use event.wheel.mouse_x/y directly instead of calling SDL_GetMouseState, ensuring accurate cursor position at the exact moment of the wheel event. - Replace Win32 timeGetTime() with SDL keyEvent.timestamp in SDL3Keyboard::getKey for portable millisecond key-down timing. - Replace Win32 VK_RETURN macro with character code 13 in IME Return key dispatch.
…ameEngine::getTicksMsec()
combo box text dropping fixed
Fix the crash when launching a second instance. Timestamp injected gamepad events so key repeat works. Ignore gamepad input while the window is unfocused. Let the window actually resize to the chosen resolution.
…oints SDL3Main.cpp had no SetUnhandledExceptionFilter/try-catch, losing crash dumps on that path; move WinMain.cpp's handler into AppMain so both entry points install it once instead of duplicating it, and wrap AppMain::run() in a catch-all so both paths get equivalent coverage.
…heck The shared fallback branch called FetchContent_MakeAvailable(SDL3) whenever SDL3_image's version check failed, even if SDL3 was already found via find_package - defining its targets twice and stopping the configure. Give each library its own guarded fallback branch.
Restore ALT-key release (resetKeys instead of the no-op reset), audio mute/unmute, and stuck gamepad-driven virtual keys on window focus change, and stop double-counting gamepad cursor motion deltas.
loadANI now returns nullptr instead of a cursor with a null SDL_Cursor when creation fails; clamp viewport-scaled mouse coordinates to the last valid pixel instead of one past it; skip loading the splash BMP in headless mode; drop the dead addSDLEvent wrappers and unused SDL3CursorManager::init(); use snprintf for the splash path buffer.
setScrolling/stopScrolling were retrofitted with null checks for TheMouse/TheDisplay et al. but canScrollAtScreenEdge still dereferenced TheMouse and TheDisplay unconditionally.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟡 Minor · 🎯 Functional Correctness · SDL3Input.cpp:948
Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp:948
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winThe review comment is supported.
virtualPulseMouse()keepslastClickTimeacross allSDL3InputManagerinstances and does not reset it when virtual gamepad inputs are released. A reconnect or manager recreation within 350 ms can mark the first virtual mouse press as a double click. Move the timestamps intoSDL3InputManagerand reset them inreleaseGamepadVirtualInputs().
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
db189db8-6cfb-452c-b2a2-49b47c374e0b
📒 Files selected for processing: 2
Core/GameEngineDevice/Include/SDL3Device/GameClient/SDL3Input.hCore/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp
🚧 Files skipped from review as they are similar to previous changes: 1
- Core/GameEngineDevice/Include/SDL3Device/GameClient/SDL3Input.h
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.
Motion can fill the mouse ring between frames, dropping the button-ups releaseGamepadVirtualInputs() queues on focus loss or gamepad disconnect and leaving a virtual mouse button or key held. The last RESERVED_RELEASE_EVENTS slots of both rings now only accept button-up/key-up events.
f9e8aea to
72252bc
Compare
…actors - BIGFileSystem replaces StdBIGFileSystem - MiniDumper moved to its own library and takes a product id - TheDebugLogCriticalSection is gone - drop BaseType.h include and WIN32_LEAN_AND_MEAN
72252bc to
6ba77d1
Compare
… them on release The timestamps were a function-local static, shared by every SDL3InputManager and never cleared, so the first virtual press after a focus loss or gamepad reconnect could count as a double-click.
| else | ||
| snprintf(resourcePath, sizeof(resourcePath), "Data/Cursors/%s.ani", mouse->m_cursorInfo[cursor].textureName.str()); | ||
|
|
||
| m_cursorResources[cursor][direction] = loadANI(resourcePath); |
There was a problem hiding this comment.
🟡 Medium GameClient/SDL3Cursor.cpp:74
initResources ignores loose cursor overrides in TheGlobalData->m_modDir, so directory mods display the base game's cursor or fail to load a cursor that exists only in the mod. It always passes the base Data/Cursors/... path to FileSystem::openFile, which does not prepend the mod directory; try the mod-prefixed path first, then fall back to the base path.
🤖 Copy this AI Prompt to have your agent fix this:
In file @Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Cursor.cpp around line 74:
`initResources` ignores loose cursor overrides in `TheGlobalData->m_modDir`, so directory mods display the base game's cursor or fail to load a cursor that exists only in the mod. It always passes the base `Data/Cursors/...` path to `FileSystem::openFile`, which does not prepend the mod directory; try the mod-prefixed path first, then fall back to the base path.
| float ly = SDL_GetGamepadAxis(m_gamepad, SDL_GAMEPAD_AXIS_LEFTY) / AXIS_MAX; | ||
| float stickMagnitude = sqrtf(lx * lx + ly * ly); | ||
| if (stickMagnitude > 1.0f) | ||
| stickMagnitude = 1.0f; | ||
|
|
||
| if (stickMagnitude > DEADZONE) | ||
| { | ||
| // Instant polar direction matching exact stick angle (0 rotational lag, 0 loops) | ||
| float dirX = lx / stickMagnitude; |
There was a problem hiding this comment.
🟡 Medium GameClient/SDL3Input.cpp:1058
Diagonal left-stick input produces cursor velocity above targetSpeed: after stickMagnitude is clamped to 1, (0.9, 0.9) is divided by 1, yielding a direction with length about 1.27. Preserve the original magnitude for direction normalization and clamp only the magnitude used for the response.
float stickMagnitude = sqrtf(lx * lx + ly * ly);
+ float directionMagnitude = stickMagnitude;
if (stickMagnitude > 1.0f)
stickMagnitude = 1.0f;
@@
- float dirX = lx / stickMagnitude;
- float dirY = ly / stickMagnitude;
+ float dirX = lx / directionMagnitude;
+ float dirY = ly / directionMagnitude;🤖 Copy this AI Prompt to have your agent fix this:
In file @Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp around lines 1058-1066:
Diagonal left-stick input produces cursor velocity above `targetSpeed`: after `stickMagnitude` is clamped to 1, `(0.9, 0.9)` is divided by 1, yielding a direction with length about 1.27. Preserve the original magnitude for direction normalization and clamp only the magnitude used for the response.
| // Set working directory to executable folder | ||
| Char buffer[_MAX_PATH]; | ||
| GetModuleFileName(nullptr, buffer, sizeof(buffer)); | ||
| if (Char* pEnd = strrchr(buffer, '\\')) | ||
| { | ||
| *pEnd = 0; | ||
| } | ||
| ::SetCurrentDirectory(buffer); |
There was a problem hiding this comment.
🟠 High Main/AppMain.cpp:85
Launches using -useCwd or -setCwd keep the executable directory as the working directory, so relative asset paths resolve from the wrong location and files may fail to load. This unconditional SetCurrentDirectory(buffer) overrides an earlier parseCommandLineForStartup() call, while the later call at line 101 skips parsing once it has already run; remove this duplicate directory change and let the existing CommandLine/WorkingDirectory logic choose the directory.
- // Set working directory to executable folder
- Char buffer[_MAX_PATH];
- GetModuleFileName(nullptr, buffer, sizeof(buffer));
- if (Char* pEnd = strrchr(buffer, '\\'))
- {
- *pEnd = 0;
- }
- ::SetCurrentDirectory(buffer);🤖 Copy this AI Prompt to have your agent fix this:
In file @Core/Main/AppMain.cpp around lines 85-92:
Launches using `-useCwd` or `-setCwd` keep the executable directory as the working directory, so relative asset paths resolve from the wrong location and files may fail to load. This unconditional `SetCurrentDirectory(buffer)` overrides an earlier `parseCommandLineForStartup()` call, while the later call at line 101 skips parsing once it has already run; remove this duplicate directory change and let the existing `CommandLine`/`WorkingDirectory` logic choose the directory.
|
|
||
| for (size_t i = 0; i < wideLength; ++i) | ||
| { | ||
| TheWindowManager->winSendInputMsg(targetWindow, GWM_IME_CHAR, static_cast<WindowMsgData>(wideText[i]), 0); |
There was a problem hiding this comment.
🟡 Medium Common/SDL3GameEngine.cpp:249
Typing committed text into a focused editable combo drops every character, so its text cannot be updated. GadgetComboBoxInput ignores GWM_IME_CHAR on the combo itself; resolve combo targets to GadgetComboBoxGetEditBox() before dispatching the characters.
| TheWindowManager->winSendInputMsg(targetWindow, GWM_IME_CHAR, static_cast<WindowMsgData>(wideText[i]), 0); | |
| TheWindowManager->winSendInputMsg(BitIsSet(targetWindow->winGetStyle(), GWS_COMBO_BOX) ? GadgetComboBoxGetEditBox(targetWindow) : targetWindow, GWM_IME_CHAR, static_cast<WindowMsgData>(wideText[i]), 0); |
🤖 Copy this AI Prompt to have your agent fix this:
In file @Core/GameEngineDevice/Source/SDL3Device/Common/SDL3GameEngine.cpp around line 249:
Typing committed text into a focused editable combo drops every character, so its text cannot be updated. `GadgetComboBoxInput` ignores `GWM_IME_CHAR` on the combo itself; resolve combo targets to `GadgetComboBoxGetEditBox()` before dispatching the characters.
| return; | ||
| } | ||
|
|
||
| SDL_Event nextEvent; |
There was a problem hiding this comment.
🟡 Medium GameClient/SDL3Input.cpp:416
An unmapped scancode makes getKey() return KEY_NONE while later keyboard events remain queued, so Keyboard::updateKeys() stops processing and delays releases such as an arrow-key release. Skip unmapped events in getKey() and return KEY_NONE only when the queue is empty.
🤖 Copy this AI Prompt to have your agent fix this:
In file @Core/GameEngineDevice/Source/SDL3Device/GameClient/SDL3Input.cpp around line 416:
An unmapped scancode makes `getKey()` return `KEY_NONE` while later keyboard events remain queued, so `Keyboard::updateKeys()` stops processing and delays releases such as an arrow-key release. Skip unmapped events in `getKey()` and return `KEY_NONE` only when the queue is empty.
| if (TheMessageStream && TheMessageStream->isReadyForMessages()) | ||
| { | ||
| TheMessageStream->appendMessage(GameMessage::MSG_META_DEMO_INSTANT_QUIT); |
There was a problem hiding this comment.
Closing the game while it is minimized can leave it running indefinitely. When TheMessageStream is ready, this handler only queues MSG_META_DEMO_INSTANT_QUIT. The minimized loop keeps polling events but never calls GameEngine::update() to consume that message, so getQuitting() stays false.
Set a quit flag that this loop can see, or leave the loop when a close request arrives.
| if (m_state.stickLeft) virtualPulseKey(SDL_SCANCODE_LEFT, false); | ||
| if (m_state.stickRight) virtualPulseKey(SDL_SCANCODE_RIGHT, false); | ||
| if (m_state.stickUp) virtualPulseKey(SDL_SCANCODE_UP, false); | ||
| if (m_state.stickDown) virtualPulseKey(SDL_SCANCODE_DOWN, false); |
There was a problem hiding this comment.
Statement bodies share breakpoint lines
The new release handlers put each if body on the same line as its condition. The repository requires if, else, for, and while bodies on separate lines so developers can place breakpoints on the body. Split these statements across lines before merging.
The same pattern appears in the button callbacks at lines 1189 and 1243, and elsewhere in this file.
Rule Used: Always place if/else/for/while statement bodies on separate lines from the condition to allow precise debugger breakpoint placement. Prefer the format: cpp if (condition) doSomethingNextLine(); over: ```cpp if (condition) doSomethingOnSam... (source)
Learned From
TheSuperHackers/GeneralsGameCode#2067
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Comments Outside DiffThese findings could not be posted inline.
|
SDL3 Input Backend
This PR updates the windowing and input pipeline to use SDL3 by default on modern toolchains (non-VC6 builds) 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.
This SDL3 input/windowing backend builds on prior work from the community: