diff options
13 files changed, 281 insertions, 46 deletions
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 7d3a2df550..00dd6ba62b 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -21,6 +21,7 @@ #if defined(__ANDROID__) #include <gui/SurfaceComposerClient.h> #endif +#include <input/Keyboard.h> #include <input/PrintTools.h> #include <unordered_set> @@ -137,6 +138,7 @@ PointerChoreographer::PointerChoreographer( mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID), mShowTouchesEnabled(false), mStylusPointerIconEnabled(false), + mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT), mRegisterListener(registerListener), mUnregisterListener(unregisterListener) {} @@ -168,6 +170,7 @@ void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationC } void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) { + fadeMouseCursorOnKeyPress(args); mNextListener.notify(args); } @@ -177,6 +180,32 @@ void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) { mNextListener.notify(newArgs); } +void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs& args) { + if (args.action == AKEY_EVENT_ACTION_UP || isMetaKey(args.keyCode)) { + return; + } + // Meta state for these keys is ignored for dismissing cursor while typing + constexpr static int32_t ALLOW_FADING_META_STATE_MASK = AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | + AMETA_SCROLL_LOCK_ON | AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON | AMETA_SHIFT_ON; + if (args.metaState & ~ALLOW_FADING_META_STATE_MASK) { + // Do not fade if any other meta state is active + return; + } + if (!mPolicy.isInputMethodConnectionActive()) { + return; + } + + std::scoped_lock _l(mLock); + ui::LogicalDisplayId targetDisplay = args.displayId; + if (targetDisplay == ui::LogicalDisplayId::INVALID) { + targetDisplay = mCurrentFocusedDisplay; + } + auto it = mMousePointersByDisplay.find(targetDisplay); + if (it != mMousePointersByDisplay.end()) { + it->second->fade(PointerControllerInterface::Transition::GRADUAL); + } +} + NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) { std::scoped_lock _l(mLock); @@ -806,6 +835,11 @@ void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId display } } +void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) { + std::scoped_lock lock(mLock); + mCurrentFocusedDisplay = displayId; +} + PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor( ui::LogicalDisplayId displayId) { std::function<std::shared_ptr<PointerControllerInterface>()> ctor = diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index d9b075f3ee..aaf1e3e962 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -76,6 +76,11 @@ public: virtual void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) = 0; /** + * Used by Dispatcher to notify changes in the current focused display. + */ + virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0; + + /** * This method may be called on any thread (usually by the input manager on a binder thread). */ virtual void dump(std::string& dump) = 0; @@ -97,6 +102,7 @@ public: bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, ui::LogicalDisplayId displayId, DeviceId deviceId) override; void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override; + void setFocusedDisplay(ui::LogicalDisplayId displayId) override; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; @@ -124,6 +130,7 @@ private: InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock); bool canUnfadeOnDisplay(ui::LogicalDisplayId displayId) REQUIRES(mLock); + void fadeMouseCursorOnKeyPress(const NotifyKeyArgs& args); NotifyMotionArgs processMotion(const NotifyMotionArgs& args); NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); @@ -192,6 +199,7 @@ private: bool mShowTouchesEnabled GUARDED_BY(mLock); bool mStylusPointerIconEnabled GUARDED_BY(mLock); std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden; + ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(mLock); protected: using WindowListenerRegisterConsumer = std::function<std::vector<gui::WindowInfo>( diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 5ed5eb8e33..47c288931d 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -5526,6 +5526,13 @@ void InputDispatcher::setFocusedDisplay(ui::LogicalDisplayId displayId) { synthesizeCancelationEventsForWindowLocked(windowHandle, options); } mFocusedDisplayId = displayId; + // Enqueue a command to run outside the lock to tell the policy that the focused display + // changed. + auto command = [this]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy.notifyFocusedDisplayChanged(mFocusedDisplayId); + }; + postCommandLocked(std::move(command)); // Only a window on the focused display can have Pointer Capture, so disable the active // Pointer Capture session if there is one, since the focused display changed. diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 0f03620f05..65fb76d274 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -76,6 +76,11 @@ public: InputDeviceSensorAccuracy accuracy) = 0; virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0; + /* + * Notifies the system that focused display has changed. + */ + virtual void notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) = 0; + /* Filters an input event. * Return true to dispatch the event unmodified, false to consume the event. * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED diff --git a/services/inputflinger/include/NotifyArgsBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h index cae638f7bf..5b94d57b8e 100644 --- a/services/inputflinger/include/NotifyArgsBuilders.h +++ b/services/inputflinger/include/NotifyArgsBuilders.h @@ -21,6 +21,7 @@ #include <attestation/HmacKeyManager.h> #include <input/Input.h> #include <input/InputEventBuilders.h> +#include <input/Keyboard.h> #include <utils/Timers.h> // for nsecs_t, systemTime #include <vector> @@ -206,6 +207,12 @@ public: return *this; } + KeyArgsBuilder& metaState(int32_t metaState) { + mMetaState |= metaState; + mMetaState = normalizeMetaState(/*oldMetaState=*/mMetaState); + return *this; + } + NotifyKeyArgs build() const { return {mEventId, mEventTime, diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h index f6dc10997a..7a85c12559 100644 --- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h +++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h @@ -55,6 +55,9 @@ public: */ virtual void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId, const FloatPoint& position) = 0; + + /* Returns true if any InputConnection is currently active. */ + virtual bool isInputMethodConnectionActive() = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 91ec62d3d4..25f4893baf 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -493,7 +493,6 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) { void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) { InputReaderContext& context = *getContext(); context.setLastKeyDownTimestamp(downTime); - // TODO(b/338652288): Move cursor fading logic into PointerChoreographer. // Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard // shortcuts bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode); diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp index e17ee3a5d9..3df05f4bae 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp @@ -219,6 +219,24 @@ void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBe mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching; } +void FakeInputDispatcherPolicy::assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + if (!mFocusedDisplayNotifiedCondition.wait_for(lock, 100ms, + [this, expectedDisplay]() REQUIRES(mLock) { + if (!mNotifiedFocusedDisplay.has_value() || + mNotifiedFocusedDisplay.value() != + expectedDisplay) { + return false; + } + return true; + })) { + ADD_FAILURE() << "Timed out waiting for notifyFocusedDisplayChanged(" << expectedDisplay + << ") to be called."; + } +} + void FakeInputDispatcherPolicy::assertUserActivityNotPoked() { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); @@ -473,4 +491,10 @@ void FakeInputDispatcherPolicy::assertFilterInputEventWasCalledInternal( mFilteredEvent = nullptr; } +void FakeInputDispatcherPolicy::notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) { + std::scoped_lock lock(mLock); + mNotifiedFocusedDisplay = displayId; + mFocusedDisplayNotifiedCondition.notify_all(); +} + } // namespace android diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index 62ff10f8c8..a0f3ea9008 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -116,6 +116,7 @@ public: void assertUnhandledKeyReported(int32_t keycode); void assertUnhandledKeyNotReported(); void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching); + void assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay); private: std::mutex mLock; @@ -126,6 +127,9 @@ private: std::condition_variable mPointerCaptureChangedCondition; + std::optional<ui::LogicalDisplayId> mNotifiedFocusedDisplay GUARDED_BY(mLock); + std::condition_variable mFocusedDisplayNotifiedCondition; + std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock); // ANR handling std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock); @@ -201,6 +205,7 @@ private: void notifyDropWindow(const sp<IBinder>& token, float x, float y) override; void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, const std::set<gui::Uid>& uids) override; + void notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) override; void assertFilterInputEventWasCalledInternal( const std::function<void(const InputEvent&)>& verify); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 56a05a3a3c..aa1462a2ff 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -8577,6 +8577,8 @@ public: // Set focus to second display window. // Set focus display to second one. mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); + mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID); + // Set focus window for second display. mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); windowInSecondary->setFocusable(true); @@ -11066,6 +11068,7 @@ TEST_F(InputDispatcherPointerCaptureTests, MultiDisplayPointerCapture) { // Make the second display the focused display. mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); + mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID); // This causes the first window to lose pointer capture, and it's unable to request capture. mWindow->consumeCaptureEvent(false); @@ -13769,4 +13772,9 @@ TEST_F(InputDispatcherPointerInWindowTest, MultipleDevicesControllingOneMouse) { /*pointerId=*/0)); } +TEST_F(InputDispatcherTest, FocusedDisplayChangeIsNotified) { + mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); + mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 44417246d2..16d3193908 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -186,6 +186,7 @@ public: (PointerControllerInterface::ControllerType), (override)); MOCK_METHOD(void, notifyPointerDisplayIdChanged, (ui::LogicalDisplayId displayId, const FloatPoint& position), (override)); + MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override)); }; } // namespace android diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index ada841d28d..ab47cc67b1 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -70,15 +70,6 @@ protected: AINPUT_SOURCE_KEYBOARD); } - void testPointerVisibilityForKeys(const std::vector<int32_t>& keyCodes, bool expectVisible) { - for (int32_t keyCode : keyCodes) { - process(EV_KEY, keyCode, 1); - process(EV_SYN, SYN_REPORT, 0); - process(EV_KEY, keyCode, 0); - process(EV_SYN, SYN_REPORT, 0); - } - } - void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes, const bool expectPrevent) { if (expectPrevent) { @@ -95,42 +86,6 @@ protected: }; /** - * Pointer visibility should remain unaffected if there is no active Input Method Connection - */ -TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDoesNotHidePointer) { - testPointerVisibilityForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectVisible= */ true); -} - -/** - * Pointer should hide if there is a active Input Method Connection - */ -TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionHidePointer) { - mFakePolicy->setIsInputMethodConnectionActive(true); - testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false); -} - -/** - * Pointer should still hide if touchpad taps are already disabled - */ -TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithTouchpadTapDisabledHidePointer) { - mFakePolicy->setIsInputMethodConnectionActive(true); - EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).WillRepeatedly(Return(true)); - testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false); -} - -/** - * Pointer visibility should remain unaffected by meta keys even if Input Method Connection is - * active - */ -TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDoesNotHidePointer) { - mFakePolicy->setIsInputMethodConnectionActive(true); - std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, - KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, - KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK}; - testPointerVisibilityForKeys(metaKeys, /* expectVisible= */ true); -} - -/** * Touchpad tap should not be disabled if there is no active Input Method Connection */ TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) { diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 3f2d6ec45c..9a5b6a73f5 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -2294,6 +2294,185 @@ TEST_F(PointerChoreographerTest, MouseAndDrawingTabletReportMouseEvents) { assertPointerControllerRemoved(pc); } +class PointerVisibilityOnKeyPressTest : public PointerChoreographerTest { +protected: + const std::unordered_map<int32_t, int32_t> + mMetaKeyStates{{AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON}, + {AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON}, + {AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON}, + {AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON}, + {AKEYCODE_SYM, AMETA_SYM_ON}, + {AKEYCODE_FUNCTION, AMETA_FUNCTION_ON}, + {AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON}, + {AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON}, + {AKEYCODE_META_LEFT, AMETA_META_LEFT_ON}, + {AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON}, + {AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON}, + {AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON}, + {AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON}}; + + void notifyKey(ui::LogicalDisplayId targetDisplay, int32_t keyCode, + int32_t metaState = AMETA_NONE) { + if (metaState == AMETA_NONE && mMetaKeyStates.contains(keyCode)) { + // For simplicity, we always set the corresponding meta state when sending a meta + // keycode. This does not take into consideration when the meta state is updated in + // reality. + metaState = mMetaKeyStates.at(keyCode); + } + mChoreographer.notifyKey(KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .displayId(targetDisplay) + .keyCode(keyCode) + .metaState(metaState) + .build()); + mChoreographer.notifyKey(KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD) + .displayId(targetDisplay) + .keyCode(keyCode) + .metaState(metaState) + .build()); + } + + void metaKeyCombinationHidesPointer(FakePointerController& pc, int32_t keyCode, + int32_t metaKeyCode) { + ASSERT_TRUE(pc.isPointerShown()); + notifyKey(DISPLAY_ID, keyCode, mMetaKeyStates.at(metaKeyCode)); + ASSERT_FALSE(pc.isPointerShown()); + + unfadePointer(); + } + + void metaKeyCombinationDoesNotHidePointer(FakePointerController& pc, int32_t keyCode, + int32_t metaKeyCode) { + ASSERT_TRUE(pc.isPointerShown()); + notifyKey(DISPLAY_ID, keyCode, mMetaKeyStates.at(metaKeyCode)); + ASSERT_TRUE(pc.isPointerShown()); + } + + void unfadePointer() { + // unfade pointer by injecting mose hover event + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + } +}; + +TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutImeConnectionDoesNotHidePointer) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc->isPointerShown()); + + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0); + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A); + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_CTRL_LEFT); + + ASSERT_TRUE(pc->isPointerShown()); +} + +TEST_F(PointerVisibilityOnKeyPressTest, AlphanumericKeystrokesWithImeConnectionHidePointer) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc->isPointerShown()); + + EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + + notifyKey(DISPLAY_ID, AKEYCODE_0); + ASSERT_FALSE(pc->isPointerShown()); + + unfadePointer(); + + notifyKey(DISPLAY_ID, AKEYCODE_A); + ASSERT_FALSE(pc->isPointerShown()); +} + +TEST_F(PointerVisibilityOnKeyPressTest, MetaKeystrokesDoNotHidePointer) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc->isPointerShown()); + + EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + + const std::vector<int32_t> metaKeyCodes{AKEYCODE_ALT_LEFT, AKEYCODE_ALT_RIGHT, + AKEYCODE_SHIFT_LEFT, AKEYCODE_SHIFT_RIGHT, + AKEYCODE_SYM, AKEYCODE_FUNCTION, + AKEYCODE_CTRL_LEFT, AKEYCODE_CTRL_RIGHT, + AKEYCODE_META_LEFT, AKEYCODE_META_RIGHT, + AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK, + AKEYCODE_SCROLL_LOCK}; + for (int32_t keyCode : metaKeyCodes) { + notifyKey(ui::LogicalDisplayId::INVALID, keyCode); + } + + ASSERT_TRUE(pc->isPointerShown()); +} + +TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplay) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + mChoreographer.setFocusedDisplay(DISPLAY_ID); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID), + generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}}); + auto pc1 = assertPointerControllerCreated(ControllerType::MOUSE); + auto pc2 = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc1->isPointerShown()); + ASSERT_TRUE(pc2->isPointerShown()); + + EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0); + ASSERT_FALSE(pc1->isPointerShown()); + ASSERT_TRUE(pc2->isPointerShown()); + unfadePointer(); + + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A); + ASSERT_FALSE(pc1->isPointerShown()); + ASSERT_TRUE(pc2->isPointerShown()); +} + +TEST_F(PointerVisibilityOnKeyPressTest, TestMetaKeyCombinations) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + + // meta key combinations that should hide pointer + metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_LEFT); + metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_RIGHT); + metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_CAPS_LOCK); + metaKeyCombinationHidesPointer(*pc, AKEYCODE_0, AKEYCODE_NUM_LOCK); + metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SCROLL_LOCK); + + // meta key combinations that should not hide pointer + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_LEFT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_RIGHT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_LEFT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_RIGHT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_SYM); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_FUNCTION); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_LEFT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT); +} + class PointerChoreographerWindowInfoListenerTest : public testing::Test {}; TEST_F_WITH_FLAGS( |