mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Port flutter.mojo to Mozart
Everything should work except the keyboard.
This commit is contained in:
parent
4d246f47d2
commit
411c6bfa4b
@ -13,6 +13,7 @@ mojom("interfaces") {
|
||||
deps = [
|
||||
"//mojo/public/interfaces/application",
|
||||
"//mojo/services/asset_bundle/interfaces",
|
||||
"//mojo/services/gfx/composition/interfaces",
|
||||
"//mojo/services/service_registry/interfaces",
|
||||
"//sky/services/pointer:interfaces",
|
||||
]
|
||||
|
||||
@ -7,6 +7,7 @@ module sky;
|
||||
import "mojo/public/interfaces/application/service_provider.mojom";
|
||||
import "mojo/public/interfaces/application/shell.mojom";
|
||||
import "mojo/services/asset_bundle/interfaces/asset_bundle.mojom";
|
||||
import "mojo/services/gfx/composition/interfaces/scheduling.mojom";
|
||||
import "mojo/services/service_registry/interfaces/service_registry.mojom";
|
||||
import "sky/services/engine/input_event.mojom";
|
||||
import "sky/services/pointer/pointer.mojom";
|
||||
@ -31,6 +32,7 @@ struct ServicesData {
|
||||
mojo.ServiceRegistry? service_registry;
|
||||
mojo.ServiceProvider? services_provided_by_embedder;
|
||||
mojo.ServiceProvider&? services_provided_to_embedder;
|
||||
mojo.gfx.composition.SceneScheduler? scene_scheduler;
|
||||
};
|
||||
|
||||
[ServiceName="sky::SkyEngine"]
|
||||
|
||||
@ -44,6 +44,7 @@ source_set("common") {
|
||||
"//mojo/public/cpp/application",
|
||||
"//mojo/public/interfaces/application",
|
||||
"//mojo/services/asset_bundle/interfaces",
|
||||
"//mojo/services/gfx/composition/interfaces",
|
||||
"//mojo/services/keyboard/interfaces",
|
||||
"//mojo/services/navigation/interfaces",
|
||||
"//mojo/services/vsync/interfaces",
|
||||
@ -76,17 +77,17 @@ source_set("gpu_direct") {
|
||||
|
||||
source_set("gpu_mojo") {
|
||||
sources = [
|
||||
"gpu/mojo/gl_texture_recycler.cc",
|
||||
"gpu/mojo/gl_texture_recycler.h",
|
||||
"gpu/mojo/rasterizer_mojo.cc",
|
||||
"gpu/mojo/rasterizer_mojo.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":common",
|
||||
"//mojo/gpu",
|
||||
"//mojo/public/c/gpu",
|
||||
"//mojo/public/c/gpu:gpu_onscreen",
|
||||
"//mojo/services/gpu/interfaces",
|
||||
"//mojo/services/native_viewport/interfaces",
|
||||
"//mojo/skia:skia_bindings",
|
||||
"//mojo/skia",
|
||||
":common",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
126
sky/shell/gpu/mojo/gl_texture_recycler.cc
Normal file
126
sky/shell/gpu/mojo/gl_texture_recycler.cc
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright 2016 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 "sky/shell/gpu/mojo/gl_texture_recycler.h"
|
||||
|
||||
#ifndef GL_GLEXT_PROTOTYPES
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#endif
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2extmojo.h>
|
||||
|
||||
#include "mojo/gpu/gl_context.h"
|
||||
#include "mojo/gpu/gl_texture.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
GLTextureRecycler::GLTextureRecycler(base::WeakPtr<mojo::GLContext> gl_context,
|
||||
uint32_t max_recycled_textures)
|
||||
: gl_context_(gl_context),
|
||||
max_recycled_textures_(max_recycled_textures),
|
||||
weak_factory_(this) {}
|
||||
|
||||
GLTextureRecycler::~GLTextureRecycler() {}
|
||||
|
||||
std::unique_ptr<mojo::GLTexture> GLTextureRecycler::GetTexture(
|
||||
const mojo::Size& requested_size) {
|
||||
if (!gl_context_) {
|
||||
recycled_textures_.clear();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (!recycled_textures_.empty()) {
|
||||
GLRecycledTextureInfo texture_info(std::move(recycled_textures_.front()));
|
||||
recycled_textures_.pop_front();
|
||||
if (texture_info.first->size().Equals(requested_size)) {
|
||||
gl_context_->MakeCurrent();
|
||||
glWaitSyncPointCHROMIUM(texture_info.second);
|
||||
return std::move(texture_info.first);
|
||||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<mojo::GLTexture>(
|
||||
new mojo::GLTexture(gl_context_, requested_size));
|
||||
}
|
||||
|
||||
mojo::gfx::composition::ResourcePtr GLTextureRecycler::BindTextureResource(
|
||||
std::unique_ptr<mojo::GLTexture> texture) {
|
||||
if (!gl_context_)
|
||||
return nullptr;
|
||||
|
||||
// Produce the texture.
|
||||
gl_context_->MakeCurrent();
|
||||
glBindTexture(GL_TEXTURE_2D, texture->texture_id());
|
||||
GLbyte mailbox[GL_MAILBOX_SIZE_CHROMIUM];
|
||||
glGenMailboxCHROMIUM(mailbox);
|
||||
glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
GLuint sync_point = glInsertSyncPointCHROMIUM();
|
||||
|
||||
// Populate the resource description.
|
||||
auto resource = mojo::gfx::composition::Resource::New();
|
||||
resource->set_mailbox_texture(
|
||||
mojo::gfx::composition::MailboxTextureResource::New());
|
||||
resource->get_mailbox_texture()->mailbox_name.resize(sizeof(mailbox));
|
||||
memcpy(resource->get_mailbox_texture()->mailbox_name.data(), mailbox,
|
||||
sizeof(mailbox));
|
||||
resource->get_mailbox_texture()->sync_point = sync_point;
|
||||
resource->get_mailbox_texture()->size = texture->size().Clone();
|
||||
resource->get_mailbox_texture()->callback =
|
||||
(new GLTextureReleaser(
|
||||
weak_factory_.GetWeakPtr(),
|
||||
GLRecycledTextureInfo(std::move(texture), sync_point)))
|
||||
->StrongBind()
|
||||
.Pass();
|
||||
|
||||
bound_textures_++;
|
||||
DVLOG(2) << "bind: bound_textures=" << bound_textures_;
|
||||
return resource;
|
||||
}
|
||||
|
||||
void GLTextureRecycler::ReleaseTexture(GLRecycledTextureInfo texture_info,
|
||||
bool recyclable) {
|
||||
DCHECK(bound_textures_);
|
||||
bound_textures_--;
|
||||
if (recyclable && recycled_textures_.size() < max_recycled_textures_) {
|
||||
recycled_textures_.emplace_back(std::move(texture_info));
|
||||
}
|
||||
DVLOG(2) << "release: bound_textures=" << bound_textures_
|
||||
<< ", recycled_textures=" << recycled_textures_.size();
|
||||
}
|
||||
|
||||
GLTextureRecycler::GLTextureReleaser::GLTextureReleaser(
|
||||
const base::WeakPtr<GLTextureRecycler>& provider,
|
||||
GLRecycledTextureInfo info)
|
||||
: provider_(provider), texture_info_(std::move(info)), binding_(this) {}
|
||||
|
||||
GLTextureRecycler::GLTextureReleaser::~GLTextureReleaser() {
|
||||
// It's possible for the object to be destroyed due to a connection
|
||||
// error on the callback pipe. When this happens we don't want to
|
||||
// recycle the texture since we have too little knowledge about its
|
||||
// state to confirm that it will be safe to do so.
|
||||
Release(false /*recyclable*/);
|
||||
}
|
||||
|
||||
mojo::gfx::composition::MailboxTextureCallbackPtr
|
||||
GLTextureRecycler::GLTextureReleaser::StrongBind() {
|
||||
mojo::gfx::composition::MailboxTextureCallbackPtr callback;
|
||||
binding_.Bind(mojo::GetProxy(&callback));
|
||||
return callback;
|
||||
}
|
||||
|
||||
void GLTextureRecycler::GLTextureReleaser::OnMailboxTextureReleased() {
|
||||
Release(true /*recyclable*/);
|
||||
}
|
||||
|
||||
void GLTextureRecycler::GLTextureReleaser::Release(bool recyclable) {
|
||||
if (provider_) {
|
||||
provider_->ReleaseTexture(std::move(texture_info_), recyclable);
|
||||
provider_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
} // namespace sky
|
||||
86
sky/shell/gpu/mojo/gl_texture_recycler.h
Normal file
86
sky/shell/gpu/mojo/gl_texture_recycler.h
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
#ifndef SKY_SHELL_GPU_GL_RENDERER_H_
|
||||
#define SKY_SHELL_GPU_GL_RENDERER_H_
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "mojo/public/cpp/bindings/strong_binding.h"
|
||||
#include "mojo/services/gfx/composition/interfaces/resources.mojom.h"
|
||||
|
||||
namespace mojo {
|
||||
class GLContext;
|
||||
class GLTexture;
|
||||
class Size;
|
||||
} // namespace mojo
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
// Provides support for rendering GL commands into a pool of textures
|
||||
// and producing scene resources for them.
|
||||
// TODO(abarth): Move to //mojo/gpu and reconcile with mojo::ui::GLRenderer.
|
||||
class GLTextureRecycler {
|
||||
public:
|
||||
GLTextureRecycler(base::WeakPtr<mojo::GLContext> gl_context,
|
||||
uint32_t max_recycled_textures = 3u);
|
||||
~GLTextureRecycler();
|
||||
|
||||
// Obtains a texture of the specified size.
|
||||
// Returns a nullptr if the GLContext was destroyed.
|
||||
std::unique_ptr<mojo::GLTexture> GetTexture(const mojo::Size& requested_size);
|
||||
|
||||
// Takes ownership of the specified texture, issues GL commands to
|
||||
// produce a mailbox texture, and returns its resource pointer.
|
||||
// The caller should add the resource to its scene.
|
||||
// Returns a nullptr if the GLContext was destroyed.
|
||||
mojo::gfx::composition::ResourcePtr BindTextureResource(
|
||||
std::unique_ptr<mojo::GLTexture> texture);
|
||||
|
||||
private:
|
||||
using GLRecycledTextureInfo =
|
||||
std::pair<std::unique_ptr<mojo::GLTexture>, uint32_t>;
|
||||
|
||||
// TODO(jeffbrown): Avoid creating new callbacks each time, perhaps by
|
||||
// migrating to image pipes.
|
||||
class GLTextureReleaser : mojo::gfx::composition::MailboxTextureCallback {
|
||||
public:
|
||||
GLTextureReleaser(const base::WeakPtr<GLTextureRecycler>& provider,
|
||||
GLRecycledTextureInfo info);
|
||||
~GLTextureReleaser() override;
|
||||
|
||||
mojo::gfx::composition::MailboxTextureCallbackPtr StrongBind();
|
||||
|
||||
private:
|
||||
void OnMailboxTextureReleased() override;
|
||||
void Release(bool recyclable);
|
||||
|
||||
base::WeakPtr<GLTextureRecycler> provider_;
|
||||
GLRecycledTextureInfo texture_info_;
|
||||
mojo::StrongBinding<mojo::gfx::composition::MailboxTextureCallback>
|
||||
binding_;
|
||||
};
|
||||
|
||||
void ReleaseTexture(GLRecycledTextureInfo texture_info, bool recyclable);
|
||||
|
||||
const base::WeakPtr<mojo::GLContext> gl_context_;
|
||||
const uint32_t max_recycled_textures_;
|
||||
|
||||
std::deque<GLRecycledTextureInfo> recycled_textures_;
|
||||
uint32_t bound_textures_ = 0u;
|
||||
|
||||
base::WeakPtrFactory<GLTextureRecycler> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GLTextureRecycler);
|
||||
};
|
||||
|
||||
} // namespace shell
|
||||
} // namespace sky
|
||||
|
||||
#endif // SKY_SHELL_GPU_GL_RENDERER_H_
|
||||
@ -1,24 +1,19 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Copyright 2016 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 "sky/shell/gpu/mojo/rasterizer_mojo.h"
|
||||
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "mojo/public/c/gpu/MGL/mgl.h"
|
||||
#include "mojo/public/c/gpu/MGL/mgl_onscreen.h"
|
||||
#include "mojo/skia/gl_bindings_skia.h"
|
||||
#include "mojo/gpu/gl_texture.h"
|
||||
#include "mojo/skia/ganesh_texture_surface.h"
|
||||
#include "third_party/skia/include/core/SkCanvas.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
namespace {
|
||||
|
||||
void ContextLostThunk(void* closure) {
|
||||
static_cast<RasterizerMojo*>(closure)->OnContextLost();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
const uint32_t kContentImageResourceId = 1;
|
||||
const uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId;
|
||||
|
||||
std::unique_ptr<Rasterizer> Rasterizer::Create() {
|
||||
return std::unique_ptr<Rasterizer>(new RasterizerMojo());
|
||||
@ -43,6 +38,12 @@ void RasterizerMojo::ConnectToRasterizer (
|
||||
binding_.Bind(request.Pass());
|
||||
}
|
||||
|
||||
void RasterizerMojo::Init(mojo::ApplicationConnectorPtr connector,
|
||||
mojo::gfx::composition::ScenePtr scene) {
|
||||
gl_state_.reset(new GLState(connector.get()));
|
||||
scene_ = scene.Pass();
|
||||
}
|
||||
|
||||
void RasterizerMojo::Draw(uint64_t layer_tree_ptr,
|
||||
const DrawCallback& callback) {
|
||||
TRACE_EVENT0("flutter", "RasterizerMojo::Draw");
|
||||
@ -50,53 +51,61 @@ void RasterizerMojo::Draw(uint64_t layer_tree_ptr,
|
||||
std::unique_ptr<flow::LayerTree> layer_tree(
|
||||
reinterpret_cast<flow::LayerTree*>(layer_tree_ptr));
|
||||
|
||||
if (!MGLGetCurrentContext()) {
|
||||
if (!scene_ || !gl_state_ || !gl_state_->gl_context_owner.context()) {
|
||||
callback.Run();
|
||||
return;
|
||||
}
|
||||
|
||||
if (layer_tree->frame_size().isEmpty()) {
|
||||
callback.Run();
|
||||
return;
|
||||
mojo::Size size;
|
||||
size.width = layer_tree->frame_size().width();
|
||||
size.height = layer_tree->frame_size().height();
|
||||
|
||||
// TODO(abarth): Handle size == 0x0.
|
||||
|
||||
std::unique_ptr<mojo::GLTexture> texture =
|
||||
gl_state_->gl_texture_recycler.GetTexture(size);
|
||||
DCHECK(texture);
|
||||
|
||||
{
|
||||
mojo::skia::GaneshContext::Scope scope(&gl_state_->ganesh_context);
|
||||
mojo::skia::GaneshTextureSurface texture_surface(scope, std::move(texture));
|
||||
|
||||
SkCanvas* canvas = texture_surface.canvas();
|
||||
flow::PaintContext::ScopedFrame frame =
|
||||
paint_context_.AcquireFrame(scope.gr_context(), *canvas);
|
||||
canvas->clear(SK_ColorBLACK);
|
||||
layer_tree->Raster(frame);
|
||||
|
||||
texture = texture_surface.TakeTexture();
|
||||
}
|
||||
|
||||
MGLResizeSurface(layer_tree->frame_size().width(),
|
||||
layer_tree->frame_size().height());
|
||||
SkCanvas* canvas = ganesh_canvas_.GetCanvas(0, layer_tree->frame_size());
|
||||
flow::PaintContext::ScopedFrame frame =
|
||||
paint_context_.AcquireFrame(ganesh_canvas_.gr_context(), *canvas);
|
||||
canvas->clear(SK_ColorBLACK);
|
||||
layer_tree->Raster(frame);
|
||||
canvas->flush();
|
||||
MGLSwapBuffers();
|
||||
mojo::gfx::composition::ResourcePtr resource =
|
||||
gl_state_->gl_texture_recycler.BindTextureResource(std::move(texture));
|
||||
DCHECK(resource);
|
||||
|
||||
auto update = mojo::gfx::composition::SceneUpdate::New();
|
||||
update->resources.insert(kContentImageResourceId, resource.Pass());
|
||||
auto root_node = mojo::gfx::composition::Node::New();
|
||||
root_node->op = mojo::gfx::composition::NodeOp::New();
|
||||
root_node->op->set_image(mojo::gfx::composition::ImageNodeOp::New());
|
||||
root_node->op->get_image()->content_rect = mojo::Rect::New();
|
||||
root_node->op->get_image()->content_rect->width = size.width;
|
||||
root_node->op->get_image()->content_rect->height = size.height;
|
||||
root_node->op->get_image()->image_resource_id = kContentImageResourceId;
|
||||
update->nodes.insert(kRootNodeId, root_node.Pass());
|
||||
|
||||
scene_->Update(update.Pass());
|
||||
scene_->Publish(nullptr);
|
||||
callback.Run();
|
||||
}
|
||||
|
||||
void RasterizerMojo::OnContextProviderAvailable(
|
||||
mojo::InterfacePtrInfo<mojo::ContextProvider> context_provider) {
|
||||
context_provider_ = mojo::MakeProxy(context_provider.Pass());
|
||||
context_provider_->Create(nullptr,
|
||||
base::Bind(&RasterizerMojo::OnContextCreated, base::Unretained(this)));
|
||||
RasterizerMojo::GLState::GLState(mojo::ApplicationConnector* connector)
|
||||
: gl_context_owner(connector),
|
||||
gl_texture_recycler(gl_context_owner.context()),
|
||||
ganesh_context(gl_context_owner.context()) {
|
||||
}
|
||||
|
||||
void RasterizerMojo::OnContextCreated(mojo::CommandBufferPtr command_buffer) {
|
||||
context_ = MGLCreateContext(
|
||||
MGL_API_VERSION_GLES2,
|
||||
command_buffer.PassInterface().PassHandle().release().value(),
|
||||
MGL_NO_CONTEXT, &ContextLostThunk, this,
|
||||
mojo::Environment::GetDefaultAsyncWaiter());
|
||||
MGLMakeCurrent(context_);
|
||||
if (!gr_gl_interface_)
|
||||
gr_gl_interface_ = skia::AdoptRef(skia_bindings::CreateMojoSkiaGLBinding());
|
||||
ganesh_canvas_.SetGrGLInterface(gr_gl_interface_.get());
|
||||
}
|
||||
|
||||
void RasterizerMojo::OnContextLost() {
|
||||
ganesh_canvas_.SetGrGLInterface(nullptr);
|
||||
MGLDestroyContext(context_);
|
||||
context_ = nullptr;
|
||||
context_provider_->Create(nullptr,
|
||||
base::Bind(&RasterizerMojo::OnContextCreated, base::Unretained(this)));
|
||||
RasterizerMojo::GLState::~GLState() {
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Copyright 2016 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.
|
||||
|
||||
@ -6,46 +6,48 @@
|
||||
#define SKY_SHELL_GPU_MOJO_RASTERIZER_MOJO_H_
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "mojo/public/c/gpu/MGL/mgl.h"
|
||||
#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
|
||||
#include "skia/ext/refptr.h"
|
||||
#include "flow/paint_context.h"
|
||||
#include "sky/shell/gpu/ganesh_canvas.h"
|
||||
#include "mojo/gpu/gl_context_owner.h"
|
||||
#include "mojo/public/interfaces/application/application_connector.mojom.h"
|
||||
#include "mojo/services/gfx/composition/interfaces/scenes.mojom.h"
|
||||
#include "mojo/skia/ganesh_context.h"
|
||||
#include "sky/shell/gpu/mojo/gl_texture_recycler.h"
|
||||
#include "sky/shell/rasterizer.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
class RasterizerMojo : public ::sky::shell::Rasterizer {
|
||||
class RasterizerMojo : public Rasterizer {
|
||||
public:
|
||||
explicit RasterizerMojo();
|
||||
~RasterizerMojo() override;
|
||||
|
||||
base::WeakPtr<RasterizerMojo> GetWeakPtr();
|
||||
|
||||
base::WeakPtr<::sky::shell::Rasterizer> GetWeakRasterizerPtr() override;
|
||||
base::WeakPtr<Rasterizer> GetWeakRasterizerPtr() override;
|
||||
|
||||
void ConnectToRasterizer(
|
||||
mojo::InterfaceRequest<rasterizer::Rasterizer> request) override;
|
||||
|
||||
void OnContextProviderAvailable(
|
||||
mojo::InterfacePtrInfo<mojo::ContextProvider> context_provder);
|
||||
|
||||
void OnContextLost();
|
||||
void Init(mojo::ApplicationConnectorPtr connector,
|
||||
mojo::gfx::composition::ScenePtr scene);
|
||||
|
||||
private:
|
||||
void OnContextCreated(mojo::CommandBufferPtr command_buffer);
|
||||
|
||||
void Draw(uint64_t layer_tree_ptr, const DrawCallback& callback) override;
|
||||
|
||||
mojo::ContextProviderPtr context_provider_;
|
||||
skia::RefPtr<GrGLInterface> gr_gl_interface_;
|
||||
MGLContext context_;
|
||||
GaneshCanvas ganesh_canvas_;
|
||||
struct GLState {
|
||||
explicit GLState(mojo::ApplicationConnector* connector);
|
||||
~GLState();
|
||||
|
||||
flow::PaintContext paint_context_;
|
||||
mojo::GLContextOwner gl_context_owner;
|
||||
GLTextureRecycler gl_texture_recycler;
|
||||
mojo::skia::GaneshContext ganesh_context;
|
||||
};
|
||||
|
||||
mojo::Binding<rasterizer::Rasterizer> binding_;
|
||||
mojo::gfx::composition::ScenePtr scene_;
|
||||
std::unique_ptr<GLState> gl_state_;
|
||||
flow::PaintContext paint_context_;
|
||||
|
||||
base::WeakPtrFactory<RasterizerMojo> weak_factory_;
|
||||
|
||||
|
||||
@ -9,13 +9,17 @@ mojo_native_application("mojo") {
|
||||
output_name = "flutter"
|
||||
|
||||
sources = [
|
||||
"application_impl.cc",
|
||||
"application_impl.h",
|
||||
"content_handler_impl.cc",
|
||||
"content_handler_impl.h",
|
||||
"main_mojo.cc",
|
||||
"platform_view_mojo.cc",
|
||||
"platform_view_mojo.h",
|
||||
"sky_application_impl.cc",
|
||||
"sky_application_impl.h",
|
||||
"pointer_converter_mojo.cc",
|
||||
"pointer_converter_mojo.h",
|
||||
"view_impl.cc",
|
||||
"view_impl.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
@ -30,10 +34,11 @@ mojo_native_application("mojo") {
|
||||
"//mojo/public/interfaces/application",
|
||||
"//mojo/services/asset_bundle/interfaces",
|
||||
"//mojo/services/content_handler/interfaces",
|
||||
"//mojo/services/gpu/interfaces",
|
||||
"//mojo/services/keyboard/interfaces",
|
||||
"//mojo/services/native_viewport/interfaces",
|
||||
"//mojo/services/gfx/composition/interfaces",
|
||||
"//mojo/services/input_events/interfaces",
|
||||
"//mojo/services/service_registry/interfaces",
|
||||
"//mojo/services/ui/input/interfaces",
|
||||
"//mojo/services/ui/views/interfaces",
|
||||
"//services/asset_bundle:lib",
|
||||
"//skia",
|
||||
"//sky/engine/public/sky",
|
||||
|
||||
84
sky/shell/platform/mojo/application_impl.cc
Normal file
84
sky/shell/platform/mojo/application_impl.cc
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2016 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 "sky/shell/platform/mojo/application_impl.h"
|
||||
|
||||
#include "mojo/public/cpp/application/connect.h"
|
||||
#include "sky/shell/platform/mojo/view_impl.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
ApplicationImpl::ApplicationImpl(
|
||||
mojo::InterfaceRequest<mojo::Application> application,
|
||||
mojo::URLResponsePtr response)
|
||||
: binding_(this, application.Pass()),
|
||||
initial_response_(response.Pass()) {
|
||||
}
|
||||
|
||||
ApplicationImpl::~ApplicationImpl() {
|
||||
}
|
||||
|
||||
void ApplicationImpl::Initialize(mojo::ShellPtr shell,
|
||||
mojo::Array<mojo::String> args,
|
||||
const mojo::String& url) {
|
||||
DCHECK(initial_response_);
|
||||
shell_ = shell.Pass();
|
||||
url_ = url;
|
||||
UnpackInitialResponse(shell_.get());
|
||||
}
|
||||
|
||||
void ApplicationImpl::AcceptConnection(
|
||||
const mojo::String& requestor_url,
|
||||
mojo::InterfaceRequest<mojo::ServiceProvider> outgoing_services,
|
||||
mojo::ServiceProviderPtr incoming_services,
|
||||
const mojo::String& resolved_url) {
|
||||
service_provider_bindings_.AddBinding(this, outgoing_services.Pass());
|
||||
}
|
||||
|
||||
void ApplicationImpl::RequestQuit() {
|
||||
}
|
||||
|
||||
void ApplicationImpl::ConnectToService(const mojo::String& service_name,
|
||||
mojo::ScopedMessagePipeHandle handle) {
|
||||
if (service_name == mojo::ui::ViewProvider::Name_) {
|
||||
view_provider_bindings_.AddBinding(
|
||||
this, mojo::MakeRequest<mojo::ui::ViewProvider>(handle.Pass()));
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationImpl::CreateView(
|
||||
mojo::InterfaceRequest<mojo::ServiceProvider> outgoing_services,
|
||||
mojo::ServiceProviderPtr incoming_services,
|
||||
const mojo::ui::ViewProvider::CreateViewCallback& callback) {
|
||||
if (!bundle_) {
|
||||
LOG(ERROR) << "We only support creating one view.";
|
||||
return;
|
||||
}
|
||||
|
||||
mojo::ServiceRegistryPtr service_registry;
|
||||
if (incoming_services)
|
||||
mojo::ConnectToService(incoming_services.get(), &service_registry);
|
||||
ServicesDataPtr services = ServicesData::New();
|
||||
services->shell = shell_.Pass();
|
||||
services->service_registry = service_registry.Pass();
|
||||
services->services_provided_by_embedder = incoming_services.Pass();
|
||||
services->services_provided_to_embedder = outgoing_services.Pass();
|
||||
|
||||
ViewImpl* view = new ViewImpl(services.Pass(), url_, callback);
|
||||
view->Run(bundle_.Pass());
|
||||
}
|
||||
|
||||
void ApplicationImpl::UnpackInitialResponse(mojo::Shell* shell) {
|
||||
DCHECK(initial_response_);
|
||||
DCHECK(!bundle_);
|
||||
mojo::asset_bundle::AssetUnpackerPtr unpacker;
|
||||
mojo::ConnectToService(shell, "mojo:asset_bundle", &unpacker);
|
||||
unpacker->UnpackZipStream(initial_response_->body.Pass(),
|
||||
mojo::GetProxy(&bundle_));
|
||||
initial_response_ = nullptr;
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
} // namespace sky
|
||||
@ -1,11 +1,10 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Copyright 2016 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.
|
||||
|
||||
#ifndef SKY_SHELL_PLATFORM_MOJO_SKY_APPLICATION_IMPL_H_
|
||||
#define SKY_SHELL_PLATFORM_MOJO_SKY_APPLICATION_IMPL_H_
|
||||
#ifndef SKY_SHELL_PLATFORM_MOJO_APPLICATION_IMPL_H_
|
||||
#define SKY_SHELL_PLATFORM_MOJO_APPLICATION_IMPL_H_
|
||||
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "mojo/public/cpp/bindings/strong_binding.h"
|
||||
#include "mojo/public/interfaces/application/application.mojom.h"
|
||||
#include "mojo/public/interfaces/application/shell.mojom.h"
|
||||
@ -14,14 +13,19 @@
|
||||
#include "sky/shell/platform/mojo/platform_view_mojo.h"
|
||||
#include "sky/shell/shell_view.h"
|
||||
|
||||
#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
|
||||
#include "mojo/common/binding_set.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
class SkyApplicationImpl : public mojo::Application {
|
||||
class ApplicationImpl : public mojo::Application,
|
||||
public mojo::ServiceProvider,
|
||||
public mojo::ui::ViewProvider {
|
||||
public:
|
||||
SkyApplicationImpl(mojo::InterfaceRequest<mojo::Application> application,
|
||||
mojo::URLResponsePtr response);
|
||||
~SkyApplicationImpl() override;
|
||||
ApplicationImpl(mojo::InterfaceRequest<mojo::Application> application,
|
||||
mojo::URLResponsePtr response);
|
||||
~ApplicationImpl() override;
|
||||
|
||||
private:
|
||||
// mojo::Application
|
||||
@ -35,20 +39,28 @@ class SkyApplicationImpl : public mojo::Application {
|
||||
const mojo::String& resolved_url) override;
|
||||
void RequestQuit() override;
|
||||
|
||||
PlatformViewMojo* platform_view() {
|
||||
return static_cast<PlatformViewMojo*>(shell_view_->view());
|
||||
}
|
||||
// mojo::ServiceProvider
|
||||
void ConnectToService(const mojo::String& service_name,
|
||||
mojo::ScopedMessagePipeHandle client_handle) override;
|
||||
|
||||
// mojo::ui::ViewProvider
|
||||
void CreateView(
|
||||
mojo::InterfaceRequest<mojo::ServiceProvider> services,
|
||||
mojo::ServiceProviderPtr exposed_services,
|
||||
const mojo::ui::ViewProvider::CreateViewCallback& callback) override;
|
||||
|
||||
void UnpackInitialResponse(mojo::Shell* shell);
|
||||
|
||||
mojo::StrongBinding<mojo::Application> binding_;
|
||||
mojo::URLResponsePtr initial_response_;
|
||||
mojo::BindingSet<mojo::ServiceProvider> service_provider_bindings_;
|
||||
mojo::BindingSet<mojo::ui::ViewProvider> view_provider_bindings_;
|
||||
std::string url_;
|
||||
mojo::ShellPtr shell_;
|
||||
mojo::asset_bundle::AssetBundlePtr bundle_;
|
||||
std::unique_ptr<ShellView> shell_view_;
|
||||
};
|
||||
|
||||
} // namespace shell
|
||||
} // namespace sky
|
||||
|
||||
#endif // SKY_SHELL_PLATFORM_MOJO_SKY_APPLICATION_IMPL_H_
|
||||
#endif // SKY_SHELL_PLATFORM_MOJO_APPLICATION_IMPL_H_
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
#include "sky/shell/platform/mojo/content_handler_impl.h"
|
||||
|
||||
#include "sky/shell/platform/mojo/sky_application_impl.h"
|
||||
#include "sky/shell/platform/mojo/application_impl.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
@ -20,7 +20,7 @@ ContentHandlerImpl::~ContentHandlerImpl() {
|
||||
void ContentHandlerImpl::StartApplication(
|
||||
mojo::InterfaceRequest<mojo::Application> application,
|
||||
mojo::URLResponsePtr response) {
|
||||
new SkyApplicationImpl(application.Pass(), response.Pass());
|
||||
new ApplicationImpl(application.Pass(), response.Pass());
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
|
||||
@ -5,9 +5,8 @@
|
||||
#ifndef SKY_SHELL_PLATFORM_MOJO_CONTENT_HANDLER_IMPL_H_
|
||||
#define SKY_SHELL_PLATFORM_MOJO_CONTENT_HANDLER_IMPL_H_
|
||||
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/macros.h"
|
||||
#include "mojo/public/cpp/bindings/strong_binding.h"
|
||||
#include "mojo/public/interfaces/application/shell.mojom.h"
|
||||
#include "mojo/services/content_handler/interfaces/content_handler.mojom.h"
|
||||
|
||||
namespace sky {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Copyright 2016 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.
|
||||
|
||||
@ -7,211 +7,32 @@
|
||||
#include "base/bind.h"
|
||||
#include "base/location.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "mojo/public/cpp/application/connect.h"
|
||||
#include "sky/shell/gpu/mojo/rasterizer_mojo.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
namespace {
|
||||
|
||||
pointer::PointerType GetTypeFromAction(mojo::EventType type) {
|
||||
switch (type) {
|
||||
case mojo::EventType::POINTER_CANCEL:
|
||||
return pointer::PointerType::CANCEL;
|
||||
case mojo::EventType::POINTER_DOWN:
|
||||
return pointer::PointerType::DOWN;
|
||||
case mojo::EventType::POINTER_MOVE:
|
||||
return pointer::PointerType::MOVE;
|
||||
case mojo::EventType::POINTER_UP:
|
||||
return pointer::PointerType::UP;
|
||||
default:
|
||||
DCHECK(false);
|
||||
return pointer::PointerType::CANCEL;
|
||||
}
|
||||
}
|
||||
|
||||
pointer::PointerKind GetKindFromKind(mojo::PointerKind kind) {
|
||||
switch (kind) {
|
||||
case mojo::PointerKind::TOUCH:
|
||||
return pointer::PointerKind::TOUCH;
|
||||
case mojo::PointerKind::MOUSE:
|
||||
return pointer::PointerKind::MOUSE;
|
||||
}
|
||||
DCHECK(false);
|
||||
return pointer::PointerKind::TOUCH;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PlatformView* PlatformView::Create(const Config& config) {
|
||||
return new PlatformViewMojo(config);
|
||||
}
|
||||
|
||||
PlatformViewMojo::PlatformViewMojo(const Config& config)
|
||||
: PlatformView(config), dispatcher_binding_(this) {
|
||||
: PlatformView(config) {
|
||||
}
|
||||
|
||||
PlatformViewMojo::~PlatformViewMojo() {
|
||||
}
|
||||
|
||||
void PlatformViewMojo::Init(mojo::Shell* shell) {
|
||||
mojo::ConnectToService(shell, "mojo:native_viewport_service", &viewport_);
|
||||
|
||||
// Grab the application connector so that we can connect to services later
|
||||
shell->CreateApplicationConnector(GetProxy(&connector_));
|
||||
|
||||
mojo::NativeViewportEventDispatcherPtr ptr;
|
||||
dispatcher_binding_.Bind(GetProxy(&ptr));
|
||||
viewport_->SetEventDispatcher(ptr.Pass());
|
||||
|
||||
mojo::SizePtr size = mojo::Size::New();
|
||||
size->width = 320;
|
||||
size->height = 640;
|
||||
|
||||
viewport_->Create(
|
||||
size.Clone(),
|
||||
mojo::SurfaceConfiguration::New(),
|
||||
[this](mojo::ViewportMetricsPtr metrics) {
|
||||
OnMetricsChanged(metrics.Pass());
|
||||
});
|
||||
viewport_->Show();
|
||||
|
||||
mojo::ContextProviderPtr context_provider;
|
||||
viewport_->GetContextProvider(GetProxy(&context_provider));
|
||||
|
||||
mojo::InterfacePtrInfo<mojo::ContextProvider> context_provider_info = context_provider.PassInterface();
|
||||
|
||||
void PlatformViewMojo::InitRasterizer(mojo::ApplicationConnectorPtr connector,
|
||||
mojo::gfx::composition::ScenePtr scene) {
|
||||
RasterizerMojo* rasterizer = static_cast<RasterizerMojo*>(config_.rasterizer);
|
||||
config_.ui_task_runner->PostTask(
|
||||
FROM_HERE, base::Bind(&UIDelegate::OnOutputSurfaceCreated,
|
||||
config_.ui_delegate,
|
||||
base::Bind(&RasterizerMojo::OnContextProviderAvailable,
|
||||
rasterizer->GetWeakPtr(), base::Passed(&context_provider_info))));
|
||||
|
||||
ConnectToEngine(GetProxy(&sky_engine_));
|
||||
|
||||
}
|
||||
|
||||
void PlatformViewMojo::Run(const mojo::String& url,
|
||||
ServicesDataPtr services,
|
||||
mojo::asset_bundle::AssetBundlePtr bundle) {
|
||||
|
||||
mojo::ServiceProviderPtr services_provided_by_embedder;
|
||||
service_provider_.Bind(GetProxy(&services_provided_by_embedder));
|
||||
service_provider_.AddService<keyboard::KeyboardService>(this);
|
||||
services->services_provided_by_embedder = services_provided_by_embedder.Pass();
|
||||
|
||||
sky_engine_->SetServices(services.Pass());
|
||||
sky_engine_->RunFromAssetBundle(url, bundle.Pass());
|
||||
}
|
||||
|
||||
void PlatformViewMojo::OnMetricsChanged(mojo::ViewportMetricsPtr metrics) {
|
||||
DCHECK(metrics);
|
||||
viewport_->RequestMetrics(
|
||||
[this](mojo::ViewportMetricsPtr metrics) {
|
||||
OnMetricsChanged(metrics.Pass());
|
||||
});
|
||||
|
||||
sky::ViewportMetricsPtr sky_metrics = sky::ViewportMetrics::New();
|
||||
sky_metrics->physical_width = metrics->size->width;
|
||||
sky_metrics->physical_height = metrics->size->height;
|
||||
sky_metrics->device_pixel_ratio = metrics->device_pixel_ratio;
|
||||
sky_engine_->OnViewportMetricsChanged(sky_metrics.Pass());
|
||||
}
|
||||
|
||||
void PlatformViewMojo::OnEvent(mojo::EventPtr event,
|
||||
const mojo::Callback<void()>& callback) {
|
||||
DCHECK(event);
|
||||
switch (event->action) {
|
||||
case mojo::EventType::POINTER_CANCEL:
|
||||
case mojo::EventType::POINTER_DOWN:
|
||||
case mojo::EventType::POINTER_MOVE:
|
||||
case mojo::EventType::POINTER_UP: {
|
||||
mojo::PointerDataPtr data = event->pointer_data.Pass();
|
||||
if (!data)
|
||||
break;
|
||||
pointer::PointerPacketPtr packet;
|
||||
int packetIndex = 0;
|
||||
if (pointer_positions_.count(data->pointer_id) > 0) {
|
||||
if (event->action == mojo::EventType::POINTER_UP ||
|
||||
event->action == mojo::EventType::POINTER_CANCEL) {
|
||||
std::pair<float, float> last_position = pointer_positions_[data->pointer_id];
|
||||
if (last_position.first != data->x || last_position.second != data->y) {
|
||||
packet = pointer::PointerPacket::New();
|
||||
packet->pointers = mojo::Array<pointer::PointerPtr>::New(2);
|
||||
packet->pointers[packetIndex] = CreateEvent(pointer::PointerType::MOVE, event.get(), data.get());
|
||||
packetIndex += 1;
|
||||
}
|
||||
pointer_positions_.erase(data->pointer_id);
|
||||
}
|
||||
} else {
|
||||
// We don't currently support hover moves.
|
||||
// If we want to support those, we have to first implement
|
||||
// added/removed events for pointers.
|
||||
// See: https://github.com/flutter/flutter/issues/720
|
||||
if (event->action != mojo::EventType::POINTER_DOWN)
|
||||
break;
|
||||
}
|
||||
if (packetIndex == 0) {
|
||||
packet = pointer::PointerPacket::New();
|
||||
packet->pointers = mojo::Array<pointer::PointerPtr>::New(1);
|
||||
}
|
||||
packet->pointers[packetIndex] = CreateEvent(GetTypeFromAction(event->action), event.get(), data.get());
|
||||
sky_engine_->OnPointerPacket(packet.Pass());
|
||||
break;
|
||||
}
|
||||
case mojo::EventType::KEY_PRESSED:
|
||||
case mojo::EventType::KEY_RELEASED:
|
||||
if (key_event_dispatcher_) {
|
||||
key_event_dispatcher_->OnEvent(event.Pass(), callback);
|
||||
return; // key_event_dispatcher_ will invoke callback
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
callback.Run();
|
||||
}
|
||||
|
||||
pointer::PointerPtr PlatformViewMojo::CreateEvent(pointer::PointerType type, mojo::Event* event, mojo::PointerData* data) {
|
||||
DCHECK(data);
|
||||
pointer::PointerPtr pointer = pointer::Pointer::New();
|
||||
pointer->time_stamp = event->time_stamp;
|
||||
pointer->pointer = data->pointer_id;
|
||||
pointer->type = type;
|
||||
pointer->kind = GetKindFromKind(data->kind);
|
||||
pointer->x = data->x;
|
||||
pointer->y = data->y;
|
||||
pointer->buttons = static_cast<int32_t>(event->flags);
|
||||
pointer->pressure = data->pressure;
|
||||
pointer->radius_major = data->radius_major;
|
||||
pointer->radius_minor = data->radius_minor;
|
||||
pointer->orientation = data->orientation;
|
||||
if (event->action != mojo::EventType::POINTER_UP &&
|
||||
event->action != mojo::EventType::POINTER_CANCEL)
|
||||
pointer_positions_[data->pointer_id] = { data->x, data->y };
|
||||
return pointer.Pass();
|
||||
}
|
||||
|
||||
void PlatformViewMojo::Create(
|
||||
mojo::ApplicationConnection* connection,
|
||||
mojo::InterfaceRequest<keyboard::KeyboardService> request) {
|
||||
|
||||
mojo::ServiceProviderPtr keyboard_service_provider;
|
||||
connector_->ConnectToApplication(
|
||||
"mojo:keyboard",
|
||||
GetProxy(&keyboard_service_provider),
|
||||
nullptr);
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
keyboard::KeyboardServiceFactoryPtr factory;
|
||||
mojo::ConnectToService(keyboard_service_provider.get(), &factory);
|
||||
factory->CreateKeyboardService(GetProxy(&key_event_dispatcher_), request.Pass());
|
||||
#else
|
||||
keyboard_service_provider->ConnectToService(
|
||||
keyboard::KeyboardService::Name_,
|
||||
request.PassMessagePipe());
|
||||
#endif
|
||||
config_.ui_task_runner->PostTask(FROM_HERE, base::Bind(
|
||||
&UIDelegate::OnOutputSurfaceCreated,
|
||||
config_.ui_delegate,
|
||||
base::Bind(&RasterizerMojo::Init,
|
||||
rasterizer->GetWeakPtr(),
|
||||
base::Passed(&connector),
|
||||
base::Passed(&scene))));
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
|
||||
@ -1,62 +1,26 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Copyright 2016 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.
|
||||
|
||||
#ifndef SKY_SHELL_PLATFORM_MOJO_PLATFORM_VIEW_MOJO_H_
|
||||
#define SKY_SHELL_PLATFORM_MOJO_PLATFORM_VIEW_MOJO_H_
|
||||
|
||||
#include "mojo/public/cpp/application/service_provider_impl.h"
|
||||
#include "mojo/public/cpp/bindings/binding.h"
|
||||
#include "mojo/public/interfaces/application/service_provider.mojom.h"
|
||||
#include "mojo/public/interfaces/application/shell.mojom.h"
|
||||
#include "mojo/services/asset_bundle/interfaces/asset_bundle.mojom.h"
|
||||
#include "mojo/services/keyboard/interfaces/keyboard.mojom.h"
|
||||
#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
|
||||
#include "mojo/public/interfaces/application/application_connector.mojom.h"
|
||||
#include "mojo/services/gfx/composition/interfaces/scenes.mojom.h"
|
||||
#include "sky/shell/platform_view.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
class PlatformViewMojo : public PlatformView,
|
||||
public mojo::NativeViewportEventDispatcher,
|
||||
public mojo::InterfaceFactory<::keyboard::KeyboardService> {
|
||||
class PlatformViewMojo : public PlatformView {
|
||||
public:
|
||||
explicit PlatformViewMojo(const Config& config);
|
||||
~PlatformViewMojo() override;
|
||||
|
||||
void Init(mojo::Shell* shell);
|
||||
|
||||
void Run(const mojo::String& url,
|
||||
ServicesDataPtr services,
|
||||
mojo::asset_bundle::AssetBundlePtr bundle);
|
||||
void InitRasterizer(mojo::ApplicationConnectorPtr connector,
|
||||
mojo::gfx::composition::ScenePtr scene);
|
||||
|
||||
private:
|
||||
void OnMetricsChanged(mojo::ViewportMetricsPtr metrics);
|
||||
|
||||
// mojo::NativeViewportEventDispatcher
|
||||
void OnEvent(mojo::EventPtr event,
|
||||
const mojo::Callback<void()>& callback) override;
|
||||
|
||||
pointer::PointerPtr CreateEvent(pointer::PointerType type, mojo::Event* event, mojo::PointerData* data);
|
||||
|
||||
// |mojo::InterfaceFactory<mojo::asset_bundle::AssetUnpacker>| implementation:
|
||||
void Create(
|
||||
mojo::ApplicationConnection* connection,
|
||||
mojo::InterfaceRequest<::keyboard::KeyboardService>) override;
|
||||
|
||||
mojo::ApplicationConnectorPtr connector_;
|
||||
|
||||
mojo::NativeViewportPtr viewport_;
|
||||
mojo::Binding<NativeViewportEventDispatcher> dispatcher_binding_;
|
||||
|
||||
sky::SkyEnginePtr sky_engine_;
|
||||
|
||||
mojo::ServiceProviderImpl service_provider_;
|
||||
|
||||
mojo::NativeViewportEventDispatcherPtr key_event_dispatcher_;
|
||||
|
||||
std::map<int, std::pair<float, float>> pointer_positions_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PlatformViewMojo);
|
||||
};
|
||||
|
||||
|
||||
111
sky/shell/platform/mojo/pointer_converter_mojo.cc
Normal file
111
sky/shell/platform/mojo/pointer_converter_mojo.cc
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2016 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 "sky/shell/platform/mojo/pointer_converter_mojo.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
namespace {
|
||||
|
||||
pointer::PointerType GetTypeFromAction(mojo::EventType type) {
|
||||
switch (type) {
|
||||
case mojo::EventType::POINTER_CANCEL:
|
||||
return pointer::PointerType::CANCEL;
|
||||
case mojo::EventType::POINTER_DOWN:
|
||||
return pointer::PointerType::DOWN;
|
||||
case mojo::EventType::POINTER_MOVE:
|
||||
return pointer::PointerType::MOVE;
|
||||
case mojo::EventType::POINTER_UP:
|
||||
return pointer::PointerType::UP;
|
||||
default:
|
||||
DCHECK(false);
|
||||
return pointer::PointerType::CANCEL;
|
||||
}
|
||||
}
|
||||
|
||||
pointer::PointerKind GetKindFromKind(mojo::PointerKind kind) {
|
||||
switch (kind) {
|
||||
case mojo::PointerKind::TOUCH:
|
||||
return pointer::PointerKind::TOUCH;
|
||||
case mojo::PointerKind::MOUSE:
|
||||
return pointer::PointerKind::MOUSE;
|
||||
}
|
||||
DCHECK(false);
|
||||
return pointer::PointerKind::TOUCH;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PointerConverterMojo::PointerConverterMojo() {
|
||||
}
|
||||
|
||||
PointerConverterMojo::~PointerConverterMojo() {
|
||||
}
|
||||
|
||||
pointer::PointerPacketPtr PointerConverterMojo::ConvertEvent(
|
||||
mojo::EventPtr event) {
|
||||
DCHECK(event->action == mojo::EventType::POINTER_CANCEL
|
||||
|| event->action == mojo::EventType::POINTER_DOWN
|
||||
|| event->action == mojo::EventType::POINTER_MOVE
|
||||
|| event->action == mojo::EventType::POINTER_UP);
|
||||
mojo::PointerDataPtr data = event->pointer_data.Pass();
|
||||
if (!data)
|
||||
return nullptr;
|
||||
pointer::PointerPacketPtr packet;
|
||||
int packetIndex = 0;
|
||||
if (pointer_positions_.count(data->pointer_id) > 0) {
|
||||
if (event->action == mojo::EventType::POINTER_UP ||
|
||||
event->action == mojo::EventType::POINTER_CANCEL) {
|
||||
auto last_position = pointer_positions_[data->pointer_id];
|
||||
if (last_position.first != data->x || last_position.second != data->y) {
|
||||
packet = pointer::PointerPacket::New();
|
||||
packet->pointers = mojo::Array<pointer::PointerPtr>::New(2);
|
||||
packet->pointers[packetIndex] = CreatePointer(
|
||||
pointer::PointerType::MOVE, event.get(), data.get());
|
||||
packetIndex += 1;
|
||||
}
|
||||
pointer_positions_.erase(data->pointer_id);
|
||||
}
|
||||
} else {
|
||||
// We don't currently support hover moves.
|
||||
// If we want to support those, we have to first implement
|
||||
// added/removed events for pointers.
|
||||
// See: https://github.com/flutter/flutter/issues/720
|
||||
if (event->action != mojo::EventType::POINTER_DOWN)
|
||||
return nullptr;
|
||||
}
|
||||
if (packetIndex == 0) {
|
||||
packet = pointer::PointerPacket::New();
|
||||
packet->pointers = mojo::Array<pointer::PointerPtr>::New(1);
|
||||
}
|
||||
packet->pointers[packetIndex] = CreatePointer(
|
||||
GetTypeFromAction(event->action), event.get(), data.get());
|
||||
return packet.Pass();
|
||||
}
|
||||
|
||||
pointer::PointerPtr PointerConverterMojo::CreatePointer(
|
||||
pointer::PointerType type, mojo::Event* event, mojo::PointerData* data) {
|
||||
DCHECK(data);
|
||||
pointer::PointerPtr pointer = pointer::Pointer::New();
|
||||
pointer->time_stamp = event->time_stamp;
|
||||
pointer->pointer = data->pointer_id;
|
||||
pointer->type = type;
|
||||
pointer->kind = GetKindFromKind(data->kind);
|
||||
pointer->x = data->x;
|
||||
pointer->y = data->y;
|
||||
pointer->buttons = static_cast<int32_t>(event->flags);
|
||||
pointer->pressure = data->pressure;
|
||||
pointer->radius_major = data->radius_major;
|
||||
pointer->radius_minor = data->radius_minor;
|
||||
pointer->orientation = data->orientation;
|
||||
if (event->action != mojo::EventType::POINTER_UP &&
|
||||
event->action != mojo::EventType::POINTER_CANCEL)
|
||||
pointer_positions_[data->pointer_id] = { data->x, data->y };
|
||||
return pointer.Pass();
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
} // namespace sky
|
||||
37
sky/shell/platform/mojo/pointer_converter_mojo.h
Normal file
37
sky/shell/platform/mojo/pointer_converter_mojo.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
#ifndef SKY_SHELL_PLATFORM_MOJO_POINTER_CONVERTER_MOJO_H_
|
||||
#define SKY_SHELL_PLATFORM_MOJO_POINTER_CONVERTER_MOJO_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "mojo/services/input_events/interfaces/input_events.mojom.h"
|
||||
#include "sky/services/pointer/pointer.mojom.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
class PointerConverterMojo {
|
||||
public:
|
||||
PointerConverterMojo();
|
||||
~PointerConverterMojo();
|
||||
|
||||
pointer::PointerPacketPtr ConvertEvent(mojo::EventPtr event);
|
||||
|
||||
private:
|
||||
pointer::PointerPtr CreatePointer(pointer::PointerType type,
|
||||
mojo::Event* event,
|
||||
mojo::PointerData* data);
|
||||
|
||||
std::map<int, std::pair<float, float>> pointer_positions_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PointerConverterMojo);
|
||||
};
|
||||
|
||||
} // namespace shell
|
||||
} // namespace sky
|
||||
|
||||
#endif // SKY_SHELL_PLATFORM_MOJO_POINTER_CONVERTER_MOJO_H_
|
||||
@ -1,69 +0,0 @@
|
||||
// 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 "sky/shell/platform/mojo/sky_application_impl.h"
|
||||
|
||||
#include "mojo/public/cpp/application/connect.h"
|
||||
#include "mojo/public/cpp/utility/run_loop.h"
|
||||
#include "sky/shell/shell.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
SkyApplicationImpl::SkyApplicationImpl(
|
||||
mojo::InterfaceRequest<mojo::Application> application,
|
||||
mojo::URLResponsePtr response)
|
||||
: binding_(this, application.Pass()),
|
||||
initial_response_(response.Pass()) {
|
||||
}
|
||||
|
||||
SkyApplicationImpl::~SkyApplicationImpl() {
|
||||
}
|
||||
|
||||
void SkyApplicationImpl::Initialize(mojo::ShellPtr shell,
|
||||
mojo::Array<mojo::String> args,
|
||||
const mojo::String& url) {
|
||||
DCHECK(initial_response_);
|
||||
UnpackInitialResponse(shell.get());
|
||||
shell_view_.reset(new ShellView(Shell::Shared()));
|
||||
platform_view()->Init(shell.get());
|
||||
shell_ = shell.Pass();
|
||||
}
|
||||
|
||||
void SkyApplicationImpl::AcceptConnection(
|
||||
const mojo::String& requestor_url,
|
||||
mojo::InterfaceRequest<mojo::ServiceProvider> outgoing_services,
|
||||
mojo::ServiceProviderPtr incoming_services,
|
||||
const mojo::String& resolved_url) {
|
||||
if (!bundle_) {
|
||||
LOG(INFO) << "Cannot handle multiple connections yet.";
|
||||
return;
|
||||
}
|
||||
|
||||
mojo::ServiceRegistryPtr service_registry;
|
||||
|
||||
if (incoming_services)
|
||||
mojo::ConnectToService(incoming_services.get(), &service_registry);
|
||||
|
||||
ServicesDataPtr services = ServicesData::New();
|
||||
services->shell = shell_.Pass();
|
||||
services->service_registry = service_registry.Pass();
|
||||
platform_view()->Run(resolved_url, services.Pass(), bundle_.Pass());
|
||||
}
|
||||
|
||||
void SkyApplicationImpl::RequestQuit() {
|
||||
}
|
||||
|
||||
void SkyApplicationImpl::UnpackInitialResponse(mojo::Shell* shell) {
|
||||
DCHECK(initial_response_);
|
||||
DCHECK(!bundle_);
|
||||
mojo::asset_bundle::AssetUnpackerPtr unpacker;
|
||||
mojo::ConnectToService(shell, "mojo:asset_bundle", &unpacker);
|
||||
unpacker->UnpackZipStream(initial_response_->body.Pass(),
|
||||
mojo::GetProxy(&bundle_));
|
||||
initial_response_ = nullptr;
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
} // namespace sky
|
||||
98
sky/shell/platform/mojo/view_impl.cc
Normal file
98
sky/shell/platform/mojo/view_impl.cc
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright 2016 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 "sky/shell/platform/mojo/view_impl.h"
|
||||
|
||||
#include "mojo/public/cpp/application/connect.h"
|
||||
#include "mojo/services/gfx/composition/interfaces/scheduling.mojom.h"
|
||||
#include "sky/shell/shell.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
ViewImpl::ViewImpl(ServicesDataPtr services,
|
||||
const std::string& url,
|
||||
const mojo::ui::ViewProvider::CreateViewCallback& callback)
|
||||
: binding_(this), url_(url), listener_binding_(this) {
|
||||
DCHECK(services);
|
||||
|
||||
// Views
|
||||
mojo::ConnectToService(
|
||||
services->shell.get(), "mojo:view_manager_service", &view_manager_);
|
||||
mojo::ui::ViewPtr view;
|
||||
binding_.Bind(mojo::GetProxy(&view));
|
||||
view_manager_->RegisterView(
|
||||
view.Pass(), mojo::GetProxy(&view_host_), url_, callback);
|
||||
view_host_->GetServiceProvider(mojo::GetProxy(&view_service_provider_));
|
||||
|
||||
// Input
|
||||
mojo::ConnectToService(view_service_provider_.get(), &input_connection_);
|
||||
mojo::ui::InputListenerPtr listener;
|
||||
listener_binding_.Bind(mojo::GetProxy(&listener));
|
||||
input_connection_->SetListener(listener.Pass());
|
||||
|
||||
// Compositing
|
||||
mojo::gfx::composition::ScenePtr scene;
|
||||
view_host_->CreateScene(mojo::GetProxy(&scene));
|
||||
scene->GetScheduler(mojo::GetProxy(&services->scene_scheduler));
|
||||
|
||||
// Engine
|
||||
shell_view_.reset(new ShellView(Shell::Shared()));
|
||||
shell_view_->view()->ConnectToEngine(GetProxy(&engine_));
|
||||
mojo::ApplicationConnectorPtr connector;
|
||||
services->shell->CreateApplicationConnector(mojo::GetProxy(&connector));
|
||||
platform_view()->InitRasterizer(connector.Pass(), scene.Pass());
|
||||
engine_->SetServices(services.Pass());
|
||||
}
|
||||
|
||||
ViewImpl::~ViewImpl() {
|
||||
}
|
||||
|
||||
void ViewImpl::Run(mojo::asset_bundle::AssetBundlePtr bundle) {
|
||||
engine_->RunFromAssetBundle(url_, bundle.Pass());
|
||||
}
|
||||
|
||||
void ViewImpl::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
|
||||
mojo::Array<uint32_t> children_needing_layout,
|
||||
const OnLayoutCallback& callback) {
|
||||
viewport_metrics_.device_pixel_ratio = layout_params->device_pixel_ratio;
|
||||
viewport_metrics_.physical_width = layout_params->constraints->max_width;
|
||||
viewport_metrics_.physical_height = layout_params->constraints->max_height;
|
||||
engine_->OnViewportMetricsChanged(viewport_metrics_.Clone());
|
||||
|
||||
auto info = mojo::ui::ViewLayoutResult::New();
|
||||
info->size = mojo::Size::New();
|
||||
info->size->width = viewport_metrics_.physical_width;
|
||||
info->size->height = viewport_metrics_.physical_height;
|
||||
callback.Run(info.Pass());
|
||||
}
|
||||
|
||||
void ViewImpl::OnChildUnavailable(uint32_t child_key,
|
||||
const OnChildUnavailableCallback& callback) {
|
||||
callback.Run();
|
||||
}
|
||||
|
||||
void ViewImpl::OnEvent(mojo::EventPtr event, const OnEventCallback& callback) {
|
||||
DCHECK(event);
|
||||
bool consumed = false;
|
||||
switch (event->action) {
|
||||
case mojo::EventType::POINTER_CANCEL:
|
||||
case mojo::EventType::POINTER_DOWN:
|
||||
case mojo::EventType::POINTER_MOVE:
|
||||
case mojo::EventType::POINTER_UP: {
|
||||
auto packet = pointer_converter_.ConvertEvent(event.Pass());
|
||||
if (packet) {
|
||||
engine_->OnPointerPacket(packet.Pass());
|
||||
consumed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
callback.Run(consumed);
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
} // namespace sky
|
||||
69
sky/shell/platform/mojo/view_impl.h
Normal file
69
sky/shell/platform/mojo/view_impl.h
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
#ifndef SKY_SHELL_PLATFORM_MOJO_VIEW_IMPL_H_
|
||||
#define SKY_SHELL_PLATFORM_MOJO_VIEW_IMPL_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "mojo/public/cpp/bindings/strong_binding.h"
|
||||
#include "mojo/public/interfaces/application/application_connector.mojom.h"
|
||||
#include "mojo/services/asset_bundle/interfaces/asset_bundle.mojom.h"
|
||||
#include "mojo/services/gfx/composition/interfaces/scenes.mojom.h"
|
||||
#include "mojo/services/ui/input/interfaces/input_connection.mojom.h"
|
||||
#include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
|
||||
#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
|
||||
#include "mojo/services/ui/views/interfaces/views.mojom.h"
|
||||
#include "sky/shell/platform/mojo/platform_view_mojo.h"
|
||||
#include "sky/shell/platform/mojo/pointer_converter_mojo.h"
|
||||
#include "sky/shell/shell_view.h"
|
||||
|
||||
namespace sky {
|
||||
namespace shell {
|
||||
|
||||
class ViewImpl : public mojo::ui::View,
|
||||
public mojo::ui::InputListener {
|
||||
public:
|
||||
ViewImpl(ServicesDataPtr services,
|
||||
const std::string& url,
|
||||
const mojo::ui::ViewProvider::CreateViewCallback& callback);
|
||||
~ViewImpl() override;
|
||||
|
||||
void Run(mojo::asset_bundle::AssetBundlePtr bundle);
|
||||
|
||||
private:
|
||||
// mojo::ui::View
|
||||
void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
|
||||
mojo::Array<uint32_t> children_needing_layout,
|
||||
const OnLayoutCallback& callback) override;
|
||||
void OnChildUnavailable(uint32_t child_key,
|
||||
const OnChildUnavailableCallback& callback) override;
|
||||
|
||||
// mojo::ui::InputListener
|
||||
void OnEvent(mojo::EventPtr event, const OnEventCallback& callback) override;
|
||||
|
||||
PlatformViewMojo* platform_view() {
|
||||
return static_cast<PlatformViewMojo*>(shell_view_->view());
|
||||
}
|
||||
|
||||
mojo::StrongBinding<mojo::ui::View> binding_;
|
||||
std::string url_;
|
||||
mojo::ui::ViewManagerPtr view_manager_;
|
||||
mojo::ui::ViewHostPtr view_host_;
|
||||
mojo::ServiceProviderPtr view_service_provider_;
|
||||
mojo::ui::InputConnectionPtr input_connection_;
|
||||
mojo::Binding<mojo::ui::InputListener> listener_binding_;
|
||||
|
||||
std::unique_ptr<ShellView> shell_view_;
|
||||
SkyEnginePtr engine_;
|
||||
ViewportMetrics viewport_metrics_;
|
||||
|
||||
PointerConverterMojo pointer_converter_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ViewImpl);
|
||||
};
|
||||
|
||||
} // namespace shell
|
||||
} // namespace sky
|
||||
|
||||
#endif // SKY_SHELL_PLATFORM_MOJO_VIEW_IMPL_H_
|
||||
@ -67,6 +67,10 @@ void Animator::Start() {
|
||||
RequestFrame();
|
||||
}
|
||||
|
||||
void Animator::Animate(mojo::gfx::composition::FrameInfoPtr frame_info) {
|
||||
BeginFrame(frame_info->frame_time);
|
||||
}
|
||||
|
||||
void Animator::BeginFrame(int64_t time_stamp) {
|
||||
TRACE_EVENT_ASYNC_END0("flutter", "Frame request pending", this);
|
||||
DCHECK(engine_requested_frame_);
|
||||
@ -110,11 +114,16 @@ void Animator::OnFrameComplete() {
|
||||
}
|
||||
|
||||
bool Animator::AwaitVSync() {
|
||||
if (!vsync_provider_)
|
||||
return false;
|
||||
vsync_provider_->AwaitVSync(
|
||||
base::Bind(&Animator::BeginFrame, weak_factory_.GetWeakPtr()));
|
||||
return true;
|
||||
if (scene_scheduler_) {
|
||||
scene_scheduler_->ScheduleFrame(
|
||||
base::Bind(&Animator::Animate, weak_factory_.GetWeakPtr()));
|
||||
return true;
|
||||
} else if (vsync_provider_) {
|
||||
vsync_provider_->AwaitVSync(
|
||||
base::Bind(&Animator::BeginFrame, weak_factory_.GetWeakPtr()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#define SKY_SHELL_UI_ANIMATOR_H_
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "mojo/services/gfx/composition/interfaces/scheduling.mojom.h"
|
||||
#include "mojo/services/vsync/interfaces/vsync.mojom.h"
|
||||
#include "sky/shell/ui/engine.h"
|
||||
|
||||
@ -24,11 +25,17 @@ class Animator {
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
void set_scene_scheduler(
|
||||
mojo::gfx::composition::SceneSchedulerPtr scene_scheduler) {
|
||||
scene_scheduler_ = scene_scheduler.Pass();
|
||||
}
|
||||
|
||||
void set_vsync_provider(vsync::VSyncProviderPtr vsync_provider) {
|
||||
vsync_provider_ = vsync_provider.Pass();
|
||||
}
|
||||
|
||||
private:
|
||||
void Animate(mojo::gfx::composition::FrameInfoPtr frame_info);
|
||||
void BeginFrame(int64_t time_stamp);
|
||||
void OnFrameComplete();
|
||||
bool AwaitVSync();
|
||||
@ -36,6 +43,7 @@ class Animator {
|
||||
Engine::Config config_;
|
||||
rasterizer::RasterizerPtr rasterizer_;
|
||||
Engine* engine_;
|
||||
mojo::gfx::composition::SceneSchedulerPtr scene_scheduler_;
|
||||
vsync::VSyncProviderPtr vsync_provider_;
|
||||
int outstanding_requests_;
|
||||
bool did_defer_frame_request_;
|
||||
|
||||
@ -118,17 +118,21 @@ void Engine::OnOutputSurfaceDestroyed(const base::Closure& gpu_continuation) {
|
||||
void Engine::SetServices(ServicesDataPtr services) {
|
||||
services_ = services.Pass();
|
||||
|
||||
#if defined(OS_ANDROID) || defined(OS_IOS)
|
||||
vsync::VSyncProviderPtr vsync_provider;
|
||||
if (services_->shell) {
|
||||
mojo::ConnectToService(services_->shell.get(), "mojo:vsync",
|
||||
&vsync_provider);
|
||||
if (services_->scene_scheduler) {
|
||||
animator_->set_scene_scheduler(services_->scene_scheduler.Pass());
|
||||
} else {
|
||||
mojo::ConnectToService(services_->services_provided_by_embedder.get(),
|
||||
&vsync_provider);
|
||||
}
|
||||
animator_->set_vsync_provider(vsync_provider.Pass());
|
||||
#if defined(OS_ANDROID) || defined(OS_IOS)
|
||||
vsync::VSyncProviderPtr vsync_provider;
|
||||
if (services_->shell) {
|
||||
mojo::ConnectToService(services_->shell.get(), "mojo:vsync",
|
||||
&vsync_provider);
|
||||
} else {
|
||||
mojo::ConnectToService(services_->services_provided_by_embedder.get(),
|
||||
&vsync_provider);
|
||||
}
|
||||
animator_->set_vsync_provider(vsync_provider.Pass());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::OnViewportMetricsChanged(ViewportMetricsPtr metrics) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user