Port Moterm to Mozart. Much of the glue code has been eliminated by use of the new UI helpers. BUG= R=abarth@google.com, viettrungluu@chromium.org Review URL: https://codereview.chromium.org/1556683004 .
diff --git a/apps/BUILD.gn b/apps/BUILD.gn index 4765a5f..7f482c9 100644 --- a/apps/BUILD.gn +++ b/apps/BUILD.gn
@@ -18,6 +18,8 @@ ":portable_apps", "//apps/benchmark", "//apps/benchmark:apptests", + "//apps/moterm", + "//apps/moterm:apptests", ] if (is_android) {
diff --git a/apps/moterm/BUILD.gn b/apps/moterm/BUILD.gn index a69461c..73b5dc9 100644 --- a/apps/moterm/BUILD.gn +++ b/apps/moterm/BUILD.gn
@@ -6,48 +6,40 @@ mojo_native_application("moterm") { sources = [ - "moterm_main.cc", + "main.cc", + "moterm_app.cc", + "moterm_app.h", "moterm_view.cc", "moterm_view.h", ] deps = [ ":driver", - ":gl_helper", ":key_util", ":model", "//base", "//mojo/application", "//mojo/common", + "//mojo/public/c/gpu", "//mojo/public/c/system", "//mojo/public/cpp/application", "//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings:callback", "//mojo/services/files/interfaces", - "//mojo/services/surfaces/interfaces:surface_id", + "//mojo/services/geometry/interfaces", + "//mojo/services/gfx/composition/interfaces", "//mojo/services/terminal/interfaces", - "//mojo/services/view_manager/cpp", + "//mojo/services/ui/input/interfaces", + "//mojo/services/ui/views/interfaces", + "//mojo/skia", + "//mojo/ui", + "//mojo/ui:ganesh", + "//mojo/ui:gl", "//skia", "//third_party/dejavu-fonts-ttf-2.34:DejaVuSansMonoRegular", ] } -mojo_native_application("gl_helper_test_app") { - sources = [ - "gl_helper_test_app.cc", - ] - - deps = [ - ":gl_helper", - "//base", - "//mojo/application", - "//mojo/services/geometry/interfaces", - "//mojo/services/gpu/interfaces", - "//mojo/services/native_viewport/interfaces", - "//mojo/services/surfaces/interfaces", - ] -} - source_set("driver") { sources = [ "moterm_driver.cc", @@ -61,33 +53,6 @@ ] } -source_set("gl_helper") { - sources = [ - "gl_helper.cc", - "gl_helper.h", - ] - - deps = [ - "//mojo/public/cpp/application", - "//mojo/public/c/gpu", - "//mojo/public/interfaces/application", - "//mojo/services/gpu/interfaces", - "//mojo/services/surfaces/interfaces", - ] - - public_deps = [ - "//base", - "//mojo/public/c/gpu", - "//mojo/public/c/gpu:GLES2", - "//mojo/services/geometry/cpp", - "//mojo/services/geometry/interfaces", - "//mojo/services/gpu/interfaces", - "//mojo/services/surfaces/cpp", - "//mojo/services/surfaces/interfaces", - "//mojo/services/surfaces/interfaces:surface_id", - ] -} - source_set("key_util") { sources = [ "key_util.cc", @@ -125,7 +90,6 @@ testonly = true sources = [ - "gl_helper_unittest.cc", "key_util_unittest.cc", "moterm_driver_unittest.cc", "moterm_model_unittest.cc", @@ -133,7 +97,6 @@ deps = [ ":driver", - ":gl_helper", ":key_util", ":model", "//mojo/application",
diff --git a/apps/moterm/gl_helper.cc b/apps/moterm/gl_helper.cc deleted file mode 100644 index f1e2cb2..0000000 --- a/apps/moterm/gl_helper.cc +++ /dev/null
@@ -1,327 +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 "apps/moterm/gl_helper.h" - -#ifndef GL_GLEXT_PROTOTYPES -#define GL_GLEXT_PROTOTYPES -#endif - -#include <GLES2/gl2.h> -#include <GLES2/gl2extmojo.h> -#include <MGL/mgl.h> - -#include "base/bind.h" -#include "base/location.h" -#include "base/message_loop/message_loop.h" -#include "base/task_runner.h" -#include "mojo/public/cpp/application/connect.h" -#include "mojo/public/interfaces/application/shell.mojom.h" -#include "mojo/services/geometry/cpp/geometry_util.h" -#include "mojo/services/surfaces/cpp/surfaces_utils.h" - -// Maximum number of (live) textures to keep around. -const size_t kMaxTextures = 10; - -GlHelper::GlHelper(Client* client, - mojo::Shell* shell, - GLint texture_format, - bool flipped, - const mojo::Size& initial_size) - : client_(client), - texture_format_(texture_format), - flipped_(flipped), - returner_binding_(this), - next_surface_size_(initial_size), - mgl_context_(MGL_NO_CONTEXT), - next_frame_id_(0), - frame_texture_(0), - id_namespace_(0), - local_id_(0), - next_resource_id_(0), - weak_factory_(this) { - mojo::ServiceProviderPtr native_viewport_service_provider; - shell->ConnectToApplication("mojo:native_viewport_service", - GetProxy(&native_viewport_service_provider), - nullptr); - mojo::ConnectToService(native_viewport_service_provider.get(), &gpu_); - - mojo::ServiceProviderPtr surfaces_service_provider; - shell->ConnectToApplication("mojo:surfaces_service", - GetProxy(&surfaces_service_provider), nullptr); - mojo::ConnectToService(surfaces_service_provider.get(), &surface_); - surface_->GetIdNamespace(base::Bind(&GlHelper::GetIdNamespaceCallback, - weak_factory_.GetWeakPtr())); - mojo::ResourceReturnerPtr returner_ptr; - returner_binding_.Bind(GetProxy(&returner_ptr)); - surface_->SetResourceReturner(returner_ptr.Pass()); -} - -GlHelper::~GlHelper() { - DCHECK(!frame_texture_); - if (mgl_context_ != MGL_NO_CONTEXT) - MGLDestroyContext(mgl_context_); -} - -void GlHelper::SetSurfaceSize(const mojo::Size& surface_size) { - next_surface_size_ = surface_size; -} - -void GlHelper::MakeCurrent() { - EnsureContext(); -} - -void GlHelper::StartFrame() { - DCHECK(!frame_texture_); - - EnsureContext(); - EnsureSurface(); - - TextureInfo texture_info = GetTexture(); - DCHECK(texture_info.texture); - frame_texture_ = texture_info.texture; - - // It's already bound. - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -} - -uint32_t GlHelper::EndFrame() { - DCHECK(frame_texture_); - - mojo::Rect size_rect; - size_rect.width = current_surface_size_.width; - size_rect.height = current_surface_size_.height; - - GLbyte mailbox[GL_MAILBOX_SIZE_CHROMIUM]; - glGenMailboxCHROMIUM(mailbox); - glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox); - GLuint sync_point = glInsertSyncPointCHROMIUM(); - - mojo::FramePtr frame = mojo::Frame::New(); - - // Frame resources: - frame->resources.push_back(mojo::TransferableResource::New()); - mojo::TransferableResource* resource = frame->resources[0].get(); - resource->id = next_resource_id_++; - textures_pending_return_.push_back( - TextureInfo(resource->id, frame_texture_, current_surface_size_)); - frame_texture_ = 0; - // TODO(vtl): This is wrong, but doesn't seem to have an effect. - resource->format = mojo::ResourceFormat::RGBA_8888; - resource->filter = GL_LINEAR; - resource->size = current_surface_size_.Clone(); - mojo::MailboxHolderPtr mailbox_holder = mojo::MailboxHolder::New(); - mailbox_holder->mailbox = mojo::Mailbox::New(); - for (int i = 0; i < GL_MAILBOX_SIZE_CHROMIUM; ++i) - mailbox_holder->mailbox->name.push_back(mailbox[i]); - mailbox_holder->texture_target = GL_TEXTURE_2D; - mailbox_holder->sync_point = sync_point; - resource->mailbox_holder = mailbox_holder.Pass(); - resource->is_repeated = false; - resource->is_software = false; - - // Frame passes: - frame->passes.push_back(mojo::CreateDefaultPass(1, size_rect)); - mojo::Pass* pass = frame->passes[0].get(); - pass->quads.push_back(mojo::Quad::New()); - mojo::Quad* quad = pass->quads[0].get(); - quad->material = mojo::Material::TEXTURE_CONTENT; - quad->rect = size_rect.Clone(); - quad->opaque_rect = size_rect.Clone(); - quad->visible_rect = size_rect.Clone(); - quad->needs_blending = true; - quad->shared_quad_state_index = 0; - quad->texture_quad_state = mojo::TextureQuadState::New(); - mojo::TextureQuadState* texture_state = quad->texture_quad_state.get(); - texture_state->resource_id = resource->id; - texture_state->premultiplied_alpha = true; - texture_state->uv_top_left = mojo::PointF::New(); - texture_state->uv_bottom_right = mojo::PointF::New(); - texture_state->uv_bottom_right->x = 1.f; - texture_state->uv_bottom_right->y = 1.f; - texture_state->background_color = mojo::Color::New(); - texture_state->background_color->rgba = 0; - for (int i = 0; i < 4; ++i) - texture_state->vertex_opacity.push_back(1.f); - texture_state->flipped = flipped_; - pass->shared_quad_states.push_back( - mojo::CreateDefaultSQS(current_surface_size_)); - - surface_->SubmitFrame(local_id_, frame.Pass(), - base::Bind(&GlHelper::SubmitFrameCallback, - weak_factory_.GetWeakPtr(), next_frame_id_)); - - return next_frame_id_++; -} - -GLuint GlHelper::GetFrameTexture() { - DCHECK(frame_texture_); - return frame_texture_; -} - -void GlHelper::ReturnResources( - mojo::Array<mojo::ReturnedResourcePtr> resources) { - DCHECK(!frame_texture_); - - if (mgl_context_ == MGL_NO_CONTEXT) { - DCHECK(textures_pending_return_.empty()); - return; - } - - MGLMakeCurrent(mgl_context_); - - // Note: This quadratic nested loop is OK, since we expect both |resources| - // and |textures_pending_return_| to be small (and |resources| should - // usually have just a single element). - for (size_t i = 0; i < resources.size(); ++i) { - mojo::ReturnedResourcePtr resource = resources[i].Pass(); - DCHECK_EQ(resource->count, 1); - - bool found = false; - for (size_t j = 0; j < textures_pending_return_.size(); j++) { - const TextureInfo& texture_info = textures_pending_return_[j]; - if (texture_info.resource_id == resource->id) { - glWaitSyncPointCHROMIUM(resource->sync_point); - ReturnTexture(texture_info); - textures_pending_return_.erase(textures_pending_return_.begin() + j); - found = true; - break; - } - } - if (!found) { - // If we don't texture ID for it, assume we lost the context. - // TODO(vtl): This may leak (but currently we don't know if the texture is - // still valid). - DVLOG(1) << "Returned texture not found (context lost?)"; - } - } -} - -void GlHelper::EnsureContext() { - DCHECK(!frame_texture_); - - if (mgl_context_ == MGL_NO_CONTEXT) { - DCHECK(textures_pending_return_.empty()); - - mojo::CommandBufferPtr command_buffer; - gpu_->CreateOffscreenGLES2Context(mojo::GetProxy(&command_buffer)); - mgl_context_ = MGLCreateContext( - MGL_API_VERSION_GLES2, - command_buffer.PassInterface().PassHandle().release().value(), nullptr, - &GlHelper::OnContextLostThunk, this, - mojo::Environment::GetDefaultAsyncWaiter()); - CHECK_NE(mgl_context_, MGL_NO_CONTEXT); - } - - MGLMakeCurrent(mgl_context_); -} - -void GlHelper::EnsureSurface() { - DCHECK(!frame_texture_); - DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); - - if (local_id_) { - if (current_surface_size_ == next_surface_size_) - return; - - surface_->DestroySurface(local_id_); - - ClearTextures(); - } - - local_id_++; - surface_->CreateSurface(local_id_); - current_surface_size_ = next_surface_size_; - if (id_namespace_) { - // Don't call the client in the nested context. - base::MessageLoop::current()->task_runner()->PostTask( - FROM_HERE, base::Bind(&GlHelper::CallOnSurfaceIdChanged, - weak_factory_.GetWeakPtr())); - } -} - -void GlHelper::CallOnSurfaceIdChanged() { - DCHECK(id_namespace_ && local_id_); - - auto qualified_id = mojo::SurfaceId::New(); - qualified_id->id_namespace = id_namespace_; - qualified_id->local = local_id_; - client_->OnSurfaceIdChanged(qualified_id.Pass()); -} - -GlHelper::TextureInfo GlHelper::GetTexture() { - DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); - - if (!textures_.empty()) { - TextureInfo rv = textures_.front(); - DCHECK(rv.size == current_surface_size_); - textures_.pop_front(); - glBindTexture(GL_TEXTURE_2D, rv.texture); - return rv; - } - - GLuint texture = 0; - glGenTextures(1, &texture); - DCHECK(texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, texture_format_, current_surface_size_.width, - current_surface_size_.height, 0, texture_format_, - GL_UNSIGNED_BYTE, nullptr); - - return TextureInfo(0, texture, current_surface_size_); -} - -void GlHelper::ReturnTexture(const TextureInfo& texture_info) { - DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); - DCHECK_NE(texture_info.texture, 0u); - - if (texture_info.size == current_surface_size_ && - textures_.size() < kMaxTextures) - textures_.push_back(texture_info); // TODO(vtl): Is |push_front()| better? - else - glDeleteTextures(1, &texture_info.texture); -} - -void GlHelper::ClearTextures() { - DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); - - for (const auto& texture_info : textures_) - glDeleteTextures(1, &texture_info.texture); - - textures_.clear(); -} - -void GlHelper::GetIdNamespaceCallback(uint32_t id_namespace) { - id_namespace_ = id_namespace; - if (local_id_) { - // We're in a callback, so we can just call the client directly. - CallOnSurfaceIdChanged(); - } -} - -// static -void GlHelper::OnContextLostThunk(void* self) { - static_cast<GlHelper*>(self)->OnContextLost(); -} - -void GlHelper::OnContextLost() { - // We shouldn't get this while we're processing a frame. - DCHECK(!frame_texture_); - - DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); - MGLDestroyContext(mgl_context_); - mgl_context_ = MGL_NO_CONTEXT; - - // TODO(vtl): We don't know if any of those textures will be valid when - // returned (if they are), so assume they aren't. - textures_pending_return_.clear(); - - // We're in a callback, so we can just call the client directly. - client_->OnContextLost(); -} - -void GlHelper::SubmitFrameCallback(uint32_t frame_id) { - // We're in a callback, so we can just call the client directly. - client_->OnFrameDisplayed(frame_id); -}
diff --git a/apps/moterm/gl_helper.h b/apps/moterm/gl_helper.h deleted file mode 100644 index a181815..0000000 --- a/apps/moterm/gl_helper.h +++ /dev/null
@@ -1,170 +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. - -#ifndef APPS_MOTERM_GL_HELPER_H_ -#define APPS_MOTERM_GL_HELPER_H_ - -#include <GLES2/gl2.h> -#include <MGL/mgl_types.h> - -#include <deque> -#include <vector> - -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "mojo/services/geometry/interfaces/geometry.mojom.h" -#include "mojo/services/gpu/interfaces/gpu.mojom.h" -#include "mojo/services/surfaces/interfaces/surface_id.mojom.h" -#include "mojo/services/surfaces/interfaces/surfaces.mojom.h" - -namespace mojo { -class Shell; -} - -// A class that helps with drawing surfaces using GL. -class GlHelper : public mojo::ResourceReturner { - public: - class Client { - public: - // Called when the surface ID changes (including the first time it becomes - // available). - virtual void OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) = 0; - - // Called when the GL context is lost. - virtual void OnContextLost() = 0; - - // Called when the frame with ID |frame_id| (from |EndFrame()|) is drawn for - // the first time. Use this to rate-limit draws. - virtual void OnFrameDisplayed(uint32_t frame_id) = 0; - }; - - // Both |client| and |shell| must outlive us. |texture_format| is the texture - // format to use, e.g., |GL_RGBA| or |GL_BGRA_EXT|. |flipped| means that the - // texture is in the usual GL orientation (origin at lower-left). This object - // will create a surface (of initial size |initial_size|), and call the - // client's |OnSurfaceIdChanged()| when it has a surface ID for it (which the - // client can then provide to its |View|). - GlHelper(Client* client, - mojo::Shell* shell, - GLint texture_format, - bool flipped, - const mojo::Size& initial_size); - ~GlHelper() override; - - // Sets the size of the surface. (The surface will only be created at this - // size lazily at the next |StartFrame()|.) - void SetSurfaceSize(const mojo::Size& surface_size); - - // Ensures that a GL context is available and makes it current. (Note that - // this is automatically called by |StartFrame()|, so this is mostly useful - // for creating GL resources outside |StartFrame()|/|EndFrame()|. - void MakeCurrent(); - - // Starts a frame; makes an appropriate GL context current, and binds a - // texture of the current size (creating it if necessary), returning its ID. - // Between |StartFrame()| and |EndFrame()|, the run loop must not be run and - // no other methods of this object other than |MakeCurrent()| and - // |GetFrameTexture()| may be called. This object must not be destroyed after - // |StartFrame()| before calling |EndFrame()|. - void StartFrame(); - // Completes the current frame (started using |StartFrame()|). Before calling - // this, the frame's texture must be bound and the GL context must be current. - // (This is the default state after |StartFrame()|, so nothing has to be done - // unless another texture is bound or another GL context is made current, - // respectively.) Returns an ID for this frame (which will be given to the - // client on |OnFrameDisplayed()|). - uint32_t EndFrame(); - - // Gets the texture that will be used for the current frame, e.g., so that it - // may be (re)bound. Only valid between |StartFrame()| and |EndFrame()|. (Note - // that this texture is automatically bound by |StartFrame()|, so this is - // typically only needed if another texture is bound.) - GLuint GetFrameTexture(); - - private: - struct TextureInfo { - TextureInfo(uint32_t resource_id, GLuint texture, const mojo::Size& size) - : resource_id(resource_id), texture(texture), size(size) {} - - // Only interesting if it's pending return. - uint32_t resource_id; - GLuint texture; - mojo::Size size; - }; - - // |mojo::ResourceReturner|: - void ReturnResources( - mojo::Array<mojo::ReturnedResourcePtr> resources) override; - - // Ensures that we have a GL context and that it is current. - void EnsureContext(); - - // Ensures that we have a surface of the appropriate size. This should only be - // called with a valid GL context which is current (e.g., after calling - // |EnsureContext()|). - void EnsureSurface(); - - // Calls the client's |OnSurfaceIdChanged()| if appropriate (both - // |id_namespace_| and |local_id_| must be set). - void CallOnSurfaceIdChanged(); - - // Texture queue functions. (For all functions, |mgl_context_| should be - // current.) - // Gets and binds a texture of size |current_surface_size_|. - TextureInfo GetTexture(); - // Returns a texture to the queue (or deletes it, if it's not of the right - // size or there are already enough textures in the queue). - void ReturnTexture(const TextureInfo& texture_info); - // Clears all textures (i.e., calls |glDeleteTextures()| on all textures in - // the texture queue). - void ClearTextures(); - - // Callbacks: - - // Callback for |GetIdNamespace()|: - void GetIdNamespaceCallback(uint32_t id_namespace); - - // "Callback" for |MojoGLES2CreateContext()|: - static void OnContextLostThunk(void* self); - void OnContextLost(); - - // Callback for |SubmitFrame()|: - void SubmitFrameCallback(uint32_t frame_id); - - Client* const client_; - const GLint texture_format_; - const bool flipped_; - - mojo::GpuPtr gpu_; - mojo::SurfacePtr surface_; - mojo::Binding<mojo::ResourceReturner> returner_binding_; - - // The size for the surface at the next |StartFrame()|. - mojo::Size next_surface_size_; - - MGLContext mgl_context_; - - std::deque<TextureInfo> textures_; - - uint32_t next_frame_id_; - // The texture that'll be used to draw the current frame. Only valid (nonzero) - // between |StartFrame()| and |EndFrame()|. - GLuint frame_texture_; - - uint32_t id_namespace_; - uint32_t local_id_; - // If |local_id_| is nonzero, there's currently a surface, in which case this - // is its size. - mojo::Size current_surface_size_; - - uint32_t next_resource_id_; - std::vector<TextureInfo> textures_pending_return_; - - base::WeakPtrFactory<GlHelper> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(GlHelper); -}; - -#endif // APPS_MOTERM_GL_HELPER_H_
diff --git a/apps/moterm/gl_helper_test_app.cc b/apps/moterm/gl_helper_test_app.cc deleted file mode 100644 index 7147f6d..0000000 --- a/apps/moterm/gl_helper_test_app.cc +++ /dev/null
@@ -1,279 +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. - -// This is a test application for gl_helper.*, which draws directly to a native -// viewport (without using the view manager). - -#include <GLES2/gl2.h> -#include <math.h> - -#include "apps/moterm/gl_helper.h" -#include "base/bind.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/application/application_runner_chromium.h" -#include "mojo/public/c/system/main.h" -#include "mojo/public/cpp/application/application_impl.h" -#include "mojo/services/geometry/interfaces/geometry.mojom.h" -#include "mojo/services/gpu/interfaces/context_provider.mojom.h" -#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.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" - -namespace { - -GLuint LoadShader(GLenum type, const char* shader_source) { - GLuint shader = glCreateShader(type); - CHECK(shader); - glShaderSource(shader, 1, &shader_source, nullptr); - glCompileShader(shader); - GLint compiled = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); - CHECK(compiled); - return shader; -} - -class GlHelperExampleApp : public mojo::ApplicationDelegate, - public GlHelper::Client { - public: - GlHelperExampleApp() - : application_impl_(), - program_(0), - transform_location_(0), - scale_location_(0), - framebuffer_(0), - vertex_buffer_(0), - draw_number_(0) {} - ~GlHelperExampleApp() override {} - - private: - void OnViewportMetricsReceived(mojo::ViewportMetricsPtr metrics) { - DVLOG(1) << "Viewport metrics received: size = " << metrics->size->width - << "x" << metrics->size->height - << ", device_pixel_ratio = " << metrics->device_pixel_ratio; - - if (metrics->size->width != viewport_size_.width || - metrics->size->height != viewport_size_.height) { - viewport_size_ = *metrics->size; - gl_helper_->SetSurfaceSize(viewport_size_); - } - - native_viewport_->RequestMetrics( - base::Bind(&GlHelperExampleApp::OnViewportMetricsReceived, - base::Unretained(this))); - } - - void InitGlState() { - static const char vertex_shader_source[] = - "attribute vec4 position; \n" - "uniform mat4 transform; \n" - "uniform vec4 scale; \n" - "void main() { \n" - " gl_Position = (position * transform) * scale; \n" - "} \n"; - static const char fragment_shader_source[] = - "precision mediump float; \n" - "void main() { \n" - " gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); \n" - "} \n"; - static const float vertices[] = { - 0.f, 0.5f, -sqrtf(5.f) / 4.f, -0.25f, sqrtf(5.f) / 4.f, -0.25f}; - GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER, vertex_shader_source); - GLuint fragment_shader = - LoadShader(GL_FRAGMENT_SHADER, fragment_shader_source); - program_ = glCreateProgram(); - CHECK(program_); - glAttachShader(program_, vertex_shader); - glAttachShader(program_, fragment_shader); - glBindAttribLocation(program_, kPositionLocation, "position"); - glLinkProgram(program_); - glDeleteShader(vertex_shader); - glDeleteShader(fragment_shader); - GLint linked = 0; - glGetProgramiv(program_, GL_LINK_STATUS, &linked); - CHECK(linked); - transform_location_ = glGetUniformLocation(program_, "transform"); - CHECK_NE(transform_location_, -1); - scale_location_ = glGetUniformLocation(program_, "scale"); - CHECK_NE(scale_location_, -1); - - glGenFramebuffers(1, &framebuffer_); - CHECK(framebuffer_); - - glGenBuffers(1, &vertex_buffer_); - CHECK(vertex_buffer_); - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glClearColor(0.f, 1.f, 0.f, 1.f); - } - - void Draw() { - DCHECK(gl_helper_); - - gl_helper_->StartFrame(); - - if (!program_) - InitGlState(); - - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - gl_helper_->GetFrameTexture(), 0); - glViewport(0, 0, viewport_size_.width, viewport_size_.height); - - glClear(GL_COLOR_BUFFER_BIT); - - glUseProgram(program_); - - float angle = static_cast<float>(draw_number_ % 360) * M_PI / 180; - float transform[4][4] = {{cosf(angle), sinf(angle), 0.f, 0.f}, - {-sinf(angle), cosf(angle), 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {0.f, 0.f, 0.f, 1.f}}; - glUniformMatrix4fv(transform_location_, 1, GL_FALSE, &transform[0][0]); - if (viewport_size_.width >= viewport_size_.height) { - glUniform4f(scale_location_, static_cast<float>(viewport_size_.height) / - static_cast<float>(viewport_size_.width), - 1.f, 1.f, 1.f); - } else { - glUniform4f(scale_location_, 1.f, - static_cast<float>(viewport_size_.width) / - static_cast<float>(viewport_size_.height), - 1.f, 1.f); - } - - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); - glVertexAttribPointer(kPositionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); - glEnableVertexAttribArray(kPositionLocation); - glDrawArrays(GL_TRIANGLES, 0, 3); - - gl_helper_->EndFrame(); - - draw_number_++; - } - - void SubmitFrame() { - DCHECK(surface_id_.local); - DCHECK(surface_id_.id_namespace); - - mojo::FramePtr frame = mojo::Frame::New(); - frame->resources = mojo::Array<mojo::TransferableResourcePtr>::New(0); - frame->passes = mojo::Array<mojo::PassPtr>::New(1); - mojo::PassPtr pass = mojo::Pass::New(); - pass->id = 0; - mojo::Rect viewport_rect; - viewport_rect.width = viewport_size_.width; - viewport_rect.height = viewport_size_.height; - pass->output_rect = viewport_rect.Clone(); - pass->damage_rect = viewport_rect.Clone(); - mojo::Transform identity_transform; - identity_transform.matrix = mojo::Array<float>::New(16); - for (size_t i = 0; i < 4; i++) { - for (size_t j = 0; j < 4; j++) - identity_transform.matrix[4 * i + j] = (i == j); - } - pass->transform_to_root_target = identity_transform.Clone(); - pass->has_transparent_background = false; - pass->quads = mojo::Array<mojo::QuadPtr>::New(1); - mojo::QuadPtr quad = mojo::Quad::New(); - quad->material = mojo::Material::SURFACE_CONTENT; - quad->rect = viewport_rect.Clone(); - quad->opaque_rect = viewport_rect.Clone(); - quad->visible_rect = viewport_rect.Clone(); - quad->needs_blending = false; - quad->shared_quad_state_index = 0; - mojo::SurfaceQuadStatePtr surface_quad_state = - mojo::SurfaceQuadState::New(); - surface_quad_state->surface = surface_id_.Clone(); - quad->surface_quad_state = surface_quad_state.Pass(); - - pass->quads[0] = quad.Pass(); - pass->shared_quad_states = mojo::Array<mojo::SharedQuadStatePtr>::New(1); - mojo::SharedQuadStatePtr shared_quad_state = mojo::SharedQuadState::New(); - shared_quad_state->content_to_target_transform = identity_transform.Clone(); - shared_quad_state->content_bounds = viewport_size_.Clone(); - shared_quad_state->visible_content_rect = viewport_rect.Clone(); - shared_quad_state->clip_rect = viewport_rect.Clone(); - shared_quad_state->is_clipped = false; - shared_quad_state->opacity = 1; - shared_quad_state->blend_mode = mojo::SkXfermode::kSrc_Mode; - shared_quad_state->sorting_context_id = 0; - pass->shared_quad_states[0] = shared_quad_state.Pass(); - frame->passes[0] = pass.Pass(); - - DCHECK(display_); - display_->SubmitFrame(frame.Pass(), mojo::Display::SubmitFrameCallback()); - } - - // |mojo::ApplicationDelegate|: - void Initialize(mojo::ApplicationImpl* application_impl) override { - DCHECK(!application_impl_); - application_impl_ = application_impl; - - application_impl->ConnectToService("mojo:native_viewport_service", - &native_viewport_); - viewport_size_.width = 800; - viewport_size_.height = 600; - native_viewport_->Create( - viewport_size_.Clone(), mojo::SurfaceConfiguration::New(), - base::Bind(&GlHelperExampleApp::OnViewportMetricsReceived, - base::Unretained(this))); - native_viewport_->Show(); - - mojo::ContextProviderPtr context_provider; - native_viewport_->GetContextProvider(GetProxy(&context_provider)); - - mojo::DisplayFactoryPtr display_factory; - application_impl_->ConnectToService("mojo:surfaces_service", - &display_factory); - display_factory->Create(context_provider.Pass(), nullptr, - GetProxy(&display_)); - - gl_helper_.reset(new GlHelper(this, application_impl_->shell(), GL_RGBA, - false, viewport_size_)); - Draw(); - } - - // |GlHelper::Client|: - void OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) override { - surface_id_ = *surface_id; - SubmitFrame(); - } - - void OnContextLost() override { CHECK(false) << "Oops, lost context."; } - - void OnFrameDisplayed(uint32_t frame_id) override { Draw(); } - - static const GLuint kPositionLocation = 0; - - mojo::ApplicationImpl* application_impl_; - mojo::NativeViewportPtr native_viewport_; - mojo::Size viewport_size_; - mojo::DisplayPtr display_; - scoped_ptr<GlHelper> gl_helper_; - - mojo::SurfaceId surface_id_; - - // GL-related state: - GLuint program_; - GLint transform_location_; - GLint scale_location_; - GLuint framebuffer_; - GLuint vertex_buffer_; - - uint64_t draw_number_; - - DISALLOW_COPY_AND_ASSIGN(GlHelperExampleApp); -}; - -const GLuint GlHelperExampleApp::kPositionLocation; - -} // namespace - -MojoResult MojoMain(MojoHandle application_request) { - mojo::ApplicationRunnerChromium runner(new GlHelperExampleApp()); - return runner.Run(application_request); -}
diff --git a/apps/moterm/gl_helper_unittest.cc b/apps/moterm/gl_helper_unittest.cc deleted file mode 100644 index 409aab6..0000000 --- a/apps/moterm/gl_helper_unittest.cc +++ /dev/null
@@ -1,169 +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. - -// Note: This test depends on |GlHelper|, which depends on an implementation of -// mojo:native_viewport_service and mojo:surfaces_service. - -#include "apps/moterm/gl_helper.h" - -#include <GLES2/gl2.h> - -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "mojo/public/cpp/application/application_impl.h" -#include "mojo/public/cpp/application/application_test_base.h" -#include "mojo/services/surfaces/interfaces/surface_id.mojom.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -class GlHelperTest : public mojo::test::ApplicationTestBase, - public GlHelper::Client { - public: - GlHelperTest() - : surface_id_changed_call_count_(0), - on_frame_displayed_call_count_(0), - last_frame_id_(0) {} - ~GlHelperTest() override {} - - void SetUp() override { - mojo::test::ApplicationTestBase::SetUp(); - - initial_size_.width = 100; - initial_size_.height = 100; - gl_helper_.reset(new GlHelper(this, application_impl()->shell(), GL_RGBA, - false, initial_size_)); - } - - protected: - const mojo::Size& initial_size() const { return initial_size_; } - GlHelper* gl_helper() { return gl_helper_.get(); } - - unsigned surface_id_changed_call_count() const { - return surface_id_changed_call_count_; - } - const mojo::SurfaceId* last_surface_id() const { - return last_surface_id_.get(); - } - unsigned on_frame_displayed_call_count() const { - return on_frame_displayed_call_count_; - } - uint32_t last_frame_id() const { return last_frame_id_; } - - private: - // |GlHelper::Client|: - void OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) override { - surface_id_changed_call_count_++; - last_surface_id_ = surface_id.Pass(); - base::MessageLoop::current()->Quit(); - } - - void OnContextLost() override { - // We don't currently test this, but we'll get it on teardown. - // TODO(vtl): We probably should figure out how to test this explicitly. - } - - void OnFrameDisplayed(uint32_t frame_id) override { - on_frame_displayed_call_count_++; - last_frame_id_ = frame_id; - base::MessageLoop::current()->Quit(); - } - - mojo::Size initial_size_; - scoped_ptr<GlHelper> gl_helper_; - - unsigned surface_id_changed_call_count_; - mojo::SurfaceIdPtr last_surface_id_; - unsigned on_frame_displayed_call_count_; - uint32_t last_frame_id_; - - DISALLOW_COPY_AND_ASSIGN(GlHelperTest); -}; - -TEST_F(GlHelperTest, Basic) { - gl_helper()->StartFrame(); - EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); - - scoped_ptr<unsigned char[]> bitmap( - new unsigned char[initial_size().width * initial_size().height * 4]); - memset(bitmap.get(), 123, initial_size().width * initial_size().height * 4); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, initial_size().width, - initial_size().height, GL_RGBA, GL_UNSIGNED_BYTE, - bitmap.get()); - EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); - - uint32_t frame_id = gl_helper()->EndFrame(); - - // We should get |OnSurfaceIdChanged()| and then |OnFrameDisplayed()|. - while (surface_id_changed_call_count() < 1u || - on_frame_displayed_call_count() < 1u) - base::MessageLoop::current()->Run(); - - EXPECT_EQ(1u, surface_id_changed_call_count()); - EXPECT_TRUE(last_surface_id()); - EXPECT_NE(last_surface_id()->local, 0u); - EXPECT_NE(last_surface_id()->id_namespace, 0u); - EXPECT_EQ(1u, on_frame_displayed_call_count()); - EXPECT_EQ(frame_id, last_frame_id()); -} - -TEST_F(GlHelperTest, SetSurfaceSizeAndMakeCurrent) { - // It only creates surfaces lazily, so we have to start/end a frame. - gl_helper()->StartFrame(); - GLuint frame_id = gl_helper()->EndFrame(); - while (surface_id_changed_call_count() < 1u || - on_frame_displayed_call_count() < 1u) - base::MessageLoop::current()->Run(); - EXPECT_EQ(1u, surface_id_changed_call_count()); - mojo::SurfaceId surface_id = *last_surface_id(); - EXPECT_EQ(1u, on_frame_displayed_call_count()); - EXPECT_EQ(frame_id, last_frame_id()); - - // Set a new surface size. - mojo::Size new_size; - new_size.width = initial_size().width * 2; - new_size.height = initial_size().height + 50; - gl_helper()->SetSurfaceSize(new_size); - - gl_helper()->StartFrame(); - EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); - - GLuint frame_texture = gl_helper()->GetFrameTexture(); - EXPECT_NE(frame_texture, 0u); - - // Binding it should be OK. - glBindTexture(GL_TEXTURE_2D, frame_texture); - EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); - - scoped_ptr<unsigned char[]> bitmap( - new unsigned char[new_size.width * new_size.height * 4]); - memset(bitmap.get(), 123, new_size.width * new_size.height * 4); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, new_size.width, new_size.height, - GL_RGBA, GL_UNSIGNED_BYTE, bitmap.get()); - EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); - - frame_id = gl_helper()->EndFrame(); - EXPECT_NE(frame_id, last_frame_id()); - - // We should get another |OnSurfaceIdChanged()| and another - // |OnFrameDisplayed()|. - while (surface_id_changed_call_count() < 2u || - on_frame_displayed_call_count() < 2u) - base::MessageLoop::current()->Run(); - EXPECT_EQ(2u, surface_id_changed_call_count()); - EXPECT_TRUE(last_surface_id()); - // Only the local part of the surface ID should have changed. - EXPECT_NE(last_surface_id()->local, surface_id.local); - EXPECT_EQ(surface_id.id_namespace, last_surface_id()->id_namespace); - EXPECT_EQ(2u, on_frame_displayed_call_count()); - EXPECT_EQ(frame_id, last_frame_id()); - - // An out-of-the-blue |MakeCurrent()| should work. - gl_helper()->MakeCurrent(); - EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); -} - -} // namespace
diff --git a/apps/moterm/main.cc b/apps/moterm/main.cc new file mode 100644 index 0000000..abeb499 --- /dev/null +++ b/apps/moterm/main.cc
@@ -0,0 +1,16 @@ +// 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. + +// This is the "main" for the embeddable Moterm terminal view, which provides +// services to the thing embedding it. (This is not very useful as a "top-level" +// application.) + +#include "apps/moterm/moterm_app.h" +#include "mojo/application/application_runner_chromium.h" +#include "mojo/public/c/system/main.h" + +MojoResult MojoMain(MojoHandle application_request) { + mojo::ApplicationRunnerChromium runner(new MotermApp()); + return runner.Run(application_request); +}
diff --git a/apps/moterm/moterm_app.cc b/apps/moterm/moterm_app.cc new file mode 100644 index 0000000..efdb0fa --- /dev/null +++ b/apps/moterm/moterm_app.cc
@@ -0,0 +1,20 @@ +// 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 "apps/moterm/moterm_app.h" + +#include "apps/moterm/moterm_view.h" + +MotermApp::MotermApp() {} + +MotermApp::~MotermApp() {} + +bool MotermApp::CreateView( + const std::string& connection_url, + mojo::InterfaceRequest<mojo::ServiceProvider> services, + mojo::ServiceProviderPtr exposed_services, + const mojo::ui::ViewProvider::CreateViewCallback& callback) { + new MotermView(app_impl(), services.Pass(), callback); + return true; +}
diff --git a/apps/moterm/moterm_app.h b/apps/moterm/moterm_app.h new file mode 100644 index 0000000..0b4aead --- /dev/null +++ b/apps/moterm/moterm_app.h
@@ -0,0 +1,26 @@ +// 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 APPS_MOTERM_MOTERM_APP_H_ +#define APPS_MOTERM_MOTERM_APP_H_ + +#include "base/macros.h" +#include "mojo/ui/view_provider_app.h" + +class MotermApp : public mojo::ui::ViewProviderApp { + public: + MotermApp(); + ~MotermApp() override; + + bool CreateView( + const std::string& connection_url, + mojo::InterfaceRequest<mojo::ServiceProvider> services, + mojo::ServiceProviderPtr exposed_services, + const mojo::ui::ViewProvider::CreateViewCallback& callback) override; + + private: + DISALLOW_COPY_AND_ASSIGN(MotermApp); +}; + +#endif // APPS_MOTERM_MOTERM_APP_H_
diff --git a/apps/moterm/moterm_main.cc b/apps/moterm/moterm_main.cc deleted file mode 100644 index 299a3b4..0000000 --- a/apps/moterm/moterm_main.cc +++ /dev/null
@@ -1,65 +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. - -// This is the "main" for the embeddable Moterm terminal view, which provides -// services to the thing embedding it. (This is not very useful as a "top-level" -// application.) - -#include "apps/moterm/moterm_view.h" -#include "base/logging.h" -#include "base/macros.h" -#include "mojo/application/application_runner_chromium.h" -#include "mojo/public/c/system/main.h" -#include "mojo/public/cpp/application/application_connection.h" -#include "mojo/public/cpp/application/application_delegate.h" -#include "mojo/public/cpp/application/application_impl.h" -#include "mojo/services/view_manager/cpp/view_manager.h" -#include "mojo/services/view_manager/cpp/view_manager_client_factory.h" -#include "mojo/services/view_manager/cpp/view_manager_delegate.h" - -namespace { - -class Moterm : public mojo::ApplicationDelegate, - public mojo::ViewManagerDelegate { - public: - Moterm() : application_impl_() {} - ~Moterm() override {} - - private: - // |mojo::ApplicationDelegate|: - void Initialize(mojo::ApplicationImpl* application_impl) override { - DCHECK(!application_impl_); - application_impl_ = application_impl; - view_manager_client_factory_.reset( - new mojo::ViewManagerClientFactory(application_impl_->shell(), this)); - } - - bool ConfigureIncomingConnection( - mojo::ApplicationConnection* connection) override { - connection->AddService(view_manager_client_factory_.get()); - return true; - } - - // |mojo::ViewManagerDelegate|: - void OnEmbed(mojo::View* root, - mojo::InterfaceRequest<mojo::ServiceProvider> services, - mojo::ServiceProviderPtr exposed_services) override { - new MotermView(application_impl_->shell(), root, services.Pass()); - } - - void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override { - } - - mojo::ApplicationImpl* application_impl_; - scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_; - - DISALLOW_COPY_AND_ASSIGN(Moterm); -}; - -} // namespace - -MojoResult MojoMain(MojoHandle application_request) { - mojo::ApplicationRunnerChromium runner(new Moterm()); - return runner.Run(application_request); -}
diff --git a/apps/moterm/moterm_view.cc b/apps/moterm/moterm_view.cc index 838b817..4f992cc 100644 --- a/apps/moterm/moterm_view.cc +++ b/apps/moterm/moterm_view.cc
@@ -15,14 +15,14 @@ #include <string> #include "apps/moterm/key_util.h" +#include "base/bind.h" #include "base/logging.h" +#include "mojo/public/cpp/application/connect.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/services/files/interfaces/file.mojom.h" #include "mojo/services/files/interfaces/types.mojom.h" -#include "mojo/services/input_events/interfaces/input_event_constants.mojom.h" -#include "mojo/services/input_events/interfaces/input_events.mojom.h" #include "mojo/services/terminal/interfaces/terminal_client.mojom.h" -#include "skia/ext/refptr.h" +#include "mojo/skia/ganesh_texture_surface.h" #include "third_party/dejavu-fonts-ttf-2.34/kDejaVuSansMonoRegular.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -33,32 +33,17 @@ #include "third_party/skia/include/core/SkStream.h" #include "third_party/skia/include/core/SkXfermode.h" -namespace { - -const GLint kTextureFormat = - (kN32_SkColorType == kRGBA_8888_SkColorType) ? GL_RGBA : GL_BGRA_EXT; - -mojo::Size RectToSize(const mojo::Rect& rect) { - mojo::Size size; - size.width = rect.width; - size.height = rect.height; - return size; -} - -} // namespace +constexpr uint32_t kMotermImageResourceId = 1; +constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId; MotermView::MotermView( - mojo::Shell* shell, - mojo::View* view, - mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request) - : view_(view), - gl_helper_(this, - shell, - kTextureFormat, - false, - RectToSize(view->bounds())), + mojo::ApplicationImpl* app_impl, + mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request, + const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback) + : GaneshView(app_impl, "Moterm", create_view_callback), + choreographer_(scene(), this), + input_handler_(view_service_provider(), this), model_(MotermModel::Size(240, 160), MotermModel::Size(24, 80), this), - frame_pending_(false), force_next_draw_(false), ascent_(0), line_height_(0), @@ -89,11 +74,6 @@ // monospace. advance_width_ = static_cast<int>(ceilf(fg_paint.measureText("X", 1))); DCHECK_GT(advance_width_, 0); - - view_->AddObserver(this); - - // Force an initial draw. - Draw(true); } MotermView::~MotermView() { @@ -101,39 +81,27 @@ driver_->Detach(); } -void MotermView::OnViewDestroyed(mojo::View* view) { - DCHECK_EQ(view, view_); - view_->RemoveObserver(this); - delete this; +void MotermView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, + mojo::Array<uint32_t> children_needing_layout, + const OnLayoutCallback& callback) { + view_size_.width = layout_params->constraints->max_width; + view_size_.height = layout_params->constraints->max_height; + + ScheduleDraw(true); + + auto info = mojo::ui::ViewLayoutResult::New(); + info->size = view_size_.Clone(); + callback.Run(info.Pass()); } -void MotermView::OnViewBoundsChanged(mojo::View* view, - const mojo::Rect& old_bounds, - const mojo::Rect& new_bounds) { - DCHECK_EQ(view, view_); - gl_helper_.SetSurfaceSize(RectToSize(view_->bounds())); - bitmap_device_.clear(); - Draw(true); -} - -void MotermView::OnViewInputEvent(mojo::View* view, - const mojo::EventPtr& event) { - if (event->action == mojo::EventType::KEY_PRESSED) - OnKeyPressed(event); -} - -void MotermView::OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) { - view_->SetSurfaceId(surface_id.Pass()); -} - -void MotermView::OnContextLost() { - // TODO(vtl): We'll need to force a draw when we regain a context. -} - -void MotermView::OnFrameDisplayed(uint32_t frame_id) { - DCHECK(frame_pending_); - frame_pending_ = false; - Draw(false); +void MotermView::OnEvent(mojo::EventPtr event, + const OnEventCallback& callback) { + if (event->action == mojo::EventType::KEY_PRESSED) { + OnKeyPressed(event.Pass()); + callback.Run(true); + } else { + callback.Run(false); + } } void MotermView::OnResponse(const void* buf, size_t size) { @@ -147,7 +115,7 @@ void MotermView::OnDataReceived(const void* bytes, size_t num_bytes) { model_.ProcessInput(bytes, num_bytes, &model_state_changes_); - Draw(false); + ScheduleDraw(false); } void MotermView::OnClosed() { @@ -225,51 +193,65 @@ bool reset, const SetSizeCallback& callback) { if (!rows) { - rows = std::max(1u, std::min(MotermModel::kMaxRows, - static_cast<uint32_t>(view_->bounds().height) / - line_height_)); + rows = std::max( + 1u, std::min(MotermModel::kMaxRows, + static_cast<uint32_t>(view_size_.height) / line_height_)); } if (!columns) { - columns = - std::max(1u, std::min(MotermModel::kMaxColumns, - static_cast<uint32_t>(view_->bounds().width) / - advance_width_)); + columns = std::max( + 1u, std::min(MotermModel::kMaxColumns, + static_cast<uint32_t>(view_size_.width) / advance_width_)); } model_.SetSize(MotermModel::Size(rows, columns), reset); callback.Run(mojo::files::Error::OK, rows, columns); - Draw(false); + ScheduleDraw(false); } -void MotermView::Draw(bool force) { - // TODO(vtl): See TODO above |frame_pending_| in the class declaration. - if (frame_pending_) { +void MotermView::ScheduleDraw(bool force) { + if (view_size_.width == 0 || view_size_.height == 0 || + (!model_state_changes_.IsDirty() && !force && !force_next_draw_)) { force_next_draw_ |= force; return; } - - force |= force_next_draw_; force_next_draw_ = false; + choreographer_.ScheduleDraw(); +} - if (!force && !model_state_changes_.IsDirty()) - return; - - // TODO(vtl): If |!force|, draw only the dirty region(s)? +void MotermView::OnDraw(const mojo::gfx::composition::FrameInfo& frame_info, + const base::TimeDelta& time_delta) { + // TODO(vtl): Draw only the dirty region(s)? model_state_changes_.Reset(); - int32_t width = view_->bounds().width; - int32_t height = view_->bounds().height; - DCHECK_GT(width, 0); - DCHECK_GT(height, 0); + mojo::Rect bounds; + bounds.width = view_size_.width; + bounds.height = view_size_.height; - if (!bitmap_device_) { - bitmap_device_ = skia::AdoptRef(SkBitmapDevice::Create(SkImageInfo::Make( - width, height, kN32_SkColorType, kOpaque_SkAlphaType))); - } + auto update = mojo::gfx::composition::SceneUpdate::New(); + mojo::gfx::composition::ResourcePtr moterm_resource = + ganesh_renderer()->DrawCanvas( + view_size_, + base::Bind(&MotermView::DrawContent, base::Unretained(this))); + DCHECK(moterm_resource); + update->resources.insert(kMotermImageResourceId, moterm_resource.Pass()); - SkCanvas canvas(bitmap_device_.get()); - canvas.clear(SK_ColorBLACK); + 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 = bounds.Clone(); + root_node->op->get_image()->image_resource_id = kMotermImageResourceId; + update->nodes.insert(kRootNodeId, root_node.Pass()); + + auto metadata = mojo::gfx::composition::SceneMetadata::New(); + metadata->presentation_time = frame_info.presentation_time; + + scene()->Update(update.Pass()); + scene()->Publish(metadata.Pass()); +} + +void MotermView::DrawContent(SkCanvas* canvas) { + canvas->clear(SK_ColorBLACK); SkPaint bg_paint; bg_paint.setStyle(SkPaint::kFill_Style); @@ -291,8 +273,8 @@ bg_paint.setColor(SkColorSetRGB(ch.background_color.red, ch.background_color.green, ch.background_color.blue)); - canvas.drawRect(SkRect::MakeXYWH(x, y, advance_width_, line_height_), - bg_paint); + canvas->drawRect(SkRect::MakeXYWH(x, y, advance_width_, line_height_), + bg_paint); // Paint the foreground. if (ch.code_point) { @@ -308,8 +290,8 @@ ch.foreground_color.green, ch.foreground_color.blue)); - canvas.drawText(&ch.code_point, sizeof(ch.code_point), x, y + ascent_, - fg_paint); + canvas->drawText(&ch.code_point, sizeof(ch.code_point), x, y + ascent_, + fg_paint); } } } @@ -324,27 +306,15 @@ // focused and/or active. bg_paint.setColor(SK_ColorWHITE); bg_paint.setXfermodeMode(SkXfermode::kDifference_Mode); - canvas.drawRect(SkRect::MakeXYWH(cursor_pos.column * advance_width_, - cursor_pos.row * line_height_, - advance_width_, line_height_), - bg_paint); + canvas->drawRect(SkRect::MakeXYWH(cursor_pos.column * advance_width_, + cursor_pos.row * line_height_, + advance_width_, line_height_), + bg_paint); } - - canvas.flush(); - - const SkBitmap& bitmap(bitmap_device_->accessBitmap(false)); - // TODO(vtl): Do we need really need to lock/unlock pixels? - SkAutoLockPixels pixel_locker(bitmap); - - gl_helper_.StartFrame(); - // (The texture is already bound.) - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, kTextureFormat, - GL_UNSIGNED_BYTE, bitmap.getPixels()); - gl_helper_.EndFrame(); - frame_pending_ = true; + canvas->flush(); } -void MotermView::OnKeyPressed(const mojo::EventPtr& key_event) { +void MotermView::OnKeyPressed(mojo::EventPtr key_event) { std::string input_sequence = GetInputSequenceForKeyPressedEvent(*key_event, keypad_application_mode_); if (input_sequence.empty())
diff --git a/apps/moterm/moterm_view.h b/apps/moterm/moterm_view.h index 4929e4a..4569f3d 100644 --- a/apps/moterm/moterm_view.h +++ b/apps/moterm/moterm_view.h
@@ -5,7 +5,8 @@ #ifndef APPS_MOTERM_MOTERM_VIEW_H_ #define APPS_MOTERM_MOTERM_VIEW_H_ -#include "apps/moterm/gl_helper.h" +#include <memory> + #include "apps/moterm/moterm_driver.h" #include "apps/moterm/moterm_model.h" #include "base/macros.h" @@ -15,42 +16,41 @@ #include "mojo/public/cpp/application/service_provider_impl.h" #include "mojo/public/cpp/bindings/callback.h" #include "mojo/public/cpp/bindings/interface_request.h" -#include "mojo/services/surfaces/interfaces/surface_id.mojom.h" +#include "mojo/services/input_events/interfaces/input_event_constants.mojom.h" +#include "mojo/services/input_events/interfaces/input_events.mojom.h" #include "mojo/services/terminal/interfaces/terminal.mojom.h" -#include "mojo/services/view_manager/cpp/view_observer.h" -#include "skia/ext/refptr.h" +#include "mojo/ui/choreographer.h" +#include "mojo/ui/ganesh_view.h" +#include "mojo/ui/input_handler.h" #include "third_party/skia/include/core/SkBitmapDevice.h" #include "third_party/skia/include/core/SkTypeface.h" -namespace mojo { -class Shell; -} - -class MotermView : public mojo::ViewObserver, - public GlHelper::Client, +class MotermView : public mojo::ui::GaneshView, + public mojo::ui::ChoreographerDelegate, + public mojo::ui::InputListener, public MotermModel::Delegate, public MotermDriver::Client, public mojo::InterfaceFactory<mojo::terminal::Terminal>, public mojo::terminal::Terminal { public: MotermView( - mojo::Shell* shell, - mojo::View* view, - mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request); + mojo::ApplicationImpl* app_impl, + mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request, + const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback); ~MotermView() override; private: - // |mojo::ViewObserver|: - void OnViewDestroyed(mojo::View* view) override; - void OnViewBoundsChanged(mojo::View* view, - const mojo::Rect& old_bounds, - const mojo::Rect& new_bounds) override; - void OnViewInputEvent(mojo::View* view, const mojo::EventPtr& event) override; + // |mojo::ui::GaneshView|: + void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, + mojo::Array<uint32_t> children_needing_layout, + const OnLayoutCallback& callback) override; - // |GlHelper::Client|: - void OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) override; - void OnContextLost() override; - void OnFrameDisplayed(uint32_t frame_id) override; + // |mojo::ui::ChoreographerDelegate|: + void OnDraw(const mojo::gfx::composition::FrameInfo& frame_info, + const base::TimeDelta& time_delta) override; + + // |mojo::ui::InputListener|: + void OnEvent(mojo::EventPtr event, const OnEventCallback& callback) override; // |MotermModel::Delegate|: void OnResponse(const void* buf, size_t size) override; @@ -81,12 +81,14 @@ // If |force| is true, it will draw everything. Otherwise it will draw only if // |model_state_changes_| is dirty. - void Draw(bool force); + void ScheduleDraw(bool force); + void DrawContent(SkCanvas* canvas); - void OnKeyPressed(const mojo::EventPtr& key_event); + void OnKeyPressed(mojo::EventPtr key_event); - mojo::View* const view_; - GlHelper gl_helper_; + mojo::Size view_size_; + mojo::ui::Choreographer choreographer_; + mojo::ui::InputHandler input_handler_; // TODO(vtl): Consider the structure of this app. Do we really want the "view" // owning the model? @@ -102,12 +104,6 @@ mojo::ServiceProviderImpl service_provider_impl_; mojo::BindingSet<mojo::terminal::Terminal> terminal_bindings_; - // TODO(vtl): For some reason, drawing while a frame is already pending (i.e., - // we've submitted it but haven't gotten a callback) interacts badly with - // resizing -- sometimes this results in us losing all future - // |OnViewBoundsChanged()| messages. So, for now, don't submit frames in that - // case. - bool frame_pending_; // If we skip drawing despite being forced to, we should force the next draw. bool force_next_draw_; @@ -117,8 +113,6 @@ int line_height_; int advance_width_; - skia::RefPtr<SkBitmapDevice> bitmap_device_; - // Keyboard state. bool keypad_application_mode_;
diff --git a/examples/BUILD.gn b/examples/BUILD.gn index 05c5a7c..2eb75dc 100644 --- a/examples/BUILD.gn +++ b/examples/BUILD.gn
@@ -36,6 +36,7 @@ deps = [ ":portable_examples", + "//examples/moterm_example_app", "//examples/ui/noodles", "//examples/ui/png_viewer", "//examples/ui/shapes",
diff --git a/examples/moterm_example_app/BUILD.gn b/examples/moterm_example_app/BUILD.gn index 0737a99..dc3701d 100644 --- a/examples/moterm_example_app/BUILD.gn +++ b/examples/moterm_example_app/BUILD.gn
@@ -12,12 +12,13 @@ deps = [ "//base", "//mojo/application", + "//mojo/common", "//mojo/public/cpp/bindings", "//mojo/public/cpp/utility", "//mojo/public/interfaces/application", "//mojo/services/files/interfaces", "//mojo/services/geometry/interfaces", "//mojo/services/terminal/interfaces", - "//mojo/services/view_manager/cpp", + "//mojo/services/ui/views/interfaces", ] }
diff --git a/examples/moterm_example_app/README.md b/examples/moterm_example_app/README.md index b31d0d3..85716cc 100644 --- a/examples/moterm_example_app/README.md +++ b/examples/moterm_example_app/README.md
@@ -9,7 +9,8 @@ On Linux, run it in the usual way, e.g.: - $ mojo/devtools/common/mojo_run --enable-multiprocess "mojo:window_manager mojo:moterm_example_app" + $ mojo/devtools/common/mojo_run --enable-multiprocess \ + "mojo:launcher mojo:moterm_example_app" You'll probably need to click on the window to give it keyboard focus. You may also want to resize the window (especially horizontally) to make it bigger.
diff --git a/examples/moterm_example_app/moterm_example_app.cc b/examples/moterm_example_app/moterm_example_app.cc index 9fabc34..edab7ac 100644 --- a/examples/moterm_example_app/moterm_example_app.cc +++ b/examples/moterm_example_app/moterm_example_app.cc
@@ -15,23 +15,23 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "mojo/application/application_runner_chromium.h" +#include "mojo/common/binding_set.h" #include "mojo/public/c/system/main.h" #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.h" #include "mojo/public/cpp/application/connect.h" #include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/interfaces/application/service_provider.mojom.h" #include "mojo/public/interfaces/application/shell.mojom.h" #include "mojo/services/files/interfaces/file.mojom.h" #include "mojo/services/files/interfaces/types.mojom.h" #include "mojo/services/terminal/interfaces/terminal.mojom.h" #include "mojo/services/terminal/interfaces/terminal_client.mojom.h" -#include "mojo/services/view_manager/cpp/view.h" -#include "mojo/services/view_manager/cpp/view_manager.h" -#include "mojo/services/view_manager/cpp/view_manager_client_factory.h" -#include "mojo/services/view_manager/cpp/view_manager_delegate.h" -#include "mojo/services/view_manager/cpp/view_observer.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" // Kind of like |fputs()| (doesn't wait for result). void Fputs(mojo::files::File* file, const char* s) { @@ -43,25 +43,32 @@ mojo::files::File::WriteCallback()); } -class MotermExampleAppView : public mojo::ViewObserver { +class MotermExampleAppView { public: - explicit MotermExampleAppView(mojo::Shell* shell, mojo::View* view) - : shell_(shell), view_(view), moterm_view_(), weak_factory_(this) { - view_->AddObserver(this); + MotermExampleAppView( + mojo::Shell* shell, + const mojo::ui::ViewProvider::CreateViewCallback& callback) + : shell_(shell), weak_factory_(this) { + // Connect to the moterm app. + LOG(INFO) << "Connecting to moterm"; + mojo::ServiceProviderPtr moterm_app; + shell->ConnectToApplication("mojo:moterm", GetProxy(&moterm_app), nullptr); - moterm_view_ = view_->view_manager()->CreateView(); - view_->AddChild(moterm_view_); - moterm_view_->SetBounds(view_->bounds()); - moterm_view_->SetVisible(true); - mojo::ServiceProviderPtr moterm_sp; - moterm_view_->Embed("mojo:moterm", GetProxy(&moterm_sp), nullptr); - moterm_sp->ConnectToService(mojo::terminal::Terminal::Name_, - GetProxy(&moterm_terminal_).PassMessagePipe()); - Resize(); + // Create the moterm view and pass it back to the client directly. + mojo::ConnectToService(moterm_app.get(), &moterm_view_provider_); + mojo::ServiceProviderPtr moterm_service_provider; + moterm_view_provider_->CreateView(GetProxy(&moterm_service_provider), + nullptr, callback); + + // Connect to the moterm terminal service associated with the view + // we just created. + mojo::ConnectToService(moterm_service_provider.get(), &moterm_terminal_); + + // Start running. StartPrompt(true); } - ~MotermExampleAppView() override {} + ~MotermExampleAppView() {} private: void Resize() { @@ -135,24 +142,9 @@ StartPrompt(false); } - // |ViewObserver|: - void OnViewDestroyed(mojo::View* view) override { - DCHECK(view == view_); - delete this; - } - - void OnViewBoundsChanged(mojo::View* view, - const mojo::Rect& old_bounds, - const mojo::Rect& new_bounds) override { - DCHECK_EQ(view, view_); - moterm_view_->SetBounds(view_->bounds()); - Resize(); - } - mojo::Shell* const shell_; - mojo::View* const view_; + mojo::ui::ViewProviderPtr moterm_view_provider_; - mojo::View* moterm_view_; mojo::terminal::TerminalPtr moterm_terminal_; // Valid while prompting. mojo::files::FilePtr moterm_file_; @@ -163,7 +155,8 @@ }; class MotermExampleApp : public mojo::ApplicationDelegate, - public mojo::ViewManagerDelegate { + public mojo::InterfaceFactory<mojo::ui::ViewProvider>, + public mojo::ui::ViewProvider { public: MotermExampleApp() : application_impl_() {} ~MotermExampleApp() override {} @@ -173,27 +166,29 @@ void Initialize(mojo::ApplicationImpl* application_impl) override { DCHECK(!application_impl_); application_impl_ = application_impl; - view_manager_client_factory_.reset( - new mojo::ViewManagerClientFactory(application_impl_->shell(), this)); } bool ConfigureIncomingConnection( mojo::ApplicationConnection* connection) override { - connection->AddService(view_manager_client_factory_.get()); + connection->AddService<mojo::ui::ViewProvider>(this); return true; } - // |mojo::ViewManagerDelegate|: - void OnEmbed(mojo::View* root, - mojo::InterfaceRequest<mojo::ServiceProvider> services, - mojo::ServiceProviderPtr exposed_services) override { - new MotermExampleAppView(application_impl_->shell(), root); + // |InterfaceFactory<mojo::ui::ViewProvider>|: + void Create(mojo::ApplicationConnection* connection, + mojo::InterfaceRequest<mojo::ui::ViewProvider> request) override { + bindings_.AddBinding(this, request.Pass()); } - void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override {} + // |ViewProvider|: + void CreateView(mojo::InterfaceRequest<mojo::ServiceProvider> services, + mojo::ServiceProviderPtr exposed_services, + const CreateViewCallback& callback) override { + new MotermExampleAppView(application_impl_->shell(), callback); + } mojo::ApplicationImpl* application_impl_; - scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_; + mojo::BindingSet<mojo::ui::ViewProvider> bindings_; DISALLOW_COPY_AND_ASSIGN(MotermExampleApp); };