blob: 434fbaf48762e2205afcb9929b2164da06757bf5 [file] [log] [blame] [edit]
// 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 <algorithm>
#include "base/bind.h"
#include "base/macros.h"
#include "mojo/application/application_runner_chromium.h"
#include "mojo/gpu/gl_texture.h"
#include "mojo/gpu/texture_cache.h"
#include "mojo/gpu/texture_uploader.h"
#include "mojo/public/c/system/main.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/services/keyboard/interfaces/keyboard.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/skia/ganesh_context.h"
#include "mojo/skia/ganesh_surface.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkTypeface.h"
namespace examples {
mojo::Size ToSize(const mojo::Rect& rect) {
mojo::Size size;
size.width = rect.width;
size.height = rect.height;
return size;
}
class ViewTextureUploader {
public:
ViewTextureUploader(base::WeakPtr<mojo::GLContext> gl_context,
mojo::SurfacePtr* surface,
mojo::View* view,
mojo::TextureCache* texture_cache,
base::Callback<void(SkCanvas*)> draw_callback)
: gl_context_(gl_context),
surface_(surface),
texture_cache_(texture_cache),
view_(view),
draw_callback_(draw_callback),
weak_factory_(this) {
gr_context_.reset(new mojo::GaneshContext(gl_context_));
submit_frame_callback_ = base::Bind(&ViewTextureUploader::OnFrameComplete,
weak_factory_.GetWeakPtr());
}
void Draw(uint32_t surface_id) {
mojo::GaneshContext::Scope scope(gr_context_.get());
mojo::Size view_size = ToSize(view_->bounds());
scoped_ptr<mojo::TextureCache::TextureInfo> texture_info(
texture_cache_->GetTexture(view_size).Pass());
mojo::GaneshSurface ganesh_surface(gr_context_.get(),
texture_info->TakeTexture().Pass());
gr_context_->gr()->resetContext(kTextureBinding_GrGLBackendState);
SkCanvas* canvas = ganesh_surface.canvas();
draw_callback_.Run(canvas);
scoped_ptr<mojo::GLTexture> surface_texture(
ganesh_surface.TakeTexture().Pass());
mojo::FramePtr frame = mojo::TextureUploader::GetUploadFrame(
gl_context_, texture_info->resource_id(), surface_texture);
texture_cache_->NotifyPendingResourceReturn(texture_info->resource_id(),
surface_texture.Pass());
(*surface_)->SubmitFrame(surface_id, frame.Pass(), submit_frame_callback_);
}
void OnFrameComplete() {}
private:
base::WeakPtr<mojo::GLContext> gl_context_;
scoped_ptr<mojo::GaneshContext> gr_context_;
mojo::SurfacePtr* surface_;
mojo::TextureCache* texture_cache_;
mojo::View* view_;
base::Callback<void(SkCanvas*)> draw_callback_;
base::Closure submit_frame_callback_;
base::WeakPtrFactory<ViewTextureUploader> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ViewTextureUploader);
};
class KeyboardDelegate : public mojo::ApplicationDelegate,
public keyboard::KeyboardClient,
public mojo::ViewManagerDelegate,
public mojo::ViewObserver {
public:
KeyboardDelegate()
: binding_(this),
shell_(nullptr),
keyboard_view_(nullptr),
text_view_(nullptr),
root_view_(nullptr),
root_view_surface_id_(1u),
text_view_surface_id_(2u),
id_namespace_(0u),
text_view_height_(0),
weak_factory_(this) {}
~KeyboardDelegate() override {}
void SetIdNamespace(uint32_t id_namespace) {
id_namespace_ = id_namespace;
UpdateSurfaceIds();
}
void UpdateSurfaceIds() {
auto text_view_surface_id = mojo::SurfaceId::New();
text_view_surface_id->id_namespace = id_namespace_;
text_view_surface_id->local = text_view_surface_id_;
text_view_->SetSurfaceId(text_view_surface_id.Pass());
auto root_view_surface_id = mojo::SurfaceId::New();
root_view_surface_id->id_namespace = id_namespace_;
root_view_surface_id->local = root_view_surface_id_;
root_view_->SetSurfaceId(root_view_surface_id.Pass());
}
// mojo::ApplicationDelegate implementation.
void Initialize(mojo::ApplicationImpl* app) override {
shell_ = app->shell();
view_manager_client_factory_.reset(
new mojo::ViewManagerClientFactory(shell_, this));
}
bool ConfigureIncomingConnection(
mojo::ApplicationConnection* connection) override {
connection->AddService(view_manager_client_factory_.get());
return true;
}
// mojo::ViewManagerDelegate implementation.
void OnEmbed(mojo::View* root,
mojo::InterfaceRequest<mojo::ServiceProvider> services,
mojo::ServiceProviderPtr exposed_services) override {
root_view_ = root;
root->AddObserver(this);
keyboard_view_ = root->view_manager()->CreateView();
text_view_ = root->view_manager()->CreateView();
skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(
SkTypeface::CreateFromName("Arial", SkTypeface::kNormal));
text_paint_.setTypeface(typeface.get());
text_paint_.setColor(SK_ColorBLACK);
text_paint_.setAntiAlias(true);
text_paint_.setTextAlign(SkPaint::kLeft_Align);
UpdateViewBounds(root->bounds());
root->AddChild(text_view_);
text_view_->SetVisible(true);
root->AddChild(keyboard_view_);
keyboard_view_->SetVisible(true);
mojo::ServiceProviderPtr keyboard_sp;
keyboard_view_->Embed("mojo:keyboard_native", GetProxy(&keyboard_sp),
nullptr);
keyboard_sp->ConnectToService(keyboard::KeyboardService::Name_,
GetProxy(&keyboard_).PassMessagePipe());
keyboard_->ShowByRequest();
keyboard_->Hide();
keyboard::KeyboardClientPtr keyboard_client;
auto request = mojo::GetProxy(&keyboard_client);
binding_.Bind(request.Pass());
keyboard_->Show(keyboard_client.Pass(), keyboard::KeyboardType::TEXT);
mojo::ServiceProviderPtr surfaces_service_provider;
shell_->ConnectToApplication("mojo:surfaces_service",
mojo::GetProxy(&surfaces_service_provider),
nullptr);
mojo::ConnectToService(surfaces_service_provider.get(), &surface_);
gl_context_ = mojo::GLContext::Create(shell_);
surface_->CreateSurface(root_view_surface_id_);
surface_->CreateSurface(text_view_surface_id_);
surface_->GetIdNamespace(
base::Bind(&KeyboardDelegate::SetIdNamespace, base::Unretained(this)));
mojo::ResourceReturnerPtr resource_returner;
texture_cache_.reset(
new mojo::TextureCache(gl_context_, &resource_returner));
text_view_texture_uploader_.reset(new ViewTextureUploader(
gl_context_, &surface_, text_view_, texture_cache_.get(),
base::Bind(&KeyboardDelegate::DrawTextView,
weak_factory_.GetWeakPtr())));
root_view_texture_uploader_.reset(new ViewTextureUploader(
gl_context_, &surface_, root, texture_cache_.get(),
base::Bind(&KeyboardDelegate::DrawRootView,
weak_factory_.GetWeakPtr())));
surface_->SetResourceReturner(resource_returner.Pass());
DrawText();
}
void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override {}
// keyboard::KeyboardClient implementation.
void CommitCompletion(keyboard::CompletionDataPtr completion) override {
DrawText();
}
void CommitCorrection(keyboard::CorrectionDataPtr correction) override {
DrawText();
}
void CommitText(const mojo::String& text,
int32_t new_cursor_position) override {
text_.append(text);
DrawText();
}
void DeleteSurroundingText(int32_t before_length,
int32_t after_length) override {
// treat negative and zero |before_length| values as no-op.
if (before_length > 0) {
if (before_length > static_cast<int32_t>(text_.size())) {
before_length = text_.size();
}
text_.erase(text_.end() - before_length, text_.end());
}
DrawText();
}
void SetComposingRegion(int32_t start, int32_t end) override { DrawText(); }
void SetComposingText(const mojo::String& text,
int32_t new_cursor_position) override {
DrawText();
}
void SetSelection(int32_t start, int32_t end) override { DrawText(); }
void Submit(keyboard::SubmitAction action) override {}
// mojo::ViewObserver implementation.
void OnViewDestroyed(mojo::View* view) override {
if (view == text_view_) {
text_view_->RemoveObserver(this);
text_view_ = nullptr;
}
}
void OnViewBoundsChanged(mojo::View* view,
const mojo::Rect& old_bounds,
const mojo::Rect& new_bounds) override {
surface_->DestroySurface(root_view_surface_id_);
surface_->DestroySurface(text_view_surface_id_);
root_view_surface_id_ += 2;
text_view_surface_id_ += 2;
surface_->CreateSurface(root_view_surface_id_);
surface_->CreateSurface(text_view_surface_id_);
UpdateSurfaceIds();
UpdateViewBounds(new_bounds);
DrawText();
}
void UpdateViewBounds(const mojo::Rect& root_bounds) {
float keyboard_height = (root_bounds.width > root_bounds.height)
? root_bounds.width * 0.3f
: root_bounds.width * 0.6f;
text_view_height_ = root_bounds.height - keyboard_height;
float row_height = text_view_height_ / 2.0f;
text_paint_.setTextSize(row_height / 1.7f);
// One third of the keyboard view's height will be used for overlapping
// views behind it (text_view_ in this case). Thus create the keyboard 50%
// taller than the area we want the keys to be drawn to.
float overlap = keyboard_height / 2;
mojo::Rect keyboard_bounds;
keyboard_bounds.x = root_bounds.x;
keyboard_bounds.y = root_bounds.y + text_view_height_ - overlap;
keyboard_bounds.width = root_bounds.width;
keyboard_bounds.height = root_bounds.height - text_view_height_ + overlap;
keyboard_view_->SetBounds(keyboard_bounds);
mojo::Rect text_view_bounds;
text_view_bounds.x = root_bounds.x;
text_view_bounds.y = root_bounds.y;
text_view_bounds.width = root_bounds.width;
text_view_bounds.height = text_view_height_;
text_view_->SetBounds(text_view_bounds);
}
void DrawText() {
if (root_view_texture_uploader_) {
root_view_texture_uploader_->Draw(root_view_surface_id_);
}
if (text_view_texture_uploader_) {
text_view_texture_uploader_->Draw(text_view_surface_id_);
}
}
void DrawTextView(SkCanvas* canvas) {
canvas->clear(SK_ColorRED);
float row_height = text_view_height_ / 2.0f;
float text_baseline_offset = row_height / 5.0f;
if (!text_.empty()) {
SkRect sk_rect;
text_paint_.measureText((const void*)(text_.c_str()), text_.length(),
&sk_rect);
if (sk_rect.width() > text_view_->bounds().width) {
std::string reverse_text = text_;
std::reverse(reverse_text.begin(), reverse_text.end());
size_t processed1 = text_paint_.breakText(
(const void*)(reverse_text.c_str()), strlen(reverse_text.c_str()),
text_view_->bounds().width);
size_t processed2 = text_paint_.breakText(
(const void*)(reverse_text.c_str() + processed1),
strlen(reverse_text.c_str()) - processed1,
text_view_->bounds().width);
if (processed1 + processed2 < text_.length()) {
DrawSecondLine(canvas, text_.length() - processed1, processed1,
row_height, text_baseline_offset);
DrawFirstLine(canvas, text_.length() - processed1 - processed2,
processed2, row_height, text_baseline_offset);
} else {
size_t processed3 =
text_paint_.breakText((const void*)(text_.c_str()),
text_.length(), text_view_->bounds().width);
DrawFirstLine(canvas, 0, processed3, row_height,
text_baseline_offset);
DrawSecondLine(canvas, processed3, text_.length() - processed3,
row_height, text_baseline_offset);
}
} else {
DrawSecondLine(canvas, 0, text_.length(), row_height,
text_baseline_offset);
}
}
canvas->flush();
}
void DrawFirstLine(SkCanvas* canvas,
const size_t offset,
const size_t length,
const float row_height,
const float text_baseline_offset) {
canvas->drawText(text_.data() + offset, length, 0.0f,
row_height - text_baseline_offset, text_paint_);
}
void DrawSecondLine(SkCanvas* canvas,
const size_t offset,
const size_t length,
const float row_height,
const float text_baseline_offset) {
canvas->drawText(text_.data() + offset, length, 0.0f,
(2.0f * row_height) - text_baseline_offset, text_paint_);
}
void DrawRootView(SkCanvas* canvas) {
canvas->clear(SK_ColorDKGRAY);
canvas->flush();
}
private:
mojo::Binding<keyboard::KeyboardClient> binding_;
keyboard::KeyboardServicePtr keyboard_;
scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
mojo::Shell* shell_;
mojo::View* keyboard_view_;
mojo::View* text_view_;
mojo::View* root_view_;
uint32_t root_view_surface_id_;
uint32_t text_view_surface_id_;
uint32_t id_namespace_;
base::WeakPtr<mojo::GLContext> gl_context_;
mojo::SurfacePtr surface_;
scoped_ptr<mojo::TextureCache> texture_cache_;
scoped_ptr<ViewTextureUploader> text_view_texture_uploader_;
scoped_ptr<ViewTextureUploader> root_view_texture_uploader_;
int text_view_height_;
std::string text_;
SkPaint text_paint_;
base::WeakPtrFactory<KeyboardDelegate> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(KeyboardDelegate);
};
} // namespace examples
MojoResult MojoMain(MojoHandle application_request) {
mojo::ApplicationRunnerChromium runner(new examples::KeyboardDelegate);
return runner.Run(application_request);
}