| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string_view> |
| |
| #include "ash/accelerators/accelerator_controller_impl.h" |
| #include "ash/accessibility/accessibility_controller.h" |
| #include "ash/accessibility/drag_event_rewriter.h" |
| #include "ash/accessibility/mouse_keys/mouse_keys_controller.h" |
| #include "ash/constants/ash_pref_names.h" |
| #include "ash/events/test_event_capturer.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller.h" |
| #include "ash/system/accessibility/mouse_keys/mouse_keys_bubble_view.h" |
| #include "ash/test/ash_test_base.h" |
| #include "base/run_loop.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/task_environment.h" |
| #include "ui/accessibility/accessibility_features.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/display/manager/display_manager.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_handler.h" |
| #include "ui/events/event_utils.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/events/types/event_type.h" |
| #include "ui/views/accessibility/view_accessibility.h" |
| #include "ui/views/controls/textfield/textfield.h" |
| #include "ui/views/layout/fill_layout.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| |
| namespace ash { |
| |
| // TODO(259372916): Add tests to verify interactions with other A11y features. |
| // TODO(259372916): Add tests for multiple screens. |
| // TODO(259372916): Add tests different DPIs. |
| // TODO(259372916): Add test to check holding down multiple movement keys. |
| |
| namespace { |
| |
| const int kMouseDeviceId = 42; |
| const gfx::Point kDefaultPosition(100, 100); |
| const double kMoveDeltaDIP = MouseKeysController::kBaseSpeedDIPPerSecond * |
| MouseKeysController::kUpdateFrequencyInSeconds; |
| |
| class EventRewriterWrapper : public ui::EventRewriter { |
| public: |
| EventRewriterWrapper() = default; |
| |
| EventRewriterWrapper(const EventRewriterWrapper&) = delete; |
| EventRewriterWrapper& operator=(const EventRewriterWrapper&) = delete; |
| |
| ~EventRewriterWrapper() override = default; |
| ui::EventDispatchDetails RewriteEvent( |
| const ui::Event& event, |
| const Continuation continuation) override { |
| bool captured = Shell::Get()->mouse_keys_controller()->RewriteEvent(event); |
| return captured ? DiscardEvent(continuation) |
| : SendEvent(continuation, &event); |
| } |
| }; |
| |
| } // namespace |
| |
| class MouseKeysTest : public AshTestBase { |
| protected: |
| MouseKeysTest() |
| : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} |
| MouseKeysTest(const MouseKeysTest&) = delete; |
| MouseKeysTest& operator=(const MouseKeysTest&) = delete; |
| ~MouseKeysTest() override = default; |
| |
| void SetUp() override { |
| scoped_feature_list_.InitAndEnableFeature( |
| ::features::kAccessibilityMouseKeys); |
| AshTestBase::SetUp(); |
| event_capturer_.set_capture_mouse_enter_exit(false); |
| GetContext()->GetHost()->GetEventSource()->AddEventRewriter(&rewriter_); |
| GetContext()->AddPreTargetHandler(&event_capturer_); |
| |
| // Set a device id so mouse events aren't ignored by the controller. |
| GetEventGenerator()->set_mouse_source_device_id(kMouseDeviceId); |
| |
| // Make sure the display is initialized so we don't fail the test due to any |
| // input events caused from creating the display. |
| Shell::Get()->display_manager()->UpdateDisplays(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void TearDown() override { |
| GetContext()->RemovePreTargetHandler(&event_capturer_); |
| GetContext()->GetHost()->GetEventSource()->RemoveEventRewriter(&rewriter_); |
| AshTestBase::TearDown(); |
| } |
| |
| void SetEnabled(bool enabled) { |
| Shell::Get()->accessibility_controller()->mouse_keys().SetEnabled(enabled); |
| } |
| |
| MouseKeysBubbleController* GetBubbleController() const { |
| return Shell::Get() |
| ->mouse_keys_controller() |
| ->GetMouseKeysBubbleControllerForTest(); |
| } |
| |
| MouseKeysBubbleView* GetBubbleView() const { |
| return GetBubbleController()->mouse_keys_bubble_view_; |
| } |
| |
| bool IsBubbleVisible() { |
| // Add a null check for widget_. |
| if (GetBubbleController()->widget_ == nullptr) { |
| return false; |
| } |
| return GetBubbleController()->widget_->IsVisible(); |
| } |
| |
| std::u16string_view GetBubbleText() const { |
| return GetBubbleView()->GetTextForTesting(); |
| } |
| |
| bool IsButtonChangeIconVisible() const { |
| return GetBubbleView()->GetMouseButtonChangeIconForTesting()->GetVisible(); |
| } |
| |
| bool IsMouseDraggedIconVisible() const { |
| return GetBubbleView()->GetMouseDragIconForTesting()->GetVisible(); |
| } |
| |
| const std::vector<ui::KeyEvent>& CheckForKeyEvents() { |
| base::RunLoop().RunUntilIdle(); |
| return event_capturer_.key_events(); |
| } |
| |
| const std::vector<ui::MouseEvent>& CheckForMouseEvents() { |
| base::RunLoop().RunUntilIdle(); |
| return event_capturer_.mouse_events(); |
| } |
| |
| void ExpectMouseMovedInCircularPattern( |
| const std::vector<ui::MouseEvent>& mouse_events, |
| const gfx::Point& starting_position, |
| double delta) { |
| EXPECT_EQ(8u, mouse_events.size()); |
| if (mouse_events.size() != 8u) { |
| return; |
| } |
| |
| // There should be 8 mouse movements. |
| for (int i = 0; i < 8; ++i) { |
| EXPECT_EQ(ui::EventType::kMouseMoved, mouse_events[i].type()); |
| } |
| |
| // The pointer should move in a circular pattern. |
| auto position = starting_position + gfx::Vector2d(-delta, -delta); |
| EXPECT_EQ(mouse_events[0].location(), position); |
| position += gfx::Vector2d(0, -delta); |
| EXPECT_EQ(mouse_events[1].location(), position); |
| position += gfx::Vector2d(delta, -delta); |
| EXPECT_EQ(mouse_events[2].location(), position); |
| position += gfx::Vector2d(-delta, 0); |
| EXPECT_EQ(mouse_events[3].location(), position); |
| position += gfx::Vector2d(delta, 0); |
| EXPECT_EQ(mouse_events[4].location(), position); |
| position += gfx::Vector2d(-delta, delta); |
| EXPECT_EQ(mouse_events[5].location(), position); |
| position += gfx::Vector2d(0, delta); |
| EXPECT_EQ(mouse_events[6].location(), position); |
| position += gfx::Vector2d(delta, delta); |
| EXPECT_EQ(mouse_events[7].location(), position); |
| } |
| |
| void ExpectClick(const std::vector<ui::MouseEvent>& mouse_events, |
| int buttons, |
| const gfx::Point& position) { |
| ASSERT_EQ(2u, mouse_events.size()); |
| ExpectClick(mouse_events[0], mouse_events[1], buttons, false, position); |
| } |
| |
| void ExpectDoubleClick(const std::vector<ui::MouseEvent>& mouse_events, |
| const gfx::Point& position) { |
| ASSERT_EQ(4u, mouse_events.size()); |
| ExpectClick(mouse_events[0], mouse_events[1], ui::EF_LEFT_MOUSE_BUTTON, |
| false, position); |
| ExpectClick(mouse_events[2], mouse_events[3], ui::EF_LEFT_MOUSE_BUTTON, |
| true, position); |
| } |
| |
| void ExpectClick(const ui::MouseEvent& mouse_event0, |
| const ui::MouseEvent& mouse_event1, |
| int buttons, |
| bool is_double_click, |
| const gfx::Point& position) { |
| EXPECT_EQ(ui::EventType::kMousePressed, mouse_event0.type()); |
| EXPECT_TRUE(buttons & mouse_event0.flags()); |
| EXPECT_EQ(mouse_event0.location(), position); |
| EXPECT_EQ(ui::EventType::kMouseReleased, mouse_event1.type()); |
| EXPECT_TRUE(buttons & mouse_event1.flags()); |
| EXPECT_EQ(mouse_event1.location(), position); |
| if (is_double_click) { |
| EXPECT_TRUE(ui::EF_IS_DOUBLE_CLICK & mouse_event0.flags()); |
| EXPECT_TRUE(ui::EF_IS_DOUBLE_CLICK & mouse_event1.flags()); |
| } else { |
| EXPECT_FALSE(ui::EF_IS_DOUBLE_CLICK & mouse_event0.flags()); |
| EXPECT_FALSE(ui::EF_IS_DOUBLE_CLICK & mouse_event1.flags()); |
| } |
| } |
| |
| MouseKeysController* GetMouseKeysController() { |
| return Shell::Get()->mouse_keys_controller(); |
| } |
| |
| void SetUsePrimaryKeys(bool value) { |
| PrefService* prefs = |
| Shell::Get()->session_controller()->GetLastActiveUserPrefService(); |
| |
| prefs->SetBoolean(prefs::kAccessibilityMouseKeysUsePrimaryKeys, value); |
| } |
| |
| void SetLeftHanded(bool value) { |
| PrefService* prefs = |
| Shell::Get()->session_controller()->GetLastActiveUserPrefService(); |
| |
| MouseKeysDominantHand dominant_hand = |
| value ? MouseKeysDominantHand::kLeftHandDominant |
| : MouseKeysDominantHand::kRightHandDominant; |
| prefs->SetInteger(prefs::kAccessibilityMouseKeysDominantHand, |
| static_cast<int>(dominant_hand)); |
| } |
| |
| void ClearEvents() { event_capturer_.ClearEvents(); } |
| |
| void PressKey(ui::KeyboardCode key_code, int flags = 0) { |
| GetEventGenerator()->PressKey(key_code, flags); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void ReleaseKey(ui::KeyboardCode key_code, int flags = 0) { |
| GetEventGenerator()->ReleaseKey(key_code, flags); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void PressAndReleaseKey(ui::KeyboardCode key_code) { |
| GetEventGenerator()->PressAndReleaseKey(key_code); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| ui::KeyEvent ColemakKeyEvent(ui::EventType type, ui::KeyboardCode key_code) { |
| switch (key_code) { |
| case ui::VKEY_7: |
| return ui::KeyEvent(type, key_code, ui::DomCode::DIGIT7, 0); |
| case ui::VKEY_8: |
| return ui::KeyEvent(type, key_code, ui::DomCode::DIGIT8, 0); |
| case ui::VKEY_9: |
| return ui::KeyEvent(type, key_code, ui::DomCode::DIGIT9, 0); |
| case ui::VKEY_L: |
| return ui::KeyEvent(type, key_code, ui::DomCode::US_U, 0); |
| case ui::VKEY_Y: |
| return ui::KeyEvent(type, key_code, ui::DomCode::US_O, 0); |
| case ui::VKEY_N: |
| return ui::KeyEvent(type, key_code, ui::DomCode::US_J, 0); |
| case ui::VKEY_E: |
| return ui::KeyEvent(type, key_code, ui::DomCode::US_K, 0); |
| case ui::VKEY_I: |
| return ui::KeyEvent(type, key_code, ui::DomCode::US_L, 0); |
| case ui::VKEY_U: |
| return ui::KeyEvent(type, key_code, ui::DomCode::US_I, 0); |
| case ui::VKEY_O: |
| return ui::KeyEvent(type, key_code, ui::DomCode::SEMICOLON, 0); |
| case ui::VKEY_J: |
| return ui::KeyEvent(type, key_code, ui::DomCode::US_Y, 0); |
| case ui::VKEY_K: |
| return ui::KeyEvent(type, key_code, ui::DomCode::US_N, 0); |
| default: |
| return ui::KeyEvent(type, ui::VKEY_UNKNOWN, 0, ui::EventTimeForNow()); |
| } |
| } |
| |
| void PressColemakKey(ui::KeyboardCode key_code) { |
| ui::KeyEvent key_event( |
| ColemakKeyEvent(ui::EventType::kKeyPressed, key_code)); |
| GetEventGenerator()->Dispatch(&key_event); |
| } |
| |
| void ReleaseColemakKey(ui::KeyboardCode key_code) { |
| ui::KeyEvent key_event( |
| ColemakKeyEvent(ui::EventType::kKeyReleased, key_code)); |
| GetEventGenerator()->Dispatch(&key_event); |
| } |
| |
| void PressAndReleaseColemakKey(ui::KeyboardCode key_code) { |
| PressColemakKey(key_code); |
| ReleaseColemakKey(key_code); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void SetAcceleration(double acceleration) { |
| PrefService* prefs = |
| Shell::Get()->session_controller()->GetLastActiveUserPrefService(); |
| |
| prefs->SetDouble(prefs::kAccessibilityMouseKeysAcceleration, acceleration); |
| } |
| |
| void SetMaxSpeed(double factor) { |
| PrefService* prefs = |
| Shell::Get()->session_controller()->GetLastActiveUserPrefService(); |
| |
| prefs->SetDouble(prefs::kAccessibilityMouseKeysMaxSpeed, factor); |
| } |
| |
| gfx::Point GetLastMousePositionFromEvents() { |
| auto events = CheckForMouseEvents(); |
| EXPECT_FALSE(events.empty()); |
| return events.back().location(); |
| } |
| |
| void ShowMouseKeysBubble() { |
| EXPECT_FALSE(IsBubbleVisible()); |
| PressAndReleaseKey(ui::VKEY_OEM_COMMA); |
| EXPECT_TRUE(IsBubbleVisible()); |
| EXPECT_EQ(GetBubbleText(), u"Right mouse button"); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| TestEventCapturer event_capturer_; |
| EventRewriterWrapper rewriter_; |
| }; |
| |
| TEST_F(MouseKeysTest, ToggleEnabled) { |
| std::vector<ui::MouseEvent> events; |
| |
| // We should not see any events. |
| ClearEvents(); |
| EXPECT_FALSE(GetMouseKeysController()->enabled()); |
| events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, events.size()); |
| |
| // Enable Mouse Keys. |
| ClearEvents(); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| |
| // We should still not get any more events. |
| events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, events.size()); |
| |
| // Disable Mouse Keys. |
| SetEnabled(false); |
| EXPECT_FALSE(GetMouseKeysController()->enabled()); |
| } |
| |
| TEST_F(MouseKeysTest, Events) { |
| // We should not see any mouse events initially, and key events should be |
| // passed through. |
| ClearEvents(); |
| EXPECT_FALSE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(2u, CheckForKeyEvents().size()); |
| |
| // Enable Mouse Keys, the key events should be absorbed. |
| ClearEvents(); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(2u, CheckForMouseEvents().size()); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| // We should not get any more events. |
| ClearEvents(); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| // Disable Mouse Keys, and we should see the original behaviour. |
| ClearEvents(); |
| SetEnabled(false); |
| EXPECT_FALSE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(2u, CheckForKeyEvents().size()); |
| } |
| |
| TEST_F(MouseKeysTest, Click) { |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| |
| // We should not see any mouse events initially. |
| ClearEvents(); |
| EXPECT_FALSE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(2u, CheckForKeyEvents().size()); |
| |
| // Enable Mouse Keys, and we should be able to click by pressing i. |
| ClearEvents(); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // We should not get any more events. |
| ClearEvents(); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| |
| // Disable Mouse Keys, and we should see the original behaviour. |
| ClearEvents(); |
| SetEnabled(false); |
| EXPECT_FALSE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| } |
| |
| TEST_F(MouseKeysTest, DoubleClick) { |
| SetEnabled(true); |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| |
| // Enable Mouse Keys, and we should be able to double click by pressing /. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_OEM_2); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectDoubleClick(CheckForMouseEvents(), kDefaultPosition); |
| |
| // Switch to right mouse button, we shouldn't get a double click. |
| PressAndReleaseKey(ui::VKEY_OEM_COMMA); |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_OEM_2); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| |
| // Switch to both mouse buttons, we shouldn't get a double click. |
| PressAndReleaseKey(ui::VKEY_OEM_COMMA); |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_OEM_2); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| } |
| |
| TEST_F(MouseKeysTest, SelectButtonRightHand) { |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| SetLeftHanded(false); |
| |
| // Initial click should be the left button. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Press , and the mouse action should be the right button. |
| ClearEvents(); |
| EXPECT_FALSE(IsBubbleVisible()); |
| PressAndReleaseKey(ui::VKEY_OEM_COMMA); |
| |
| // Bubble view with right button change message and button change icon |
| // should be displayed. |
| EXPECT_TRUE(IsBubbleVisible()); |
| EXPECT_EQ(GetBubbleText(), u"Right mouse button"); |
| EXPECT_TRUE(IsButtonChangeIconVisible()); |
| EXPECT_FALSE(IsMouseDraggedIconVisible()); |
| |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_RIGHT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Press , and the mouse action should be both buttons. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_OEM_COMMA); |
| |
| // Bubble view with both mouse buttons change message |
| // and button change icon should be displayed. |
| EXPECT_TRUE(IsBubbleVisible()); |
| EXPECT_EQ(GetBubbleText(), u"Both mouse buttons"); |
| EXPECT_TRUE(IsButtonChangeIconVisible()); |
| EXPECT_FALSE(IsMouseDraggedIconVisible()); |
| |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), |
| ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Press , and the mouse action should be the left button. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_OEM_COMMA); |
| |
| // Bubble view with left mouse buttons change message |
| // and button change icon should be displayed. |
| EXPECT_TRUE(IsBubbleVisible()); |
| EXPECT_EQ(GetBubbleText(), u"Left mouse button"); |
| EXPECT_TRUE(IsButtonChangeIconVisible()); |
| EXPECT_FALSE(IsMouseDraggedIconVisible()); |
| |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| } |
| |
| TEST_F(MouseKeysTest, SelectButtonLeftHand) { |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| SetLeftHanded(true); |
| |
| // Initial click should be the left button. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_W); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Press , and the mouse action should be the right button. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_X); |
| PressAndReleaseKey(ui::VKEY_W); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_RIGHT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Press , and the mouse action should be both buttons. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_W); |
| PressAndReleaseKey(ui::VKEY_X); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), |
| ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Press , and the mouse action should be the left button. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_W); |
| PressAndReleaseKey(ui::VKEY_X); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| } |
| |
| TEST_F(MouseKeysTest, SelectButtonNumPad) { |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| |
| SetUsePrimaryKeys(false); |
| |
| // Press - and the mouse action should be the right button. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_SUBTRACT); |
| PressAndReleaseKey(ui::VKEY_NUMPAD5); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_RIGHT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Press * and the mouse action should be both buttons. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_MULTIPLY); |
| PressAndReleaseKey(ui::VKEY_NUMPAD5); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), |
| ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Press / and the mouse action should be the left button. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_DIVIDE); |
| PressAndReleaseKey(ui::VKEY_NUMPAD5); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| } |
| |
| TEST_F(MouseKeysTest, IgnoreKeyRepeat) { |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| |
| // Enable Mouse Keys, and we should be able to click by pressing i. |
| ClearEvents(); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| PressKey(ui::VKEY_I); |
| auto mouse_events = CheckForMouseEvents(); |
| ASSERT_EQ(1u, mouse_events.size()); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| EXPECT_EQ(ui::EventType::kMousePressed, mouse_events[0].type()); |
| EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & mouse_events[0].flags()); |
| EXPECT_EQ(mouse_events[0].location(), kDefaultPosition); |
| |
| // A repeated key shouldn't cause another click. |
| ClearEvents(); |
| PressKey(ui::VKEY_I, ui::EF_IS_REPEAT); |
| ASSERT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| // Releasing the key should release the mouse. |
| ClearEvents(); |
| ReleaseKey(ui::VKEY_I); |
| mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(1u, mouse_events.size()); |
| EXPECT_EQ(ui::EventType::kMouseReleased, mouse_events[0].type()); |
| EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & mouse_events[0].flags()); |
| EXPECT_EQ(mouse_events[0].location(), kDefaultPosition); |
| } |
| |
| TEST_F(MouseKeysTest, MouseKeysBubbleMovesWithMouse) { |
| // Initialize mouse and enable Mouse Keys. |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| ClearEvents(); |
| |
| // Get initial mouse position with a small move up-left. |
| PressAndReleaseKey(ui::VKEY_7); |
| gfx::Point initialMousePosition = GetLastMousePositionFromEvents(); |
| |
| // Show the bubble and get its initial position. |
| ShowMouseKeysBubble(); |
| gfx::Point initialBubblePosition = |
| GetBubbleView()->GetAnchorRect().CenterPoint(); |
| |
| // Simulate mouse movement up-left. |
| PressAndReleaseKey(ui::VKEY_7); |
| |
| // Get the final mouse position. |
| auto finalMousePosition = GetLastMousePositionFromEvents(); |
| |
| // Get the final bubble position. |
| gfx::Point finalBubblePosition = |
| GetBubbleView()->GetAnchorRect().CenterPoint(); |
| |
| // Verify mouse and bubble movements match. |
| EXPECT_EQ(finalMousePosition - initialMousePosition, |
| finalBubblePosition - initialBubblePosition); |
| } |
| |
| TEST_F(MouseKeysTest, Move) { |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| |
| // We should not see any mouse events initially. |
| ClearEvents(); |
| EXPECT_FALSE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_7); |
| PressAndReleaseKey(ui::VKEY_8); |
| PressAndReleaseKey(ui::VKEY_9); |
| PressAndReleaseKey(ui::VKEY_U); |
| PressAndReleaseKey(ui::VKEY_O); |
| PressAndReleaseKey(ui::VKEY_J); |
| PressAndReleaseKey(ui::VKEY_K); |
| PressAndReleaseKey(ui::VKEY_L); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(16u, CheckForKeyEvents().size()); |
| |
| // Enable Mouse Keys, and we should be able to move the mouse with 7, 8, 9, u, |
| // o, j, k, l. |
| ClearEvents(); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_7); |
| PressAndReleaseKey(ui::VKEY_8); |
| PressAndReleaseKey(ui::VKEY_9); |
| PressAndReleaseKey(ui::VKEY_U); |
| PressAndReleaseKey(ui::VKEY_O); |
| PressAndReleaseKey(ui::VKEY_J); |
| PressAndReleaseKey(ui::VKEY_K); |
| PressAndReleaseKey(ui::VKEY_L); |
| auto mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| ExpectMouseMovedInCircularPattern(mouse_events, kDefaultPosition, |
| kMoveDeltaDIP); |
| |
| // We should not get any more events. |
| ClearEvents(); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| |
| // Disable Mouse Keys, and we should see the original behaviour. |
| ClearEvents(); |
| SetEnabled(false); |
| EXPECT_FALSE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_7); |
| PressAndReleaseKey(ui::VKEY_8); |
| PressAndReleaseKey(ui::VKEY_9); |
| PressAndReleaseKey(ui::VKEY_U); |
| PressAndReleaseKey(ui::VKEY_O); |
| PressAndReleaseKey(ui::VKEY_J); |
| PressAndReleaseKey(ui::VKEY_K); |
| PressAndReleaseKey(ui::VKEY_L); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(16u, CheckForKeyEvents().size()); |
| } |
| |
| TEST_F(MouseKeysTest, KeyboardLayout) { |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| |
| // Enable Mouse Keys, and we should be able to move the mouse with 7, 8, 9, k, |
| // y, n, e, i. |
| ClearEvents(); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| PressAndReleaseColemakKey(ui::VKEY_7); |
| PressAndReleaseColemakKey(ui::VKEY_8); |
| PressAndReleaseColemakKey(ui::VKEY_9); |
| PressAndReleaseColemakKey(ui::VKEY_L); |
| PressAndReleaseColemakKey(ui::VKEY_Y); |
| PressAndReleaseColemakKey(ui::VKEY_N); |
| PressAndReleaseColemakKey(ui::VKEY_E); |
| PressAndReleaseColemakKey(ui::VKEY_I); |
| auto mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| ExpectMouseMovedInCircularPattern(mouse_events, kDefaultPosition, |
| kMoveDeltaDIP); |
| |
| ClearEvents(); |
| // Click |
| PressAndReleaseColemakKey(ui::VKEY_U); |
| |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| ClearEvents(); |
| // Unmapped |
| PressAndReleaseColemakKey(ui::VKEY_O); |
| PressAndReleaseColemakKey(ui::VKEY_J); |
| PressAndReleaseColemakKey(ui::VKEY_K); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(6u, CheckForKeyEvents().size()); |
| } |
| |
| TEST_F(MouseKeysTest, MaxSpeed) { |
| // Enough time for the initial event and 9 updates. |
| constexpr auto kTenEventsInSeconds = |
| MouseKeysController::kUpdateFrequencyInSeconds * 9.5; |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| |
| // No acceleration. |
| constexpr int kMaxSpeed = 3; |
| SetMaxSpeed(kMaxSpeed); |
| SetAcceleration(0); |
| |
| // Move right. |
| ClearEvents(); |
| PressKey(ui::VKEY_O); |
| task_environment()->FastForwardBy(base::Seconds(kTenEventsInSeconds)); |
| ReleaseKey(ui::VKEY_O); |
| auto mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| ASSERT_EQ(10u, mouse_events.size()); |
| gfx::Vector2d move_delta(kMoveDeltaDIP * kMaxSpeed, 0); |
| auto position = kDefaultPosition; |
| for (size_t i = 0; i < mouse_events.size(); ++i) { |
| position += move_delta; |
| EXPECT_EQ(ui::EventType::kMouseMoved, mouse_events[i].type()); |
| EXPECT_EQ(mouse_events[i].location(), position); |
| } |
| |
| // Move down and left. |
| ClearEvents(); |
| PressKey(ui::VKEY_J); |
| task_environment()->FastForwardBy(base::Seconds(kTenEventsInSeconds)); |
| ReleaseKey(ui::VKEY_J); |
| mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| EXPECT_EQ(10u, mouse_events.size()); |
| move_delta = |
| gfx::Vector2d(-kMoveDeltaDIP * kMaxSpeed, kMoveDeltaDIP * kMaxSpeed); |
| for (size_t i = 0; i < mouse_events.size(); ++i) { |
| position += move_delta; |
| EXPECT_EQ(ui::EventType::kMouseMoved, mouse_events[i].type()); |
| EXPECT_EQ(mouse_events[i].location(), position); |
| } |
| } |
| |
| TEST_F(MouseKeysTest, Acceleration) { |
| // Enough time for the initial event and 9 updates. |
| constexpr auto kTenEventsInSeconds = |
| MouseKeysController::kUpdateFrequencyInSeconds * 9.5; |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| |
| // Some acceleration. |
| constexpr double kAcceleration = 0.2; |
| const double kAccelerationDelta = |
| kAcceleration * |
| MouseKeysController::kBaseAccelerationDIPPerSecondSquared * |
| MouseKeysController::kUpdateFrequencyInSeconds; |
| SetMaxSpeed(10); |
| SetAcceleration(kAcceleration); |
| |
| // Move down. |
| ClearEvents(); |
| PressKey(ui::VKEY_K); |
| task_environment()->FastForwardBy(base::Seconds(kTenEventsInSeconds)); |
| ReleaseKey(ui::VKEY_K); |
| auto mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| EXPECT_EQ(10u, mouse_events.size()); |
| double move_delta = kMoveDeltaDIP; |
| auto position = kDefaultPosition; |
| for (size_t i = 0; i < mouse_events.size(); ++i) { |
| position += gfx::Vector2d(0, move_delta); |
| EXPECT_EQ(ui::EventType::kMouseMoved, mouse_events[i].type()); |
| EXPECT_EQ(mouse_events[i].location(), position); |
| move_delta += kAccelerationDelta; |
| } |
| |
| // Move up and right. |
| ClearEvents(); |
| PressKey(ui::VKEY_9); |
| task_environment()->FastForwardBy(base::Seconds(kTenEventsInSeconds)); |
| ReleaseKey(ui::VKEY_9); |
| mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| EXPECT_EQ(10u, mouse_events.size()); |
| move_delta = kMoveDeltaDIP; |
| for (size_t i = 0; i < mouse_events.size(); ++i) { |
| position += gfx::Vector2d(move_delta, -move_delta); |
| EXPECT_EQ(ui::EventType::kMouseMoved, mouse_events[i].type()); |
| EXPECT_EQ(mouse_events[i].location(), position); |
| move_delta += kAccelerationDelta; |
| } |
| } |
| |
| TEST_F(MouseKeysTest, AccelerationAndMaxSpeed) { |
| // Enough time for the initial event and 9 updates. |
| constexpr auto kTenEventsInSeconds = |
| MouseKeysController::kUpdateFrequencyInSeconds * 9.5; |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| |
| // Some acceleration. |
| constexpr double kAcceleration = 0.5; |
| constexpr double kMaxSpeedFactor = 3; |
| constexpr double kMaxSpeed = kMaxSpeedFactor * |
| MouseKeysController::kBaseSpeedDIPPerSecond * |
| MouseKeysController::kUpdateFrequencyInSeconds; |
| const double kAccelerationDelta = |
| kAcceleration * |
| MouseKeysController::kBaseAccelerationDIPPerSecondSquared * |
| MouseKeysController::kUpdateFrequencyInSeconds; |
| SetMaxSpeed(kMaxSpeedFactor); |
| SetAcceleration(kAcceleration); |
| |
| // Move right. |
| ClearEvents(); |
| PressKey(ui::VKEY_O); |
| task_environment()->FastForwardBy(base::Seconds(kTenEventsInSeconds)); |
| ReleaseKey(ui::VKEY_O); |
| auto mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| |
| EXPECT_EQ(10u, mouse_events.size()); |
| double move_delta = kMoveDeltaDIP; |
| auto position = kDefaultPosition; |
| for (size_t i = 0; i < mouse_events.size(); ++i) { |
| position += gfx::Vector2d(move_delta, 0); |
| EXPECT_EQ(ui::EventType::kMouseMoved, mouse_events[i].type()); |
| EXPECT_EQ(mouse_events[i].location(), position); |
| move_delta += kAccelerationDelta; |
| move_delta = std::clamp(move_delta, 0.0, kMaxSpeed); |
| } |
| } |
| |
| TEST_F(MouseKeysTest, LeftHanded) { |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| |
| ClearEvents(); |
| SetEnabled(true); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| |
| // We should not see any mouse events initially from the left hand. |
| PressAndReleaseKey(ui::VKEY_1); |
| PressAndReleaseKey(ui::VKEY_2); |
| PressAndReleaseKey(ui::VKEY_3); |
| PressAndReleaseKey(ui::VKEY_Q); |
| PressAndReleaseKey(ui::VKEY_E); |
| PressAndReleaseKey(ui::VKEY_A); |
| PressAndReleaseKey(ui::VKEY_S); |
| PressAndReleaseKey(ui::VKEY_D); |
| PressAndReleaseKey(ui::VKEY_W); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(18u, CheckForKeyEvents().size()); |
| |
| // Switch to left handed. |
| SetLeftHanded(true); |
| |
| ClearEvents(); |
| // We should not see any mouse events from the right hand. |
| PressAndReleaseKey(ui::VKEY_7); |
| PressAndReleaseKey(ui::VKEY_8); |
| PressAndReleaseKey(ui::VKEY_9); |
| PressAndReleaseKey(ui::VKEY_U); |
| PressAndReleaseKey(ui::VKEY_O); |
| PressAndReleaseKey(ui::VKEY_J); |
| PressAndReleaseKey(ui::VKEY_K); |
| PressAndReleaseKey(ui::VKEY_L); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(18u, CheckForKeyEvents().size()); |
| |
| // We should be able to click by pressing w. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_W); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Enable Mouse Keys, and we should be able to move the mouse with 1, 2, 3, q, |
| // e, a, s, d. |
| ClearEvents(); |
| EXPECT_TRUE(GetMouseKeysController()->enabled()); |
| PressAndReleaseKey(ui::VKEY_1); |
| PressAndReleaseKey(ui::VKEY_2); |
| PressAndReleaseKey(ui::VKEY_3); |
| PressAndReleaseKey(ui::VKEY_Q); |
| PressAndReleaseKey(ui::VKEY_E); |
| PressAndReleaseKey(ui::VKEY_A); |
| PressAndReleaseKey(ui::VKEY_S); |
| PressAndReleaseKey(ui::VKEY_D); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectMouseMovedInCircularPattern(CheckForMouseEvents(), kDefaultPosition, |
| kMoveDeltaDIP); |
| } |
| |
| TEST_F(MouseKeysTest, NumPad) { |
| SetEnabled(true); |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| |
| // Switch to the num pad. |
| SetUsePrimaryKeys(false); |
| |
| // We should be able to click with the num pad 5. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_NUMPAD5); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // We should be able to move the mouse with the num pad. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_NUMPAD7); |
| PressAndReleaseKey(ui::VKEY_NUMPAD8); |
| PressAndReleaseKey(ui::VKEY_NUMPAD9); |
| PressAndReleaseKey(ui::VKEY_NUMPAD4); |
| PressAndReleaseKey(ui::VKEY_NUMPAD6); |
| PressAndReleaseKey(ui::VKEY_NUMPAD1); |
| PressAndReleaseKey(ui::VKEY_NUMPAD2); |
| PressAndReleaseKey(ui::VKEY_NUMPAD3); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectMouseMovedInCircularPattern(CheckForMouseEvents(), kDefaultPosition, |
| kMoveDeltaDIP); |
| } |
| |
| TEST_F(MouseKeysTest, UsePrimaryKeyboard) { |
| SetEnabled(true); |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| |
| // Turn off the primary keyboard. |
| SetUsePrimaryKeys(false); |
| |
| // Switch to left handed. |
| SetLeftHanded(true); |
| |
| ClearEvents(); |
| // We should not see any mouse events from the left hand. |
| PressAndReleaseKey(ui::VKEY_1); |
| PressAndReleaseKey(ui::VKEY_2); |
| PressAndReleaseKey(ui::VKEY_3); |
| PressAndReleaseKey(ui::VKEY_Q); |
| PressAndReleaseKey(ui::VKEY_E); |
| PressAndReleaseKey(ui::VKEY_A); |
| PressAndReleaseKey(ui::VKEY_S); |
| PressAndReleaseKey(ui::VKEY_D); |
| PressAndReleaseKey(ui::VKEY_W); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(18u, CheckForKeyEvents().size()); |
| |
| // Switch to right handed. |
| SetLeftHanded(false); |
| |
| ClearEvents(); |
| // We should not see any mouse events from the right hand. |
| PressAndReleaseKey(ui::VKEY_7); |
| PressAndReleaseKey(ui::VKEY_8); |
| PressAndReleaseKey(ui::VKEY_9); |
| PressAndReleaseKey(ui::VKEY_U); |
| PressAndReleaseKey(ui::VKEY_O); |
| PressAndReleaseKey(ui::VKEY_J); |
| PressAndReleaseKey(ui::VKEY_K); |
| PressAndReleaseKey(ui::VKEY_L); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| EXPECT_EQ(18u, CheckForKeyEvents().size()); |
| } |
| |
| TEST_F(MouseKeysTest, Dragging) { |
| // Enough time for the initial event and 9 updates. |
| constexpr auto kTenEventsInSeconds = |
| MouseKeysController::kUpdateFrequencyInSeconds * 9.5; |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| // No acceleration. |
| constexpr int kMaxSpeed = 3; |
| SetMaxSpeed(kMaxSpeed); |
| SetAcceleration(0); |
| |
| // Start Drag. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_M); |
| |
| // Bubble view with the correct message and icon should be displayed. |
| EXPECT_TRUE(IsBubbleVisible()); |
| EXPECT_EQ(GetBubbleText(), u"Press \".\" to release"); |
| EXPECT_TRUE(IsMouseDraggedIconVisible()); |
| EXPECT_FALSE(IsButtonChangeIconVisible()); |
| |
| auto* drag_event_rewriter = |
| Shell::Get()->mouse_keys_controller()->GetDragEventRewriterForTest(); |
| ASSERT_NE(drag_event_rewriter, nullptr); |
| ASSERT_TRUE(drag_event_rewriter->IsEnabled()); |
| |
| auto mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(1u, mouse_events.size()); |
| EXPECT_EQ(ui::EventType::kMousePressed, mouse_events[0].type()); |
| EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & mouse_events[0].flags()); |
| EXPECT_EQ(mouse_events[0].location(), kDefaultPosition); |
| |
| // Move right. |
| ClearEvents(); |
| PressKey(ui::VKEY_O); |
| EXPECT_TRUE(IsBubbleVisible()); |
| EXPECT_TRUE(IsMouseDraggedIconVisible()); |
| EXPECT_FALSE(IsButtonChangeIconVisible()); |
| task_environment()->FastForwardBy(base::Seconds(kTenEventsInSeconds)); |
| ReleaseKey(ui::VKEY_O); |
| mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(10u, mouse_events.size()); |
| gfx::Vector2d move_delta(kMoveDeltaDIP * kMaxSpeed, 0); |
| auto position = kDefaultPosition; |
| for (size_t i = 0; i < mouse_events.size(); ++i) { |
| position += move_delta; |
| EXPECT_EQ(ui::EventType::kMouseDragged, mouse_events[i].type()); |
| EXPECT_EQ(mouse_events[i].location(), position); |
| } |
| |
| // Stop Drag. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_OEM_PERIOD); |
| EXPECT_FALSE(IsBubbleVisible()); |
| ASSERT_FALSE(drag_event_rewriter->IsEnabled()); |
| mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(1u, mouse_events.size()); |
| EXPECT_EQ(ui::EventType::kMouseReleased, mouse_events[0].type()); |
| EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & mouse_events[0].flags()); |
| EXPECT_EQ(mouse_events[0].location(), position); |
| } |
| |
| TEST_F(MouseKeysTest, LeftHandDraggingBubble) { |
| SetEnabled(true); |
| SetLeftHanded(true); |
| ClearEvents(); |
| |
| // Start Drag. |
| PressAndReleaseKey(ui::VKEY_Z); |
| |
| // Bubble view with the correct message and icon should be displayed. |
| EXPECT_TRUE(IsBubbleVisible()); |
| EXPECT_EQ(GetBubbleText(), u"Press \"c\" to release"); |
| EXPECT_TRUE(IsMouseDraggedIconVisible()); |
| EXPECT_FALSE(IsButtonChangeIconVisible()); |
| } |
| |
| TEST_F(MouseKeysTest, DragWithClick) { |
| // Enough time for the initial event and 9 updates. |
| constexpr auto kTenEventsInSeconds = |
| MouseKeysController::kUpdateFrequencyInSeconds * 9.5; |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| // No acceleration. |
| constexpr int kMaxSpeed = 3; |
| SetMaxSpeed(kMaxSpeed); |
| SetAcceleration(0); |
| |
| // Start Drag. |
| ClearEvents(); |
| PressKey(ui::VKEY_I); |
| EXPECT_FALSE(IsBubbleVisible()); |
| auto mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(1u, mouse_events.size()); |
| EXPECT_EQ(ui::EventType::kMousePressed, mouse_events[0].type()); |
| EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & mouse_events[0].flags()); |
| EXPECT_EQ(mouse_events[0].location(), kDefaultPosition); |
| |
| // Move right. |
| ClearEvents(); |
| PressKey(ui::VKEY_O); |
| task_environment()->FastForwardBy(base::Seconds(kTenEventsInSeconds)); |
| ReleaseKey(ui::VKEY_O); |
| mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(10u, mouse_events.size()); |
| gfx::Vector2d move_delta(kMoveDeltaDIP * kMaxSpeed, 0); |
| auto position = kDefaultPosition; |
| for (size_t i = 0; i < mouse_events.size(); ++i) { |
| position += move_delta; |
| EXPECT_EQ(ui::EventType::kMouseDragged, mouse_events[i].type()); |
| EXPECT_EQ(mouse_events[i].location(), position); |
| } |
| |
| // Stop Drag. |
| ClearEvents(); |
| ReleaseKey(ui::VKEY_I); |
| EXPECT_FALSE(IsBubbleVisible()); |
| mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(1u, mouse_events.size()); |
| EXPECT_EQ(ui::EventType::kMouseReleased, mouse_events[0].type()); |
| EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & mouse_events[0].flags()); |
| EXPECT_EQ(mouse_events[0].location(), position); |
| } |
| |
| TEST_F(MouseKeysTest, DragWithMixed) { |
| // Enough time for the initial event and 9 updates. |
| constexpr auto kTenEventsInSeconds = |
| MouseKeysController::kUpdateFrequencyInSeconds * 9.5; |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| SetEnabled(true); |
| // No acceleration. |
| constexpr int kMaxSpeed = 3; |
| SetMaxSpeed(kMaxSpeed); |
| SetAcceleration(0); |
| |
| // Start Drag. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_M); |
| auto mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(1u, mouse_events.size()); |
| EXPECT_EQ(ui::EventType::kMousePressed, mouse_events[0].type()); |
| EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & mouse_events[0].flags()); |
| EXPECT_EQ(mouse_events[0].location(), kDefaultPosition); |
| |
| // Move right. |
| ClearEvents(); |
| PressKey(ui::VKEY_O); |
| task_environment()->FastForwardBy(base::Seconds(kTenEventsInSeconds)); |
| ReleaseKey(ui::VKEY_O); |
| mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(10u, mouse_events.size()); |
| gfx::Vector2d move_delta(kMoveDeltaDIP * kMaxSpeed, 0); |
| auto position = kDefaultPosition; |
| for (size_t i = 0; i < mouse_events.size(); ++i) { |
| position += move_delta; |
| EXPECT_EQ(ui::EventType::kMouseDragged, mouse_events[i].type()); |
| EXPECT_EQ(mouse_events[i].location(), position); |
| } |
| |
| // Stop Drag. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_I); |
| mouse_events = CheckForMouseEvents(); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ASSERT_EQ(1u, mouse_events.size()); |
| EXPECT_EQ(ui::EventType::kMouseReleased, mouse_events[0].type()); |
| EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & mouse_events[0].flags()); |
| EXPECT_EQ(mouse_events[0].location(), position); |
| } |
| |
| TEST_F(MouseKeysTest, Accelerator) { |
| SetEnabled(true); |
| auto* accelerator_controller = Shell::Get()->accelerator_controller(); |
| GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition, |
| kDefaultPosition); |
| |
| // Enable Mouse Keys, and we should be able to click by pressing i. |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| |
| // Toggle Mouse Keys off, and we should see no mouse events. |
| accelerator_controller->PerformActionIfEnabled( |
| AcceleratorAction::kToggleMouseKeys, {}); |
| |
| // Bubble view with the paused message and no icon should be displayed. |
| EXPECT_TRUE(IsBubbleVisible()); |
| EXPECT_EQ(GetBubbleText(), u"Mouse keys paused"); |
| EXPECT_FALSE(IsMouseDraggedIconVisible()); |
| EXPECT_FALSE(IsButtonChangeIconVisible()); |
| |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForMouseEvents().size()); |
| |
| // Toggle Mouse Keys on, and we should see the original behaviour. |
| accelerator_controller->PerformActionIfEnabled( |
| AcceleratorAction::kToggleMouseKeys, {}); |
| |
| // Bubble view with the resumed message and no icon should be displayed. |
| EXPECT_TRUE(IsBubbleVisible()); |
| EXPECT_EQ(GetBubbleText(), u"Mouse keys resumed"); |
| EXPECT_FALSE(IsMouseDraggedIconVisible()); |
| EXPECT_FALSE(IsButtonChangeIconVisible()); |
| |
| ClearEvents(); |
| PressAndReleaseKey(ui::VKEY_I); |
| EXPECT_EQ(0u, CheckForKeyEvents().size()); |
| ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON, |
| kDefaultPosition); |
| } |
| |
| } // namespace ash |