blob: 0d047d851a15764cec792c9a8db59ba561d288e5 [file] [log] [blame]
// 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 "ash/accessibility/disable_touchpad_event_rewriter.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/public/cpp/accessibility_controller_enums.h"
#include "ash/public/mojom/input_device_settings.mojom.h"
#include "ash/shell.h"
#include "ash/system/input_device_settings/input_device_settings_controller_impl.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/event.h"
#include "ui/wm/core/cursor_manager.h"
namespace ash {
namespace {
bool IsExternalDevice(const ui::InputDevice& device) {
return device.type == ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH ||
device.type == ui::InputDeviceType::INPUT_DEVICE_USB;
}
bool IsExternalMouseOrTouchpadConnected() {
ui::DeviceDataManager* device_data_manager =
ui::DeviceDataManager::GetInstance();
InputDeviceSettingsControllerImpl* input_device_settings_controller =
Shell::Get()->input_device_settings_controller();
// Check for external touchpads.
for (const auto& touchpad : device_data_manager->GetTouchpadDevices()) {
const mojom::Touchpad* found_device =
input_device_settings_controller->GetTouchpad(touchpad.id);
if (found_device != nullptr && IsExternalDevice(touchpad)) {
return true;
}
}
// Check for external mice.
for (const auto& mouse : device_data_manager->GetMouseDevices()) {
const mojom::Mouse* found_device =
input_device_settings_controller->GetMouse(mouse.id);
if (found_device != nullptr && IsExternalDevice(mouse)) {
return true;
}
}
return false;
}
int GetInternalTouchpadDeviceId() {
ui::DeviceDataManager* device_data_manager =
ui::DeviceDataManager::GetInstance();
InputDeviceSettingsControllerImpl* input_device_settings_controller =
Shell::Get()->input_device_settings_controller();
for (const auto& touchpad : device_data_manager->GetTouchpadDevices()) {
const mojom::Touchpad* found_device =
input_device_settings_controller->GetTouchpad(touchpad.id);
if (found_device != nullptr &&
touchpad.type == ui::InputDeviceType::INPUT_DEVICE_INTERNAL) {
return touchpad.id;
}
}
return ui::InputDevice::kInvalidId;
}
bool IsFromInternalTouchpad(const ui::Event& event) {
return event.source_device_id() == GetInternalTouchpadDeviceId();
}
constexpr base::TimeDelta kEnableTouchpadKeyPressWindow = base::Seconds(2);
} // namespace
DisableTouchpadEventRewriter::DisableTouchpadEventRewriter() {
Shell::Get()->accessibility_controller()->SetDisableTouchpadEventRewriter(
this);
}
DisableTouchpadEventRewriter::~DisableTouchpadEventRewriter() {
Shell::Get()->accessibility_controller()->SetDisableTouchpadEventRewriter(
nullptr);
}
void DisableTouchpadEventRewriter::SetEnabled(bool enabled) {
enabled_ = enabled;
}
bool DisableTouchpadEventRewriter::IsEnabled() {
return enabled_;
}
ui::EventDispatchDetails DisableTouchpadEventRewriter::RewriteEvent(
const ui::Event& event,
const Continuation continuation) {
if (!IsEnabled()) {
return SendEvent(continuation, &event);
}
if (event.IsKeyEvent()) {
HandleKeyEvent(event.AsKeyEvent());
}
if (event.IsMouseEvent() || event.IsScrollEvent()) {
return HandleMouseOrScrollEvent(event, continuation);
}
return SendEvent(continuation, &event);
}
ui::EventDispatchDetails DisableTouchpadEventRewriter::HandleMouseOrScrollEvent(
const ui::Event& event,
const Continuation continuation) {
DisableTouchpadMode disable_touchpad_mode =
Shell::Get()->accessibility_controller()->GetDisableTouchpadMode();
bool is_internal_touchpad_event = IsFromInternalTouchpad(event);
bool is_external_device_connected = IsExternalMouseOrTouchpadConnected();
switch (disable_touchpad_mode) {
case DisableTouchpadMode::kNever:
Shell::Get()->cursor_manager()->ShowCursor();
return SendEvent(continuation, &event);
case DisableTouchpadMode::kAlways:
if (is_internal_touchpad_event) {
Shell::Get()->cursor_manager()->HideCursor();
return DiscardEvent(continuation);
}
return SendEvent(continuation, &event);
case DisableTouchpadMode::kOnExternalMouseConnected:
if (is_internal_touchpad_event && is_external_device_connected) {
Shell::Get()->cursor_manager()->HideCursor();
return DiscardEvent(continuation);
}
return SendEvent(continuation, &event);
}
}
void DisableTouchpadEventRewriter::HandleKeyEvent(const ui::KeyEvent* event) {
if (event->type() != ui::EventType::kKeyReleased) {
return;
}
event->key_code() == ui::VKEY_SHIFT ? HandleShiftKeyRelease()
: ResetShiftKeyReleaseTracking();
}
void DisableTouchpadEventRewriter::HandleShiftKeyRelease() {
if (shift_release_count_ == 0) {
first_shift_release_time_ = ui::EventTimeForNow();
}
++shift_release_count_;
base::TimeDelta elapsed_time =
ui::EventTimeForNow() - first_shift_release_time_;
if (elapsed_time > kEnableTouchpadKeyPressWindow) {
ResetShiftKeyReleaseTracking();
return;
}
if (shift_release_count_ >= 5) {
SetEnabled(false);
Shell::Get()->accessibility_controller()->EnableInternalTouchpad();
ResetShiftKeyReleaseTracking();
}
}
void DisableTouchpadEventRewriter::ResetShiftKeyReleaseTracking() {
shift_release_count_ = 0;
first_shift_release_time_ = base::TimeTicks();
}
} // namespace ash