blob: 86655e05ab901c679a2a42105d5d76388bc85731 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/events/switch_access_event_handler.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/public/cpp/switch_access_event_handler_delegate.h"
#include "ash/shell.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_utils.h"
#include "ui/events/types/event_type.h"
namespace ash {
namespace {
bool IsSwitchAccessEnabled() {
return Shell::Get()->accessibility_controller()->switch_access_enabled();
}
void CancelEvent(ui::Event* event) {
DCHECK(event);
if (event->cancelable()) {
event->SetHandled();
event->StopPropagation();
}
}
} // namespace
SwitchAccessEventHandler::SwitchAccessEventHandler(
SwitchAccessEventHandlerDelegate* delegate)
: delegate_(delegate) {
DCHECK(delegate_);
Shell::Get()->AddPreTargetHandler(this,
ui::EventTarget::Priority::kAccessibility);
}
SwitchAccessEventHandler::~SwitchAccessEventHandler() {
Shell::Get()->RemovePreTargetHandler(this);
}
bool SwitchAccessEventHandler::SetKeyCodesForCommand(
std::set<int> new_key_codes,
SwitchAccessCommand command) {
bool has_changed = false;
std::set<int> to_clear;
// Clear old values that conflict with the new assignment.
for (const auto& val : command_for_key_code_) {
int old_key_code = val.first;
SwitchAccessCommand old_command = val.second;
if (new_key_codes.count(old_key_code) > 0) {
if (old_command != command) {
has_changed = true;
// Modifying the map while iterating through it causes reference
// failures.
to_clear.insert(old_key_code);
} else {
new_key_codes.erase(old_key_code);
}
continue;
}
// This value was previously mapped to the command, but is no longer.
if (old_command == command) {
has_changed = true;
to_clear.insert(old_key_code);
key_codes_to_capture_.erase(old_key_code);
}
}
for (int key_code : to_clear) {
command_for_key_code_.erase(key_code);
}
if (new_key_codes.size() == 0)
return has_changed;
// Add any new key codes to the map.
for (int key_code : new_key_codes) {
key_codes_to_capture_.insert(key_code);
command_for_key_code_[key_code] = command;
}
return true;
}
void SwitchAccessEventHandler::OnKeyEvent(ui::KeyEvent* event) {
DCHECK(IsSwitchAccessEnabled());
DCHECK(event);
if (ShouldCancelEvent(*event)) {
CancelEvent(event);
if (ShouldForwardEvent(*event)) {
SwitchAccessCommand command = command_for_key_code_[event->key_code()];
delegate_->SendSwitchAccessCommand(command);
}
}
}
bool SwitchAccessEventHandler::ShouldCancelEvent(
const ui::KeyEvent& event) const {
// Ignore virtual key events so users can type with the onscreen keyboard.
if (ignore_virtual_key_events_ &&
event.source_device_id() == ui::ED_UNKNOWN_DEVICE) {
// When running Chrome OS on Linux, the source_device_id property is never
// populated.
auto* properties = event.properties();
bool is_linux_xevent =
properties &&
properties->find(ui::kPropertyKeyboardHwKeyCode) != properties->end();
if (!is_linux_xevent)
return false;
}
if (forward_key_events_)
return true;
return key_codes_to_capture_.count(event.key_code()) > 0;
}
// Returns whether to forward an event, assuming that ShouldCancelEvent(event)
// returns true.
bool SwitchAccessEventHandler::ShouldForwardEvent(
const ui::KeyEvent& event) const {
return event.type() == ui::ET_KEY_PRESSED;
}
} // namespace ash