Skip to content

adds SDL Keyboard and Mouse #296

Draft
AL2009man wants to merge 31 commits intoGooberRF:masterfrom
AL2009man:SDL-Mouse
Draft

adds SDL Keyboard and Mouse #296
AL2009man wants to merge 31 commits intoGooberRF:masterfrom
AL2009man:SDL-Mouse

Conversation

@AL2009man
Copy link
Copy Markdown
Contributor

@AL2009man AL2009man commented Mar 14, 2026

This commit repurposes DirectInput mode with a Input Handling system, allowing introduction to SDL's Keyboard and Mouse Input systems alongside minor (accidental) improvements to the Windowing Management.

SDL also allow us to adds support for additional keybinds, including Mouse 3/4 buttons.

For those who prefer either the Legacy handling and/or it's DirectInput Mouse mode: you can always swap it back. (Note: SDL Cursor is always in use for Main Menu navigation regardless of Input handles)

optionally: this PR is best merged alongside #295 for the smoothest transition

@AL2009man AL2009man marked this pull request as draft March 14, 2026 23:19
@AL2009man AL2009man marked this pull request as ready for review March 14, 2026 23:28
@GooberRF
Copy link
Copy Markdown
Owner

Could you explain a bit about the benefits of this approach vs. existing DirectInput?

I am most certainly not opposed to improvements, however unless I am mistaken, this would this remove the stock software input method in its entirety? That is an approach that certainly comes with drawbacks. For instance, although DirectInput is better in virtually every way, there are still many people who use the stock input method to this day because they're used to and prefer it.

@GooberRF GooberRF requested a review from Copilot March 15, 2026 00:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR replaces the game’s DirectInput-based mouse path with SDL3 mouse input, removing the DirectInput toggle/settings/UI and wiring SDL init/polling into the main OS/game loops.

Changes:

  • Removed DirectInput headers, runtime state, config serialization, and the “DirectInput” UI/console toggle.
  • Added SDL3 initialization/teardown and per-frame SDL mouse polling.
  • Hooked mouse delta retrieval to feed SDL relative motion and enabled SDL relative mouse mode when “keep centered” is active.

Reviewed changes

Copilot reviewed 9 out of 99 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
game_patch/rf/input.h Removes DirectInput includes and DirectInput-related memory refs.
game_patch/os/os.cpp Initializes SDL, polls SDL mouse each frame, and quits SDL on shutdown.
game_patch/misc/ui.cpp Removes DirectInput checkbox and reflows panel controls.
game_patch/misc/alpine_settings.h Removes persisted direct_input setting.
game_patch/misc/alpine_settings.cpp Stops loading/saving the DirectInput config key.
game_patch/main/main.cpp Creates the SDL window wrapper after RF init.
game_patch/input/mouse.cpp Implements SDL relative motion accumulation + hooks into RF mouse delta flow.
game_patch/input/input.h Exposes SDL mouse init/poll functions.
game_patch/CMakeLists.txt Links SDL3 static target and additional Windows system libs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

AL2009man added a commit to AL2009man/alpinefaction that referenced this pull request Mar 15, 2026
@AL2009man AL2009man marked this pull request as draft March 15, 2026 03:49
@AL2009man
Copy link
Copy Markdown
Contributor Author

AL2009man commented Mar 15, 2026

Could you explain a bit about the benefits of this approach vs. existing DirectInput?

I am most certainly not opposed to improvements, however unless I am mistaken, this would this remove the stock software input method in its entirety? That is an approach that certainly comes with drawbacks. For instance, although DirectInput is better in virtually every way, there are still many people who use the stock input method to this day because they're used to and prefer it.

basically: consolidation. no need for two different Input APIs when you can have one that does it for you, and more future-proof.

SDL is meant to be a hardware abstraction layer that does alot of things, it does Inputs so it'll use the best Input API available for the OS in question, and this applies to game controllers. as far as I can tell: SDL Mouse on Windows will use Raw Input (or Win32 WM_MOUSE?). As far as I can tell: overall Mouse Input performance on my own trackball should behave the same regardless.

that being said, I've restored the ability to switch to stock input.

@AL2009man AL2009man changed the title Replace DirectInput's Mouse with SDL Replace DirectInput's Mouse and Keyboard with SDL Mar 16, 2026
upgrade portions of the keyboard handler with SDL.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates core mouse + keyboard input handling from DirectInput/Win32 message processing to an SDL3-driven polling approach, and adds SDL3 as a vendored FetchContent dependency (plus a new UI/config toggle to switch SDL mouse vs “stock” mouse input).

Changes:

  • Add SDL3 (FetchContent) to the build and link AlpineFaction against SDL3::SDL3-static, plus required Win32 libs for static SDL.
  • Introduce a central SDL event pump (sdl_input_poll) and wire it into the OS poll loop; implement SDL-based mouse delta and keyboard event feeding into RF.
  • Replace the old “DirectInput” config/UI toggle with an “SDL mouse” toggle and persist it as SDLMouse in the config.

Reviewed changes

Copilot reviewed 12 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
vendor/sdl/LICENSE.txt Adds SDL license text for the new SDL dependency.
vendor/sdl/CMakeLists.txt FetchContent setup to build SDL3 from source (static).
vendor/CMakeLists.txt Includes the new vendor/sdl subdirectory.
resources/licensing-info.txt Adds SDL license block to licensing info aggregation.
game_patch/rf/input.h Removes DirectInput-specific globals/includes from RF input header.
game_patch/os/os.cpp Initializes SDL, polls SDL input during OS polling, and switches cursor show/hide to SDL helpers.
game_patch/misc/ui.cpp Renames the options checkbox from DirectInput to SDL mouse and hooks toggle to SDL mouse enable/disable.
game_patch/misc/alpine_settings.h Replaces direct_input config flag with sdl_mouse.
game_patch/misc/alpine_settings.cpp Loads/saves the new SDLMouse config key instead of DirectInput.
game_patch/main/main.cpp Creates the SDL window wrapper after RF init via mouse_init_sdl_window().
game_patch/input/mouse.cpp Implements SDL mouse deltas + relative mode integration; provides toggle and hooks.
game_patch/input/key.cpp Implements SDL keyboard polling and replaces Win32 keyboard message forwarding with SDL-fed events.
game_patch/input/input.h Declares SDL input polling and initialization helpers.
game_patch/input/input.cpp Adds central SDL event pumping function used by OS poll hook.
game_patch/CMakeLists.txt Adds input/input.cpp and links SDL3 static (plus extra Win32 libs).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@AL2009man AL2009man marked this pull request as ready for review March 16, 2026 16:47
@AL2009man
Copy link
Copy Markdown
Contributor Author

AL2009man commented Mar 16, 2026

SDL Keyboard has been officially added into it. with that, i felt that it's complete enough for a full review.

that being said, i did have to do last-minute timer fixes but i'm not too sure if the master branch is going to break it due to side effects

the camera will no longer speen when disabling SDL Mouse
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces an SDL3-based keyboard+mouse input path (selectable alongside existing stock/DirectInput behavior), aiming to improve layout handling (notably on Wine/non‑US layouts) and provide a smoother input backend transition (optionally alongside #295).

Changes:

  • Vendor SDL3 (3.4.2) via CMake and link it into AlpineFaction.
  • Add an input_mode setting (0=stock, 1=DirectInput mouse, 2=SDL mouse+keyboard) with UI/config support and legacy config migration.
  • Add an SDL event pump and hooks to feed RF keyboard/mouse deltas from SDL when in SDL mode.

Reviewed changes

Copilot reviewed 13 out of 16 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
vendor/sdl/LICENSE.txt Adds SDL license text to the vendored SDL directory.
vendor/sdl/CMakeLists.txt Fetches/builds SDL3 from release tarball via FetchContent and configures static build.
vendor/CMakeLists.txt Registers the new vendor/sdl subdirectory.
resources/licensing-info.txt Adds SDL license block to the project licensing info.
game_patch/rf/input.h Minor formatting cleanup for DIRECTINPUT_VERSION define.
game_patch/os/os.cpp Initializes SDL, pumps SDL input during os_poll, and uses SDL cursor visibility calls.
game_patch/os/frametime.cpp Adds os.h include (for wait_for/timer usage).
game_patch/misc/ui.cpp Replaces DirectInput checkbox with an “Input mode” cycler UI element.
game_patch/misc/alpine_settings.h Replaces direct_input bool with input_mode int and documents mode meanings.
game_patch/misc/alpine_settings.cpp Loads/saves InputMode and migrates legacy SDLMouse/DirectInput keys.
game_patch/main/main.cpp Creates the SDL window wrapper after RF init.
game_patch/input/mouse.cpp Adds SDL relative mouse handling, SDL delta accumulation, and mode switching logic.
game_patch/input/key.cpp Adds SDL keyboard polling and SDL-based key naming / key-to-ascii mapping in SDL mode.
game_patch/input/input.h Declares SDL input pump/poll helpers and set_input_mode.
game_patch/input/input.cpp Implements a central SDL event pump for keyboard/mouse subsystems.
game_patch/CMakeLists.txt Adds new source file and links against SDL3 static + Win32 deps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR integrates SDL3 as an additional input backend, adding SDL-based keyboard + mouse handling (including a Wine non‑US layout workaround) and a configurable “input mode” that can be switched at runtime and persisted in settings.

Changes:

  • Vendor SDL3 via CMake FetchContent and link it into the AlpineFaction target, plus add licensing entries.
  • Introduce input_mode (stock / DirectInput / SDL) with UI + config serialization/migration from legacy keys.
  • Add an SDL event pump and implement SDL-based keyboard scancode mapping + mouse relative deltas, including extra mouse button rebinding support.

Reviewed changes

Copilot reviewed 13 out of 16 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
vendor/sdl/LICENSE.txt Adds SDL license text for vendored dependency documentation.
vendor/sdl/CMakeLists.txt Fetches/builds SDL3 (3.4.2) from source via FetchContent.
vendor/CMakeLists.txt Adds vendor/sdl subdirectory to the vendor build.
resources/licensing-info.txt Adds SDL section to project licensing info.
game_patch/rf/input.h Minor formatting change for DIRECTINPUT_VERSION.
game_patch/os/os.cpp Initializes SDL, pumps SDL input during OS polling, and updates cursor show/hide handling; calls SDL_Quit() on shutdown.
game_patch/os/frametime.cpp Includes os.h to access wait_for/WaitableTimer.
game_patch/misc/ui.cpp Replaces DirectInput toggle with an “Input mode” selector and adds extra-mouse rebind finalization logic.
game_patch/misc/alpine_settings.h Replaces direct_input bool with input_mode int and documents mode meanings.
game_patch/misc/alpine_settings.cpp Loads/saves InputMode and migrates from legacy SDLMouse / DirectInput keys.
game_patch/main/main.cpp Creates the SDL window wrapper after RF initialization.
game_patch/input/mouse.cpp Implements SDL mouse motion accumulation, mode switching, and extra mouse button injection/rebind handling.
game_patch/input/key.cpp Adds SDL keyboard polling + scancode mapping and switches key-to-ascii/key-name behavior based on input mode.
game_patch/input/input.h Defines SDL input plumbing + custom scan codes for extra mouse buttons.
game_patch/input/input.cpp Centralized SDL event pumping + dispatch to keyboard/mouse pollers.
game_patch/CMakeLists.txt Adds input/input.cpp to sources and links SDL3::SDL3-static (+ Win32 deps).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +55 to +59
void set_input_mode(int mode)
{
const int old_mode = g_alpine_game_config.input_mode;
g_alpine_game_config.input_mode = mode;


// Sentinel scan code injected during rebind to make RF process the binding entry.
// On the falling edge of waiting_for_key, the sentinel is replaced with the real scan code.
static constexpr int CTRL_REBIND_SENTINEL = 0x58; // KEY_F12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR integrates SDL3 as an additional keyboard/mouse input backend, adds an input-mode selector (Legacy/DirectInput/SDL), and wires SDL event pumping into the game loop alongside related config/UI updates.

Changes:

  • Vendor SDL3 via CMake FetchContent and link it into AlpineFaction, with licensing updates.
  • Replace the legacy direct_input toggle with a 3-mode input_mode setting, including UI and config load/save support.
  • Add SDL-backed mouse/keyboard polling plus extra bind support (Mouse 4–8, extra keys) and hook it into the main OS poll.

Reviewed changes

Copilot reviewed 14 out of 17 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
vendor/sdl/LICENSE.txt Adds SDL license text to the vendored SDL directory.
vendor/sdl/CMakeLists.txt Fetches/builds SDL3 (static) via FetchContent.
vendor/CMakeLists.txt Registers SDL vendor subdirectory in the build.
resources/licensing-info.txt Adds SDL licensing section to consolidated licensing info.
game_patch/rf/input.h Minor formatting adjustment for DirectInput version define.
game_patch/os/os.cpp Initializes SDL, pumps SDL events during OS poll, updates cursor handling, and changes window init/close paths.
game_patch/os/frametime.cpp Includes os.h (context dependency adjustment).
game_patch/misc/ui.cpp Adds “Input mode” UI control and rebind handling for extra mouse/buttons & extra keys.
game_patch/misc/alpine_settings.h Replaces direct_input with input_mode setting.
game_patch/misc/alpine_settings.cpp Loads/saves InputMode (and legacy keys) and calls set_input_mode().
game_patch/main/main.cpp Initializes SDL window wrapper for mouse subsystem post game init.
game_patch/input/mouse.h Declares SDL mouse support + extra mouse scan code constants/APIs.
game_patch/input/mouse.cpp Implements SDL mouse mode, mode switching, extra mouse binds, and delta hooks.
game_patch/input/key.cpp Implements SDL key-to-text path, SDL key polling, extra key names, and rebind sentinel handling.
game_patch/input/input.h Centralizes input constants/APIs (sentinel, extra scan codes, SDL poll entrypoints).
game_patch/input/input.cpp Adds central SDL event pump and dispatch to keyboard/mouse pollers.
game_patch/CMakeLists.txt Adds new input source files and links SDL3::SDL3-static (+ Win32 libs).
Comments suppressed due to low confidence (1)

game_patch/input/key.cpp:6

  • This file now uses std::snprintf and std::size(), but only includes (and project headers). Depending on toolchain/standard library, this can fail to compile because std::snprintf is declared in and std::size in (or ). Add the appropriate standard headers explicitly rather than relying on transitive includes.
#include <cctype>
#include <SDL3/SDL.h>
#include <patch_common/FunHook.h>
#include <patch_common/CodeInjection.h>
#include <patch_common/AsmWriter.h>
#include <xlog/xlog.h>

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

bool verbose_time_left_display = true;
bool nearest_texture_filtering = false;
bool direct_input = true;
int input_mode = 2; // 0=Win32 mouse+keyboard (referred as Classic), 1=DInput mouse+Classic keyboard, 2=SDL mouse+keyboard
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

AlpineGameSettings replaced direct_input with input_mode, but there is still at least one write to g_alpine_game_config.direct_input (e.g. in alpine_settings.cpp set_headless_bot_defaults), which will no longer compile. Update headless/defaults and any remaining references to use input_mode semantics (likely force mode 0 or 2 depending on intended headless behavior).

Suggested change
int input_mode = 2; // 0=Win32 mouse+keyboard (referred as Classic), 1=DInput mouse+Classic keyboard, 2=SDL mouse+keyboard
int input_mode = 0; // 0=Win32 mouse+keyboard (referred as Classic), 1=DInput mouse+Classic keyboard, 2=SDL mouse+keyboard

Copilot uses AI. Check for mistakes.
Comment on lines +540 to +544
SDL_Event events[16];
int n;
while ((n = SDL_PeepEvents(events, static_cast<int>(std::size(events)),
SDL_GETEVENT, SDL_EVENT_MOUSE_MOTION,
SDL_EVENT_MOUSE_REMOVED)) > 0) {
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

mouse_sdl_poll() uses std::size(events) but the file doesn't include the standard header that provides std::size for arrays ( in C++17+, or ). Add the correct include explicitly to avoid non-portable reliance on transitive includes.

Copilot uses AI. Check for mistakes.
Comment on lines +201 to +207
if (!rf::is_dedicated_server) {
if (SDL_Init(SDL_INIT_VIDEO)) {
g_sdl_video_initialized = true;
} else {
xlog::error("SDL_Init(SDL_INIT_VIDEO) failed: {}", SDL_GetError());
}
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

SDL_Init() success check is inverted. SDL_Init returns 0 on success and a negative value on failure, so this currently logs an error and leaves g_sdl_video_initialized=false on successful init (disabling SDL input polling/cursor calls). Flip the condition (e.g., check == 0) and only log SDL_GetError() on failure.

Copilot uses AI. Check for mistakes.
Comment on lines 106 to 116
static FunHook<void(const char *, const char *, bool, bool)> os_init_window_server_hook{
0x00524B70,
[](const char *wclass, const char *title, bool hooks, bool server_console) {
const bool bot_headless = client_bot_headless_enabled();
win32_console_set_forced(bot_headless);
if (server_console || bot_headless) {
if (server_console) {
win32_console_init();
}
if (server_console && win32_console_is_enabled()) {
return;
}

os_init_window_server_hook.call_target(wclass, title, hooks, server_console);

if (bot_headless && rf::main_wnd) {
// Keep client-side message/window plumbing (for networking reliability),
// but do not show the regular game window in headless mode.
LONG wnd_style = GetWindowLongA(rf::main_wnd, GWL_STYLE);
if (wnd_style & WS_VISIBLE) {
SetWindowLongA(rf::main_wnd, GWL_STYLE, wnd_style & ~WS_VISIBLE);
}
SetWindowPos(
rf::main_wnd,
nullptr,
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW
);
ShowWindow(rf::main_wnd, SW_HIDE);
rf::is_main_wnd_active = true;
if (HWND console_wnd = GetConsoleWindow()) {
SetActiveWindow(console_wnd);
SetForegroundWindow(console_wnd);
}
if (!win32_console_is_enabled()) {
os_init_window_server_hook.call_target(wclass, title, hooks, server_console);
}
},
};
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

This change removes the implementations of cmdline parsing helpers (cmdline_token_equals_ci/cmdline_has_switch_token/raw_command_line_has_switch/is_client_bot_requested_from_cmdline/is_client_debugbot_requested_from_cmdline/headless_bot_requested_from_raw_cmdline) that are still declared in game_patch/os/os.h and referenced from other translation units (e.g. multi.cpp). As-is, this will cause unresolved externals at link time. Restore these functions (or move them to another .cpp that is compiled) and ensure os.h declarations match the new location.

Copilot uses AI. Check for mistakes.
Comment on lines 64 to 86
case WM_ACTIVATE:
if (client_bot_headless_enabled()) {
// In headless mode, the console window will have focus and WM_ACTIVATE for the
// hidden game window may report inactive. Keep active state pinned so client
// simulation/network timing does not fall into background-throttled behavior.
rf::is_main_wnd_active = true;
return 0;
}

if (!rf::is_dedicated_server) {
// Show cursor if window is not active
if (w_param) {
ShowCursor(FALSE);
if (g_sdl_video_initialized) {
SDL_HideCursor();
}
// Drive Win32 counter to exactly -1 (hidden)
while (ShowCursor(FALSE) >= 0)
;
}
else {
ShowCursor(TRUE);
if (g_sdl_video_initialized) {
SDL_ShowCursor();
}
// Drive Win32 counter to exactly 0 (visible) so external dialogs
// (assertions, crash popups) always see the correct cursor state.
while (ShowCursor(TRUE) < 0)
;
}
}

rf::is_main_wnd_active = w_param;
return 0; //DefWindowProcA(wnd_handle, msg, w_param, l_param);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

Headless-bot window management behavior was removed here: the client_bot_headless_enabled() special-case that pinned rf::is_main_wnd_active=true and prevented the hidden game window from being shown is gone. Since headless mode is still used elsewhere (graphics init bypass, menu init skip), this risks regressions like background-throttled timing or an unwanted visible window. Consider restoring the headless-bot branches for WM_ACTIVATE / show-window suppression, or relocating that logic to the new input/windowing path so headless behavior stays consistent.

Copilot uses AI. Check for mistakes.
@AL2009man AL2009man closed this Mar 20, 2026
@AL2009man AL2009man reopened this Mar 20, 2026
@AL2009man AL2009man marked this pull request as draft March 26, 2026 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants