910e62b5创建于 1月15日历史提交
// 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