blob: 51ca83c6d733f521f075f2e8773b6e80e10569db [file] [log] [blame]
// Copyright 2022 The Fuchsia 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 "src/ui/scenic/lib/input/mouse_system.h"
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include "src/lib/fsl/handles/object_info.h"
#include "src/ui/scenic/lib/input/internal_pointer_event.h"
#include "src/ui/scenic/lib/input/mouse_source.h"
#include "src/ui/scenic/lib/utils/helpers.h"
#include "src/ui/scenic/lib/utils/math.h"
#include <glm/glm.hpp>
namespace scenic_impl::input {
MouseSystem::MouseSystem(sys::ComponentContext* context,
std::shared_ptr<const view_tree::Snapshot>& view_tree_snapshot,
HitTester& hit_tester, fit::function<void(zx_koid_t)> request_focus)
: view_tree_snapshot_(view_tree_snapshot),
hit_tester_(hit_tester),
request_focus_(std::move(request_focus)) {}
void MouseSystem::RegisterMouseSource(
fidl::InterfaceRequest<fuchsia::ui::pointer::MouseSource> mouse_source_request,
zx_koid_t client_view_ref_koid) {
const auto [it, success] = mouse_sources_.emplace(
client_view_ref_koid,
std::make_unique<MouseSource>(std::move(mouse_source_request),
/*error_handler*/ [this, client_view_ref_koid] {
mouse_sources_.erase(client_view_ref_koid);
}));
FX_DCHECK(success);
}
zx_koid_t MouseSystem::FindViewRefKoidOfRelatedChannel(
const fidl::InterfaceHandle<fuchsia::ui::pointer::MouseSource>& original) const {
const zx_koid_t related_koid = fsl::GetRelatedKoid(original.channel().get());
const auto it = std::find_if(
mouse_sources_.begin(), mouse_sources_.end(),
[related_koid](const auto& kv) { return kv.second->channel_koid() == related_koid; });
return it == mouse_sources_.end() ? ZX_KOID_INVALID : it->first;
}
void MouseSystem::SendEventToMouse(zx_koid_t receiver, const InternalMouseEvent& event,
const StreamId stream_id, bool view_exit) {
const auto it = mouse_sources_.find(receiver);
if (it != mouse_sources_.end()) {
if (view_exit) {
// Bounding box and correct transform does not matter on view exit (since we don't send any
// pointer samples), and we are likely working with a broken ViewTree, so skip them.
it->second->UpdateStream(stream_id, event, {}, view_exit);
} else {
it->second->UpdateStream(
stream_id, EventWithReceiverFromViewportTransform(event, receiver, *view_tree_snapshot_),
view_tree_snapshot_->view_tree.at(receiver).bounding_box, view_exit);
}
}
}
void MouseSystem::InjectMouseEventExclusive(const InternalMouseEvent& event,
const StreamId stream_id) {
FX_DCHECK(view_tree_snapshot_->IsDescendant(event.target, event.context))
<< "Should never allow injection into broken scene graph";
FX_DCHECK(current_exclusive_mouse_receivers_.count(stream_id) == 0 ||
current_exclusive_mouse_receivers_.at(stream_id) == event.target);
current_exclusive_mouse_receivers_[stream_id] = event.target;
SendEventToMouse(event.target, event, stream_id, /*view_exit=*/false);
}
void MouseSystem::InjectMouseEventHitTested(const InternalMouseEvent& event,
const StreamId stream_id) {
FX_DCHECK(view_tree_snapshot_->IsDescendant(event.target, event.context))
<< "Should never allow injection into broken scene graph";
// Grab the current mouse receiver or create a new one.
MouseReceiver& mouse_receiver = current_mouse_receivers_[stream_id];
// Unlatch a current latch if all buttons are released.
const bool button_down = !event.buttons.pressed.empty();
mouse_receiver.latched = mouse_receiver.latched && button_down;
// If the scene graph breaks while latched -> send a "View Exited" event and invalidate the
// receiver for the remainder of the latch.
if (mouse_receiver.latched &&
!view_tree_snapshot_->IsDescendant(mouse_receiver.view_koid, event.target) &&
mouse_receiver.view_koid != event.target) {
SendEventToMouse(mouse_receiver.view_koid, event, stream_id, /*view_exit=*/true);
mouse_receiver.view_koid = ZX_KOID_INVALID;
return;
}
// If not latched, choose the current target by finding the top view.
if (!mouse_receiver.latched) {
const zx_koid_t top_koid = hit_tester_.TopHitTest(event, /*semantic_hit_test*/ false);
// Determine the currently hovered view. If it's different than previously, send the
// previous one a "View Exited" event.
if (mouse_receiver.view_koid != top_koid) {
SendEventToMouse(mouse_receiver.view_koid, event, stream_id, /*view_exit=*/true);
}
mouse_receiver.view_koid = top_koid;
// Button down on an unlatched stream -> latch it to the top-most view.
if (button_down) {
mouse_receiver.latched = true;
request_focus_(mouse_receiver.view_koid);
}
}
// Finally, send the event to the hovered/latched view.
SendEventToMouse(mouse_receiver.view_koid, event, stream_id, /*view_exit=*/false);
}
void MouseSystem::CancelMouseStream(StreamId stream_id) {
zx_koid_t receiver = ZX_KOID_INVALID;
{
const auto it = current_mouse_receivers_.find(stream_id);
if (it != current_mouse_receivers_.end()) {
receiver = it->second.view_koid;
current_mouse_receivers_.erase(it);
}
}
{
const auto it = current_exclusive_mouse_receivers_.find(stream_id);
if (it != current_exclusive_mouse_receivers_.end()) {
receiver = it->second;
current_exclusive_mouse_receivers_.erase(it);
}
}
const auto it = mouse_sources_.find(receiver);
if (it != mouse_sources_.end()) {
it->second->UpdateStream(stream_id, {}, {}, /*view_exit=*/true);
}
}
} // namespace scenic_impl::input