flutter_flutter/shell/platform/windows/flutter_windows.cc
James Clarke d0d6a4c236
Refactor Win32FlutterWindow in preparation for UWP windowing implementation (#18878)
* Add flutter_windows_view and window_binding_handler

Switch input handling infra to FlutterWindowsView

win32_flutter_window implement WindowBindingHandler

Strip unneeded functionality from win32flutterwindow

Fulfill WindowBindingHandler interface in Win32FlutterWindow

Add implementations for missing input handling in Win32FlutterWindow

Cleanup dead code

Correctly hook up rendering again

Fix resizing

clang-format

Fix clipboard

Cleanup

Rename

Add comments

cleanup

* clang-format

* CR Feedback

* clang-format; gn format

* Fix licensing

* CR feedback

* CR feedback

* CR feedback

* Git rid of unnecessar :: prefixes

* Extract WindowBindingHandlerDelegate as an interface

* Missing file

* Extract physical window bounds as a struct

* CR Feedback

* CR feedback

* clang-format

Co-authored-by: Stuart Morgan <stuartmorgan@google.com>
2020-07-07 06:49:51 -07:00

386 lines
14 KiB
C++

// 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/key_event_handler.h"
#include "flutter/shell/platform/windows/keyboard_hook_handler.h"
#include "flutter/shell/platform/windows/text_input_plugin.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);
}