diff --git a/include/eepp/window/input.hpp b/include/eepp/window/input.hpp index a2799745d..48aa8a1ab 100644 --- a/include/eepp/window/input.hpp +++ b/include/eepp/window/input.hpp @@ -233,6 +233,10 @@ class EE_API Input { /** Send an input event to the window */ void sendEvent( InputEvent* Event ); + /** Injects an input event through the backend event translation path and routes it according to + * InputEvent::WinID. */ + virtual bool pushEvent( const InputEvent& event ); + /** @return The joystick manager */ JoystickManager* getJoystickManager() const; diff --git a/src/eepp/window/backend/SDL2/inputsdl2.cpp b/src/eepp/window/backend/SDL2/inputsdl2.cpp index b71354ec7..c16115528 100644 --- a/src/eepp/window/backend/SDL2/inputsdl2.cpp +++ b/src/eepp/window/backend/SDL2/inputsdl2.cpp @@ -11,6 +11,14 @@ namespace EE { namespace Window { namespace Backend { namespace SDL2 { +static Float getEventWindowScale( EE::Window::Window* pollingWindow, Uint32 windowId ) { + if ( windowId == 0 || windowId == pollingWindow->getWindowID() ) + return pollingWindow->getScale(); + if ( auto* eventWindow = Engine::instance()->getWindowID( windowId ) ) + return eventWindow->getScale(); + return pollingWindow->getScale(); +} + InputSDL::InputSDL( EE::Window::Window* window ) : Input( window, eeNew( JoystickManagerSDL, () ) ), mDPIScale( 1.f ) { #if defined( EE_X11_PLATFORM ) @@ -86,6 +94,26 @@ bool InputSDL::isMouseCaptured() const { SDL_WINDOW_MOUSE_CAPTURE; } +bool InputSDL::pushEvent( const InputEvent& event ) { + if ( event.Type != InputEvent::MouseWheel ) + return Input::pushEvent( event ); + + SDL_Event sdlEvent{}; + sdlEvent.type = SDL_MOUSEWHEEL; + sdlEvent.wheel.windowID = event.WinID; + sdlEvent.wheel.x = static_cast( event.wheel.x ); + sdlEvent.wheel.y = static_cast( event.wheel.y ); + sdlEvent.wheel.direction = event.wheel.direction == InputEvent::WheelEvent::Normal + ? SDL_MOUSEWHEEL_NORMAL + : SDL_MOUSEWHEEL_FLIPPED; +#if SDL_VERSION_ATLEAST( 2, 0, 18 ) + sdlEvent.wheel.preciseX = event.wheel.x; + sdlEvent.wheel.preciseY = event.wheel.y; +#endif + sendEvent( sdlEvent ); + return true; +} + std::string InputSDL::getKeyName( const Keycode& keyCode ) const { return std::string( SDL_GetKeyName( keyCode ) ); } @@ -123,9 +151,12 @@ void InputSDL::sendEvent( const SDL_Event& SDLEvent ) { case SDL_WINDOWEVENT_RESIZED: { event.Type = InputEvent::VideoResize; event.WinID = SDLEvent.window.windowID; - mDPIScale = mWindow->getScale(); - event.resize.w = SDLEvent.window.data1 * mDPIScale; - event.resize.h = SDLEvent.window.data2 * mDPIScale; + const Float eventWindowScale = + getEventWindowScale( mWindow, SDLEvent.window.windowID ); + if ( SDLEvent.window.windowID == mWindow->getWindowID() ) + mDPIScale = eventWindowScale; + event.resize.w = SDLEvent.window.data1 * eventWindowScale; + event.resize.h = SDLEvent.window.data2 * eventWindowScale; break; } case SDL_WINDOWEVENT_HIT_TEST: { @@ -291,13 +322,14 @@ void InputSDL::sendEvent( const SDL_Event& SDLEvent ) { break; } case SDL_MOUSEMOTION: { + const Float eventWindowScale = getEventWindowScale( mWindow, SDLEvent.motion.windowID ); event.Type = InputEvent::MouseMotion; event.motion.which = SDLEvent.motion.windowID; event.motion.state = SDLEvent.motion.state; - event.motion.x = SDLEvent.motion.x * mDPIScale; - event.motion.y = SDLEvent.motion.y * mDPIScale; - event.motion.xrel = SDLEvent.motion.xrel * mDPIScale; - event.motion.yrel = SDLEvent.motion.yrel * mDPIScale; + event.motion.x = SDLEvent.motion.x * eventWindowScale; + event.motion.y = SDLEvent.motion.y * eventWindowScale; + event.motion.xrel = SDLEvent.motion.xrel * eventWindowScale; + event.motion.yrel = SDLEvent.motion.yrel * eventWindowScale; event.WinID = SDLEvent.motion.windowID; break; } diff --git a/src/eepp/window/backend/SDL2/inputsdl2.hpp b/src/eepp/window/backend/SDL2/inputsdl2.hpp index d786ffb1d..8b37a6113 100644 --- a/src/eepp/window/backend/SDL2/inputsdl2.hpp +++ b/src/eepp/window/backend/SDL2/inputsdl2.hpp @@ -30,6 +30,8 @@ class EE_API InputSDL : public Input { bool isMouseCaptured() const; + bool pushEvent( const InputEvent& event ); + std::string getKeyName( const Keycode& keycode ) const; Keycode getKeyFromName( const std::string& keycode ) const; diff --git a/src/eepp/window/backend/SDL3/inputsdl3.cpp b/src/eepp/window/backend/SDL3/inputsdl3.cpp index aad28916f..50c67dfb5 100644 --- a/src/eepp/window/backend/SDL3/inputsdl3.cpp +++ b/src/eepp/window/backend/SDL3/inputsdl3.cpp @@ -7,6 +7,14 @@ namespace EE { namespace Window { namespace Backend { namespace SDL3 { +static Float getEventWindowScale( EE::Window::Window* pollingWindow, Uint32 windowId ) { + if ( windowId == 0 || windowId == pollingWindow->getWindowID() ) + return pollingWindow->getScale(); + if ( auto* eventWindow = Engine::instance()->getWindowID( windowId ) ) + return eventWindow->getScale(); + return pollingWindow->getScale(); +} + InputSDL::InputSDL( Window* window ) : Input( window, eeNew( JoystickManagerSDL, () ) ), mDPIScale( 1.f ) { #if defined( EE_X11_PLATFORM ) @@ -77,6 +85,26 @@ bool InputSDL::isMouseCaptured() const { return SDL_GetWindowFlags( win ) & SDL_WINDOW_MOUSE_CAPTURE; } +bool InputSDL::pushEvent( const InputEvent& event ) { + if ( event.Type != InputEvent::MouseWheel ) + return Input::pushEvent( event ); + + SDL_Event sdlEvent{}; + sdlEvent.type = SDL_EVENT_MOUSE_WHEEL; + sdlEvent.wheel.windowID = event.WinID; + sdlEvent.wheel.x = event.wheel.x; + sdlEvent.wheel.y = event.wheel.y; + sdlEvent.wheel.direction = event.wheel.direction == InputEvent::WheelEvent::Normal + ? SDL_MOUSEWHEEL_NORMAL + : SDL_MOUSEWHEEL_FLIPPED; +#if SDL_VERSION_ATLEAST( 3, 2, 12 ) + sdlEvent.wheel.integer_x = static_cast( event.wheel.x ); + sdlEvent.wheel.integer_y = static_cast( event.wheel.y ); +#endif + sendEvent( sdlEvent ); + return true; +} + std::string InputSDL::getKeyName( const Keycode& keycode ) const { return std::string( SDL_GetKeyName( static_cast( keycode ) ) ); } @@ -144,9 +172,11 @@ void InputSDL::sendEvent( const SDL_Event& SDLEvent ) { case SDL_EVENT_WINDOW_RESIZED: { event.Type = InputEvent::VideoResize; event.WinID = SDLEvent.window.windowID; - mDPIScale = mWindow->getScale(); - event.resize.w = static_cast( SDLEvent.window.data1 * mDPIScale ); - event.resize.h = static_cast( SDLEvent.window.data2 * mDPIScale ); + const Float eventWindowScale = getEventWindowScale( mWindow, SDLEvent.window.windowID ); + if ( SDLEvent.window.windowID == mWindow->getWindowID() ) + mDPIScale = eventWindowScale; + event.resize.w = static_cast( SDLEvent.window.data1 * eventWindowScale ); + event.resize.h = static_cast( SDLEvent.window.data2 * eventWindowScale ); break; } case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: { @@ -267,44 +297,50 @@ void InputSDL::sendEvent( const SDL_Event& SDLEvent ) { break; } case SDL_EVENT_MOUSE_MOTION: { + const Float eventWindowScale = getEventWindowScale( mWindow, SDLEvent.motion.windowID ); event.Type = InputEvent::MouseMotion; event.motion.which = SDLEvent.motion.windowID; event.motion.state = SDLEvent.motion.state; - event.motion.x = static_cast( SDLEvent.motion.x * mDPIScale ); - event.motion.y = static_cast( SDLEvent.motion.y * mDPIScale ); - event.motion.xrel = static_cast( SDLEvent.motion.xrel * mDPIScale ); - event.motion.yrel = static_cast( SDLEvent.motion.yrel * mDPIScale ); + event.motion.x = static_cast( SDLEvent.motion.x * eventWindowScale ); + event.motion.y = static_cast( SDLEvent.motion.y * eventWindowScale ); + event.motion.xrel = static_cast( SDLEvent.motion.xrel * eventWindowScale ); + event.motion.yrel = static_cast( SDLEvent.motion.yrel * eventWindowScale ); event.WinID = SDLEvent.motion.windowID; break; } case SDL_EVENT_MOUSE_BUTTON_DOWN: { + const Float eventWindowScale = getEventWindowScale( mWindow, SDLEvent.button.windowID ); event.Type = InputEvent::MouseButtonDown; event.button.button = SDLEvent.button.button; event.button.which = SDLEvent.button.windowID; event.button.state = SDLEvent.button.down ? 1 : 0; - event.button.x = static_cast( SDLEvent.button.x * mDPIScale ); - event.button.y = static_cast( SDLEvent.button.y * mDPIScale ); + event.button.x = static_cast( SDLEvent.button.x * eventWindowScale ); + event.button.y = static_cast( SDLEvent.button.y * eventWindowScale ); event.WinID = SDLEvent.button.windowID; break; } case SDL_EVENT_MOUSE_BUTTON_UP: { + const Float eventWindowScale = getEventWindowScale( mWindow, SDLEvent.button.windowID ); event.Type = InputEvent::MouseButtonUp; event.button.button = SDLEvent.button.button; event.button.which = SDLEvent.button.windowID; event.button.state = SDLEvent.button.down ? 1 : 0; - event.button.x = static_cast( SDLEvent.button.x * mDPIScale ); - event.button.y = static_cast( SDLEvent.button.y * mDPIScale ); + event.button.x = static_cast( SDLEvent.button.x * eventWindowScale ); + event.button.y = static_cast( SDLEvent.button.y * eventWindowScale ); event.WinID = SDLEvent.button.windowID; break; } case SDL_EVENT_MOUSE_WHEEL: { Uint8 button; + const Float eventWindowScale = getEventWindowScale( mWindow, SDLEvent.wheel.windowID ); #if SDL_VERSION_ATLEAST( 3, 2, 12 ) const Sint32 integerX = SDLEvent.wheel.integer_x; const Sint32 integerY = SDLEvent.wheel.integer_y; #else - const Sint32 integerX = x > 0.f ? 1 : ( x < 0.f ? -1 : 0 ); - const Sint32 integerY = y > 0.f ? 1 : ( y < 0.f ? -1 : 0 ); + const Sint32 integerX = + SDLEvent.wheel.x > 0.f ? 1 : ( SDLEvent.wheel.x < 0.f ? -1 : 0 ); + const Sint32 integerY = + SDLEvent.wheel.y > 0.f ? 1 : ( SDLEvent.wheel.y < 0.f ? -1 : 0 ); #endif if ( integerY == 0 && integerX == 0 ) @@ -324,8 +360,8 @@ void InputSDL::sendEvent( const SDL_Event& SDLEvent ) { // Get mouse position from the event (mouse_x, mouse_y are in window coordinates) event.button.button = button; - event.button.x = static_cast( SDLEvent.wheel.mouse_x * mDPIScale ); - event.button.y = static_cast( SDLEvent.wheel.mouse_y * mDPIScale ); + event.button.x = static_cast( SDLEvent.wheel.mouse_x * eventWindowScale ); + event.button.y = static_cast( SDLEvent.wheel.mouse_y * eventWindowScale ); event.button.which = SDLEvent.wheel.windowID; event.WinID = SDLEvent.wheel.windowID; diff --git a/src/eepp/window/backend/SDL3/inputsdl3.hpp b/src/eepp/window/backend/SDL3/inputsdl3.hpp index 87ca5b293..f6b3e90f2 100644 --- a/src/eepp/window/backend/SDL3/inputsdl3.hpp +++ b/src/eepp/window/backend/SDL3/inputsdl3.hpp @@ -30,6 +30,8 @@ class EE_API InputSDL : public Input { bool isMouseCaptured() const; + bool pushEvent( const InputEvent& event ); + std::string getKeyName( const Keycode& keycode ) const; Keycode getKeyFromName( const std::string& keycode ) const; diff --git a/src/eepp/window/input.cpp b/src/eepp/window/input.cpp index ddde23efd..d7f33088e 100644 --- a/src/eepp/window/input.cpp +++ b/src/eepp/window/input.cpp @@ -61,6 +61,12 @@ void Input::sendEvent( InputEvent* Event ) { } } +bool Input::pushEvent( const InputEvent& event ) { + InputEvent eventCopy( event ); + processEventForWindow( &eventCopy ); + return true; +} + void Input::processEvent( InputEvent* Event ) { mLastEvent.restart(); diff --git a/src/tests/unit_tests/uiscenenode_tests.cpp b/src/tests/unit_tests/uiscenenode_tests.cpp index b5d28f40a..38cd8615b 100644 --- a/src/tests/unit_tests/uiscenenode_tests.cpp +++ b/src/tests/unit_tests/uiscenenode_tests.cpp @@ -81,6 +81,56 @@ class TestUIApplication : public UIApplication { void tickOnce() { tick(); } }; +UTEST( UIApplication, RoutesNativeMouseWheelExactlyOnceToTargetWindow ) { + TestUIApplication app( WindowSettings( 320, 240, "Primary Wheel Routing", WindowStyle::Default, + WindowBackend::Default, 32, {}, 1, false, true ), + UIApplication::Settings( + Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1.f, true ), + ContextSettings( false, 0, 0, GLv_default, true, false ) ); + ASSERT_TRUE( app.getWindow() != nullptr ); + auto* secondaryUI = + app.createWindow( WindowSettings( 240, 180, "Secondary Wheel Routing", WindowStyle::Default, + WindowBackend::Default, 32, {}, 1, false, true ), + ContextSettings( false, 0, 0, GLv_default, true, false ) ); + ASSERT_TRUE( secondaryUI != nullptr ); + + auto* primaryInput = app.getWindow()->getInput(); + auto* secondaryInput = secondaryUI->getWindow()->getInput(); + const Uint32 targetWindowId = secondaryUI->getWindow()->getWindowID(); + int primaryWheelEvents = 0; + int secondaryWheelEvents = 0; + Vector2f secondaryWheelOffset; + Uint32 receivedWindowId = 0; + const Uint32 primaryCallback = primaryInput->pushCallback( [&]( InputEvent* event ) { + if ( event->Type == InputEvent::MouseWheel ) + ++primaryWheelEvents; + } ); + const Uint32 secondaryCallback = secondaryInput->pushCallback( [&]( InputEvent* event ) { + if ( event->Type == InputEvent::MouseWheel ) { + ++secondaryWheelEvents; + secondaryWheelOffset = { event->wheel.x, event->wheel.y }; + receivedWindowId = event->WinID; + } + } ); + + InputEvent event{}; + event.Type = InputEvent::MouseWheel; + event.WinID = targetWindowId; + event.wheel.x = 0.f; + event.wheel.y = -1.f; + event.wheel.direction = InputEvent::WheelEvent::Normal; + ASSERT_TRUE( primaryInput->pushEvent( event ) ); + Engine::instance()->updateInput(); + + EXPECT_EQ( primaryWheelEvents, 0 ); + EXPECT_EQ( secondaryWheelEvents, 1 ); + EXPECT_EQ( receivedWindowId, targetWindowId ); + EXPECT_EQ( secondaryWheelOffset.x, 0.f ); + EXPECT_EQ( secondaryWheelOffset.y, -1.f ); + primaryInput->popCallback( primaryCallback ); + secondaryInput->popCallback( secondaryCallback ); +} + UTEST( UIApplication, CreatesSecondaryWindowWithoutChangingAmbientScene ) { TestUIApplication app( WindowSettings( 320, 240, "Primary UI Context", WindowStyle::Default,