| // Copyright 2017 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/sketchy/canvas.h" |
| |
| #include "garnet/bin/ui/sketchy/frame.h" |
| #include "garnet/bin/ui/sketchy/resources/import_node.h" |
| #include "garnet/bin/ui/sketchy/resources/stroke.h" |
| #include "lib/escher/util/fuchsia_utils.h" |
| #include "lib/fsl/tasks/message_loop.h" |
| |
| namespace sketchy_service { |
| |
| CanvasImpl::CanvasImpl(scenic_lib::Session* session, escher::Escher* escher) |
| : session_(session), |
| shared_buffer_pool_(session, escher), |
| stroke_manager_(escher) {} |
| |
| void CanvasImpl::Init(fidl::InterfaceHandle<sketchy::CanvasListener> listener) { |
| // TODO(MZ-269): unimplemented. |
| FXL_LOG(ERROR) << "Init: unimplemented."; |
| } |
| |
| void CanvasImpl::Enqueue(fidl::VectorPtr<sketchy::Command> commands) { |
| // TODO: Use `AddAll()` when fidl::VectorPtr supports it. |
| for (size_t i = 0; i < commands->size(); ++i) { |
| commands_.push_back(std::move(commands->at(i))); |
| } |
| } |
| |
| void CanvasImpl::Present(uint64_t presentation_time, PresentCallback callback) { |
| // TODO(MZ-269): Present() should behave the same way as Scenic. Specifically, |
| // Commands shouldn't be applied immediately. Instead a frame-request should |
| // be triggered and the Commands enqueue; when the corresponding frame is |
| // processed all Commands that are scheduled for the current frame's |
| // presentation time are applied. |
| for (auto it = commands_->begin(); it != commands_->end(); ++it) { |
| if (!ApplyCommand(std::move(*it))) { |
| fsl::MessageLoop::GetCurrent()->QuitNow(); |
| } |
| } |
| commands_.reset(); |
| callbacks_.push_back(std::move(callback)); |
| RequestScenicPresent(presentation_time); |
| } |
| |
| void CanvasImpl::RequestScenicPresent(uint64_t presentation_time) { |
| if (is_scenic_present_requested_) { |
| return; |
| } |
| is_scenic_present_requested_ = true; |
| |
| auto session_callback = [this, callbacks = std::move(callbacks_)]( |
| images::PresentationInfo info) { |
| FXL_DCHECK(is_scenic_present_requested_); |
| is_scenic_present_requested_ = false; |
| for (auto& callback : callbacks) { |
| callback(info); |
| } |
| RequestScenicPresent(info.presentation_time + info.presentation_interval); |
| }; |
| callbacks_.clear(); |
| |
| auto frame = Frame(&shared_buffer_pool_); |
| if (frame.init_failed()) { |
| session_->Present(presentation_time, std::move(session_callback)); |
| return; |
| } |
| |
| stroke_manager_.Update(&frame); |
| frame.RequestScenicPresent(session_, presentation_time, |
| std::move(session_callback)); |
| } |
| |
| bool CanvasImpl::ApplyCommand(sketchy::Command command) { |
| switch (command.Which()) { |
| case sketchy::Command::Tag::kCreateResource: |
| return ApplyCreateResourceCommand(std::move(command.create_resource())); |
| case sketchy::Command::Tag::kReleaseResource: |
| return ApplyReleaseResourceCommand(std::move(command.release_resource())); |
| case sketchy::Command::Tag::kSetPath: |
| return ApplySetPathCommand(std::move(command.set_path())); |
| case sketchy::Command::Tag::kAddStroke: |
| return ApplyAddStrokeCommand(std::move(command.add_stroke())); |
| case sketchy::Command::Tag::kRemoveStroke: |
| return ApplyRemoveStrokeCommand(std::move(command.remove_stroke())); |
| case sketchy::Command::Tag::kBeginStroke: |
| return ApplyBeginStrokeCommand(std::move(command.begin_stroke())); |
| case sketchy::Command::Tag::kExtendStroke: |
| return ApplyExtendStrokeCommand(std::move(command.extend_stroke())); |
| case sketchy::Command::Tag::kFinishStroke: |
| return ApplyFinishStrokeCommand(std::move(command.finish_stroke())); |
| case sketchy::Command::Tag::kClearGroup: |
| return ApplyClearGroupCommand(std::move(command.clear_group())); |
| case sketchy::Command::Tag::kScenicImportResource: |
| return ApplyScenicImportResourceCommand( |
| std::move(command.scenic_import_resource())); |
| case sketchy::Command::Tag::kScenicAddChild: |
| return ApplyScenicAddChildCommand(std::move(command.scenic_add_child())); |
| default: |
| FXL_DCHECK(false) << "Unsupported op: " |
| << static_cast<uint32_t>(command.Which()); |
| return false; |
| } |
| } |
| |
| bool CanvasImpl::ApplyCreateResourceCommand( |
| sketchy::CreateResourceCommand create_resource) { |
| switch (create_resource.args.Which()) { |
| case sketchy::ResourceArgs::Tag::kStroke: |
| return CreateStroke(create_resource.id, create_resource.args.stroke()); |
| case sketchy::ResourceArgs::Tag::kStrokeGroup: |
| return CreateStrokeGroup(create_resource.id, |
| create_resource.args.stroke_group()); |
| default: |
| FXL_DCHECK(false) << "Unsupported resource: " |
| << static_cast<uint32_t>(create_resource.args.Which()); |
| return false; |
| } |
| } |
| |
| bool CanvasImpl::CreateStroke(ResourceId id, sketchy::Stroke stroke) { |
| return resource_map_.AddResource( |
| id, fxl::MakeRefCounted<Stroke>(stroke_manager_.stroke_tessellator(), |
| shared_buffer_pool_.factory())); |
| } |
| |
| bool CanvasImpl::CreateStrokeGroup(ResourceId id, |
| sketchy::StrokeGroup stroke_group) { |
| return resource_map_.AddResource(id, |
| fxl::MakeRefCounted<StrokeGroup>(session_)); |
| } |
| |
| bool CanvasImpl::ApplyReleaseResourceCommand( |
| sketchy::ReleaseResourceCommand command) { |
| return resource_map_.RemoveResource(command.id); |
| } |
| |
| bool CanvasImpl::ApplySetPathCommand(sketchy::SetStrokePathCommand command) { |
| auto stroke = resource_map_.FindResource<Stroke>(command.stroke_id); |
| if (!stroke) { |
| FXL_LOG(ERROR) << "No Stroke of id " << command.stroke_id << " was found!"; |
| return false; |
| } |
| return stroke_manager_.SetStrokePath( |
| stroke, std::make_unique<StrokePath>(std::move(command.path))); |
| } |
| |
| bool CanvasImpl::ApplyAddStrokeCommand(sketchy::AddStrokeCommand command) { |
| auto stroke = resource_map_.FindResource<Stroke>(command.stroke_id); |
| if (!stroke) { |
| FXL_LOG(ERROR) << "No Stroke of id " << command.stroke_id << " was found!"; |
| return false; |
| } |
| auto group = resource_map_.FindResource<StrokeGroup>(command.group_id); |
| if (!group) { |
| FXL_LOG(ERROR) << "No StrokeGroup of id " << command.group_id |
| << " was found!"; |
| return false; |
| } |
| return stroke_manager_.AddStrokeToGroup(stroke, group); |
| } |
| |
| bool CanvasImpl::ApplyRemoveStrokeCommand( |
| sketchy::RemoveStrokeCommand command) { |
| auto stroke = resource_map_.FindResource<Stroke>(command.stroke_id); |
| if (!stroke) { |
| FXL_LOG(ERROR) << "No Stroke of id " << command.stroke_id << " was found!"; |
| return false; |
| } |
| auto group = resource_map_.FindResource<StrokeGroup>(command.group_id); |
| if (!group) { |
| FXL_LOG(ERROR) << "No StrokeGroup of id " << command.group_id |
| << " was found!"; |
| return false; |
| } |
| return stroke_manager_.RemoveStrokeFromGroup(stroke, group); |
| } |
| |
| bool CanvasImpl::ApplyBeginStrokeCommand(sketchy::BeginStrokeCommand command) { |
| auto stroke = resource_map_.FindResource<Stroke>(command.stroke_id); |
| if (!stroke) { |
| FXL_LOG(ERROR) << "No Stroke of id " << command.stroke_id << " was found!"; |
| return false; |
| } |
| const auto& pos = command.touch.position; |
| return stroke_manager_.BeginStroke(stroke, {pos.x, pos.y}); |
| } |
| |
| bool CanvasImpl::ApplyExtendStrokeCommand( |
| sketchy::ExtendStrokeCommand command) { |
| auto stroke = resource_map_.FindResource<Stroke>(command.stroke_id); |
| if (!stroke) { |
| FXL_LOG(ERROR) << "No Stroke of id " << command.stroke_id << " was found!"; |
| return false; |
| } |
| std::vector<glm::vec2> pts; |
| pts.reserve(command.touches->size()); |
| for (const auto& touch : *command.touches) { |
| pts.push_back({touch.position.x, touch.position.y}); |
| } |
| return stroke_manager_.ExtendStroke(stroke, std::move(pts)); |
| } |
| |
| bool CanvasImpl::ApplyFinishStrokeCommand( |
| sketchy::FinishStrokeCommand command) { |
| auto stroke = resource_map_.FindResource<Stroke>(command.stroke_id); |
| if (!stroke) { |
| FXL_LOG(ERROR) << "No Stroke of id " << command.stroke_id << " was found!"; |
| return false; |
| } |
| return stroke_manager_.FinishStroke(stroke); |
| } |
| |
| bool CanvasImpl::ApplyClearGroupCommand(sketchy::ClearGroupCommand command) { |
| auto group = resource_map_.FindResource<StrokeGroup>(command.group_id); |
| if (!group) { |
| FXL_LOG(ERROR) << "No Group of id " << command.group_id << " was found!"; |
| return false; |
| } |
| return stroke_manager_.ClearGroup(group); |
| } |
| |
| bool CanvasImpl::ApplyScenicImportResourceCommand( |
| gfx::ImportResourceCommand import_resource) { |
| switch (import_resource.spec) { |
| case gfx::ImportSpec::NODE: |
| return ScenicImportNode(import_resource.id, |
| std::move(import_resource.token)); |
| } |
| } |
| |
| bool CanvasImpl::ScenicImportNode(ResourceId id, zx::eventpair token) { |
| // As a client of Scenic, Canvas creates an ImportNode given token. |
| auto node = fxl::MakeRefCounted<ImportNode>(session_, std::move(token)); |
| resource_map_.AddResource(id, std::move(node)); |
| return true; |
| } |
| |
| bool CanvasImpl::ApplyScenicAddChildCommand(gfx::AddChildCommand add_child) { |
| auto import_node = resource_map_.FindResource<ImportNode>(add_child.node_id); |
| auto stroke_group = |
| resource_map_.FindResource<StrokeGroup>(add_child.child_id); |
| if (!import_node || !stroke_group) { |
| return false; |
| } |
| import_node->AddChild(stroke_group); |
| return stroke_manager_.AddNewGroup(stroke_group); |
| } |
| |
| } // namespace sketchy_service |