forked from electronicarts/CnC_Generals_Zero_Hour
-
Notifications
You must be signed in to change notification settings - Fork 199
feat(input): Implement SDL3 input and window management #2639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
githubawn
wants to merge
18
commits into
TheSuperHackers:main
Choose a base branch
from
githubawn:feature/sdl3-input-backport
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 11 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
1a69378
SDL3 Input: Complete backport with Gamepad support and hardening
githubawn 8df9b86
SDL3 Input: Complete backport with Gamepad support and hardening
githubawn e3b674b
added build guards
githubawn 2499e0b
greptile feedback
githubawn f8fb43f
greptile feedback
githubawn 4ac91e0
fixed build error
githubawn a0ac8f4
greptile feedback
githubawn d9d9ff0
added vcpkg
githubawn d048867
vcpkg fix
githubawn 534e694
changed dpad group numbers
githubawn 09fd4e6
switch back to static linking
githubawn 9b0262c
move SDL3_image out SDL3input
githubawn 3a40b11
properly fail on SDL_INIT failure
githubawn 4d82b81
removed custom ICO decoder
githubawn 884218b
fixes SDL_WarpMouseInWindow mismatch
githubawn b268cda
removed some dead declarations
githubawn a7883e8
temporary re-added custom decoder
githubawn 038aea0
dont early return for headless mode
githubawn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
211 changes: 211 additions & 0 deletions
211
Core/GameEngineDevice/Include/SDL3Device/GameClient/SDL3Input.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,211 @@ | ||
| /* | ||
| ** Command & Conquer Generals Zero Hour(tm) | ||
| ** Copyright 2026 TheSuperHackers | ||
| ** | ||
| ** This program is free software: you can redistribute it and/or modify | ||
| ** it under the terms of the GNU General Public License as published by | ||
| ** the Free Software Foundation, either version 3 of the License, or | ||
| ** (at your option) any later version. | ||
| ** | ||
| ** This program is distributed in the hope that it will be useful, | ||
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| ** GNU General Public License for more details. | ||
| ** | ||
| ** You should have received a copy of the GNU General Public License | ||
| ** along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| /* | ||
| ** Derived from the GeneralsX branch by fbraz3 | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "Lib/BaseType.h" | ||
|
|
||
| // SYSTEM INCLUDES | ||
| #include <SDL3/SDL.h> | ||
| #include <SDL3_image/SDL_image.h> | ||
| #include <array> | ||
| #include <functional> | ||
|
|
||
| // USER INCLUDES | ||
| #include "GameClient/Mouse.h" | ||
| #include "GameClient/Keyboard.h" | ||
| #include "GameClient/KeyDefs.h" | ||
|
|
||
| // FORWARD REFERENCES | ||
| struct AnimatedCursor; | ||
| class SDL3InputManager; | ||
|
|
||
| // GLOBALS --------------------------------------------------------------------- | ||
| extern SDL3InputManager* TheSDL3InputManager; | ||
|
|
||
| // TYPE DEFINES ---------------------------------------------------------------- | ||
| typedef KeyDefType KeyVal; | ||
|
|
||
| // SDL3Mouse ------------------------------------------------------------------ | ||
| /** Mouse interface using SDL3 APIs */ | ||
| //----------------------------------------------------------------------------- | ||
| class SDL3Mouse : public Mouse | ||
| { | ||
| public: | ||
| SDL3Mouse(SDL_Window* window); | ||
| virtual ~SDL3Mouse(void); | ||
|
|
||
| // SubsystemInterface | ||
| virtual void init(void) override; | ||
| virtual void reset(void) override; | ||
| virtual void update(void) override; | ||
| virtual void initCursorResources(void) override; | ||
| static void freeCursorResources(void); | ||
|
|
||
| // Mouse interface | ||
| virtual void setCursor(MouseCursor cursor) override; | ||
| virtual void setVisibility(Bool visible) override; | ||
| virtual void loseFocus() override; | ||
| virtual void regainFocus() override; | ||
|
|
||
| // SDL3-specific methods | ||
| void addSDLEvent(SDL_Event *event); | ||
|
|
||
| protected: | ||
| virtual void capture(void) override; | ||
| virtual void releaseCapture(void) override; | ||
| virtual UnsignedByte getMouseEvent(MouseIO *result, Bool flush) override; | ||
|
|
||
| private: | ||
| // Event translation from SDL_Event (Clean Slate implementation) | ||
| void translateEvent(const SDL_Event& event, MouseIO *result); | ||
|
|
||
| // Scale raw SDL window coordinates to game internal resolution | ||
| void scaleMouseCoordinates(int rawX, int rawY, Uint32 windowID, int& scaledX, int& scaledY); | ||
|
|
||
| // Load cursor from ANI file (fighter19 pattern) | ||
| AnimatedCursor* loadCursorFromFile(const char* filepath); | ||
|
|
||
| SDL_Window* m_Window; | ||
| Bool m_IsCaptured; | ||
| Bool m_IsVisible; | ||
| Bool m_LostFocus; | ||
|
|
||
| Uint32 m_LeftButtonDownTime; | ||
| Uint32 m_RightButtonDownTime; | ||
| Uint32 m_MiddleButtonDownTime; | ||
| UnsignedInt m_LastFrameNumber; | ||
|
|
||
| ICoord2D m_LeftButtonDownPos; | ||
| ICoord2D m_RightButtonDownPos; | ||
| ICoord2D m_MiddleButtonDownPos; | ||
|
|
||
| Int m_directionFrame; | ||
| UnsignedInt m_inputFrame; | ||
|
|
||
| float m_accumulatedDeltaX; | ||
| float m_accumulatedDeltaY; | ||
|
|
||
| SDL_Cursor* m_activeSDLCursor; | ||
| Bool m_cursorDirty; | ||
| }; | ||
|
|
||
| // SDL3Keyboard --------------------------------------------------------------- | ||
| /** Keyboard interface using SDL3 APIs */ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style: Avoid /**/ block comments |
||
| //----------------------------------------------------------------------------- | ||
| class SDL3Keyboard : public Keyboard | ||
| { | ||
| public: | ||
| SDL3Keyboard(void); | ||
| virtual ~SDL3Keyboard(void); | ||
|
|
||
| // SubsystemInterface | ||
| virtual void init(void) override; | ||
| virtual void reset(void) override; | ||
| virtual void update(void) override; | ||
|
|
||
| // Keyboard interface | ||
| virtual Bool getCapsState(void) override; | ||
|
|
||
| // SDL3-specific methods | ||
| void addSDLEvent(SDL_Event *event); | ||
|
|
||
| protected: | ||
| virtual void getKey(KeyboardIO *key) override; | ||
| virtual KeyVal translateScanCodeToKeyVal(unsigned char scan); | ||
|
|
||
| private: | ||
| void translateKeyEvent(const SDL_KeyboardEvent& event); | ||
| }; | ||
|
|
||
| // SDL3InputManager ----------------------------------------------------------- | ||
| /** Unified manager for SDL3 input events */ | ||
| //----------------------------------------------------------------------------- | ||
| class SDL3InputManager | ||
| { | ||
| public: | ||
| SDL3InputManager(SDL_Window* window); | ||
| virtual ~SDL3InputManager(); | ||
|
|
||
| void update(); | ||
|
|
||
| // Buffer access | ||
| Bool getNextMouseEvent(SDL_Event& outEvent); | ||
| Bool getNextKeyboardEvent(SDL_Event& outEvent); | ||
|
|
||
| void addMouseSDLEvent(const SDL_Event& event); | ||
| void addKeyboardSDLEvent(const SDL_Event& event); | ||
|
|
||
| Bool isQuitting() const { return m_isQuitting; } | ||
|
|
||
| // Constants | ||
| static constexpr float AXIS_MAX = 32767.0f; | ||
| static constexpr int TRIGGER_THRESHOLD = 16384; | ||
| static constexpr float DEFAULT_DEADZONE = 0.15f; | ||
| static constexpr float DEFAULT_CURSOR_SPEED = 800.0f; | ||
|
|
||
| private: | ||
| struct GamepadState { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style: { |
||
| bool buttonState[SDL_GAMEPAD_BUTTON_COUNT]; | ||
| bool stickLeft, stickRight, stickUp, stickDown; | ||
| bool ltDown, rtDown; | ||
|
|
||
| GamepadState() { | ||
| memset(buttonState, 0, sizeof(buttonState)); | ||
| stickLeft = stickRight = stickUp = stickDown = false; | ||
| ltDown = rtDown = false; | ||
| } | ||
| }; | ||
|
|
||
| private: | ||
| // Gamepad management | ||
| void openFirstGamepad(); | ||
| void closeGamepad(); | ||
|
|
||
| SDL_Window* m_window; | ||
| SDL_Gamepad* m_gamepad; | ||
| void processGamepadInput(); | ||
| void handleGamepadButton(SDL_GamepadButton button, bool& currentState, bool isDown, std::function<void(bool)> action); | ||
|
|
||
| // Virtual event injection | ||
| void virtualPulseKey(SDL_Scancode scancode, bool down); | ||
| void virtualPulseMouse(Uint8 button, bool down); | ||
|
|
||
| // Event buffers | ||
| static const UnsignedInt MAX_MOUSE_EVENTS = 256; | ||
| static const UnsignedInt MAX_KEY_EVENTS = 256; | ||
|
|
||
| SDL_Event m_mouseEvents[MAX_MOUSE_EVENTS]; | ||
| UnsignedInt m_mouseNextFree; | ||
| UnsignedInt m_mouseNextGet; | ||
|
|
||
| SDL_Event m_keyEvents[MAX_KEY_EVENTS]; | ||
| UnsignedInt m_keyNextFree; | ||
| UnsignedInt m_keyNextGet; | ||
|
|
||
| // Gamepad state | ||
| GamepadState m_state; | ||
|
|
||
| Bool m_precisionMode; | ||
| Uint64 m_lastUpdateTime; | ||
| Bool m_isQuitting; | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| /* | ||
| ** Command & Conquer Generals Zero Hour(tm) | ||
| ** Copyright 2026 TheSuperHackers | ||
| ** | ||
| ** This program is free software: you can redistribute it and/or modify | ||
| ** it under the terms of the GNU General Public License as published by | ||
| ** the Free Software Foundation, either version 3 of the License, or | ||
| ** (at your option) any later version. | ||
| ** | ||
| ** This program is distributed in the hope that it will be useful, | ||
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| ** GNU General Public License for more details. | ||
| ** | ||
| ** You should have received a copy of the GNU General Public License | ||
| ** along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| /* | ||
| ** Derived from the GeneralsX branch by fbraz3 | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "Lib/BaseType.h" | ||
|
|
||
| #include "Common/GameEngine.h" | ||
| #include <SDL3/SDL.h> | ||
|
|
||
| // EXTERNALS | ||
| // SDL3 window typically provided by WinMain integration | ||
| extern SDL_Window* TheSDL3Window; | ||
|
|
||
| // Forward declarations for base classes | ||
| class AudioManager; | ||
| class Mouse; | ||
| class Keyboard; | ||
| class GameWindow; | ||
| class LocalFileSystem; | ||
| class ArchiveFileSystem; | ||
| class ThingFactory; | ||
| class ModuleFactory; | ||
| class FunctionLexicon; | ||
| class Radar; | ||
| class WebBrowser; | ||
| class ParticleSystemManager; | ||
|
|
||
| /** | ||
| * SDL3GameEngine | ||
| * | ||
| * GameEngine subclass that uses SDL3 for windowing and input. | ||
| * Replaces or supplements Win32-specific window handling with SDL3. | ||
| */ | ||
| class SDL3GameEngine : public GameEngine | ||
| { | ||
| public: | ||
| SDL3GameEngine(); | ||
| virtual ~SDL3GameEngine(); | ||
|
|
||
| // GameEngine interface | ||
| virtual void init(void) override; | ||
| virtual void reset(void) override; | ||
| virtual void update(void) override; | ||
| virtual void serviceWindowsOS(void) override; | ||
| virtual Bool isActive(void) override; | ||
| virtual void setIsActive(Bool isActive) override; | ||
|
|
||
| // Factory methods (override GameEngine) | ||
| virtual LocalFileSystem *createLocalFileSystem(void) override; | ||
| virtual ArchiveFileSystem *createArchiveFileSystem(void) override; | ||
| virtual GameLogic *createGameLogic(void) override; | ||
| virtual GameClient *createGameClient(void) override; | ||
| virtual ModuleFactory *createModuleFactory(void) override; | ||
| virtual ThingFactory *createThingFactory(void) override; | ||
| virtual FunctionLexicon *createFunctionLexicon(void) override; | ||
| virtual Radar *createRadar(Bool dummy) override; | ||
| virtual WebBrowser *createWebBrowser(void) override; | ||
| virtual ParticleSystemManager* createParticleSystemManager(Bool dummy) override; | ||
| virtual AudioManager *createAudioManager(Bool dummy) override; | ||
|
|
||
| // SDL3 specific | ||
| virtual SDL_Window* getSDLWindow(void) const { return m_SDLWindow; } | ||
| virtual void forwardTextInputEvent(const char* utf8Text); | ||
|
|
||
| protected: | ||
| SDL_Window* m_SDLWindow; | ||
| Bool m_IsInitialized; | ||
| Bool m_IsActive; | ||
| Bool m_IsTextInputActive; | ||
| GameWindow* m_TextInputFocusWindow; | ||
|
|
||
| // Event processing | ||
| void pollSDL3Events(void); | ||
| void updateTextInputState(void); | ||
| }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does the SAGE_ prefix come from? We have not used that before for anything else.