| // Copyright 2018 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/gfx/resources/view.h" |
| |
| #include <trace/event.h> |
| |
| #include "src/lib/fsl/handles/object_info.h" |
| #include "src/lib/fxl/logging.h" |
| #include "src/ui/scenic/lib/gfx/engine/engine.h" |
| #include "src/ui/scenic/lib/gfx/engine/object_linker.h" |
| #include "src/ui/scenic/lib/gfx/engine/session.h" |
| #include "src/ui/scenic/lib/gfx/resources/nodes/node.h" |
| #include "src/ui/scenic/lib/gfx/resources/view_holder.h" |
| #include "src/ui/scenic/lib/gfx/util/validate_eventpair.h" |
| #include "src/ui/scenic/lib/scenic/event_reporter.h" |
| |
| namespace scenic_impl { |
| namespace gfx { |
| |
| using fuchsia::ui::views::ViewHolderToken; |
| using fuchsia::ui::views::ViewRef; |
| using fuchsia::ui::views::ViewRefControl; |
| |
| const ResourceTypeInfo View::kTypeInfo = {ResourceType::kView, "View"}; |
| |
| View::View(Session* session, ResourceId id, ViewRefControl control_ref, ViewRef view_ref, |
| std::string debug_name, std::shared_ptr<ErrorReporter> error_reporter, |
| fxl::WeakPtr<ViewTreeUpdater> view_tree_updater, EventReporterWeakPtr event_reporter) |
| : Resource(session, session->id(), id, View::kTypeInfo), |
| control_ref_(std::move(control_ref)), |
| view_ref_(std::move(view_ref)), |
| view_ref_koid_(fsl::GetKoid(view_ref_.reference.get())), |
| error_reporter_(std::move(error_reporter)), |
| event_reporter_(event_reporter), |
| view_tree_updater_(view_tree_updater), |
| debug_name_(debug_name), |
| weak_factory_(this) { |
| FXL_DCHECK(error_reporter_); |
| FXL_DCHECK(view_ref_koid_ != ZX_KOID_INVALID); |
| |
| node_ = fxl::AdoptRef<ViewNode>(new ViewNode(session, session->id(), weak_factory_.GetWeakPtr())); |
| |
| { |
| TRACE_DURATION_BEGIN("gfx", "ResourceCtorViewRefClone"); |
| ViewRef clone; |
| fidl::Clone(view_ref_, &clone); |
| TRACE_DURATION_END("gfx", "ResourceCtorViewRefClone"); |
| |
| EventReporterWeakPtr reporter = event_reporter->GetWeakPtr(); |
| |
| fit::function<bool()> may_receive_focus = [view_ptr = GetWeakPtr()] { |
| if (view_ptr && view_ptr->view_holder_) { |
| return view_ptr->view_holder_->GetViewProperties().focus_change; |
| } |
| |
| // By default, a view may receive focus. |
| return true; |
| }; |
| |
| fit::function<std::optional<glm::mat4>()> global_transform = [weak_ptr = GetWeakPtr()] { |
| // Return the global transform if the view is still alive and attached to a scene. |
| return weak_ptr && weak_ptr->GetViewNode()->scene() |
| ? std::optional<glm::mat4>{weak_ptr->GetViewNode()->GetGlobalTransform()} |
| : std::nullopt; |
| }; |
| |
| fit::function<void(ViewHolderPtr)> create_callback = |
| [weak_ptr = GetWeakPtr()](ViewHolderPtr annotation_view_holder) { |
| FXL_CHECK(weak_ptr); |
| FXL_DCHECK(annotation_view_holder); |
| weak_ptr->AddAnnotationViewHolder(annotation_view_holder); |
| |
| // If View has valid properties, initialize ViewProperties for the |
| // annotation ViewHolder, otherwise we will defer it until the View |
| // is attached to Scene. We inherit the parent View's bounding box and |
| // inset, but suppress all focus changes and hit testing behaviors. |
| ViewHolder* view_holder = weak_ptr->view_holder(); |
| if (view_holder && |
| !fidl::Equals(view_holder->GetViewProperties(), fuchsia::ui::gfx::ViewProperties())) { |
| auto annotation_view_properties = view_holder->GetViewProperties(); |
| annotation_view_properties.focus_change = false; |
| annotation_view_holder->SetViewProperties(annotation_view_properties, |
| weak_ptr->error_reporter_.get()); |
| } |
| }; |
| |
| FXL_DCHECK(session->id() != 0u) << "GFX-side invariant for ViewTree"; |
| if (view_tree_updater_) { |
| view_tree_updater_->AddUpdate( |
| ViewTreeNewRefNode{.view_ref = std::move(clone), |
| .event_reporter = std::move(reporter), |
| .may_receive_focus = std::move(may_receive_focus), |
| .global_transform = std::move(global_transform), |
| .add_annotation_view_holder = std::move(create_callback), |
| .session_id = session->id()}); |
| } |
| } |
| |
| FXL_DCHECK(validate_viewref(control_ref_, view_ref_)); |
| } |
| |
| View::~View() { |
| if (view_tree_updater_) { |
| view_tree_updater_->AddUpdate(ViewTreeDeleteNode({.koid = view_ref_koid_})); |
| } |
| |
| // Explicitly detach the phantom node to ensure it is cleaned up. |
| node_->Detach(error_reporter_.get()); |
| } |
| |
| void View::Connect(ViewLinker::ImportLink link) { |
| FXL_DCHECK(!link_); |
| FXL_DCHECK(link.valid()); |
| FXL_DCHECK(!link.initialized()); |
| |
| link_ = std::move(link); |
| link_->Initialize(fit::bind_member(this, &View::LinkResolved), |
| fit::bind_member(this, &View::LinkInvalidated)); |
| } |
| |
| void View::SignalRender() { |
| if (!render_handle_) { |
| return; |
| } |
| |
| // Verify the render_handle_ is still valid before attempting to signal it. |
| if (zx_object_get_info(render_handle_, ZX_INFO_HANDLE_VALID, /*buffer=*/NULL, |
| /*buffer_size=*/0, /*actual=*/NULL, |
| /*avail=*/NULL) == ZX_OK) { |
| zx_status_t status = zx_object_signal(render_handle_, /*clear_mask=*/0u, ZX_EVENT_SIGNALED); |
| ZX_ASSERT(status == ZX_OK); |
| } |
| } |
| |
| zx_koid_t View::view_ref_koid() const { return view_ref_koid_; } |
| |
| void View::LinkResolved(ViewHolder* view_holder) { |
| FXL_DCHECK(!view_holder_); |
| FXL_DCHECK(view_holder); |
| view_holder_ = view_holder; |
| |
| // Attaching our node to the holder should never fail. |
| FXL_CHECK(view_holder_->AddChild(node_, ErrorReporter::Default().get())) |
| << "View::LinkResolved(): error while adding ViewNode as child of ViewHolder"; |
| |
| SendViewHolderConnectedEvent(); |
| |
| if (view_tree_updater_) { |
| view_tree_updater_->AddUpdate(ViewTreeConnectToParent{ |
| .child = view_ref_koid_, .parent = view_holder_->view_holder_koid()}); |
| } |
| } |
| |
| void View::LinkInvalidated(bool on_link_destruction) { |
| // The link is only destroyed when this View is being destroyed, and therefore all cleanup can |
| // be skipped anyway. |
| if (on_link_destruction) { |
| return; |
| } |
| |
| // The connection ViewHolder no longer exists, detach the phantom node from |
| // the ViewHolder. |
| node_->Detach(error_reporter_.get()); |
| |
| view_holder_ = nullptr; |
| // ViewHolder was disconnected. There are no guarantees on liveness of the |
| // render event, so invalidate the handle. |
| InvalidateRenderEventHandle(); |
| |
| SendViewHolderDisconnectedEvent(); |
| |
| if (view_tree_updater_) { |
| view_tree_updater_->AddUpdate(ViewTreeDisconnectFromParent{.koid = view_ref_koid_}); |
| } |
| } |
| |
| void View::SendViewHolderConnectedEvent() { |
| if (event_reporter_) { |
| fuchsia::ui::gfx::Event event; |
| event.set_view_holder_connected({.view_id = id()}); |
| event_reporter_->EnqueueEvent(std::move(event)); |
| } |
| } |
| |
| void View::SendViewHolderDisconnectedEvent() { |
| if (event_reporter_) { |
| fuchsia::ui::gfx::Event event; |
| event.set_view_holder_disconnected({.view_id = id()}); |
| event_reporter_->EnqueueEvent(std::move(event)); |
| } |
| } |
| |
| void View::BroadcastViewPropertiesChangedEvent(fuchsia::ui::gfx::ViewProperties view_properties) { |
| // Update annotation ViewHolders' properties. |
| // Focus changes are always suppressed. |
| for (const ViewHolderPtr& annotation_view_holder : annotation_view_holders()) { |
| auto new_annotation_view_properties = view_properties; |
| new_annotation_view_properties.focus_change = false; |
| annotation_view_holder->SetViewProperties(new_annotation_view_properties, |
| error_reporter_.get()); |
| } |
| } |
| |
| void View::OnAnnotationViewHolderDestroyed(ViewHolder* view_holder) { |
| RemoveAnnotationViewHolder(fxl::Ref(view_holder)); |
| } |
| |
| bool View::AddAnnotationViewHolder(ViewHolderPtr view_holder) { |
| if (annotation_view_holders_.find(view_holder) != annotation_view_holders_.end()) { |
| return false; |
| } |
| // |view_holder| doesn't exist, add it to the set. |
| GetViewNode()->AddChild(view_holder, error_reporter_.get()); |
| view_holder->SetOnDestroyedCallback( |
| [view_weak_ptr = GetWeakPtr(), view_holder = view_holder.get()]() { |
| // View may be destroyed earlier, so we check the validity of WeakPtr first. |
| if (view_weak_ptr) { |
| view_weak_ptr->OnAnnotationViewHolderDestroyed(view_holder); |
| } |
| }); |
| annotation_view_holders_.insert(std::move(view_holder)); |
| return true; |
| } |
| |
| bool View::RemoveAnnotationViewHolder(ViewHolderPtr view_holder) { |
| if (annotation_view_holders_.find(view_holder) == annotation_view_holders_.end()) { |
| return false; |
| } |
| // |view_holder| exists, remove it from the set. |
| view_holder->Detach(error_reporter_.get()); |
| annotation_view_holders_.erase(view_holder); |
| return true; |
| } |
| |
| } // namespace gfx |
| } // namespace scenic_impl |