Phase 7-8 of the migration. Replaces the per-subsystem stubs added in
the earlier "builds clean" commit with proper SDL 3 implementations.
- gfxengine: add SDL_Window* accessor window_handle() so callers that
need to talk to SDL 3's window APIs (grab, text input) can reach it.
- states.cpp mouse grab: SDL_WM_GrabInput → SDL_SetWindowMouseGrab.
Drops the local SDL_GRAB_* / SDL_WM_GrabInput stub macros.
- states.cpp text input: SDL_EnableUNICODE → SDL_StartTextInput /
SDL_StopTextInput. kobo.cpp event loop now handles
SDL_EVENT_TEXT_INPUT (in both quit_requested() and the main game
loop) and forwards each byte of the UTF-8 string as a unicode value
to gsm.press(-1, ch) so the high-score name-entry path keeps
working without any state-side changes.
- kobo.cpp joystick: full re-port of init_js / close_js using
SDL_GetJoysticks (returns SDL_JoystickID array), SDL_OpenJoystick,
SDL_CloseJoystick. options.cpp joystick enumeration already used
the new API from the previous commit.
- sound/audio.c: replace the silent stub with a real SDL 3 backend
built on SDL_OpenAudioDeviceStream + an SDL_AudioStreamCallback.
The engine's existing _audio_callback (Sint16 stereo fill-buffer)
is kept; a small adapter (sdl3_stream_callback) runs it on a
scratch buffer and pushes the result via SDL_PutAudioStreamData.
audio_lock/unlock now call SDL_LockAudioStream / SDL_UnlockAudioStream.
After this commit the binary opens an audio device successfully and
the joystick path resolves real SDL 3 calls. Renderer + audio +
input are now all on SDL 3 with no compat shims.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Import existing source tree; original VCS history is no longer available.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>