OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "apps/moterm/moterm_view.h" | 5 #include "apps/moterm/moterm_view.h" |
6 | 6 |
7 #ifndef GL_GLEXT_PROTOTYPES | 7 #ifndef GL_GLEXT_PROTOTYPES |
8 #define GL_GLEXT_PROTOTYPES | 8 #define GL_GLEXT_PROTOTYPES |
9 #endif | 9 #endif |
10 | 10 |
11 #include <GLES2/gl2.h> | 11 #include <GLES2/gl2.h> |
12 #include <GLES2/gl2ext.h> | 12 #include <GLES2/gl2ext.h> |
13 | 13 |
14 #include <algorithm> | 14 #include <algorithm> |
15 #include <string> | 15 #include <string> |
16 | 16 |
17 #include "apps/moterm/key_util.h" | 17 #include "apps/moterm/key_util.h" |
18 #include "base/bind.h" | |
18 #include "base/logging.h" | 19 #include "base/logging.h" |
20 #include "mojo/public/cpp/application/connect.h" | |
19 #include "mojo/public/cpp/bindings/interface_request.h" | 21 #include "mojo/public/cpp/bindings/interface_request.h" |
20 #include "mojo/services/files/interfaces/file.mojom.h" | 22 #include "mojo/services/files/interfaces/file.mojom.h" |
21 #include "mojo/services/files/interfaces/types.mojom.h" | 23 #include "mojo/services/files/interfaces/types.mojom.h" |
22 #include "mojo/services/input_events/interfaces/input_event_constants.mojom.h" | |
23 #include "mojo/services/input_events/interfaces/input_events.mojom.h" | |
24 #include "mojo/services/terminal/interfaces/terminal_client.mojom.h" | 24 #include "mojo/services/terminal/interfaces/terminal_client.mojom.h" |
25 #include "skia/ext/refptr.h" | 25 #include "mojo/skia/ganesh_texture_surface.h" |
26 #include "third_party/dejavu-fonts-ttf-2.34/kDejaVuSansMonoRegular.h" | 26 #include "third_party/dejavu-fonts-ttf-2.34/kDejaVuSansMonoRegular.h" |
27 #include "third_party/skia/include/core/SkBitmap.h" | 27 #include "third_party/skia/include/core/SkBitmap.h" |
28 #include "third_party/skia/include/core/SkCanvas.h" | 28 #include "third_party/skia/include/core/SkCanvas.h" |
29 #include "third_party/skia/include/core/SkColor.h" | 29 #include "third_party/skia/include/core/SkColor.h" |
30 #include "third_party/skia/include/core/SkImageInfo.h" | 30 #include "third_party/skia/include/core/SkImageInfo.h" |
31 #include "third_party/skia/include/core/SkPaint.h" | 31 #include "third_party/skia/include/core/SkPaint.h" |
32 #include "third_party/skia/include/core/SkRect.h" | 32 #include "third_party/skia/include/core/SkRect.h" |
33 #include "third_party/skia/include/core/SkStream.h" | 33 #include "third_party/skia/include/core/SkStream.h" |
34 #include "third_party/skia/include/core/SkXfermode.h" | 34 #include "third_party/skia/include/core/SkXfermode.h" |
35 | 35 |
36 namespace { | 36 constexpr uint32_t kMotermImageResourceId = 1; |
37 | 37 constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId; |
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 | 38 |
50 MotermView::MotermView( | 39 MotermView::MotermView( |
51 mojo::Shell* shell, | 40 mojo::ApplicationImpl* app_impl, |
52 mojo::View* view, | 41 mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request, |
53 mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request) | 42 const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback) |
54 : view_(view), | 43 : GaneshView(app_impl, "Moterm", create_view_callback), |
55 gl_helper_(this, | 44 choreographer_(scene(), this), |
56 shell, | 45 input_handler_(view_service_provider(), this), |
57 kTextureFormat, | |
58 false, | |
59 RectToSize(view->bounds())), | |
60 model_(MotermModel::Size(240, 160), MotermModel::Size(24, 80), this), | 46 model_(MotermModel::Size(240, 160), MotermModel::Size(24, 80), this), |
61 frame_pending_(false), | |
62 force_next_draw_(false), | 47 force_next_draw_(false), |
63 ascent_(0), | 48 ascent_(0), |
64 line_height_(0), | 49 line_height_(0), |
65 advance_width_(0), | 50 advance_width_(0), |
66 keypad_application_mode_(false) { | 51 keypad_application_mode_(false) { |
67 // TODO(vtl): |service_provider_impl_|'s ctor doesn't like an invalid request, | 52 // TODO(vtl): |service_provider_impl_|'s ctor doesn't like an invalid request, |
68 // so we have to conditionally, explicitly bind. | 53 // so we have to conditionally, explicitly bind. |
69 if (service_provider_request.is_pending()) { | 54 if (service_provider_request.is_pending()) { |
70 service_provider_impl_.Bind(service_provider_request.Pass()); | 55 service_provider_impl_.Bind(service_provider_request.Pass()); |
71 service_provider_impl_.AddService<mojo::terminal::Terminal>(this); | 56 service_provider_impl_.AddService<mojo::terminal::Terminal>(this); |
(...skipping 10 matching lines...) Expand all Loading... | |
82 // Figure out appropriate metrics. | 67 // Figure out appropriate metrics. |
83 SkPaint::FontMetrics fm = {}; | 68 SkPaint::FontMetrics fm = {}; |
84 fg_paint.getFontMetrics(&fm); | 69 fg_paint.getFontMetrics(&fm); |
85 ascent_ = static_cast<int>(ceilf(-fm.fAscent)); | 70 ascent_ = static_cast<int>(ceilf(-fm.fAscent)); |
86 line_height_ = ascent_ + static_cast<int>(ceilf(fm.fDescent + fm.fLeading)); | 71 line_height_ = ascent_ + static_cast<int>(ceilf(fm.fDescent + fm.fLeading)); |
87 DCHECK_GT(line_height_, 0); | 72 DCHECK_GT(line_height_, 0); |
88 // To figure out the advance width, measure an X. Better hope the font is | 73 // To figure out the advance width, measure an X. Better hope the font is |
89 // monospace. | 74 // monospace. |
90 advance_width_ = static_cast<int>(ceilf(fg_paint.measureText("X", 1))); | 75 advance_width_ = static_cast<int>(ceilf(fg_paint.measureText("X", 1))); |
91 DCHECK_GT(advance_width_, 0); | 76 DCHECK_GT(advance_width_, 0); |
92 | |
93 view_->AddObserver(this); | |
94 | |
95 // Force an initial draw. | |
96 Draw(true); | |
97 } | 77 } |
98 | 78 |
99 MotermView::~MotermView() { | 79 MotermView::~MotermView() { |
100 if (driver_) | 80 if (driver_) |
101 driver_->Detach(); | 81 driver_->Detach(); |
102 } | 82 } |
103 | 83 |
104 void MotermView::OnViewDestroyed(mojo::View* view) { | 84 void MotermView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params, |
105 DCHECK_EQ(view, view_); | 85 mojo::Array<uint32_t> children_needing_layout, |
106 view_->RemoveObserver(this); | 86 const OnLayoutCallback& callback) { |
107 delete this; | 87 view_size_.width = layout_params->constraints->max_width; |
88 view_size_.height = layout_params->constraints->max_height; | |
89 | |
90 ScheduleDraw(true); | |
91 | |
92 auto info = mojo::ui::ViewLayoutResult::New(); | |
93 info->size = view_size_.Clone(); | |
94 callback.Run(info.Pass()); | |
108 } | 95 } |
109 | 96 |
110 void MotermView::OnViewBoundsChanged(mojo::View* view, | 97 void MotermView::OnEvent(mojo::EventPtr event, |
111 const mojo::Rect& old_bounds, | 98 const OnEventCallback& callback) { |
112 const mojo::Rect& new_bounds) { | 99 if (event->action == mojo::EventType::KEY_PRESSED) { |
113 DCHECK_EQ(view, view_); | 100 OnKeyPressed(event.Pass()); |
114 gl_helper_.SetSurfaceSize(RectToSize(view_->bounds())); | 101 callback.Run(true); |
115 bitmap_device_.clear(); | 102 } else { |
116 Draw(true); | 103 callback.Run(false); |
117 } | 104 } |
118 | |
119 void MotermView::OnViewInputEvent(mojo::View* view, | |
120 const mojo::EventPtr& event) { | |
121 if (event->action == mojo::EventType::KEY_PRESSED) | |
122 OnKeyPressed(event); | |
123 } | |
124 | |
125 void MotermView::OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) { | |
126 view_->SetSurfaceId(surface_id.Pass()); | |
127 } | |
128 | |
129 void MotermView::OnContextLost() { | |
130 // TODO(vtl): We'll need to force a draw when we regain a context. | |
131 } | |
132 | |
133 void MotermView::OnFrameDisplayed(uint32_t frame_id) { | |
134 DCHECK(frame_pending_); | |
135 frame_pending_ = false; | |
136 Draw(false); | |
137 } | 105 } |
138 | 106 |
139 void MotermView::OnResponse(const void* buf, size_t size) { | 107 void MotermView::OnResponse(const void* buf, size_t size) { |
140 if (driver_) | 108 if (driver_) |
141 driver_->SendData(buf, size); | 109 driver_->SendData(buf, size); |
142 } | 110 } |
143 | 111 |
144 void MotermView::OnSetKeypadMode(bool application_mode) { | 112 void MotermView::OnSetKeypadMode(bool application_mode) { |
145 keypad_application_mode_ = application_mode; | 113 keypad_application_mode_ = application_mode; |
146 } | 114 } |
147 | 115 |
148 void MotermView::OnDataReceived(const void* bytes, size_t num_bytes) { | 116 void MotermView::OnDataReceived(const void* bytes, size_t num_bytes) { |
149 model_.ProcessInput(bytes, num_bytes, &model_state_changes_); | 117 model_.ProcessInput(bytes, num_bytes, &model_state_changes_); |
150 Draw(false); | 118 ScheduleDraw(false); |
abarth
2016/01/10 03:31:57
Should this be conditional on whether ProcessInput
jeffbrown
2016/01/26 08:33:41
Handles below. I tried not to change the existing
| |
151 } | 119 } |
152 | 120 |
153 void MotermView::OnClosed() { | 121 void MotermView::OnClosed() { |
154 DCHECK(driver_); | 122 DCHECK(driver_); |
155 driver_->Detach(); | 123 driver_->Detach(); |
156 driver_.reset(); | 124 driver_.reset(); |
157 | 125 |
158 OnDestroyed(); | 126 OnDestroyed(); |
159 } | 127 } |
160 | 128 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
218 void MotermView::GetSize(const GetSizeCallback& callback) { | 186 void MotermView::GetSize(const GetSizeCallback& callback) { |
219 MotermModel::Size size = model_.GetSize(); | 187 MotermModel::Size size = model_.GetSize(); |
220 callback.Run(mojo::files::Error::OK, size.rows, size.columns); | 188 callback.Run(mojo::files::Error::OK, size.rows, size.columns); |
221 } | 189 } |
222 | 190 |
223 void MotermView::SetSize(uint32_t rows, | 191 void MotermView::SetSize(uint32_t rows, |
224 uint32_t columns, | 192 uint32_t columns, |
225 bool reset, | 193 bool reset, |
226 const SetSizeCallback& callback) { | 194 const SetSizeCallback& callback) { |
227 if (!rows) { | 195 if (!rows) { |
228 rows = std::max(1u, std::min(MotermModel::kMaxRows, | 196 rows = std::max( |
229 static_cast<uint32_t>(view_->bounds().height) / | 197 1u, std::min(MotermModel::kMaxRows, |
230 line_height_)); | 198 static_cast<uint32_t>(view_size_.height) / line_height_)); |
231 } | 199 } |
232 if (!columns) { | 200 if (!columns) { |
233 columns = | 201 columns = std::max( |
234 std::max(1u, std::min(MotermModel::kMaxColumns, | 202 1u, std::min(MotermModel::kMaxColumns, |
235 static_cast<uint32_t>(view_->bounds().width) / | 203 static_cast<uint32_t>(view_size_.width) / advance_width_)); |
236 advance_width_)); | |
237 } | 204 } |
238 | 205 |
239 model_.SetSize(MotermModel::Size(rows, columns), reset); | 206 model_.SetSize(MotermModel::Size(rows, columns), reset); |
240 callback.Run(mojo::files::Error::OK, rows, columns); | 207 callback.Run(mojo::files::Error::OK, rows, columns); |
241 | 208 |
242 Draw(false); | 209 ScheduleDraw(false); |
243 } | 210 } |
244 | 211 |
245 void MotermView::Draw(bool force) { | 212 void MotermView::ScheduleDraw(bool force) { |
246 // TODO(vtl): See TODO above |frame_pending_| in the class declaration. | 213 if (view_size_.width == 0 || view_size_.height == 0 || |
247 if (frame_pending_) { | 214 (!model_state_changes_.IsDirty() && !force && !force_next_draw_)) { |
abarth
2016/01/10 03:31:57
Oh, I see. We check the model here.
jeffbrown
2016/01/26 08:33:41
Acknowledged.
| |
248 force_next_draw_ |= force; | 215 force_next_draw_ |= force; |
249 return; | 216 return; |
250 } | 217 } |
218 force_next_draw_ = false; | |
219 choreographer_.ScheduleDraw(); | |
220 } | |
251 | 221 |
252 force |= force_next_draw_; | 222 void MotermView::OnDraw(const mojo::gfx::composition::FrameInfo& frame_info, |
253 force_next_draw_ = false; | 223 const base::TimeDelta& time_delta) { |
254 | |
255 if (!force && !model_state_changes_.IsDirty()) | |
256 return; | |
257 | |
258 // TODO(vtl): If |!force|, draw only the dirty region(s)? | 224 // TODO(vtl): If |!force|, draw only the dirty region(s)? |
259 model_state_changes_.Reset(); | 225 model_state_changes_.Reset(); |
260 | 226 |
261 int32_t width = view_->bounds().width; | 227 mojo::Rect bounds; |
262 int32_t height = view_->bounds().height; | 228 bounds.width = view_size_.width; |
263 DCHECK_GT(width, 0); | 229 bounds.height = view_size_.height; |
264 DCHECK_GT(height, 0); | |
265 | 230 |
266 if (!bitmap_device_) { | 231 auto update = mojo::gfx::composition::SceneUpdate::New(); |
267 bitmap_device_ = skia::AdoptRef(SkBitmapDevice::Create(SkImageInfo::Make( | 232 mojo::gfx::composition::ResourcePtr moterm_resource = |
268 width, height, kN32_SkColorType, kOpaque_SkAlphaType))); | 233 ganesh_renderer()->DrawCanvas( |
269 } | 234 view_size_, |
235 base::Bind(&MotermView::DrawContent, base::Unretained(this))); | |
236 DCHECK(moterm_resource); | |
237 update->resources.insert(kMotermImageResourceId, moterm_resource.Pass()); | |
270 | 238 |
271 SkCanvas canvas(bitmap_device_.get()); | 239 auto root_node = mojo::gfx::composition::Node::New(); |
272 canvas.clear(SK_ColorBLACK); | 240 root_node->op = mojo::gfx::composition::NodeOp::New(); |
241 root_node->op->set_image(mojo::gfx::composition::ImageNodeOp::New()); | |
242 root_node->op->get_image()->content_rect = bounds.Clone(); | |
243 root_node->op->get_image()->image_resource_id = kMotermImageResourceId; | |
244 update->nodes.insert(kRootNodeId, root_node.Pass()); | |
245 | |
246 auto metadata = mojo::gfx::composition::SceneMetadata::New(); | |
247 metadata->presentation_time = frame_info.presentation_time; | |
248 | |
249 scene()->Update(update.Pass()); | |
250 scene()->Publish(metadata.Pass()); | |
251 } | |
252 | |
253 void MotermView::DrawContent(SkCanvas* canvas) { | |
254 canvas->clear(SK_ColorBLACK); | |
273 | 255 |
274 SkPaint bg_paint; | 256 SkPaint bg_paint; |
275 bg_paint.setStyle(SkPaint::kFill_Style); | 257 bg_paint.setStyle(SkPaint::kFill_Style); |
276 | 258 |
277 SkPaint fg_paint; | 259 SkPaint fg_paint; |
278 fg_paint.setTypeface(regular_typeface_.get()); | 260 fg_paint.setTypeface(regular_typeface_.get()); |
279 fg_paint.setTextSize(16); | 261 fg_paint.setTextSize(16); |
280 fg_paint.setTextEncoding(SkPaint::kUTF32_TextEncoding); | 262 fg_paint.setTextEncoding(SkPaint::kUTF32_TextEncoding); |
281 | 263 |
282 MotermModel::Size size = model_.GetSize(); | 264 MotermModel::Size size = model_.GetSize(); |
283 int y = 0; | 265 int y = 0; |
284 for (unsigned i = 0; i < size.rows; i++, y += line_height_) { | 266 for (unsigned i = 0; i < size.rows; i++, y += line_height_) { |
285 int x = 0; | 267 int x = 0; |
286 for (unsigned j = 0; j < size.columns; j++, x += advance_width_) { | 268 for (unsigned j = 0; j < size.columns; j++, x += advance_width_) { |
287 MotermModel::CharacterInfo ch = | 269 MotermModel::CharacterInfo ch = |
288 model_.GetCharacterInfoAt(MotermModel::Position(i, j)); | 270 model_.GetCharacterInfoAt(MotermModel::Position(i, j)); |
289 | 271 |
290 // Paint the background. | 272 // Paint the background. |
291 bg_paint.setColor(SkColorSetRGB(ch.background_color.red, | 273 bg_paint.setColor(SkColorSetRGB(ch.background_color.red, |
292 ch.background_color.green, | 274 ch.background_color.green, |
293 ch.background_color.blue)); | 275 ch.background_color.blue)); |
294 canvas.drawRect(SkRect::MakeXYWH(x, y, advance_width_, line_height_), | 276 canvas->drawRect(SkRect::MakeXYWH(x, y, advance_width_, line_height_), |
295 bg_paint); | 277 bg_paint); |
296 | 278 |
297 // Paint the foreground. | 279 // Paint the foreground. |
298 if (ch.code_point) { | 280 if (ch.code_point) { |
299 uint32_t flags = SkPaint::kAntiAlias_Flag; | 281 uint32_t flags = SkPaint::kAntiAlias_Flag; |
300 // TODO(vtl): Use real bold font? | 282 // TODO(vtl): Use real bold font? |
301 if ((ch.attributes & MotermModel::kAttributesBold)) | 283 if ((ch.attributes & MotermModel::kAttributesBold)) |
302 flags |= SkPaint::kFakeBoldText_Flag; | 284 flags |= SkPaint::kFakeBoldText_Flag; |
303 if ((ch.attributes & MotermModel::kAttributesUnderline)) | 285 if ((ch.attributes & MotermModel::kAttributesUnderline)) |
304 flags |= SkPaint::kUnderlineText_Flag; | 286 flags |= SkPaint::kUnderlineText_Flag; |
305 // TODO(vtl): Handle blink, because that's awesome. | 287 // TODO(vtl): Handle blink, because that's awesome. |
306 fg_paint.setFlags(flags); | 288 fg_paint.setFlags(flags); |
307 fg_paint.setColor(SkColorSetRGB(ch.foreground_color.red, | 289 fg_paint.setColor(SkColorSetRGB(ch.foreground_color.red, |
308 ch.foreground_color.green, | 290 ch.foreground_color.green, |
309 ch.foreground_color.blue)); | 291 ch.foreground_color.blue)); |
310 | 292 |
311 canvas.drawText(&ch.code_point, sizeof(ch.code_point), x, y + ascent_, | 293 canvas->drawText(&ch.code_point, sizeof(ch.code_point), x, y + ascent_, |
312 fg_paint); | 294 fg_paint); |
313 } | 295 } |
314 } | 296 } |
315 } | 297 } |
316 | 298 |
317 if (model_.GetCursorVisibility()) { | 299 if (model_.GetCursorVisibility()) { |
318 // Draw the cursor. | 300 // Draw the cursor. |
319 MotermModel::Position cursor_pos = model_.GetCursorPosition(); | 301 MotermModel::Position cursor_pos = model_.GetCursorPosition(); |
320 // Reuse the background paint, but don't just paint over. | 302 // Reuse the background paint, but don't just paint over. |
321 // TODO(vtl): Consider doing other things. Maybe make it blink, to be extra | 303 // TODO(vtl): Consider doing other things. Maybe make it blink, to be extra |
322 // annoying. | 304 // annoying. |
323 // TODO(vtl): Maybe vary how we draw the cursor, depending on if we're | 305 // TODO(vtl): Maybe vary how we draw the cursor, depending on if we're |
324 // focused and/or active. | 306 // focused and/or active. |
325 bg_paint.setColor(SK_ColorWHITE); | 307 bg_paint.setColor(SK_ColorWHITE); |
326 bg_paint.setXfermodeMode(SkXfermode::kDifference_Mode); | 308 bg_paint.setXfermodeMode(SkXfermode::kDifference_Mode); |
327 canvas.drawRect(SkRect::MakeXYWH(cursor_pos.column * advance_width_, | 309 canvas->drawRect(SkRect::MakeXYWH(cursor_pos.column * advance_width_, |
328 cursor_pos.row * line_height_, | 310 cursor_pos.row * line_height_, |
329 advance_width_, line_height_), | 311 advance_width_, line_height_), |
330 bg_paint); | 312 bg_paint); |
331 } | 313 } |
332 | 314 canvas->flush(); |
333 canvas.flush(); | |
334 | |
335 const SkBitmap& bitmap(bitmap_device_->accessBitmap(false)); | |
336 // TODO(vtl): Do we need really need to lock/unlock pixels? | |
337 SkAutoLockPixels pixel_locker(bitmap); | |
338 | |
339 gl_helper_.StartFrame(); | |
340 // (The texture is already bound.) | |
341 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, kTextureFormat, | |
342 GL_UNSIGNED_BYTE, bitmap.getPixels()); | |
343 gl_helper_.EndFrame(); | |
344 frame_pending_ = true; | |
345 } | 315 } |
346 | 316 |
347 void MotermView::OnKeyPressed(const mojo::EventPtr& key_event) { | 317 void MotermView::OnKeyPressed(mojo::EventPtr key_event) { |
348 std::string input_sequence = | 318 std::string input_sequence = |
349 GetInputSequenceForKeyPressedEvent(*key_event, keypad_application_mode_); | 319 GetInputSequenceForKeyPressedEvent(*key_event, keypad_application_mode_); |
350 if (input_sequence.empty()) | 320 if (input_sequence.empty()) |
351 return; | 321 return; |
352 | 322 |
353 if (driver_) | 323 if (driver_) |
354 driver_->SendData(input_sequence.data(), input_sequence.size()); | 324 driver_->SendData(input_sequence.data(), input_sequence.size()); |
355 } | 325 } |
OLD | NEW |