blob: eb0599f05c2838c9ed68ce9bd5b8444e5feb16b1 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 <fuchsia/test/ui/cpp/fidl.h>
#include <fuchsia/ui/app/cpp/fidl.h>
#include <fuchsia/ui/gfx/cpp/fidl.h>
#include <fuchsia/ui/input/cpp/fidl.h>
#include <fuchsia/ui/scenic/cpp/fidl.h>
#include <fuchsia/ui/views/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/fostr/fidl/fuchsia/ui/gfx/formatting.h>
#include <lib/fostr/fidl/fuchsia/ui/input/formatting.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/syslog/global.h>
#include <lib/ui/scenic/cpp/resources.h>
#include <lib/zx/eventpair.h>
#include <zircon/status.h>
#include <array>
#include <memory>
namespace cpp_gfx_client {
// Implementation of a very simple Scenic client.
class CppGfxClient : public fuchsia::ui::app::ViewProvider {
public:
CppGfxClient(async::Loop* loop) : loop_(loop), view_provider_binding_(this) {
FX_CHECK(loop_);
context_ = sys::ComponentContext::CreateAndServeOutgoingDirectory();
context_->outgoing()->AddPublicService<fuchsia::ui::app::ViewProvider>(
[this](fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider> request) {
view_provider_binding_.Bind(std::move(request));
});
response_listener_ = context_->svc()->Connect<fuchsia::test::ui::ResponseListener>();
response_listener_.set_error_handler([](zx_status_t status) {
FX_LOGS(WARNING) << "JFYI. Test response listener disconnected, status: " <<
zx_status_get_string(status);
// Don't quit, because we should be able to run this client outside of a test.
});
scenic_ = context_->svc()->Connect<fuchsia::ui::scenic::Scenic>();
scenic_.set_error_handler([this](zx_status_t status) {
FX_LOGS(ERROR) << "Quitting. Scenic disconnected, status: " << zx_status_get_string(status);
loop_->Quit();
});
session_ = std::make_unique<scenic::Session>(scenic_.get());
session_->set_error_handler([this](zx_status_t status) {
FX_LOGS(ERROR) << "Quitting. Scenic session disconnected, status: " <<
zx_status_get_string(status); loop_->Quit();
});
session_->set_event_handler(fit::bind_member(this, &CppGfxClient::OnEvents));
session_->set_on_frame_presented_handler([](auto frame_presented_info) {});
root_node_ = std::make_unique<scenic::EntityNode>(session_.get());
root_node_->SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
session_->Present2(zx_clock_get_monotonic(), 0, [](auto future_presentation_times) {});
}
private:
static constexpr std::array<std::array<uint8_t, 4>, 6> kColorsRgba = {
{{255, 0, 0, 255}, // red
{255, 128, 0, 255}, // orange
{255, 255, 0, 255}, // yellow
{0, 255, 0, 255}, // green
{0, 0, 255, 255}, // blue
{128, 0, 255, 255}}}; // purple
// |fuchsia::ui::app::ViewProvider|
void CreateView(zx::eventpair token,
fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> /*unused*/,
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> /*unused*/) override {
FX_LOGS(INFO) << "CreateView called.";
view_ = std::make_unique<scenic::View>(session_.get(), std::move(token), "cpp-gfx-client view");
view_->AddChild(*root_node_);
session_->Present2(zx_clock_get_monotonic(), 0, [](auto future_presentation_times) {});
}
// |fuchsia::ui::app::ViewProvider|
void CreateViewWithViewRef(zx::eventpair token,
fuchsia::ui::views::ViewRefControl view_ref_control,
fuchsia::ui::views::ViewRef view_ref) override {
FX_LOGS(INFO) << "CreateViewWithViewRef called.";
view_ = std::make_unique<scenic::View>(
session_.get(), fuchsia::ui::views::ViewToken{.value = std::move(token)},
std::move(view_ref_control), std::move(view_ref), "cpp-gfx-client view");
view_->AddChild(*root_node_);
session_->Present2(zx_clock_get_monotonic(), 0, [](auto future_presentation_times) {});
}
// Scenic Session event handler, passed to |fuchsia::ui::scenic::SessionListener|.
void OnEvents(std::vector<fuchsia::ui::scenic::Event> events) {
for (const auto& event : events) {
if (event.is_gfx()) {
switch (event.gfx().Which()) {
case fuchsia::ui::gfx::Event::Tag::kMetrics: {
FX_LOGS(INFO) << "Metrics received.";
metrics_ = event.gfx().metrics().metrics;
break;
}
case fuchsia::ui::gfx::Event::Tag::kViewPropertiesChanged: {
FX_LOGS(INFO) << "View properties received.";
view_properties_ = event.gfx().view_properties_changed().properties;
break;
}
default:
break; // nop
}
} else if (event.is_input()) {
switch (event.input().Which()) {
case fuchsia::ui::input::InputEvent::Tag::kPointer: {
if (event.input().pointer().phase == fuchsia::ui::input::PointerEventPhase::DOWN &&
material_) {
{
// Cycle to next color.
color_index_ = (color_index_ + 1) % kColorsRgba.size();
std::array<uint8_t, 4> color = kColorsRgba[color_index_];
material_->SetColor(color[0], color[1], color[2], color[3]);
session_->Present2(zx_clock_get_monotonic(), 0,
[](auto future_presentation_times) {});
}
if (response_listener_) {
fuchsia::test::ui::PointerData data;
// The raw pointer event's coordinates are in pips (logical pixels). The test
// expects coordinates in physical pixels. The former is transformed into the latter
// with the scale factor provided in the metrics event.
data.set_local_x(event.input().pointer().x * metrics_.scale_x);
data.set_local_y(event.input().pointer().y * metrics_.scale_y);
data.set_time_received(zx_clock_get_monotonic());
response_listener_->Respond(std::move(data));
}
}
break;
}
default:
break; // nop
}
}
}
if (!scene_created_ && ViewSize().x > 0 && ViewSize().y > 0) {
CreateScene();
scene_created_ = true;
}
}
// Calculates view size based on view properties and metrics event.
fuchsia::ui::gfx::vec2 ViewSize() const {
const fuchsia::ui::gfx::ViewProperties& p = view_properties_;
float size_x = ((p.bounding_box.max.x - p.inset_from_max.x) -
(p.bounding_box.min.x + p.inset_from_min.x)) *
metrics_.scale_x;
float size_y = ((p.bounding_box.max.y - p.inset_from_max.y) -
(p.bounding_box.min.y + p.inset_from_min.y)) *
metrics_.scale_y;
return fuchsia::ui::gfx::vec2{.x = size_x, .y = size_y};
}
// Encapsulates scene setup.
void CreateScene() {
FX_CHECK(session_) << "precondition";
FX_CHECK(root_node_) << "precondition";
scenic::Session* session = session_.get();
scenic::ShapeNode shape(session);
scenic::Rectangle rec(session, ViewSize().x, ViewSize().y);
shape.SetShape(rec);
shape.SetTranslation(ViewSize().x / 2, ViewSize().y / 2, 0.f);
material_ = std::make_unique<scenic::Material>(session);
std::array<uint8_t, 4> color = kColorsRgba[color_index_];
material_->SetColor(color[0], color[1], color[2], color[3]);
shape.SetMaterial(*material_);
root_node_->AddChild(shape);
session_->Present2(zx_clock_get_monotonic(), 0, [](auto future_presentation_times) {});
}
// The main thread's message loop.
async::Loop* loop_ = nullptr;
// This component's global context.
std::unique_ptr<sys::ComponentContext> context_;
// Protocols used by this component.
fuchsia::ui::scenic::ScenicPtr scenic_;
fuchsia::test::ui::ResponseListenerPtr response_listener_;
// Protocols vended by this component.
fidl::Binding<fuchsia::ui::app::ViewProvider> view_provider_binding_;
// Scene state.
std::unique_ptr<scenic::Session> session_;
std::unique_ptr<scenic::View> view_;
std::unique_ptr<scenic::EntityNode> root_node_;
std::unique_ptr<scenic::Material> material_;
fuchsia::ui::gfx::ViewProperties view_properties_;
fuchsia::ui::gfx::Metrics metrics_;
bool scene_created_ = false;
uint32_t color_index_ = 0;
};
} // namespace cpp_gfx_client
// Component entry point.
int main(int argc, char** argv) {
FX_LOGS(INFO) << "Starting cpp-gfx-client.";
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
cpp_gfx_client::CppGfxClient client(&loop);
return loop.Run();
}