#include #include "XCUIBackend/XCUIInputBridge.h" #include namespace { using XCEngine::Editor::XCUIBackend::XCUIInputBridge; using XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions; using XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta; using XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot; using XCEngine::Editor::XCUIBackend::XCUIInputBridgeKeyState; using XCEngine::Editor::XCUIBackend::XCUIWin32InputSource; using XCEngine::Input::KeyCode; using XCEngine::UI::UIInputEventType; using XCEngine::UI::UIPoint; XCUIInputBridgeKeyState MakeKeyState(KeyCode keyCode, bool down, bool repeat = false) { XCUIInputBridgeKeyState state = {}; state.keyCode = static_cast(keyCode); state.down = down; state.repeat = repeat; return state; } } // namespace TEST(XCUIInputBridgeTest, TranslateBuildsPointerKeyboardAndCharacterEventsFromSnapshots) { XCUIInputBridgeFrameSnapshot previous = {}; XCUIInputBridgeFrameSnapshot current = {}; current.pointerInside = true; current.pointerPosition = UIPoint(32.0f, 48.0f); current.pointerButtonsDown[0] = true; current.windowFocused = true; current.keys.push_back(MakeKeyState(KeyCode::P, true)); current.characters.push_back(static_cast('p')); const XCUIInputBridgeFrameDelta delta = XCUIInputBridge::Translate(previous, current); EXPECT_TRUE(delta.focusGained); EXPECT_TRUE(delta.pointer.entered); EXPECT_TRUE(delta.pointer.moved); EXPECT_TRUE(delta.pointer.pressed[0]); ASSERT_EQ(delta.keyboard.pressedKeys.size(), 1u); EXPECT_EQ(delta.keyboard.pressedKeys[0], static_cast(KeyCode::P)); ASSERT_EQ(delta.keyboard.characters.size(), 1u); EXPECT_EQ(delta.keyboard.characters[0], static_cast('p')); EXPECT_TRUE(delta.HasEventType(UIInputEventType::FocusGained)); EXPECT_TRUE(delta.HasEventType(UIInputEventType::PointerEnter)); EXPECT_TRUE(delta.HasEventType(UIInputEventType::PointerMove)); EXPECT_TRUE(delta.HasEventType(UIInputEventType::PointerButtonDown)); EXPECT_TRUE(delta.HasEventType(UIInputEventType::KeyDown)); EXPECT_TRUE(delta.HasEventType(UIInputEventType::Character)); } TEST(XCUIInputBridgeTest, PrimeSuppressesSyntheticFirstFrameTransitions) { XCUIInputBridge bridge = {}; XCUIInputBridgeFrameSnapshot baseline = {}; baseline.pointerInside = true; baseline.pointerPosition = UIPoint(24.0f, 12.0f); baseline.pointerButtonsDown[0] = true; baseline.windowFocused = true; baseline.keys.push_back(MakeKeyState(KeyCode::P, true)); bridge.Prime(baseline); const XCUIInputBridgeFrameDelta firstDelta = bridge.Translate(baseline); EXPECT_FALSE(firstDelta.HasEvents()); XCUIInputBridgeFrameSnapshot released = baseline; released.pointerButtonsDown[0] = false; released.keys.clear(); const XCUIInputBridgeFrameDelta secondDelta = bridge.Translate(released); EXPECT_TRUE(secondDelta.pointer.released[0]); ASSERT_EQ(secondDelta.keyboard.releasedKeys.size(), 1u); EXPECT_EQ(secondDelta.keyboard.releasedKeys[0], static_cast(KeyCode::P)); EXPECT_TRUE(secondDelta.HasEventType(UIInputEventType::PointerButtonUp)); EXPECT_TRUE(secondDelta.HasEventType(UIInputEventType::KeyUp)); } TEST(XCUIInputBridgeTest, Win32InputSourceCapturesPointerWheelKeyRepeatAndCharacters) { XCUIWin32InputSource inputSource = {}; inputSource.HandleWindowMessage(nullptr, WM_SETFOCUS, 0, 0); inputSource.HandleWindowMessage(nullptr, WM_MOUSEMOVE, 0, MAKELPARAM(64, 96)); inputSource.HandleWindowMessage(nullptr, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(64, 96)); inputSource.HandleWindowMessage(nullptr, WM_MOUSEWHEEL, MAKEWPARAM(0, WHEEL_DELTA), 0); inputSource.HandleWindowMessage(nullptr, WM_KEYDOWN, 'P', 0); inputSource.HandleWindowMessage(nullptr, WM_KEYDOWN, 'P', 1u << 30u); inputSource.HandleWindowMessage(nullptr, WM_CHAR, 'p', 0); XCUIInputBridgeCaptureOptions options = {}; options.windowFocused = true; const XCUIInputBridgeFrameSnapshot snapshot = inputSource.CaptureSnapshot(options); EXPECT_TRUE(snapshot.windowFocused); EXPECT_TRUE(snapshot.pointerInside); EXPECT_EQ(snapshot.pointerPosition.x, 64.0f); EXPECT_EQ(snapshot.pointerPosition.y, 96.0f); EXPECT_TRUE(snapshot.pointerButtonsDown[0]); EXPECT_FLOAT_EQ(snapshot.wheelDelta.y, 1.0f); EXPECT_TRUE(snapshot.IsKeyDown(static_cast(KeyCode::P))); ASSERT_EQ(snapshot.characters.size(), 1u); EXPECT_EQ(snapshot.characters[0], static_cast('p')); const XCUIInputBridgeFrameDelta delta = XCUIInputBridge::Translate(XCUIInputBridgeFrameSnapshot(), snapshot); EXPECT_TRUE(delta.pointer.pressed[0]); ASSERT_EQ(delta.keyboard.pressedKeys.size(), 1u); EXPECT_EQ(delta.keyboard.pressedKeys[0], static_cast(KeyCode::P)); EXPECT_TRUE(delta.HasEventType(UIInputEventType::Character)); inputSource.ClearFrameTransients(); const XCUIInputBridgeFrameSnapshot afterClear = inputSource.CaptureSnapshot(options); EXPECT_FLOAT_EQ(afterClear.wheelDelta.x, 0.0f); EXPECT_FLOAT_EQ(afterClear.wheelDelta.y, 0.0f); EXPECT_TRUE(afterClear.characters.empty()); const XCUIInputBridgeKeyState* repeatedKey = afterClear.FindKeyState(static_cast(KeyCode::P)); ASSERT_NE(repeatedKey, nullptr); EXPECT_FALSE(repeatedKey->repeat); } TEST(XCUIInputBridgeTest, Win32InputSourceClearsPressedStateOnFocusLoss) { XCUIWin32InputSource inputSource = {}; inputSource.HandleWindowMessage(nullptr, WM_SETFOCUS, 0, 0); inputSource.HandleWindowMessage(nullptr, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(20, 24)); inputSource.HandleWindowMessage(nullptr, WM_KEYDOWN, 'P', 0); XCUIInputBridgeCaptureOptions options = {}; options.windowFocused = true; XCUIInputBridgeFrameSnapshot focused = inputSource.CaptureSnapshot(options); EXPECT_TRUE(focused.pointerButtonsDown[0]); EXPECT_TRUE(focused.IsKeyDown(static_cast(KeyCode::P))); inputSource.HandleWindowMessage(nullptr, WM_KILLFOCUS, 0, 0); XCUIInputBridgeFrameSnapshot blurred = inputSource.CaptureSnapshot(options); EXPECT_FALSE(blurred.windowFocused); EXPECT_FALSE(blurred.pointerButtonsDown[0]); EXPECT_FALSE(blurred.IsKeyDown(static_cast(KeyCode::P))); }