Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2631)

Unified Diff: apps/moterm/moterm_view.cc

Issue 1136673004: Moterm part 5: Add moterm itself. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « apps/moterm/moterm_view.h ('k') | mojo/services/terminal/public/interfaces/BUILD.gn » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: apps/moterm/moterm_view.cc
diff --git a/apps/moterm/moterm_view.cc b/apps/moterm/moterm_view.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e7018b9bf850a2c38faf3c4d610bb6e6d98a5b05
--- /dev/null
+++ b/apps/moterm/moterm_view.cc
@@ -0,0 +1,338 @@
+// 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_view.h"
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <algorithm>
+#include <string>
+
+#include "apps/moterm/key_util.h"
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/services/files/public/interfaces/file.mojom.h"
+#include "mojo/services/files/public/interfaces/types.mojom.h"
+#include "mojo/services/input_events/public/interfaces/input_event_constants.mojom.h"
+#include "mojo/services/input_events/public/interfaces/input_events.mojom.h"
+#include "mojo/services/terminal/public/interfaces/terminal_client.mojom.h"
+#include "skia/ext/refptr.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"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkRect.h"
+#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
+
+MotermView::MotermView(
+ mojo::Shell* shell,
+ mojo::View* view,
+ mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request)
+ : view_(view),
+ gl_helper_(this, shell, kTextureFormat, RectToSize(view->bounds())),
+ model_(MotermModel::Size(240, 160), MotermModel::Size(24, 80)),
+ frame_pending_(false),
+ force_next_draw_(false),
+ ascent_(0),
+ line_height_(0),
+ advance_width_(0) {
+ // TODO(vtl): |service_provider_impl_|'s ctor doesn't like an invalid request,
+ // so we have to conditionally, explicitly bind.
+ if (service_provider_request.is_pending()) {
+ service_provider_impl_.Bind(service_provider_request.Pass());
+ service_provider_impl_.AddService<mojo::terminal::Terminal>(this);
+ }
+
+ regular_typeface_ = skia::AdoptRef(SkTypeface::CreateFromStream(
+ new SkMemoryStream(font_data::kDejaVuSansMonoRegular.data,
+ font_data::kDejaVuSansMonoRegular.size)));
+
+ // TODO(vtl): This duplicates some code.
+ SkPaint fg_paint;
+ fg_paint.setTypeface(regular_typeface_.get());
+ fg_paint.setTextSize(16);
+ // Figure out appropriate metrics.
+ SkPaint::FontMetrics fm = {};
+ fg_paint.getFontMetrics(&fm);
+ ascent_ = static_cast<int>(ceilf(-fm.fAscent));
+ line_height_ = ascent_ + static_cast<int>(ceilf(fm.fDescent + fm.fLeading));
+ DCHECK_GT(line_height_, 0);
+ // To figure out the advance width, measure an X. Better hope the font is
+ // 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() {
+ if (driver_)
+ driver_->Detach();
+}
+
+void MotermView::OnViewDestroyed(mojo::View* view) {
+ DCHECK(view == view_);
+ view_->RemoveObserver(this);
+ delete this;
+}
+
+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::EVENT_TYPE_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::OnDataReceived(const void* bytes, size_t num_bytes) {
+ model_.ProcessInput(bytes, num_bytes, &model_state_changes_);
+ Draw(false);
+}
+
+void MotermView::OnClosed() {
+ DCHECK(driver_);
+ driver_->Detach();
+ driver_.reset();
+
+ OnDestroyed();
+}
+
+void MotermView::OnDestroyed() {
+ DCHECK(!driver_);
+ if (!on_closed_callback_.is_null()) {
+ mojo::Closure callback;
+ std::swap(callback, on_closed_callback_);
+ callback.Run();
+ }
+}
+
+void MotermView::Create(
+ mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<mojo::terminal::Terminal> request) {
+ terminal_bindings_.AddBinding(this, request.Pass());
+}
+
+void MotermView::Connect(
+ mojo::InterfaceRequest<mojo::files::File> terminal_file,
+ bool force,
+ const ConnectCallback& callback) {
+ if (driver_) {
+ // We already have a connection.
+ if (force) {
+ OnClosed();
+ } else {
+ // TODO(vtl): Is this error code right?
+ callback.Run(mojo::files::ERROR_UNAVAILABLE);
+ return;
+ }
+ }
+
+ driver_ = MotermDriver::Create(this, terminal_file.Pass());
+ DCHECK(on_closed_callback_.is_null());
+ on_closed_callback_ = [callback] { callback.Run(mojo::files::ERROR_OK); };
+}
+
+void MotermView::ConnectToClient(
+ mojo::terminal::TerminalClientPtr terminal_client,
+ bool force,
+ const ConnectToClientCallback& callback) {
+ if (driver_) {
+ // We already have a connection.
+ if (force) {
+ OnClosed();
+ } else {
+ // TODO(vtl): Is this error code right?
+ callback.Run(mojo::files::ERROR_UNAVAILABLE);
+ return;
+ }
+ }
+
+ mojo::files::FilePtr file;
+ driver_ = MotermDriver::Create(this, GetProxy(&file));
+ terminal_client->ConnectToTerminal(file.Pass());
+ DCHECK(on_closed_callback_.is_null());
+ on_closed_callback_ = [callback] { callback.Run(mojo::files::ERROR_OK); };
+}
+
+void MotermView::GetSize(const GetSizeCallback& callback) {
+ MotermModel::Size size = model_.GetSize();
+ callback.Run(mojo::files::ERROR_OK, size.rows, size.columns);
+}
+
+void MotermView::SetSize(uint32_t rows,
+ uint32_t columns,
+ bool reset,
+ const SetSizeCallback& callback) {
+ if (!rows) {
+ rows = std::max(1u, std::min(MotermModel::kMaxRows,
+ static_cast<uint32_t>(view_->bounds().height) /
+ line_height_));
+ }
+ if (!columns) {
+ columns =
+ std::max(1u, std::min(MotermModel::kMaxColumns,
+ static_cast<uint32_t>(view_->bounds().width) /
+ advance_width_));
+ }
+
+ model_.SetSize(MotermModel::Size(rows, columns), reset);
+ callback.Run(mojo::files::ERROR_OK, rows, columns);
+
+ Draw(false);
+}
+
+void MotermView::Draw(bool force) {
+ // TODO(vtl): See TODO above |frame_pending_| in the class declaration.
+ if (frame_pending_) {
+ force_next_draw_ |= force;
+ return;
+ }
+
+ force |= force_next_draw_;
+ force_next_draw_ = false;
+
+ if (!force && !model_state_changes_.IsDirty())
+ return;
+
+ // TODO(vtl): If |!force|, 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);
+
+ if (!bitmap_device_) {
+ bitmap_device_ = skia::AdoptRef(SkBitmapDevice::Create(SkImageInfo::Make(
+ width, height, kN32_SkColorType, kOpaque_SkAlphaType)));
+ }
+
+ SkCanvas canvas(bitmap_device_.get());
+ canvas.clear(SK_ColorBLACK);
+
+ SkPaint bg_paint;
+ bg_paint.setStyle(SkPaint::kFill_Style);
+
+ SkPaint fg_paint;
+ fg_paint.setTypeface(regular_typeface_.get());
+ fg_paint.setTextSize(16);
+ fg_paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
+
+ MotermModel::Size size = model_.GetSize();
+ int y = 0;
+ for (unsigned i = 0; i < size.rows; i++, y += line_height_) {
+ int x = 0;
+ for (unsigned j = 0; j < size.columns; j++, x += advance_width_) {
+ MotermModel::CharacterInfo ch =
+ model_.GetCharacterInfoAt(MotermModel::Position(i, j));
+
+ // Paint the background.
+ 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);
+
+ // Paint the foreground.
+ if (ch.code_point) {
+ uint32_t flags = SkPaint::kAntiAlias_Flag;
+ // TODO(vtl): Use real bold font?
+ if ((ch.attributes & MotermModel::kAttributesBold))
+ flags |= SkPaint::kFakeBoldText_Flag;
+ if ((ch.attributes & MotermModel::kAttributesUnderline))
+ flags |= SkPaint::kUnderlineText_Flag;
+ // TODO(vtl): Handle blink, because that's awesome.
+ fg_paint.setFlags(flags);
+ fg_paint.setColor(SkColorSetRGB(ch.foreground_color.red,
+ ch.foreground_color.green,
+ ch.foreground_color.blue));
+
+ canvas.drawText(&ch.code_point, sizeof(ch.code_point), x, y + ascent_,
+ fg_paint);
+ }
+ }
+ }
+
+ // Draw the cursor.
+ MotermModel::Position cursor_pos = model_.GetCursorPosition();
+ // Reuse the background paint, but don't just paint over.
+ // TODO(vtl): Consider doing other things. Maybe make it blink, to be extra
+ // annoying.
+ // TODO(vtl): Maybe vary how we draw the cursor, depending on if we're 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.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;
+}
+
+void MotermView::OnKeyPressed(const mojo::EventPtr& key_event) {
+ std::string input_sequence = GetInputSequenceForKeyPressedEvent(*key_event);
+ if (input_sequence.empty())
+ return;
+
+ if (driver_)
+ driver_->SendData(input_sequence.data(), input_sequence.size());
+}
« no previous file with comments | « apps/moterm/moterm_view.h ('k') | mojo/services/terminal/public/interfaces/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698