| // Copyright 2016 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 <unistd.h> |
| |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "mojo/application/application_runner_chromium.h" |
| #include "mojo/public/c/system/main.h" |
| #include "mojo/public/cpp/application/connect.h" |
| #include "mojo/ui/choreographer.h" |
| #include "mojo/ui/ganesh_view.h" |
| #include "mojo/ui/input_handler.h" |
| #include "mojo/ui/view_provider_app.h" |
| #include "third_party/dejavu-fonts-ttf-2.34/kDejaVuSansMonoRegular.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "third_party/skia/include/core/SkRect.h" |
| #include "third_party/skia/include/core/SkStream.h" |
| #include "third_party/skia/include/core/SkTypeface.h" |
| |
| namespace examples { |
| |
| namespace { |
| constexpr uint32_t kContentImageResourceId = 1; |
| constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId; |
| |
| enum class Action { |
| kHang10, |
| kStutter30, |
| kCrash, |
| }; |
| |
| struct Button { |
| const char* label; |
| Action action; |
| }; |
| |
| const Button kButtons[] = { |
| {"Hang for 10 seconds", Action::kHang10}, |
| {"Stutter for 30 seconds", Action::kStutter30}, |
| {"Crash!", Action::kCrash}, |
| }; |
| |
| constexpr SkScalar kButtonWidth = 300; |
| constexpr SkScalar kButtonHeight = 30; |
| constexpr SkScalar kMargin = 15; |
| } // namespace |
| |
| class JankView : public mojo::ui::GaneshView, |
| public mojo::ui::ChoreographerDelegate, |
| public mojo::ui::InputListener { |
| public: |
| JankView(mojo::InterfaceHandle<mojo::ApplicationConnector> app_connector, |
| mojo::InterfaceRequest<mojo::ui::ViewOwner> view_owner_request) |
| : GaneshView(app_connector.Pass(), view_owner_request.Pass(), "Jank"), |
| choreographer_(scene(), this), |
| input_handler_(GetViewServiceProvider(), this), |
| typeface_(skia::AdoptRef(SkTypeface::CreateFromStream( |
| new SkMemoryStream(font_data::kDejaVuSansMonoRegular.data, |
| font_data::kDejaVuSansMonoRegular.size)))) {} |
| |
| ~JankView() override {} |
| |
| private: |
| // |GaneshView|: |
| void OnPropertiesChanged( |
| uint32_t old_scene_version, |
| mojo::ui::ViewPropertiesPtr old_properties) override { |
| choreographer_.ScheduleDraw(); |
| } |
| |
| // |InputListener|: |
| void OnEvent(mojo::EventPtr event, const OnEventCallback& callback) override { |
| if (event->pointer_data && event->action == mojo::EventType::POINTER_DOWN) { |
| SkScalar x = event->pointer_data->x; |
| SkScalar y = event->pointer_data->y; |
| if (x >= kMargin && x <= kButtonWidth + kMargin) { |
| int index = (y - kMargin) / (kButtonHeight + kMargin); |
| if (index >= 0 && |
| size_t(index) < sizeof(kButtons) / sizeof(kButtons[0]) && |
| y < (kButtonHeight + kMargin) * (index + 1)) |
| OnClick(kButtons[index]); |
| } |
| } |
| callback.Run(false); |
| } |
| |
| // |ChoreographerDelegate|: |
| void OnDraw(const mojo::gfx::composition::FrameInfo& frame_info, |
| const base::TimeDelta& time_delta) override { |
| if (!properties()) |
| return; |
| |
| auto update = mojo::gfx::composition::SceneUpdate::New(); |
| |
| const mojo::Size& size = *properties()->view_layout->size; |
| if (size.width > 0 && size.height > 0) { |
| mojo::RectF bounds; |
| bounds.width = size.width; |
| bounds.height = size.height; |
| |
| mojo::gfx::composition::ResourcePtr content_resource = |
| ganesh_renderer()->DrawCanvas( |
| size, base::Bind(&JankView::DrawContent, base::Unretained(this))); |
| DCHECK(content_resource); |
| update->resources.insert(kContentImageResourceId, |
| content_resource.Pass()); |
| |
| auto root_node = mojo::gfx::composition::Node::New(); |
| root_node->hit_test_behavior = |
| mojo::gfx::composition::HitTestBehavior::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 = kContentImageResourceId; |
| update->nodes.insert(kRootNodeId, root_node.Pass()); |
| } else { |
| auto root_node = mojo::gfx::composition::Node::New(); |
| update->nodes.insert(kRootNodeId, root_node.Pass()); |
| } |
| |
| scene()->Update(update.Pass()); |
| |
| auto metadata = mojo::gfx::composition::SceneMetadata::New(); |
| metadata->version = scene_version(); |
| scene()->Publish(metadata.Pass()); |
| |
| choreographer_.ScheduleDraw(); |
| |
| if (MojoGetTimeTicksNow() < stutter_end_time_) |
| sleep(2); |
| } |
| |
| void DrawContent(const mojo::skia::GaneshContext::Scope& ganesh_scope, |
| const mojo::Size& size, |
| SkCanvas* canvas) { |
| SkScalar hsv[3] = {fmod(MojoGetTimeTicksNow() * 0.000001f * 60, 360.f), 1, |
| 1}; |
| canvas->clear(SkHSVToColor(hsv)); |
| |
| SkScalar x = kMargin; |
| SkScalar y = kMargin; |
| for (const auto& button : kButtons) { |
| DrawButton(canvas, button.label, |
| SkRect::MakeXYWH(x, y, kButtonWidth, kButtonHeight)); |
| y += kButtonHeight + kMargin; |
| } |
| } |
| |
| void DrawButton(SkCanvas* canvas, const char* label, const SkRect& bounds) { |
| SkPaint boxPaint; |
| boxPaint.setColor(SkColorSetRGB(200, 200, 200)); |
| canvas->drawRect(bounds, boxPaint); |
| boxPaint.setColor(SkColorSetRGB(40, 40, 40)); |
| boxPaint.setStyle(SkPaint::kStroke_Style); |
| canvas->drawRect(bounds, boxPaint); |
| |
| SkPaint textPaint; |
| textPaint.setColor(SK_ColorBLACK); |
| textPaint.setTextSize(16); |
| textPaint.setTextEncoding(SkPaint::kUTF8_TextEncoding); |
| textPaint.setTypeface(typeface_.get()); |
| textPaint.setAntiAlias(true); |
| SkRect textBounds; |
| textPaint.measureText(label, strlen(label), &textBounds); |
| canvas->drawText(label, strlen(label), |
| bounds.centerX() - textBounds.centerX(), |
| bounds.centerY() - textBounds.centerY(), textPaint); |
| } |
| |
| void OnClick(const Button& button) { |
| LOG(INFO) << "Clicked: " << button.label; |
| |
| switch (button.action) { |
| case Action::kHang10: { |
| sleep(10); |
| break; |
| } |
| |
| case Action::kStutter30: { |
| stutter_end_time_ = MojoGetTimeTicksNow() + 30 * 1000000; |
| break; |
| } |
| |
| case Action::kCrash: { |
| abort(); |
| break; |
| } |
| } |
| } |
| |
| mojo::ui::Choreographer choreographer_; |
| mojo::ui::InputHandler input_handler_; |
| skia::RefPtr<SkTypeface> typeface_; |
| int64_t stutter_end_time_ = 0u; |
| |
| DISALLOW_COPY_AND_ASSIGN(JankView); |
| }; |
| |
| class JankApp : public mojo::ui::ViewProviderApp { |
| public: |
| JankApp() {} |
| ~JankApp() override {} |
| |
| void CreateView( |
| const std::string& connection_url, |
| mojo::InterfaceRequest<mojo::ui::ViewOwner> view_owner_request, |
| mojo::InterfaceRequest<mojo::ServiceProvider> services) override { |
| new JankView(mojo::CreateApplicationConnector(app_impl()->shell()), |
| view_owner_request.Pass()); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(JankApp); |
| }; |
| |
| } // namespace examples |
| |
| MojoResult MojoMain(MojoHandle application_request) { |
| mojo::ApplicationRunnerChromium runner(new examples::JankApp()); |
| return runner.Run(application_request); |
| } |