mozart: Initial commit of the launcher.
The launcher is a simple application which binds a ViewTree to
a NativeViewport, thereby allowing Mozart based applications to be
shown on screen.
Note: This version does not support input yet.
R=abarth@google.com, jamesr@chromium.org
Review URL: https://codereview.chromium.org/1404423007 .
diff --git a/services/ui/BUILD.gn b/services/ui/BUILD.gn
index bb93fba..9480d5e 100644
--- a/services/ui/BUILD.gn
+++ b/services/ui/BUILD.gn
@@ -4,6 +4,7 @@
group("ui") {
deps = [
+ "//services/ui/launcher",
"//services/ui/view_manager",
]
}
diff --git a/services/ui/launcher/BUILD.gn b/services/ui/launcher/BUILD.gn
new file mode 100644
index 0000000..e85fda3
--- /dev/null
+++ b/services/ui/launcher/BUILD.gn
@@ -0,0 +1,28 @@
+# 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.
+
+import("//mojo/public/mojo_application.gni")
+import("//testing/test.gni")
+
+mojo_native_application("launcher") {
+ sources = [
+ "launcher_app.cc",
+ "launcher_app.h",
+ "launcher_view_tree.cc",
+ "launcher_view_tree.h",
+ "main.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/common:tracing_impl",
+ "//mojo/environment:chromium",
+ "//mojo/public/cpp/bindings:bindings",
+ "//mojo/services/native_viewport/interfaces",
+ "//mojo/services/surfaces/cpp",
+ "//mojo/services/surfaces/interfaces",
+ "//mojo/services/ui/views/interfaces",
+ ]
+}
diff --git a/services/ui/launcher/README.md b/services/ui/launcher/README.md
new file mode 100644
index 0000000..eb4e776
--- /dev/null
+++ b/services/ui/launcher/README.md
@@ -0,0 +1,11 @@
+# Mozart Launcher
+
+This directory contains the Launcher, a simple tool for hosting a View
+inside of the NativeViewport.
+
+In other words, use this to run UI applications which implement the
+ViewProvider interface and which register their views with the ViewManager.
+
+## USAGE
+
+ out/Debug/mojo_shell "mojo:launcher <app url>"
diff --git a/services/ui/launcher/launcher_app.cc b/services/ui/launcher/launcher_app.cc
new file mode 100644
index 0000000..07e796f
--- /dev/null
+++ b/services/ui/launcher/launcher_app.cc
@@ -0,0 +1,128 @@
+// 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 "base/trace_event/trace_event.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/common/tracing_impl.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/surfaces/cpp/surfaces_utils.h"
+#include "mojo/services/surfaces/interfaces/quads.mojom.h"
+#include "services/ui/launcher/launcher_app.h"
+#include "services/ui/launcher/launcher_view_tree.h"
+
+namespace launcher {
+
+LauncherApp::LauncherApp()
+ : app_impl_(nullptr), viewport_event_dispatcher_binding_(this) {}
+
+LauncherApp::~LauncherApp() {}
+
+void LauncherApp::Initialize(mojo::ApplicationImpl* app) {
+ app_impl_ = app;
+ tracing_.Initialize(app);
+ TRACE_EVENT0("launcher", __func__);
+
+ if (app->args().size() != 2) {
+ LOG(ERROR) << "Invalid arguments.\n\n"
+ "Usage: mojo_shell \"mojo:launcher <app url>\"";
+ app->Terminate();
+ return;
+ }
+
+ InitViewport();
+ LaunchClient(app->args()[1]);
+}
+
+void LauncherApp::InitViewport() {
+ app_impl_->ConnectToService("mojo:native_viewport_service",
+ &viewport_service_);
+ viewport_service_.set_connection_error_handler(base::Bind(
+ &LauncherApp::OnViewportConnectionError, base::Unretained(this)));
+
+ mojo::NativeViewportEventDispatcherPtr dispatcher;
+ viewport_event_dispatcher_binding_.Bind(GetProxy(&dispatcher));
+ viewport_service_->SetEventDispatcher(dispatcher.Pass());
+
+ // Match the Nexus 5 aspect ratio initially.
+ auto size = mojo::Size::New();
+ size->width = 320;
+ size->height = 640;
+
+ auto requested_configuration = mojo::SurfaceConfiguration::New();
+ viewport_service_->Create(
+ size.Clone(), requested_configuration.Pass(),
+ base::Bind(&LauncherApp::OnViewportCreated, base::Unretained(this)));
+}
+
+void LauncherApp::OnViewportConnectionError() {
+ LOG(ERROR) << "Exiting due to viewport connection error.";
+ app_impl_->Terminate();
+}
+
+void LauncherApp::OnViewportCreated(mojo::ViewportMetricsPtr metrics) {
+ viewport_service_->Show();
+ mojo::ContextProviderPtr context_provider;
+ viewport_service_->GetContextProvider(GetProxy(&context_provider));
+
+ mojo::DisplayFactoryPtr display_factory;
+ app_impl_->ConnectToService("mojo:surfaces_service", &display_factory);
+
+ mojo::DisplayPtr display;
+ display_factory->Create(context_provider.Pass(), nullptr, GetProxy(&display));
+
+ view_tree_.reset(
+ new LauncherViewTree(app_impl_, display.Pass(), metrics.Pass()));
+ UpdateClientView();
+ RequestUpdatedViewportMetrics();
+}
+
+void LauncherApp::OnViewportMetricsChanged(mojo::ViewportMetricsPtr metrics) {
+ if (view_tree_) {
+ view_tree_->SetViewportMetrics(metrics.Pass());
+ RequestUpdatedViewportMetrics();
+ }
+}
+
+void LauncherApp::RequestUpdatedViewportMetrics() {
+ viewport_service_->RequestMetrics(base::Bind(
+ &LauncherApp::OnViewportMetricsChanged, base::Unretained(this)));
+}
+
+void LauncherApp::OnEvent(mojo::EventPtr event,
+ const mojo::Callback<void()>& callback) {
+ if (view_tree_)
+ view_tree_->DispatchEvent(event.Pass());
+ callback.Run();
+}
+
+void LauncherApp::LaunchClient(std::string app_url) {
+ DVLOG(1) << "Launching " << app_url;
+
+ app_impl_->ConnectToService(app_url, &client_view_provider_);
+ client_view_provider_.set_connection_error_handler(base::Bind(
+ &LauncherApp::OnClientConnectionError, base::Unretained(this)));
+
+ client_view_provider_->CreateView(
+ nullptr, nullptr,
+ base::Bind(&LauncherApp::OnClientViewCreated, base::Unretained(this)));
+}
+
+void LauncherApp::OnClientConnectionError() {
+ LOG(ERROR) << "Exiting due to client application connection error.";
+ app_impl_->Terminate();
+}
+
+void LauncherApp::OnClientViewCreated(mojo::ui::ViewTokenPtr view_token) {
+ client_view_token_ = view_token.Pass();
+ UpdateClientView();
+}
+
+void LauncherApp::UpdateClientView() {
+ if (view_tree_)
+ view_tree_->SetRoot(client_view_token_.Clone());
+}
+
+} // namespace launcher
diff --git a/services/ui/launcher/launcher_app.h b/services/ui/launcher/launcher_app.h
new file mode 100644
index 0000000..d614956
--- /dev/null
+++ b/services/ui/launcher/launcher_app.h
@@ -0,0 +1,65 @@
+// 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.
+
+#ifndef SERVICES_UI_LAUNCHER_LAUNCHER_APP_H_
+#define SERVICES_UI_LAUNCHER_LAUNCHER_APP_H_
+
+#include <memory>
+
+#include "mojo/common/tracing_impl.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
+#include "mojo/services/surfaces/interfaces/display.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
+
+namespace launcher {
+
+class LauncherViewTree;
+
+class LauncherApp : public mojo::ApplicationDelegate,
+ public mojo::NativeViewportEventDispatcher {
+ public:
+ LauncherApp();
+ ~LauncherApp() override;
+
+ private:
+ // |ApplicationDelegate|:
+ void Initialize(mojo::ApplicationImpl* app) override;
+
+ // |NativeViewportEventDispatcher|:
+ void OnEvent(mojo::EventPtr event,
+ const mojo::Callback<void()>& callback) override;
+
+ void InitViewport();
+ void OnViewportConnectionError();
+ void OnViewportCreated(mojo::ViewportMetricsPtr metrics);
+ void OnViewportMetricsChanged(mojo::ViewportMetricsPtr metrics);
+ void RequestUpdatedViewportMetrics();
+
+ void OnViewManagerConnectionError();
+
+ void LaunchClient(std::string app_url);
+ void OnClientConnectionError();
+ void OnClientViewCreated(mojo::ui::ViewTokenPtr view_token);
+
+ void UpdateClientView();
+
+ mojo::ApplicationImpl* app_impl_;
+ mojo::TracingImpl tracing_;
+
+ mojo::NativeViewportPtr viewport_service_;
+ mojo::Binding<NativeViewportEventDispatcher>
+ viewport_event_dispatcher_binding_;
+
+ std::unique_ptr<LauncherViewTree> view_tree_;
+
+ mojo::ui::ViewProviderPtr client_view_provider_;
+ mojo::ui::ViewTokenPtr client_view_token_;
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherApp);
+};
+
+} // namespace launcher
+
+#endif // SERVICES_UI_LAUNCHER_LAUNCHER_APP_H_
diff --git a/services/ui/launcher/launcher_view_tree.cc b/services/ui/launcher/launcher_view_tree.cc
new file mode 100644
index 0000000..052b3c0
--- /dev/null
+++ b/services/ui/launcher/launcher_view_tree.cc
@@ -0,0 +1,159 @@
+// 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 "base/bind.h"
+#include "services/ui/launcher/launcher_view_tree.h"
+
+namespace launcher {
+
+LauncherViewTree::LauncherViewTree(mojo::ApplicationImpl* app_impl,
+ mojo::DisplayPtr display,
+ mojo::ViewportMetricsPtr viewport_metrics)
+ : display_(display.Pass()),
+ viewport_metrics_(viewport_metrics.Pass()),
+ binding_(this),
+ root_key_(0),
+ frame_scheduled_(false),
+ frame_pending_(false) {
+ app_impl->ConnectToService("mojo:view_manager_service", &view_manager_);
+ view_manager_.set_connection_error_handler(base::Bind(
+ &LauncherViewTree::OnViewManagerConnectionError, base::Unretained(this)));
+
+ mojo::ui::ViewTreePtr view_tree;
+ binding_.Bind(mojo::GetProxy(&view_tree));
+ view_manager_->RegisterViewTree(
+ view_tree.Pass(), mojo::GetProxy(&view_tree_host_),
+ base::Bind(&LauncherViewTree::OnViewTreeRegistered,
+ base::Unretained(this)));
+
+ ScheduleFrame();
+}
+
+LauncherViewTree::~LauncherViewTree() {}
+
+void LauncherViewTree::SetRoot(mojo::ui::ViewTokenPtr token) {
+ root_ = token.Pass();
+ if (root_)
+ view_tree_host_->SetRoot(++root_key_, root_.Clone());
+ else
+ view_tree_host_->ResetRoot();
+ root_layout_info_.reset();
+}
+
+void LauncherViewTree::SetViewportMetrics(
+ mojo::ViewportMetricsPtr viewport_metrics) {
+ viewport_metrics_ = viewport_metrics.Pass();
+ view_tree_host_->RequestLayout();
+}
+
+void LauncherViewTree::DispatchEvent(mojo::EventPtr event) {
+ // TODO(jeffbrown): Support input dispatch.
+}
+
+void LauncherViewTree::OnViewManagerConnectionError() {
+ LOG(ERROR) << "View manager connection error.";
+}
+
+void LauncherViewTree::OnViewTreeRegistered() {}
+
+void LauncherViewTree::OnLayout(const OnLayoutCallback& callback) {
+ LayoutRoot();
+ callback.Run();
+}
+
+void LauncherViewTree::OnRootUnavailable(
+ uint32_t root_key,
+ const OnRootUnavailableCallback& callback) {
+ if (root_key_ == root_key) {
+ // TODO(jeffbrown): We should probably shut down the launcher.
+ LOG(ERROR) << "Root view terminated unexpectedly.";
+ SetRoot(mojo::ui::ViewTokenPtr());
+ }
+ callback.Run();
+}
+
+void LauncherViewTree::LayoutRoot() {
+ if (!root_)
+ return;
+
+ auto params = mojo::ui::ViewLayoutParams::New();
+ params->constraints = mojo::ui::BoxConstraints::New();
+ params->constraints->min_width = viewport_metrics_->size->width;
+ params->constraints->max_width = viewport_metrics_->size->width;
+ params->constraints->min_height = viewport_metrics_->size->height;
+ params->constraints->max_height = viewport_metrics_->size->height;
+ params->device_pixel_ratio = viewport_metrics_->device_pixel_ratio;
+ view_tree_host_->LayoutRoot(
+ params.Pass(),
+ base::Bind(&LauncherViewTree::OnLayoutResult, base::Unretained(this)));
+}
+
+void LauncherViewTree::OnLayoutResult(mojo::ui::ViewLayoutInfoPtr info) {
+ if (!info) {
+ DVLOG(1) << "Root layout: <stale>";
+ return;
+ }
+
+ DVLOG(1) << "Root layout: size.width=" << info->size->width
+ << ", size.height=" << info->size->height
+ << ", surface_id.id_namespace=" << info->surface_id->id_namespace
+ << ", surface_id.local=" << info->surface_id->local;
+
+ root_layout_info_ = info.Pass();
+ ScheduleFrame();
+}
+
+void LauncherViewTree::ScheduleFrame() {
+ frame_scheduled_ = true;
+ FinishFrame();
+}
+
+void LauncherViewTree::FinishFrame() {
+ if (!frame_scheduled_ || frame_pending_)
+ return;
+ frame_scheduled_ = false;
+
+ mojo::FramePtr frame = mojo::Frame::New();
+ frame->resources.resize(0u);
+
+ mojo::Rect bounds;
+ bounds.width = viewport_metrics_->size->width;
+ bounds.height = viewport_metrics_->size->height;
+ mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds);
+ pass->shared_quad_states.push_back(
+ mojo::CreateDefaultSQS(*viewport_metrics_->size));
+
+ mojo::QuadPtr quad = mojo::Quad::New();
+ quad->rect = bounds.Clone();
+ quad->opaque_rect = bounds.Clone();
+ quad->visible_rect = bounds.Clone();
+ quad->shared_quad_state_index = 0u;
+
+ if (root_layout_info_) {
+ quad->material = mojo::Material::SURFACE_CONTENT;
+ quad->surface_quad_state = mojo::SurfaceQuadState::New();
+ quad->surface_quad_state->surface = root_layout_info_->surface_id.Clone();
+ } else {
+ quad->material = mojo::Material::SOLID_COLOR;
+ quad->solid_color_quad_state = mojo::SolidColorQuadState::New();
+ quad->solid_color_quad_state->color = mojo::Color::New();
+ quad->solid_color_quad_state->color->rgba = 0xffff0000;
+ }
+
+ pass->quads.push_back(quad.Pass());
+ frame->passes.push_back(pass.Pass());
+
+ frame_pending_ = true;
+ display_->SubmitFrame(
+ frame.Pass(),
+ base::Bind(&LauncherViewTree::OnFrameSubmitted, base::Unretained(this)));
+}
+
+void LauncherViewTree::OnFrameSubmitted() {
+ DCHECK(frame_pending_);
+ frame_pending_ = false;
+ FinishFrame();
+}
+
+} // namespace launcher
diff --git a/services/ui/launcher/launcher_view_tree.h b/services/ui/launcher/launcher_view_tree.h
new file mode 100644
index 0000000..58ab563
--- /dev/null
+++ b/services/ui/launcher/launcher_view_tree.h
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef SERVICES_UI_LAUNCHER_VIEW_TREE_IMPL_H_
+#define SERVICES_UI_LAUNCHER_VIEW_TREE_IMPL_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
+#include "mojo/services/surfaces/cpp/surfaces_utils.h"
+#include "mojo/services/surfaces/interfaces/display.mojom.h"
+#include "mojo/services/surfaces/interfaces/quads.mojom.h"
+#include "mojo/services/surfaces/interfaces/surfaces.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
+
+namespace launcher {
+
+class LauncherViewTree : public mojo::ui::ViewTree {
+ public:
+ LauncherViewTree(mojo::ApplicationImpl* app_impl,
+ mojo::DisplayPtr display,
+ mojo::ViewportMetricsPtr viewport_metrics);
+
+ ~LauncherViewTree() override;
+
+ void SetRoot(mojo::ui::ViewTokenPtr token);
+ void SetViewportMetrics(mojo::ViewportMetricsPtr viewport_metrics);
+ void DispatchEvent(mojo::EventPtr event);
+
+ private:
+ // |ViewTree|:
+ void OnLayout(const OnLayoutCallback& callback) override;
+ void OnRootUnavailable(uint32_t root_key,
+ const OnRootUnavailableCallback& callback) override;
+
+ void OnViewManagerConnectionError();
+ void OnViewTreeRegistered();
+
+ void LayoutRoot();
+ void OnLayoutResult(mojo::ui::ViewLayoutInfoPtr info);
+
+ void ScheduleFrame();
+ void FinishFrame();
+ void OnFrameSubmitted();
+
+ mojo::ui::ViewManagerPtr view_manager_;
+ mojo::DisplayPtr display_;
+ mojo::ViewportMetricsPtr viewport_metrics_;
+ mojo::Binding<mojo::ui::ViewTree> binding_;
+
+ mojo::ui::ViewTreeHostPtr view_tree_host_;
+
+ mojo::ui::ViewTokenPtr root_;
+ uint32_t root_key_;
+ mojo::ui::ViewLayoutInfoPtr root_layout_info_;
+
+ bool frame_scheduled_;
+ bool frame_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherViewTree);
+};
+
+} // namespace launcher
+
+#endif // SERVICES_UI_LAUNCHER_VIEW_TREE_IMPL_H_
diff --git a/services/ui/launcher/main.cc b/services/ui/launcher/main.cc
new file mode 100644
index 0000000..fa82e08
--- /dev/null
+++ b/services/ui/launcher/main.cc
@@ -0,0 +1,12 @@
+// 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 "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "services/ui/launcher/launcher_app.h"
+
+MojoResult MojoMain(MojoHandle application_request) {
+ mojo::ApplicationRunnerChromium runner(new launcher::LauncherApp);
+ return runner.Run(application_request);
+}