The SDL pump did the translation itself, so it had to know about keyboard,
joystick, touch, window, graphics, audio and ui: eight upward dependencies from
one of the engine's lowest modules, and the source of every remaining
dependency cycle.
Each module now registers an IPlatformEventSink for the events it owns and the
pump only dispatches. Key and text events move to keyboard/sdl, joystick and
game-controller events (including the hotplug device table) to joystick/sdl,
finger events to touch/sdl, resize and the mobile background/foreground surface
watch to window/sdl, the ImGui observer to ui, and Audio::pump to an
end-of-pump hook in audio. The pump keeps only quit and window-close, which are
about the event loop rather than any one module.
Sinks run in priority order -- observer, then surface, then input -- so ImGui
still sees events first and the window is resized before anything reads its
size. The mobile surface teardown keeps using SDL_AddEventWatch rather than the
poll path, since by the time SDL_PollEvent returns the native surface is gone;
the watch is now installed by the window module on its first pump.
Also removes exceptionIfInRenderPass, whose body has been commented out since
it was written and whose only remaining effect was a graphics include.
The engine dependency graph now has zero cycles, down from 26, and the back-edge
whitelist is down to 2 (window -> graphics, window -> image). Layering is also
right way up: event sits at L0 with the input modules above it at L1/L2.
Verified: unit tests for event/input/ui/audio/thread pass, and the example
smoke suite gives the same 19 pass / 9 fail as the pre-change baseline (those 9
fail on main too, from script API drift in the examples themselves).
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>