blob: 2b46ac4c57cca93febe4c1a6058fc1f45b843509 [file] [log] [blame]
// Copyright 2015 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/mus/accelerators/accelerator_registrar_impl.h"
#include <stdint.h>
#include <utility>
#include "ash/common/accelerators/accelerator_controller.h"
#include "ash/common/wm_shell.h"
#include "ash/mus/accelerators/accelerator_ids.h"
#include "ash/mus/root_window_controller.h"
#include "ash/mus/window_manager.h"
#include "base/bind.h"
#include "services/ui/public/cpp/window_manager_delegate.h"
#include "ui/events/event.h"
namespace ash {
namespace mus {
namespace {
void CallAddAcceleratorCallback(
const ui::mojom::AcceleratorRegistrar::AddAcceleratorCallback& callback,
bool result) {
callback.Run(result);
}
// Returns true if |event_matcher| corresponds to matching an Accelerator.
bool IsMatcherForKeyAccelerator(const ui::mojom::EventMatcher& event_matcher) {
return (
event_matcher.accelerator_phase ==
ui::mojom::AcceleratorPhase::PRE_TARGET &&
event_matcher.type_matcher &&
(event_matcher.type_matcher->type == ui::mojom::EventType::KEY_PRESSED ||
event_matcher.type_matcher->type ==
ui::mojom::EventType::KEY_RELEASED) &&
event_matcher.key_matcher && event_matcher.flags_matcher);
}
// Converts |event_matcher| into the ui::Accelerator it matches. Only valid if
// IsMatcherForKeyAccelerator() returns true.
ui::Accelerator EventMatcherToAccelerator(
const ui::mojom::EventMatcher& event_matcher) {
DCHECK(IsMatcherForKeyAccelerator(event_matcher));
ui::Accelerator accelerator(
static_cast<ui::KeyboardCode>(event_matcher.key_matcher->keyboard_code),
event_matcher.flags_matcher->flags);
accelerator.set_type(event_matcher.type_matcher->type ==
ui::mojom::EventType::KEY_PRESSED
? ui::ET_KEY_PRESSED
: ui::ET_KEY_RELEASED);
return accelerator;
}
} // namespace
AcceleratorRegistrarImpl::AcceleratorRegistrarImpl(
WindowManager* window_manager,
uint16_t accelerator_namespace,
mojo::InterfaceRequest<AcceleratorRegistrar> request,
const DestroyCallback& destroy_callback)
: window_manager_(window_manager),
binding_(this, std::move(request)),
accelerator_namespace_(accelerator_namespace),
destroy_callback_(destroy_callback) {
window_manager_->AddObserver(this);
window_manager_->AddAcceleratorHandler(accelerator_namespace_, this);
binding_.set_connection_error_handler(base::Bind(
&AcceleratorRegistrarImpl::OnBindingGone, base::Unretained(this)));
}
void AcceleratorRegistrarImpl::Destroy() {
delete this;
}
bool AcceleratorRegistrarImpl::OwnsAccelerator(uint32_t accelerator_id) const {
return !!accelerators_.count(accelerator_id);
}
void AcceleratorRegistrarImpl::ProcessAccelerator(uint32_t accelerator_id,
const ui::Event& event) {
DCHECK(OwnsAccelerator(accelerator_id));
// TODO(moshayedi): crbug.com/617167. Don't clone even once we map
// mojom::Event directly to ui::Event.
accelerator_handler_->OnAccelerator(accelerator_id & kLocalIdMask,
ui::Event::Clone(event));
}
AcceleratorRegistrarImpl::~AcceleratorRegistrarImpl() {
window_manager_->RemoveAcceleratorHandler(accelerator_namespace_);
window_manager_->RemoveObserver(this);
RemoveAllAccelerators();
destroy_callback_.Run(this);
}
void AcceleratorRegistrarImpl::OnBindingGone() {
binding_.Unbind();
// If there's no outstanding accelerators for this connection, then destroy
// it.
if (accelerators_.empty() && keyboard_accelerator_to_id_.empty())
delete this;
}
void AcceleratorRegistrarImpl::OnHandlerGone() {
// The handler is dead. If AcceleratorRegistrar connection is also closed,
// then destroy this. Otherwise, remove all the accelerators, but keep the
// AcceleratorRegistrar connection alive (the client could still set another
// handler and install new accelerators).
if (!binding_.is_bound()) {
delete this;
return;
}
accelerator_handler_.reset();
RemoveAllAccelerators();
}
void AcceleratorRegistrarImpl::RemoveAllAccelerators() {
for (uint32_t accelerator : accelerators_)
window_manager_->window_manager_client()->RemoveAccelerator(accelerator);
WmShell::Get()->accelerator_controller()->UnregisterAll(this);
keyboard_accelerator_to_id_.clear();
id_to_keyboard_accelerator_.clear();
accelerators_.clear();
}
bool AcceleratorRegistrarImpl::AddAcceleratorForKeyBinding(
uint32_t accelerator_id,
const ui::mojom::EventMatcher& matcher,
const AddAcceleratorCallback& callback) {
if (!IsMatcherForKeyAccelerator(matcher))
return false;
const ui::Accelerator accelerator = EventMatcherToAccelerator(matcher);
if (keyboard_accelerator_to_id_.count(accelerator)) {
callback.Run(false);
return true;
}
AcceleratorController* accelerator_controller =
WmShell::Get()->accelerator_controller();
// TODO(sky): reenable this when we decide on the future of AppDriver.
// http://crbug.com/631836.
/*
if (accelerator_controller->IsRegistered(accelerator)) {
DVLOG(1) << "Attempt to register accelerator that is already registered";
callback.Run(false);
// Even though we're not registering the accelerator it's a key that should
// be handled as an accelerator, so return true.
return true;
}
*/
const uint16_t local_id = GetAcceleratorLocalId(accelerator_id);
keyboard_accelerator_to_id_[accelerator] = local_id;
id_to_keyboard_accelerator_[local_id] = accelerator;
accelerator_controller->Register(accelerator, this);
callback.Run(true);
return true;
}
void AcceleratorRegistrarImpl::SetHandler(
ui::mojom::AcceleratorHandlerPtr handler) {
accelerator_handler_ = std::move(handler);
accelerator_handler_.set_connection_error_handler(base::Bind(
&AcceleratorRegistrarImpl::OnHandlerGone, base::Unretained(this)));
}
void AcceleratorRegistrarImpl::AddAccelerator(
uint32_t accelerator_id,
ui::mojom::EventMatcherPtr matcher,
const AddAcceleratorCallback& callback) {
if (!accelerator_handler_ || accelerator_id > 0xFFFF) {
// The |accelerator_id| is too large, and it can't be handled correctly.
callback.Run(false);
DVLOG(1) << "AddAccelerator failed because of bogus id";
return;
}
uint32_t namespaced_accelerator_id = ComputeAcceleratorId(
accelerator_namespace_, static_cast<uint16_t>(accelerator_id));
if (accelerators_.count(namespaced_accelerator_id) ||
id_to_keyboard_accelerator_.count(
GetAcceleratorLocalId(namespaced_accelerator_id))) {
callback.Run(false);
DVLOG(1) << "AddAccelerator failed because id already in use "
<< accelerator_id;
return;
}
if (AddAcceleratorForKeyBinding(accelerator_id, *matcher, callback))
return;
accelerators_.insert(namespaced_accelerator_id);
window_manager_->window_manager_client()->AddAccelerator(
namespaced_accelerator_id, std::move(matcher),
base::Bind(&CallAddAcceleratorCallback, callback));
}
void AcceleratorRegistrarImpl::RemoveAccelerator(uint32_t accelerator_id) {
if (accelerator_id > 0xFFFF)
return;
const uint16_t local_id = GetAcceleratorLocalId(accelerator_id);
auto iter = id_to_keyboard_accelerator_.find(local_id);
if (iter != id_to_keyboard_accelerator_.end()) {
WmShell::Get()->accelerator_controller()->Unregister(iter->second, this);
keyboard_accelerator_to_id_.erase(iter->second);
id_to_keyboard_accelerator_.erase(iter);
return;
}
uint32_t namespaced_accelerator_id = ComputeAcceleratorId(
accelerator_namespace_, static_cast<uint16_t>(accelerator_id));
if (accelerators_.erase(namespaced_accelerator_id) == 0)
return;
window_manager_->window_manager_client()->RemoveAccelerator(
namespaced_accelerator_id);
// If the registrar is not bound anymore (i.e. the client can no longer
// install new accelerators), and the last accelerator has been removed, then
// there's no point keeping this alive anymore.
if (accelerators_.empty() && keyboard_accelerator_to_id_.empty() &&
!binding_.is_bound()) {
delete this;
}
}
ui::mojom::EventResult AcceleratorRegistrarImpl::OnAccelerator(
uint32_t id,
const ui::Event& event) {
if (OwnsAccelerator(id))
ProcessAccelerator(id, event);
return ui::mojom::EventResult::HANDLED;
}
void AcceleratorRegistrarImpl::OnWindowTreeClientDestroyed() {
delete this;
}
bool AcceleratorRegistrarImpl::AcceleratorPressed(
const ui::Accelerator& accelerator) {
auto iter = keyboard_accelerator_to_id_.find(accelerator);
DCHECK(iter != keyboard_accelerator_to_id_.end());
const ui::KeyEvent key_event(accelerator.type(), accelerator.key_code(),
accelerator.modifiers());
// TODO(moshayedi): crbug.com/617167. Don't clone even once we map
// mojom::Event directly to ui::Event.
accelerator_handler_->OnAccelerator(iter->second,
ui::Event::Clone(key_event));
return true;
}
bool AcceleratorRegistrarImpl::CanHandleAccelerators() const {
return true;
}
} // namespace mus
} // namespace ash