blob: 25622224f4d4c7dcbfeae2fa29a7f6a33a9e0376 [file] [log] [blame]
// Copyright 2022 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.
//
// Entrypoint for running a Flutter app on Fuchsia.
//
// Usage: ./main <path_to_flutter_asset_bundle>
#include <fuchsia/ui/app/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/default.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/global.h>
#include <string>
#include <zircon/status.h>
#include "src/embedder/engine/embedder.h"
namespace {
constexpr char kLogTag[] = "flutter_embedder";
// TODO(akbiggs): This is just being used to verify that we can
// create and bind things on the component context. We should
// replace this with an actual ViewProvider impl.
class DummyViewProvider : public fuchsia::ui::app::ViewProvider {
public:
DummyViewProvider() {}
~DummyViewProvider() override {}
DummyViewProvider(const DummyViewProvider &) = delete;
DummyViewProvider &operator=(const DummyViewProvider &) = delete;
void CreateView(
zx::eventpair token,
fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services)
override {
FX_LOG(INFO, kLogTag, "ViewProvider::CreateView");
}
void CreateViewWithViewRef(zx::eventpair view_token,
fuchsia::ui::views::ViewRefControl control_ref,
fuchsia::ui::views::ViewRef view_ref) override {
FX_LOG(INFO, kLogTag, "ViewProvider::CreateViewWithViewRef");
}
void CreateView2(fuchsia::ui::app::CreateView2Args view_args) override {
FX_LOG(INFO, kLogTag, "ViewProvider::CreateView2");
}
};
// State for the embedder. The engine callbacks
// get this data as an argument so we can access
// this data from our implementation of the callbacks.
struct EmbedderData {
// WARNING: |component_context| MUST BE THE FIRST FIELD OF THIS DATA.
// We do a terrible hack in https://github.com/flutter/engine/pull/33472 that
// requires reading this field to work around fxb/75282 to set the inspect
// node inside the embedder on the Fuchsia platform.
// TODO(fxb/75282): Properly fix this and remove the terrible hack.
std::unique_ptr<sys::ComponentContext> component_context;
};
bool FuchsiaPresentSoftwareSurface(void *user_data, const void *allocation,
size_t row_bytes, size_t height) {
// TODO(akbiggs): Present the surface to the screen.
FX_LOGF(ERROR, kLogTag,
"surface_present_callback (row_bytes=%lu, height=%lu)", row_bytes,
height);
return true;
}
void FuchsiaLogMessage(const char *tag, const char *message, void *user_data) {
// TODO(akbiggs): This does not report the file and line number of the Dart
// app that the log came from.
FX_LOG(INFO, tag, message);
}
bool RunFlutterApp(const char *assets_path, EmbedderData *embedder_data) {
FlutterRendererConfig renderer_config = {
.type = kSoftware,
.software =
{
.struct_size = sizeof(FlutterSoftwareRendererConfig),
.surface_present_callback = FuchsiaPresentSoftwareSurface,
// TODO(akbiggs): Add callback for acquiring the software surface.
},
};
FlutterProjectArgs project_args = {
.struct_size = sizeof(FlutterProjectArgs),
.assets_path = assets_path,
.log_message_callback = FuchsiaLogMessage,
.log_tag = "flutter_app",
};
// TODO(akbiggs): Store this FlutterEngine instance somewhere instead of
// throwing it away.
FlutterEngine engine;
FlutterEngineResult result =
FlutterEngineRun(FLUTTER_ENGINE_VERSION, &renderer_config, &project_args,
embedder_data, &engine);
if (result != kSuccess || engine == nullptr) {
FX_LOGF(ERROR, kLogTag, "Could not run the Flutter Engine. Error code: %d",
static_cast<int>(result));
return false;
}
return true;
}
} // namespace
int main(int argc, const char *argv[]) {
if (argc != 2) {
FX_LOG(ERROR, kLogTag, "usage: executable <path to flutter bundle>");
return EXIT_FAILURE;
}
const char *assets_path = argv[1];
EmbedderData embedder_data = {
.component_context = sys::ComponentContext::Create(),
};
// TODO(akbiggs): Remove debug logs once we're sure this is stable.
FX_LOG(INFO, kLogTag, "Running Flutter app.");
// Note: Serving the component context must happen after RunFlutterApp
// because the embedder platform has a hack that adds inspect data into the
// Dart VM using the component context
// (https://github.com/flutter/engine/pull/33472).
RunFlutterApp(assets_path, &embedder_data);
FX_LOG(INFO, kLogTag, "Binding ViewProvider.");
DummyViewProvider view_provider;
fidl::BindingSet<fuchsia::ui::app::ViewProvider> view_provider_bindings;
zx_status_t status =
embedder_data.component_context->outgoing()
->AddPublicService<fuchsia::ui::app::ViewProvider>(
[&view_provider, &view_provider_bindings](
fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider>
request) {
FX_LOG(INFO, kLogTag, "Adding ViewProvider binding.");
view_provider_bindings.AddBinding(&view_provider,
std::move(request));
});
if (status != ZX_OK) {
FX_LOGF(ERROR, kLogTag, "Failed to add ViewProvider service: %s",
zx_status_get_string(status));
}
// Our run loop's dispatcher must be used as the dispatcher for serving
// the component context in order for requests handlers to get called while
// we're looping.
FX_LOG(INFO, kLogTag, "Serving component context.");
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
status = embedder_data.component_context->outgoing()->ServeFromStartupInfo(
loop.dispatcher());
if (status != ZX_OK) {
FX_LOGF(ERROR, kLogTag, "Failed to serve component context: %s",
zx_status_get_string(status));
}
// Loop until we're done.
FX_LOG(INFO, kLogTag, "Looping.");
loop.Run();
FX_LOG(INFO, kLogTag, "Done looping.");
return EXIT_SUCCESS;
}