diff --git a/src/platform/macos/macos_backend.cpp b/src/platform/macos/macos_backend.cpp index afa06aa..b393852 100644 --- a/src/platform/macos/macos_backend.cpp +++ b/src/platform/macos/macos_backend.cpp @@ -531,6 +531,14 @@ namespace lvh::detail { CGEventType up_event {}; ///< CoreGraphics up event type. }; + /** + * @brief CoreGraphics metadata for a mouse motion event. + */ + struct MacosMouseMotion { + CGMouseButton button {}; ///< CoreGraphics button associated with the motion. + CGEventType event_type {}; ///< CoreGraphics motion event type. + }; + /** * @brief Translate a portable mouse button to CoreGraphics metadata. * @@ -555,6 +563,25 @@ namespace lvh::detail { return std::nullopt; } + /** + * @brief Select CoreGraphics motion metadata for the currently held mouse buttons. + * + * @param mouse_down Local left, right, and middle button state. + * @return Matching CoreGraphics button and motion event type. + */ + inline MacosMouseMotion macos_mouse_motion(const std::array &mouse_down) { + if (mouse_down[0]) { + return {kCGMouseButtonLeft, kCGEventLeftMouseDragged}; + } + if (mouse_down[1]) { + return {kCGMouseButtonRight, kCGEventRightMouseDragged}; + } + if (mouse_down[2]) { + return {kCGMouseButtonCenter, kCGEventOtherMouseDragged}; + } + return {kCGMouseButtonLeft, kCGEventMouseMoved}; + } + /** * @brief Backend mouse backed by CoreGraphics mouse and scroll events. */ @@ -613,19 +640,6 @@ namespace lvh::detail { return current; } - CGEventType event_type_for_current_buttons() const { - if (mouse_down_[0]) { - return kCGEventLeftMouseDragged; - } - if (mouse_down_[1]) { - return kCGEventOtherMouseDragged; - } - if (mouse_down_[2]) { - return kCGEventRightMouseDragged; - } - return kCGEventMouseMoved; - } - OperationStatus post_mouse( CGMouseButton button, CGEventType type, @@ -660,13 +674,15 @@ namespace lvh::detail { OperationStatus submit_relative_motion(std::int32_t delta_x, std::int32_t delta_y) { const auto current = current_location(); const auto location = CGPoint {current.x + delta_x, current.y + delta_y}; - return post_mouse(kCGMouseButtonLeft, event_type_for_current_buttons(), location, current, 0); + const auto motion = macos_mouse_motion(mouse_down_); + return post_mouse(motion.button, motion.event_type, location, current, 0); } OperationStatus submit_absolute_motion(const MouseEvent &event) { const auto display_bounds = CGDisplayBounds(state_->display); const auto location = absolute_mouse_location(event, display_bounds); - return post_mouse(kCGMouseButtonLeft, event_type_for_current_buttons(), location, current_location(), 0); + const auto motion = macos_mouse_motion(mouse_down_); + return post_mouse(motion.button, motion.event_type, location, current_location(), 0); } OperationStatus submit_button(const MouseEvent &event) { diff --git a/tests/fixtures/include/fixtures/macos_backend_test_hooks.hpp b/tests/fixtures/include/fixtures/macos_backend_test_hooks.hpp index 26f8686..ed1dc06 100644 --- a/tests/fixtures/include/fixtures/macos_backend_test_hooks.hpp +++ b/tests/fixtures/include/fixtures/macos_backend_test_hooks.hpp @@ -21,6 +21,14 @@ namespace lvh::detail::test { double y {}; ///< Vertical coordinate. }; + /** + * @brief Portable representation of CoreGraphics mouse motion metadata for tests. + */ + struct MacosMouseMotionResult { + std::uint32_t button {}; ///< CoreGraphics mouse button value. + std::uint32_t event_type {}; ///< CoreGraphics mouse event type value. + }; + /** * @brief Result set for macOS backend lifecycle utility coverage. */ @@ -93,6 +101,16 @@ namespace lvh::detail::test { double height ); + /** + * @brief Select CoreGraphics motion metadata for a mouse button state. + * + * @param left_down Whether the left button is held. + * @param right_down Whether the right button is held. + * @param middle_down Whether the middle button is held. + * @return CoreGraphics button and motion event type values. + */ + MacosMouseMotionResult macos_backend_mouse_motion(bool left_down, bool right_down, bool middle_down); + /** * @brief Exercise macOS backend creation and unsupported-device paths. * diff --git a/tests/fixtures/macos_backend_test_hooks.cpp b/tests/fixtures/macos_backend_test_hooks.cpp index 1b66f34..58dbd73 100644 --- a/tests/fixtures/macos_backend_test_hooks.cpp +++ b/tests/fixtures/macos_backend_test_hooks.cpp @@ -56,6 +56,14 @@ namespace lvh::detail::test { return {.x = location.x, .y = location.y}; } + MacosMouseMotionResult macos_backend_mouse_motion(bool left_down, bool right_down, bool middle_down) { + const auto motion = macos::macos_mouse_motion({left_down, right_down, middle_down}); + return { + .button = static_cast(motion.button), + .event_type = static_cast(motion.event_type), + }; + } + MacosBackendUtilityResult macos_backend_utilities() { auto backend = create_platform_backend_for_macos_backend_test_hooks(); MacosBackendUtilityResult result; diff --git a/tests/unit/test_macos_backend.cpp b/tests/unit/test_macos_backend.cpp index 452956b..986e81d 100644 --- a/tests/unit/test_macos_backend.cpp +++ b/tests/unit/test_macos_backend.cpp @@ -104,6 +104,26 @@ TEST_F(MacosBackendTest, ConvertsAbsoluteMouseCoordinates) { EXPECT_DOUBLE_EQ(location.y, 170.0); } +TEST_F(MacosBackendTest, SelectsMouseMotionMetadataForHeldButtons) { + using lvh::detail::test::macos_backend_mouse_motion; + + auto motion = macos_backend_mouse_motion(false, false, false); + EXPECT_EQ(motion.button, kCGMouseButtonLeft); + EXPECT_EQ(motion.event_type, kCGEventMouseMoved); + + motion = macos_backend_mouse_motion(true, false, false); + EXPECT_EQ(motion.button, kCGMouseButtonLeft); + EXPECT_EQ(motion.event_type, kCGEventLeftMouseDragged); + + motion = macos_backend_mouse_motion(false, true, false); + EXPECT_EQ(motion.button, kCGMouseButtonRight); + EXPECT_EQ(motion.event_type, kCGEventRightMouseDragged); + + motion = macos_backend_mouse_motion(false, false, true); + EXPECT_EQ(motion.button, kCGMouseButtonCenter); + EXPECT_EQ(motion.event_type, kCGEventOtherMouseDragged); +} + TEST_F(MacosBackendTest, ReportsCapabilitiesAndUnsupportedDevices) { const auto result = lvh::detail::test::macos_backend_utilities();