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

Side by Side 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "apps/moterm/moterm_view.h"
6
7 #ifndef GL_GLEXT_PROTOTYPES
8 #define GL_GLEXT_PROTOTYPES
9 #endif
10
11 #include <GLES2/gl2.h>
12 #include <GLES2/gl2ext.h>
13
14 #include <algorithm>
15 #include <string>
16
17 #include "apps/moterm/key_util.h"
18 #include "base/logging.h"
19 #include "mojo/public/cpp/bindings/interface_request.h"
20 #include "mojo/services/files/public/interfaces/file.mojom.h"
21 #include "mojo/services/files/public/interfaces/types.mojom.h"
22 #include "mojo/services/input_events/public/interfaces/input_event_constants.moj om.h"
23 #include "mojo/services/input_events/public/interfaces/input_events.mojom.h"
24 #include "mojo/services/terminal/public/interfaces/terminal_client.mojom.h"
25 #include "skia/ext/refptr.h"
26 #include "third_party/dejavu-fonts-ttf-2.34/kDejaVuSansMonoRegular.h"
27 #include "third_party/skia/include/core/SkBitmap.h"
28 #include "third_party/skia/include/core/SkCanvas.h"
29 #include "third_party/skia/include/core/SkColor.h"
30 #include "third_party/skia/include/core/SkImageInfo.h"
31 #include "third_party/skia/include/core/SkPaint.h"
32 #include "third_party/skia/include/core/SkRect.h"
33 #include "third_party/skia/include/core/SkStream.h"
34 #include "third_party/skia/include/core/SkXfermode.h"
35
36 namespace {
37
38 const GLint kTextureFormat =
39 (kN32_SkColorType == kRGBA_8888_SkColorType) ? GL_RGBA : GL_BGRA_EXT;
40
41 mojo::Size RectToSize(const mojo::Rect& rect) {
42 mojo::Size size;
43 size.width = rect.width;
44 size.height = rect.height;
45 return size;
46 }
47
48 } // namespace
49
50 MotermView::MotermView(
51 mojo::Shell* shell,
52 mojo::View* view,
53 mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request)
54 : view_(view),
55 gl_helper_(this, shell, kTextureFormat, RectToSize(view->bounds())),
56 model_(MotermModel::Size(240, 160), MotermModel::Size(24, 80)),
57 frame_pending_(false),
58 force_next_draw_(false),
59 ascent_(0),
60 line_height_(0),
61 advance_width_(0) {
62 // TODO(vtl): |service_provider_impl_|'s ctor doesn't like an invalid request,
63 // so we have to conditionally, explicitly bind.
64 if (service_provider_request.is_pending()) {
65 service_provider_impl_.Bind(service_provider_request.Pass());
66 service_provider_impl_.AddService<mojo::terminal::Terminal>(this);
67 }
68
69 regular_typeface_ = skia::AdoptRef(SkTypeface::CreateFromStream(
70 new SkMemoryStream(font_data::kDejaVuSansMonoRegular.data,
71 font_data::kDejaVuSansMonoRegular.size)));
72
73 // TODO(vtl): This duplicates some code.
74 SkPaint fg_paint;
75 fg_paint.setTypeface(regular_typeface_.get());
76 fg_paint.setTextSize(16);
77 // Figure out appropriate metrics.
78 SkPaint::FontMetrics fm = {};
79 fg_paint.getFontMetrics(&fm);
80 ascent_ = static_cast<int>(ceilf(-fm.fAscent));
81 line_height_ = ascent_ + static_cast<int>(ceilf(fm.fDescent + fm.fLeading));
82 DCHECK_GT(line_height_, 0);
83 // To figure out the advance width, measure an X. Better hope the font is
84 // monospace.
85 advance_width_ = static_cast<int>(ceilf(fg_paint.measureText("X", 1)));
86 DCHECK_GT(advance_width_, 0);
87
88 view_->AddObserver(this);
89
90 // Force an initial draw.
91 Draw(true);
92 }
93
94 MotermView::~MotermView() {
95 if (driver_)
96 driver_->Detach();
97 }
98
99 void MotermView::OnViewDestroyed(mojo::View* view) {
100 DCHECK(view == view_);
101 view_->RemoveObserver(this);
102 delete this;
103 }
104
105 void MotermView::OnViewBoundsChanged(mojo::View* view,
106 const mojo::Rect& old_bounds,
107 const mojo::Rect& new_bounds) {
108 DCHECK_EQ(view, view_);
109 gl_helper_.SetSurfaceSize(RectToSize(view_->bounds()));
110 bitmap_device_.clear();
111 Draw(true);
112 }
113
114 void MotermView::OnViewInputEvent(mojo::View* view,
115 const mojo::EventPtr& event) {
116 if (event->action == mojo::EVENT_TYPE_KEY_PRESSED)
117 OnKeyPressed(event);
118 }
119
120 void MotermView::OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) {
121 view_->SetSurfaceId(surface_id.Pass());
122 }
123
124 void MotermView::OnContextLost() {
125 // TODO(vtl): We'll need to force a draw when we regain a context.
126 }
127
128 void MotermView::OnFrameDisplayed(uint32_t frame_id) {
129 DCHECK(frame_pending_);
130 frame_pending_ = false;
131 Draw(false);
132 }
133
134 void MotermView::OnDataReceived(const void* bytes, size_t num_bytes) {
135 model_.ProcessInput(bytes, num_bytes, &model_state_changes_);
136 Draw(false);
137 }
138
139 void MotermView::OnClosed() {
140 DCHECK(driver_);
141 driver_->Detach();
142 driver_.reset();
143
144 OnDestroyed();
145 }
146
147 void MotermView::OnDestroyed() {
148 DCHECK(!driver_);
149 if (!on_closed_callback_.is_null()) {
150 mojo::Closure callback;
151 std::swap(callback, on_closed_callback_);
152 callback.Run();
153 }
154 }
155
156 void MotermView::Create(
157 mojo::ApplicationConnection* connection,
158 mojo::InterfaceRequest<mojo::terminal::Terminal> request) {
159 terminal_bindings_.AddBinding(this, request.Pass());
160 }
161
162 void MotermView::Connect(
163 mojo::InterfaceRequest<mojo::files::File> terminal_file,
164 bool force,
165 const ConnectCallback& callback) {
166 if (driver_) {
167 // We already have a connection.
168 if (force) {
169 OnClosed();
170 } else {
171 // TODO(vtl): Is this error code right?
172 callback.Run(mojo::files::ERROR_UNAVAILABLE);
173 return;
174 }
175 }
176
177 driver_ = MotermDriver::Create(this, terminal_file.Pass());
178 DCHECK(on_closed_callback_.is_null());
179 on_closed_callback_ = [callback] { callback.Run(mojo::files::ERROR_OK); };
180 }
181
182 void MotermView::ConnectToClient(
183 mojo::terminal::TerminalClientPtr terminal_client,
184 bool force,
185 const ConnectToClientCallback& callback) {
186 if (driver_) {
187 // We already have a connection.
188 if (force) {
189 OnClosed();
190 } else {
191 // TODO(vtl): Is this error code right?
192 callback.Run(mojo::files::ERROR_UNAVAILABLE);
193 return;
194 }
195 }
196
197 mojo::files::FilePtr file;
198 driver_ = MotermDriver::Create(this, GetProxy(&file));
199 terminal_client->ConnectToTerminal(file.Pass());
200 DCHECK(on_closed_callback_.is_null());
201 on_closed_callback_ = [callback] { callback.Run(mojo::files::ERROR_OK); };
202 }
203
204 void MotermView::GetSize(const GetSizeCallback& callback) {
205 MotermModel::Size size = model_.GetSize();
206 callback.Run(mojo::files::ERROR_OK, size.rows, size.columns);
207 }
208
209 void MotermView::SetSize(uint32_t rows,
210 uint32_t columns,
211 bool reset,
212 const SetSizeCallback& callback) {
213 if (!rows) {
214 rows = std::max(1u, std::min(MotermModel::kMaxRows,
215 static_cast<uint32_t>(view_->bounds().height) /
216 line_height_));
217 }
218 if (!columns) {
219 columns =
220 std::max(1u, std::min(MotermModel::kMaxColumns,
221 static_cast<uint32_t>(view_->bounds().width) /
222 advance_width_));
223 }
224
225 model_.SetSize(MotermModel::Size(rows, columns), reset);
226 callback.Run(mojo::files::ERROR_OK, rows, columns);
227
228 Draw(false);
229 }
230
231 void MotermView::Draw(bool force) {
232 // TODO(vtl): See TODO above |frame_pending_| in the class declaration.
233 if (frame_pending_) {
234 force_next_draw_ |= force;
235 return;
236 }
237
238 force |= force_next_draw_;
239 force_next_draw_ = false;
240
241 if (!force && !model_state_changes_.IsDirty())
242 return;
243
244 // TODO(vtl): If |!force|, draw only the dirty region(s)?
245 model_state_changes_.Reset();
246
247 int32_t width = view_->bounds().width;
248 int32_t height = view_->bounds().height;
249 DCHECK_GT(width, 0);
250 DCHECK_GT(height, 0);
251
252 if (!bitmap_device_) {
253 bitmap_device_ = skia::AdoptRef(SkBitmapDevice::Create(SkImageInfo::Make(
254 width, height, kN32_SkColorType, kOpaque_SkAlphaType)));
255 }
256
257 SkCanvas canvas(bitmap_device_.get());
258 canvas.clear(SK_ColorBLACK);
259
260 SkPaint bg_paint;
261 bg_paint.setStyle(SkPaint::kFill_Style);
262
263 SkPaint fg_paint;
264 fg_paint.setTypeface(regular_typeface_.get());
265 fg_paint.setTextSize(16);
266 fg_paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
267
268 MotermModel::Size size = model_.GetSize();
269 int y = 0;
270 for (unsigned i = 0; i < size.rows; i++, y += line_height_) {
271 int x = 0;
272 for (unsigned j = 0; j < size.columns; j++, x += advance_width_) {
273 MotermModel::CharacterInfo ch =
274 model_.GetCharacterInfoAt(MotermModel::Position(i, j));
275
276 // Paint the background.
277 bg_paint.setColor(SkColorSetRGB(ch.background_color.red,
278 ch.background_color.green,
279 ch.background_color.blue));
280 canvas.drawRect(SkRect::MakeXYWH(x, y, advance_width_, line_height_),
281 bg_paint);
282
283 // Paint the foreground.
284 if (ch.code_point) {
285 uint32_t flags = SkPaint::kAntiAlias_Flag;
286 // TODO(vtl): Use real bold font?
287 if ((ch.attributes & MotermModel::kAttributesBold))
288 flags |= SkPaint::kFakeBoldText_Flag;
289 if ((ch.attributes & MotermModel::kAttributesUnderline))
290 flags |= SkPaint::kUnderlineText_Flag;
291 // TODO(vtl): Handle blink, because that's awesome.
292 fg_paint.setFlags(flags);
293 fg_paint.setColor(SkColorSetRGB(ch.foreground_color.red,
294 ch.foreground_color.green,
295 ch.foreground_color.blue));
296
297 canvas.drawText(&ch.code_point, sizeof(ch.code_point), x, y + ascent_,
298 fg_paint);
299 }
300 }
301 }
302
303 // Draw the cursor.
304 MotermModel::Position cursor_pos = model_.GetCursorPosition();
305 // Reuse the background paint, but don't just paint over.
306 // TODO(vtl): Consider doing other things. Maybe make it blink, to be extra
307 // annoying.
308 // TODO(vtl): Maybe vary how we draw the cursor, depending on if we're focused
309 // and/or active.
310 bg_paint.setColor(SK_ColorWHITE);
311 bg_paint.setXfermodeMode(SkXfermode::kDifference_Mode);
312 canvas.drawRect(SkRect::MakeXYWH(cursor_pos.column * advance_width_,
313 cursor_pos.row * line_height_,
314 advance_width_, line_height_),
315 bg_paint);
316
317 canvas.flush();
318
319 const SkBitmap& bitmap(bitmap_device_->accessBitmap(false));
320 // TODO(vtl): Do we need really need to lock/unlock pixels?
321 SkAutoLockPixels pixel_locker(bitmap);
322
323 gl_helper_.StartFrame();
324 // (The texture is already bound.)
325 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, kTextureFormat,
326 GL_UNSIGNED_BYTE, bitmap.getPixels());
327 gl_helper_.EndFrame();
328 frame_pending_ = true;
329 }
330
331 void MotermView::OnKeyPressed(const mojo::EventPtr& key_event) {
332 std::string input_sequence = GetInputSequenceForKeyPressedEvent(*key_event);
333 if (input_sequence.empty())
334 return;
335
336 if (driver_)
337 driver_->SendData(input_sequence.data(), input_sequence.size());
338 }
OLDNEW
« 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