| // Copyright 2015 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 "garnet/bin/ui/root_presenter/app.h" |
| |
| #include <algorithm> |
| #include <cstdlib> |
| #include <string> |
| |
| #include <fuchsia/ui/input/cpp/fidl.h> |
| |
| #include "lib/component/cpp/connect.h" |
| #include "lib/fidl/cpp/clone.h" |
| #include "lib/fxl/files/file.h" |
| #include "lib/fxl/functional/make_copyable.h" |
| #include "lib/fxl/logging.h" |
| #include "lib/ui/input/cpp/formatting.h" |
| |
| namespace root_presenter { |
| |
| App::App(const fxl::CommandLine& command_line) |
| : startup_context_(component::StartupContext::CreateFromStartupInfo()), |
| input_reader_(this) { |
| FXL_DCHECK(startup_context_); |
| |
| input_reader_.Start(); |
| |
| startup_context_->outgoing().AddPublicService( |
| presenter_bindings_.GetHandler(this)); |
| startup_context_->outgoing().AddPublicService( |
| presenter2_bindings_.GetHandler(this)); |
| startup_context_->outgoing().AddPublicService( |
| input_receiver_bindings_.GetHandler(this)); |
| } |
| |
| App::~App() {} |
| |
| Presentation::YieldCallback App::GetYieldCallback() { |
| return fxl::MakeCopyable([this](bool yield_to_next) { |
| if (yield_to_next) { |
| SwitchToNextPresentation(); |
| } else { |
| SwitchToPreviousPresentation(); |
| } |
| }); |
| } |
| |
| Presentation::ShutdownCallback App::GetShutdownCallback( |
| Presentation* presentation) { |
| return fxl::MakeCopyable([this, presentation] { |
| size_t idx; |
| for (idx = 0; idx < presentations_.size(); ++idx) { |
| if (presentations_[idx].get() == presentation) { |
| break; |
| } |
| } |
| FXL_DCHECK(idx != presentations_.size()); |
| |
| if (idx == active_presentation_idx_) { |
| // This works fine when idx == 0, because the previous idx chosen will |
| // also be 0, and it will be an no-op within SwitchToPreviousPresentation. |
| // Finally, at the end of the callback, everything will be cleaned up. |
| SwitchToPreviousPresentation(); |
| } |
| |
| presentations_.erase(presentations_.begin() + idx); |
| if (idx < active_presentation_idx_) { |
| // Adjust index into presentations_. |
| active_presentation_idx_--; |
| } |
| |
| if (presentations_.empty()) { |
| layer_stack_->RemoveAllLayers(); |
| active_presentation_idx_ = std::numeric_limits<size_t>::max(); |
| } |
| }); |
| } |
| |
| void App::Present2(zx::eventpair view_holder_token, |
| fidl::InterfaceRequest<fuchsia::ui::policy::Presentation> |
| presentation_request) { |
| InitializeServices(); |
| |
| // Duplication intentional, this copy will go away soon. |
| int32_t display_startup_rotation_adjustment = 0; |
| { |
| std::string rotation_value; |
| if (files::ReadFileToString("/system/data/root_presenter/display_rotation", |
| &rotation_value)) { |
| display_startup_rotation_adjustment = atoi(rotation_value.c_str()); |
| FXL_LOG(INFO) << "Display rotation adjustment applied: " |
| << display_startup_rotation_adjustment << " degrees."; |
| } |
| } |
| |
| auto presentation = std::make_unique<Presentation1>( |
| scenic_.get(), session_.get(), compositor_->id(), |
| std::move(view_holder_token), renderer_params_, |
| display_startup_rotation_adjustment, startup_context_.get()); |
| presentation->Present(std::move(presentation_request), GetYieldCallback(), |
| GetShutdownCallback(presentation.get())); |
| |
| AddPresentation(std::move(presentation)); |
| } |
| |
| void App::PresentView( |
| zx::eventpair view_holder_token, |
| ::fidl::InterfaceRequest<fuchsia::ui::policy::Presentation> |
| presentation_request) { |
| InitializeServices(); |
| |
| int32_t display_startup_rotation_adjustment = 0; |
| { |
| std::string rotation_value; |
| if (files::ReadFileToString("/system/data/root_presenter/display_rotation", |
| &rotation_value)) { |
| display_startup_rotation_adjustment = atoi(rotation_value.c_str()); |
| FXL_LOG(INFO) << "Display rotation adjustment applied: " |
| << display_startup_rotation_adjustment << " degrees."; |
| } |
| } |
| |
| auto presentation = std::make_unique<Presentation2>( |
| scenic_.get(), session_.get(), compositor_->id(), |
| std::move(view_holder_token), renderer_params_, |
| display_startup_rotation_adjustment); |
| presentation->PresentView(std::move(presentation_request), GetYieldCallback(), |
| GetShutdownCallback(presentation.get())); |
| AddPresentation(std::move(presentation)); |
| } |
| |
| void App::AddPresentation(std::unique_ptr<Presentation> presentation) { |
| for (auto& it : devices_by_id_) { |
| presentation->OnDeviceAdded(it.second.get()); |
| } |
| |
| presentations_.push_back(std::move(presentation)); |
| SwitchToPresentation(presentations_.size() - 1); |
| } |
| |
| void App::HACK_SetRendererParams( |
| bool enable_clipping, |
| ::std::vector<fuchsia::ui::gfx::RendererParam> params) { |
| renderer_params_.clipping_enabled = enable_clipping; |
| FXL_LOG(INFO) |
| << "Presenter::HACK_SetRendererParams: Setting clipping enabled to " |
| << (enable_clipping ? "true" : "false"); |
| for (auto& param : params) { |
| switch (param.Which()) { |
| case ::fuchsia::ui::gfx::RendererParam::Tag::kShadowTechnique: |
| renderer_params_.shadow_technique = param.shadow_technique(); |
| FXL_LOG(INFO) |
| << "Presenter::HACK_SetRendererParams: Setting shadow technique to " |
| << fidl::ToUnderlying(param.shadow_technique()); |
| continue; |
| case fuchsia::ui::gfx::RendererParam::Tag::kRenderFrequency: |
| renderer_params_.render_frequency = param.render_frequency(); |
| FXL_LOG(INFO) |
| << "Presenter::HACK_SetRendererParams: Setting render frequency to " |
| << fidl::ToUnderlying(param.render_frequency()); |
| continue; |
| case fuchsia::ui::gfx::RendererParam::Tag::Invalid: |
| continue; |
| } |
| } |
| for (const auto& presentation : presentations_) { |
| presentation->OverrideRendererParams(renderer_params_); |
| } |
| } |
| |
| void App::SwitchToPresentation(const size_t presentation_idx) { |
| FXL_DCHECK(presentation_idx < presentations_.size()); |
| if (presentation_idx == active_presentation_idx_) { |
| return; |
| } |
| active_presentation_idx_ = presentation_idx; |
| layer_stack_->RemoveAllLayers(); |
| layer_stack_->AddLayer(presentations_[presentation_idx]->layer()); |
| session_->Present(0, [](fuchsia::images::PresentationInfo info) {}); |
| } |
| |
| void App::SwitchToNextPresentation() { |
| SwitchToPresentation((active_presentation_idx_ + 1) % presentations_.size()); |
| } |
| |
| void App::SwitchToPreviousPresentation() { |
| SwitchToPresentation((active_presentation_idx_ + presentations_.size() - 1) % |
| presentations_.size()); |
| } |
| |
| void App::RegisterDevice(fuchsia::ui::input::DeviceDescriptor descriptor, |
| fidl::InterfaceRequest<fuchsia::ui::input::InputDevice> |
| input_device_request) { |
| uint32_t device_id = ++next_device_token_; |
| |
| FXL_VLOG(1) << "RegisterDevice " << device_id << " " << descriptor; |
| std::unique_ptr<mozart::InputDeviceImpl> input_device = |
| std::make_unique<mozart::InputDeviceImpl>( |
| device_id, std::move(descriptor), std::move(input_device_request), |
| this); |
| |
| for (auto& presentation : presentations_) { |
| presentation->OnDeviceAdded(input_device.get()); |
| } |
| |
| devices_by_id_.emplace(device_id, std::move(input_device)); |
| } |
| |
| void App::OnDeviceDisconnected(mozart::InputDeviceImpl* input_device) { |
| if (devices_by_id_.count(input_device->id()) == 0) |
| return; |
| |
| FXL_VLOG(1) << "UnregisterDevice " << input_device->id(); |
| |
| for (auto& presentation : presentations_) { |
| presentation->OnDeviceRemoved(input_device->id()); |
| } |
| devices_by_id_.erase(input_device->id()); |
| } |
| |
| void App::OnReport(mozart::InputDeviceImpl* input_device, |
| fuchsia::ui::input::InputReport report) { |
| FXL_VLOG(3) << "OnReport from " << input_device->id() << " " << report; |
| if (devices_by_id_.count(input_device->id()) == 0 || |
| presentations_.size() == 0) |
| return; |
| |
| FXL_DCHECK(active_presentation_idx_ < presentations_.size()); |
| FXL_VLOG(3) << "OnReport to " << active_presentation_idx_; |
| |
| // Input events are only reported to the active presentation. |
| presentations_[active_presentation_idx_]->OnReport(input_device->id(), |
| std::move(report)); |
| } |
| |
| void App::InitializeServices() { |
| if (!scenic_) { |
| startup_context_->ConnectToEnvironmentService(scenic_.NewRequest()); |
| scenic_.set_error_handler([this](zx_status_t error) { |
| FXL_LOG(ERROR) << "Scenic died, destroying all presentations."; |
| Reset(); |
| }); |
| |
| session_ = std::make_unique<scenic::Session>(scenic_.get()); |
| session_->set_error_handler([this](zx_status_t error) { |
| FXL_LOG(ERROR) << "Session died, destroying all presentations."; |
| Reset(); |
| }); |
| |
| // Globally disable parallel dispatch of input events. |
| // TODO(SCN-1047): Enable parallel dispatch. |
| { |
| fuchsia::ui::input::SetParallelDispatchCmd cmd; |
| cmd.parallel_dispatch = false; |
| fuchsia::ui::input::Command input_cmd; |
| input_cmd.set_set_parallel_dispatch(std::move(cmd)); |
| session_->Enqueue(std::move(input_cmd)); |
| } |
| |
| compositor_ = std::make_unique<scenic::DisplayCompositor>(session_.get()); |
| layer_stack_ = std::make_unique<scenic::LayerStack>(session_.get()); |
| compositor_->SetLayerStack(*layer_stack_.get()); |
| session_->Present(0, [](fuchsia::images::PresentationInfo info) {}); |
| |
| scenic_->GetDisplayOwnershipEvent([this](zx::event event) { |
| input_reader_.SetOwnershipEvent(std::move(event)); |
| }); |
| } |
| } |
| |
| void App::Reset() { |
| presentations_.clear(); // must be first, holds pointers to services |
| active_presentation_idx_ = std::numeric_limits<size_t>::max(); |
| layer_stack_ = nullptr; |
| compositor_ = nullptr; |
| session_ = nullptr; |
| scenic_.Unbind(); |
| } |
| |
| } // namespace root_presenter |