Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
7 changes: 7 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1154,15 +1154,22 @@ FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/vmo.cc
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/vmo.h
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/vmservice_object.cc
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/vmservice_object.h
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_engine.cc
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_engine_unittests.cc
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_window_controller.cc
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_window_controller_unittests.cc
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_window_unittests.cc
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_engine.h
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window.h
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window_controller.h
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/plugin_registrar_glfw.h
FILE: ../../../flutter/shell/platform/glfw/event_loop.cc
FILE: ../../../flutter/shell/platform/glfw/event_loop.h
FILE: ../../../flutter/shell/platform/glfw/flutter_glfw.cc
FILE: ../../../flutter/shell/platform/glfw/glfw_event_loop.cc
FILE: ../../../flutter/shell/platform/glfw/glfw_event_loop.h
FILE: ../../../flutter/shell/platform/glfw/headless_event_loop.cc
FILE: ../../../flutter/shell/platform/glfw/headless_event_loop.h
FILE: ../../../flutter/shell/platform/glfw/key_event_handler.cc
FILE: ../../../flutter/shell/platform/glfw/key_event_handler.h
FILE: ../../../flutter/shell/platform/glfw/keyboard_hook_handler.h
Expand Down
4 changes: 4 additions & 0 deletions shell/platform/glfw/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ source_set("flutter_glfw_headers") {

source_set("flutter_glfw") {
sources = [
"event_loop.cc",
"event_loop.h",
"flutter_glfw.cc",
"glfw_event_loop.cc",
"glfw_event_loop.h",
"headless_event_loop.cc",
"headless_event_loop.h",
"key_event_handler.cc",
"key_event_handler.h",
"keyboard_hook_handler.h",
Expand Down
8 changes: 6 additions & 2 deletions shell/platform/glfw/client_wrapper/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import("//flutter/shell/platform/common/cpp/client_wrapper/publish.gni")
import("//flutter/testing/testing.gni")

_wrapper_includes = [
"include/flutter/flutter_engine.h",
"include/flutter/flutter_window.h",
"include/flutter/flutter_window_controller.h",
"include/flutter/plugin_registrar_glfw.h",
]

_wrapper_sources = [ "flutter_window_controller.cc" ]
_wrapper_sources = [
"flutter_engine.cc",
"flutter_window_controller.cc",
]

# This code will be merged into .../common/cpp/client_wrapper for client use,
# so uses header paths that assume the merged state. Include the header
Expand Down Expand Up @@ -70,8 +74,8 @@ test_fixtures("client_wrapper_glfw_fixtures") {
executable("client_wrapper_glfw_unittests") {
testonly = true

# TODO: Add more unit tests.
sources = [
"flutter_engine_unittests.cc",
"flutter_window_controller_unittests.cc",
"flutter_window_unittests.cc",
]
Expand Down
82 changes: 82 additions & 0 deletions shell/platform/glfw/client_wrapper/flutter_engine.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2013 The Flutter 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 "include/flutter/flutter_engine.h"

#include <algorithm>
#include <iostream>

namespace flutter {

FlutterEngine::FlutterEngine() {}

FlutterEngine::~FlutterEngine() {}

bool FlutterEngine::Start(const std::string& icu_data_path,
const std::string& assets_path,
const std::vector<std::string>& arguments) {
if (engine_) {
std::cerr << "Cannot run an already running engine. Create a new instance "
"or call ShutDown first."
<< std::endl;
return false;
}

FlutterDesktopEngineProperties c_engine_properties = {};
c_engine_properties.assets_path = assets_path.c_str();
c_engine_properties.icu_data_path = icu_data_path.c_str();
std::vector<const char*> engine_switches;
std::transform(
arguments.begin(), arguments.end(), std::back_inserter(engine_switches),
[](const std::string& arg) -> const char* { return arg.c_str(); });
if (engine_switches.size() > 0) {
c_engine_properties.switches = &engine_switches[0];
c_engine_properties.switches_count = engine_switches.size();
}

engine_ = UniqueEnginePtr(FlutterDesktopRunEngine(c_engine_properties),
FlutterDesktopShutDownEngine);
if (!engine_) {
std::cerr << "Failed to start engine." << std::endl;
return false;
}
return true;
}

void FlutterEngine::ShutDown() {
engine_ = nullptr;
}

FlutterDesktopPluginRegistrarRef FlutterEngine::GetRegistrarForPlugin(
const std::string& plugin_name) {
if (!engine_) {
std::cerr << "Cannot get plugin registrar on an engine that isn't running; "
"call Run first."
<< std::endl;
return nullptr;
}
return FlutterDesktopGetPluginRegistrar(engine_.get(), plugin_name.c_str());
}

void FlutterEngine::RunEventLoopWithTimeout(std::chrono::milliseconds timeout) {
if (!engine_) {
std::cerr << "Cannot run event loop without a running engine; call "
"Run first."
<< std::endl;
return;
}
uint32_t timeout_milliseconds;
if (timeout == std::chrono::milliseconds::max()) {
// The C API uses 0 to represent no timeout, so convert |max| to 0.
timeout_milliseconds = 0;
} else if (timeout.count() > UINT32_MAX) {
timeout_milliseconds = UINT32_MAX;
} else {
timeout_milliseconds = static_cast<uint32_t>(timeout.count());
}
FlutterDesktopRunEngineEventLoopWithTimeout(engine_.get(),
timeout_milliseconds);
}

} // namespace flutter
103 changes: 103 additions & 0 deletions shell/platform/glfw/client_wrapper/flutter_engine_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2013 The Flutter 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 <memory>
#include <string>

#include "flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_engine.h"
#include "flutter/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h"
#include "gtest/gtest.h"

namespace flutter {

namespace {

// Stub implementation to validate calls to the API.
class TestGlfwApi : public testing::StubFlutterGlfwApi {
public:
// |flutter::testing::StubFlutterGlfwApi|
FlutterDesktopEngineRef RunEngine(
const FlutterDesktopEngineProperties& properties) override {
run_called_ = true;
return reinterpret_cast<FlutterDesktopEngineRef>(1);
}

// |flutter::testing::StubFlutterGlfwApi|
void RunEngineEventLoopWithTimeout(uint32_t millisecond_timeout) override {
last_run_loop_timeout_ = millisecond_timeout;
}

// |flutter::testing::StubFlutterGlfwApi|
bool ShutDownEngine() override {
shut_down_called_ = true;
return true;
}

bool run_called() { return run_called_; }

bool shut_down_called() { return shut_down_called_; }

uint32_t last_run_loop_timeout() { return last_run_loop_timeout_; }

private:
bool run_called_ = false;
bool shut_down_called_ = false;
uint32_t last_run_loop_timeout_ = 0;
};

} // namespace

TEST(FlutterEngineTest, CreateDestroy) {
const std::string icu_data_path = "fake/path/to/icu";
const std::string assets_path = "fake/path/to/assets";
testing::ScopedStubFlutterGlfwApi scoped_api_stub(
std::make_unique<TestGlfwApi>());
auto test_api = static_cast<TestGlfwApi*>(scoped_api_stub.stub());
{
FlutterEngine engine;
engine.Start(icu_data_path, assets_path, {});
EXPECT_EQ(test_api->run_called(), true);
EXPECT_EQ(test_api->shut_down_called(), false);
}
// Destroying should implicitly shut down if it hasn't been done manually.
EXPECT_EQ(test_api->shut_down_called(), true);
}

TEST(FlutterEngineTest, ExplicitShutDown) {
const std::string icu_data_path = "fake/path/to/icu";
const std::string assets_path = "fake/path/to/assets";
testing::ScopedStubFlutterGlfwApi scoped_api_stub(
std::make_unique<TestGlfwApi>());
auto test_api = static_cast<TestGlfwApi*>(scoped_api_stub.stub());

FlutterEngine engine;
engine.Start(icu_data_path, assets_path, {});
EXPECT_EQ(test_api->run_called(), true);
EXPECT_EQ(test_api->shut_down_called(), false);
engine.ShutDown();
EXPECT_EQ(test_api->shut_down_called(), true);
}

TEST(FlutterEngineTest, RunloopTimeoutTranslation) {
const std::string icu_data_path = "fake/path/to/icu";
const std::string assets_path = "fake/path/to/assets";
testing::ScopedStubFlutterGlfwApi scoped_api_stub(
std::make_unique<TestGlfwApi>());
auto test_api = static_cast<TestGlfwApi*>(scoped_api_stub.stub());

FlutterEngine engine;
engine.Start(icu_data_path, assets_path, {});

engine.RunEventLoopWithTimeout(std::chrono::milliseconds(100));
EXPECT_EQ(test_api->last_run_loop_timeout(), 100U);

engine.RunEventLoopWithTimeout(std::chrono::milliseconds::max() -
std::chrono::milliseconds(1));
EXPECT_EQ(test_api->last_run_loop_timeout(), UINT32_MAX);

engine.RunEventLoopWithTimeout(std::chrono::milliseconds::max());
EXPECT_EQ(test_api->last_run_loop_timeout(), 0U);
}

} // namespace flutter
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ FlutterDesktopPluginRegistrarRef FlutterWindowController::GetRegistrarForPlugin(
<< std::endl;
return nullptr;
}
return FlutterDesktopGetPluginRegistrar(controller_, plugin_name.c_str());
return FlutterDesktopGetPluginRegistrar(FlutterDesktopGetEngine(controller_),
plugin_name.c_str());
}

bool FlutterWindowController::RunEventLoopWithTimeout(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_ENGINE_H_
#define FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_ENGINE_H_

#include <flutter_glfw.h>

#include <chrono>
#include <memory>
#include <string>
#include <vector>

#include "plugin_registrar.h"
#include "plugin_registry.h"

namespace flutter {

// An engine for running a headless Flutter application.
class FlutterEngine : public PluginRegistry {
public:
explicit FlutterEngine();

virtual ~FlutterEngine();

// Prevent copying.
FlutterEngine(FlutterEngine const&) = delete;
FlutterEngine& operator=(FlutterEngine const&) = delete;

// Starts running the engine with the given parameters, returning true if
// successful.
bool Start(const std::string& icu_data_path,
const std::string& assets_path,
const std::vector<std::string>& arguments);

// Terminates the running engine.
void ShutDown();

// Processes the next event for the engine, or returns early if |timeout| is
// reached before the next event.
void RunEventLoopWithTimeout(
std::chrono::milliseconds timeout = std::chrono::milliseconds::max());

// flutter::PluginRegistry:
FlutterDesktopPluginRegistrarRef GetRegistrarForPlugin(
const std::string& plugin_name) override;

private:
using UniqueEnginePtr = std::unique_ptr<FlutterDesktopEngineState,
bool (*)(FlutterDesktopEngineState*)>;

// Handle for interacting with the C API's engine reference.
UniqueEnginePtr engine_ =
UniqueEnginePtr(nullptr, FlutterDesktopShutDownEngine);
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_ENGINE_H_
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ FlutterDesktopEngineRef FlutterDesktopRunEngine(
return nullptr;
}

void FlutterDesktopRunEngineEventLoopWithTimeout(
FlutterDesktopEngineRef engine,
uint32_t timeout_milliseconds) {
if (s_stub_implementation) {
s_stub_implementation->RunEngineEventLoopWithTimeout(timeout_milliseconds);
}
}

bool FlutterDesktopShutDownEngine(FlutterDesktopEngineRef engine_ref) {
if (s_stub_implementation) {
return s_stub_implementation->ShutDownEngine();
Expand All @@ -162,8 +170,14 @@ FlutterDesktopWindowRef FlutterDesktopGetWindow(
return reinterpret_cast<FlutterDesktopWindowRef>(1);
}

FlutterDesktopEngineRef FlutterDesktopGetEngine(
FlutterDesktopWindowControllerRef controller) {
// The stub ignores this, so just return an arbitrary non-zero value.
return reinterpret_cast<FlutterDesktopEngineRef>(3);
}

FlutterDesktopPluginRegistrarRef FlutterDesktopGetPluginRegistrar(
FlutterDesktopWindowControllerRef controller,
FlutterDesktopEngineRef engine,
const char* plugin_name) {
// The stub ignores this, so just return an arbitrary non-zero value.
return reinterpret_cast<FlutterDesktopPluginRegistrarRef>(2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class StubFlutterGlfwApi {
return nullptr;
}

// Called for FlutterDesktopRunEngineEventLoopWithTimeout.
virtual void RunEngineEventLoopWithTimeout(uint32_t millisecond_timeout) {}

// Called for FlutterDesktopShutDownEngine.
virtual bool ShutDownEngine() { return true; }
};
Expand Down
Loading