blob: 19bf9c97c77ec96fa5f5f63e4ebf45b0d17ebefe [file] [log] [blame]
// 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 "flutter/shell/platform/windows/public/flutter_windows.h"
#include <assert.h>
#include <io.h>
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <memory>
#include <vector>
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h"
#include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h"
#include "flutter/shell/platform/common/cpp/path_utils.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/flutter_windows_view.h"
#include "flutter/shell/platform/windows/win32_dpi_utils.h"
#include "flutter/shell/platform/windows/win32_flutter_window.h"
#include "flutter/shell/platform/windows/win32_platform_handler.h"
#include "flutter/shell/platform/windows/win32_task_runner.h"
#include "flutter/shell/platform/windows/window_binding_handler.h"
#include "flutter/shell/platform/windows/window_state.h"
static_assert(FLUTTER_ENGINE_VERSION == 1, "");
// Attempts to load AOT data from the given path, which must be absolute and
// non-empty. Logs and returns nullptr on failure.
UniqueAotDataPtr LoadAotData(std::filesystem::path aot_data_path) {
if (aot_data_path.empty()) {
std::cerr
<< "Attempted to load AOT data, but no aot_library_path was provided."
<< std::endl;
return nullptr;
}
if (!std::filesystem::exists(aot_data_path)) {
std::cerr << "Can't load AOT data from " << aot_data_path.u8string()
<< "; no such file." << std::endl;
return nullptr;
}
std::string path_string = aot_data_path.u8string();
FlutterEngineAOTDataSource source = {};
source.type = kFlutterEngineAOTDataSourceTypeElfPath;
source.elf_path = path_string.c_str();
FlutterEngineAOTData data = nullptr;
auto result = FlutterEngineCreateAOTData(&source, &data);
if (result != kSuccess) {
std::cerr << "Failed to load AOT data from: " << path_string << std::endl;
return nullptr;
}
return UniqueAotDataPtr(data);
}
// Spins up an instance of the Flutter Engine.
//
// This function launches the Flutter Engine in a background thread, supplying
// the necessary callbacks for rendering within a win32window (if one is
// provided).
//
// Returns the state object for the engine, or null on failure to start the
// engine.
static std::unique_ptr<FlutterDesktopEngineState> RunFlutterEngine(
flutter::FlutterWindowsView* view,
const FlutterDesktopEngineProperties& engine_properties) {
auto state = std::make_unique<FlutterDesktopEngineState>();
// FlutterProjectArgs is expecting a full argv, so when processing it for
// flags the first item is treated as the executable and ignored. Add a dummy
// value so that all provided arguments are used.
std::vector<const char*> argv = {"placeholder"};
if (engine_properties.switches_count > 0) {
argv.insert(argv.end(), &engine_properties.switches[0],
&engine_properties.switches[engine_properties.switches_count]);
}
view->CreateRenderSurface();
// Provide the necessary callbacks for rendering within a win32 child window.
FlutterRendererConfig config = {};
config.type = kOpenGL;
config.open_gl.struct_size = sizeof(config.open_gl);
config.open_gl.make_current = [](void* user_data) -> bool {
auto host = static_cast<flutter::FlutterWindowsView*>(user_data);
return host->MakeCurrent();
};
config.open_gl.clear_current = [](void* user_data) -> bool {
auto host = static_cast<flutter::FlutterWindowsView*>(user_data);
return host->ClearContext();
};
config.open_gl.present = [](void* user_data) -> bool {
auto host = static_cast<flutter::FlutterWindowsView*>(user_data);
return host->SwapBuffers();
};
config.open_gl.fbo_callback = [](void* user_data) -> uint32_t { return 0; };
config.open_gl.gl_proc_resolver = [](void* user_data,
const char* what) -> void* {
return reinterpret_cast<void*>(eglGetProcAddress(what));
};
config.open_gl.make_resource_current = [](void* user_data) -> bool {
auto host = static_cast<flutter::FlutterWindowsView*>(user_data);
return host->MakeResourceCurrent();
};
// Configure task runner interop.
auto state_ptr = state.get();
state->task_runner = std::make_unique<flutter::Win32TaskRunner>(
GetCurrentThreadId(), [state_ptr](const auto* task) {
if (FlutterEngineRunTask(state_ptr->engine, task) != kSuccess) {
std::cerr << "Could not post an engine task." << std::endl;
}
});
FlutterTaskRunnerDescription platform_task_runner = {};
platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
platform_task_runner.user_data = state->task_runner.get();
platform_task_runner.runs_task_on_current_thread_callback =
[](void* user_data) -> bool {
return reinterpret_cast<flutter::Win32TaskRunner*>(user_data)
->RunsTasksOnCurrentThread();
};
platform_task_runner.post_task_callback = [](FlutterTask task,
uint64_t target_time_nanos,
void* user_data) -> void {
reinterpret_cast<flutter::Win32TaskRunner*>(user_data)->PostTask(
task, target_time_nanos);
};
FlutterCustomTaskRunners custom_task_runners = {};
custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
custom_task_runners.platform_task_runner = &platform_task_runner;
std::filesystem::path assets_path(engine_properties.assets_path);
std::filesystem::path icu_path(engine_properties.icu_data_path);
std::filesystem::path aot_library_path =
engine_properties.aot_library_path == nullptr
? std::filesystem::path()
: std::filesystem::path(engine_properties.aot_library_path);
if (assets_path.is_relative() || icu_path.is_relative() ||
(!aot_library_path.empty() && aot_library_path.is_relative())) {
// Treat relative paths as relative to the directory of this executable.
std::filesystem::path executable_location =
flutter::GetExecutableDirectory();
if (executable_location.empty()) {
std::cerr
<< "Unable to find executable location to resolve resource paths."
<< std::endl;
return nullptr;
}
assets_path = std::filesystem::path(executable_location) / assets_path;
icu_path = std::filesystem::path(executable_location) / icu_path;
if (!aot_library_path.empty()) {
aot_library_path =
std::filesystem::path(executable_location) / aot_library_path;
}
}
std::string assets_path_string = assets_path.u8string();
std::string icu_path_string = icu_path.u8string();
if (FlutterEngineRunsAOTCompiledDartCode()) {
state->aot_data = LoadAotData(aot_library_path);
if (!state->aot_data) {
std::cerr << "Unable to start engine without AOT data." << std::endl;
return nullptr;
}
}
FlutterProjectArgs args = {};
args.struct_size = sizeof(FlutterProjectArgs);
args.assets_path = assets_path_string.c_str();
args.icu_data_path = icu_path_string.c_str();
args.command_line_argc = static_cast<int>(argv.size());
args.command_line_argv = &argv[0];
args.platform_message_callback =
[](const FlutterPlatformMessage* engine_message,
void* user_data) -> void {
auto window = reinterpret_cast<flutter::FlutterWindowsView*>(user_data);
return window->HandlePlatformMessage(engine_message);
};
args.custom_task_runners = &custom_task_runners;
if (state->aot_data) {
args.aot_data = state->aot_data.get();
}
FLUTTER_API_SYMBOL(FlutterEngine) engine = nullptr;
auto result =
FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &args, view, &engine);
if (result != kSuccess || engine == nullptr) {
std::cerr << "Failed to start Flutter engine: error " << result
<< std::endl;
return nullptr;
}
state->engine = engine;
return state;
}
FlutterDesktopViewControllerRef FlutterDesktopCreateViewController(
int width,
int height,
const FlutterDesktopEngineProperties& engine_properties) {
std::unique_ptr<flutter::WindowBindingHandler> window_wrapper =
std::make_unique<flutter::Win32FlutterWindow>(width, height);
FlutterDesktopViewControllerRef state =
flutter::FlutterWindowsView::CreateFlutterWindowsView(
std::move(window_wrapper));
auto engine_state = RunFlutterEngine(state->view.get(), engine_properties);
if (!engine_state) {
return nullptr;
}
state->view->SetState(engine_state->engine);
state->engine_state = std::move(engine_state);
return state;
}
FlutterDesktopViewControllerRef FlutterDesktopCreateViewControllerLegacy(
int initial_width,
int initial_height,
const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count) {
std::filesystem::path assets_path_fs = std::filesystem::u8path(assets_path);
std::filesystem::path icu_data_path_fs =
std::filesystem::u8path(icu_data_path);
FlutterDesktopEngineProperties engine_properties = {};
engine_properties.assets_path = assets_path_fs.c_str();
engine_properties.icu_data_path = icu_data_path_fs.c_str();
engine_properties.switches = arguments;
engine_properties.switches_count = argument_count;
return FlutterDesktopCreateViewController(initial_width, initial_height,
engine_properties);
}
uint64_t FlutterDesktopProcessMessages(
FlutterDesktopViewControllerRef controller) {
return controller->engine_state->task_runner->ProcessTasks().count();
}
void FlutterDesktopDestroyViewController(
FlutterDesktopViewControllerRef controller) {
FlutterEngineShutdown(controller->engine_state->engine);
delete controller;
}
FlutterDesktopPluginRegistrarRef FlutterDesktopGetPluginRegistrar(
FlutterDesktopViewControllerRef controller,
const char* plugin_name) {
// Currently, one registrar acts as the registrar for all plugins, so the
// name is ignored. It is part of the API to reduce churn in the future when
// aligning more closely with the Flutter registrar system.
return controller->view->GetRegistrar();
}
FlutterDesktopViewRef FlutterDesktopGetView(
FlutterDesktopViewControllerRef controller) {
return controller->view_wrapper.get();
}
HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view_ref) {
return std::get<HWND>(*view_ref->view->GetRenderTarget());
}
UINT FlutterDesktopGetDpiForHWND(HWND hwnd) {
return flutter::GetDpiForHWND(hwnd);
}
UINT FlutterDesktopGetDpiForMonitor(HMONITOR monitor) {
return flutter::GetDpiForMonitor(monitor);
}
void FlutterDesktopResyncOutputStreams() {
FILE* unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
}
FlutterDesktopEngineRef FlutterDesktopRunEngine(
const FlutterDesktopEngineProperties& engine_properties) {
auto engine = RunFlutterEngine(nullptr, engine_properties);
return engine.release();
}
bool FlutterDesktopShutDownEngine(FlutterDesktopEngineRef engine_ref) {
std::cout << "Shutting down flutter engine process." << std::endl;
auto result = FlutterEngineShutdown(engine_ref->engine);
delete engine_ref;
return (result == kSuccess);
}
void FlutterDesktopRegistrarEnableInputBlocking(
FlutterDesktopPluginRegistrarRef registrar,
const char* channel) {
registrar->messenger->dispatcher->EnableInputBlockingForChannel(channel);
}
FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(
FlutterDesktopPluginRegistrarRef registrar) {
return registrar->messenger.get();
}
void FlutterDesktopRegistrarSetDestructionHandler(
FlutterDesktopPluginRegistrarRef registrar,
FlutterDesktopOnRegistrarDestroyed callback) {
registrar->destruction_handler = callback;
}
FlutterDesktopViewRef FlutterDesktopRegistrarGetView(
FlutterDesktopPluginRegistrarRef registrar) {
return registrar->view;
}
bool FlutterDesktopMessengerSendWithReply(FlutterDesktopMessengerRef messenger,
const char* channel,
const uint8_t* message,
const size_t message_size,
const FlutterDesktopBinaryReply reply,
void* user_data) {
FlutterPlatformMessageResponseHandle* response_handle = nullptr;
if (reply != nullptr && user_data != nullptr) {
FlutterEngineResult result = FlutterPlatformMessageCreateResponseHandle(
messenger->engine, reply, user_data, &response_handle);
if (result != kSuccess) {
std::cout << "Failed to create response handle\n";
return false;
}
}
FlutterPlatformMessage platform_message = {
sizeof(FlutterPlatformMessage),
channel,
message,
message_size,
response_handle,
};
FlutterEngineResult message_result =
FlutterEngineSendPlatformMessage(messenger->engine, &platform_message);
if (response_handle != nullptr) {
FlutterPlatformMessageReleaseResponseHandle(messenger->engine,
response_handle);
}
return message_result == kSuccess;
}
bool FlutterDesktopMessengerSend(FlutterDesktopMessengerRef messenger,
const char* channel,
const uint8_t* message,
const size_t message_size) {
return FlutterDesktopMessengerSendWithReply(messenger, channel, message,
message_size, nullptr, nullptr);
}
void FlutterDesktopMessengerSendResponse(
FlutterDesktopMessengerRef messenger,
const FlutterDesktopMessageResponseHandle* handle,
const uint8_t* data,
size_t data_length) {
FlutterEngineSendPlatformMessageResponse(messenger->engine, handle, data,
data_length);
}
void FlutterDesktopMessengerSetCallback(FlutterDesktopMessengerRef messenger,
const char* channel,
FlutterDesktopMessageCallback callback,
void* user_data) {
messenger->dispatcher->SetMessageCallback(channel, callback, user_data);
}