flutter_flutter/shell/platform/windows/win32_flutter_window.cc
James Clarke ff484d4f69 [Windows] Alternative Windows shell platform implementation (#9835)
Start work on flutter/flutter#30726 by adding an alternative win32 shell platform implementation for Windows that is not based on GLFW and that uses LIBANGLE for rendering and native win32 windowing and input. This change does not replace the GLFW implementation but rather runs side by side with it producing a secondary flutter_windows_win32.dll artifact. The following items must be added to attain parity with the GLFW implementation:
- Custom task scheduling
- Support for keyboard modifier keys
- Async texture uploads
- Correct high DPI handling on Windows versions < 1703
and will be added in subsequent changes.
2019-08-14 15:52:52 -07:00

328 lines
10 KiB
C++

#include "flutter/shell/platform/windows/win32_flutter_window.h"
#include <chrono>
namespace flutter {
// the Windows DPI system is based on this
// constant for machines running at 100% scaling.
constexpr int base_dpi = 96;
Win32FlutterWindow::Win32FlutterWindow() {
surface_manager = std::make_unique<AngleSurfaceManager>();
}
Win32FlutterWindow::Win32FlutterWindow(const char* title,
const int x,
const int y,
const int width,
const int height) noexcept
: Win32FlutterWindow() {
Win32Window::Initialize(title, x, y, width, height);
}
Win32FlutterWindow::~Win32FlutterWindow() {
DestroyRenderSurface();
}
FlutterDesktopWindowControllerRef Win32FlutterWindow::CreateWin32FlutterWindow(
const char* title,
const int x,
const int y,
const int width,
const int height) {
auto state = std::make_unique<FlutterDesktopWindowControllerState>();
state->window = std::make_unique<flutter::Win32FlutterWindow>(title, 10, 10,
width, height);
// a window wrapper for the state block, distinct from the
// window_wrapper handed to plugin_registrar.
state->window_wrapper = std::make_unique<FlutterDesktopWindow>();
state->window_wrapper->window = state->window.get();
return state.release();
}
void Win32FlutterWindow::SetState(FLUTTER_API_SYMBOL(FlutterEngine) eng) {
engine_ = eng;
auto messenger = std::make_unique<FlutterDesktopMessenger>();
message_dispatcher_ =
std::make_unique<flutter::IncomingMessageDispatcher>(messenger.get());
messenger->engine = engine_;
messenger->dispatcher = message_dispatcher_.get();
window_wrapper_ = std::make_unique<FlutterDesktopWindow>();
window_wrapper_->window = this;
plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
plugin_registrar_->messenger = std::move(messenger);
plugin_registrar_->window = window_wrapper_.get();
internal_plugin_registrar_ =
std::make_unique<flutter::PluginRegistrar>(plugin_registrar_.get());
// Set up the keyboard handlers.
auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
keyboard_hook_handlers_.push_back(
std::make_unique<flutter::KeyEventHandler>(internal_plugin_messenger));
keyboard_hook_handlers_.push_back(
std::make_unique<flutter::TextInputPlugin>(internal_plugin_messenger));
platform_handler_ = std::make_unique<flutter::PlatformHandler>(
internal_plugin_messenger, this);
auto state = std::make_unique<FlutterDesktopWindowControllerState>();
state->engine = engine_;
process_events_ = true;
}
FlutterDesktopPluginRegistrarRef Win32FlutterWindow::GetRegistrar() {
return plugin_registrar_.get();
}
// Converts a FlutterPlatformMessage to an equivalent FlutterDesktopMessage.
static FlutterDesktopMessage ConvertToDesktopMessage(
const FlutterPlatformMessage& engine_message) {
FlutterDesktopMessage message = {};
message.struct_size = sizeof(message);
message.channel = engine_message.channel;
message.message = engine_message.message;
message.message_size = engine_message.message_size;
message.response_handle = engine_message.response_handle;
return message;
}
// The Flutter Engine calls out to this function when new platform messages
// are available.
void Win32FlutterWindow::HandlePlatformMessage(
const FlutterPlatformMessage* engine_message) {
if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
std::cerr << "Invalid message size received. Expected: "
<< sizeof(FlutterPlatformMessage) << " but received "
<< engine_message->struct_size << std::endl;
return;
}
auto message = ConvertToDesktopMessage(*engine_message);
message_dispatcher_->HandleMessage(
message, [this] { this->process_events_ = false; },
[this] { this->process_events_ = true; });
}
void Win32FlutterWindow::OnDpiScale(unsigned int dpi){};
// When DesktopWindow notifies that a WM_Size message has come in
// lets FlutterEngine know about the new size.
void Win32FlutterWindow::OnResize(unsigned int width, unsigned int height) {
SendWindowMetrics();
}
void Win32FlutterWindow::OnPointerMove(double x, double y) {
if (process_events_) {
SendPointerMove(x, y);
}
}
void Win32FlutterWindow::OnPointerDown(double x, double y) {
if (process_events_) {
SendPointerDown(x, y);
}
}
void Win32FlutterWindow::OnPointerUp(double x, double y) {
if (process_events_) {
SendPointerUp(x, y);
}
}
void Win32FlutterWindow::OnChar(unsigned int code_point) {
if (process_events_) {
SendChar(code_point);
}
}
void Win32FlutterWindow::OnKey(int key, int scancode, int action, int mods) {
if (process_events_) {
SendKey(key, scancode, action, 0);
}
}
void Win32FlutterWindow::OnScroll(double delta_x, double delta_y) {
if (process_events_) {
SendScroll(delta_x, delta_y);
}
}
void Win32FlutterWindow::OnClose() {
messageloop_running_ = false;
}
void Win32FlutterWindow::FlutterMessageLoop() {
MSG message;
messageloop_running_ = true;
// TODO: need either non-blocking meesage loop or custom dispatch
// implementation per https://github.com/flutter/flutter/issues/36420
while (GetMessage(&message, nullptr, 0, 0) && messageloop_running_) {
TranslateMessage(&message);
DispatchMessage(&message);
__FlutterEngineFlushPendingTasksNow();
}
}
// Sends new size information to FlutterEngine.
void Win32FlutterWindow::SendWindowMetrics() {
if (engine_ == nullptr) {
return;
}
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
event.width = GetCurrentWidth();
event.height = GetCurrentHeight();
event.pixel_ratio = static_cast<double>(GetCurrentDPI()) / base_dpi;
auto result = FlutterEngineSendWindowMetricsEvent(engine_, &event);
}
// Updates |event_data| with the current location of the mouse cursor.
void Win32FlutterWindow::SetEventLocationFromCursorPosition(
FlutterPointerEvent* event_data) {
POINT point;
GetCursorPos(&point);
ScreenToClient(GetWindowHandle(), &point);
event_data->x = point.x;
event_data->y = point.y;
}
// Set's |event_data|'s phase to either kMove or kHover depending on the current
// primary mouse button state.
void Win32FlutterWindow::SetEventPhaseFromCursorButtonState(
FlutterPointerEvent* event_data) {
event_data->phase = pointer_is_down_ ? FlutterPointerPhase::kMove
: FlutterPointerPhase::kHover;
}
void Win32FlutterWindow::SendPointerMove(double x, double y) {
FlutterPointerEvent event = {};
event.x = x;
event.y = y;
SetEventPhaseFromCursorButtonState(&event);
SendPointerEventWithData(event);
}
void Win32FlutterWindow::SendPointerDown(double x, double y) {
pointer_is_down_ = true;
FlutterPointerEvent event = {};
event.phase = FlutterPointerPhase::kDown;
event.x = x;
event.y = y;
SendPointerEventWithData(event);
}
void Win32FlutterWindow::SendPointerUp(double x, double y) {
pointer_is_down_ = false;
FlutterPointerEvent event = {};
event.phase = FlutterPointerPhase::kUp;
event.x = x;
event.y = y;
SendPointerEventWithData(event);
}
void Win32FlutterWindow::SendChar(unsigned int code_point) {
for (const auto& handler : keyboard_hook_handlers_) {
handler->CharHook(this, code_point);
}
}
void Win32FlutterWindow::SendKey(int key, int scancode, int action, int mods) {
for (const auto& handler : keyboard_hook_handlers_) {
handler->KeyboardHook(this, key, scancode, action, mods);
}
}
void Win32FlutterWindow::SendScroll(double delta_x, double delta_y) {
FlutterPointerEvent event = {};
SetEventLocationFromCursorPosition(&event);
SetEventPhaseFromCursorButtonState(&event);
event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
// TODO: See if this can be queried from the OS; this value is chosen
// arbitrarily to get something that feels reasonable.
const int kScrollOffsetMultiplier = 20;
event.scroll_delta_x = delta_x * kScrollOffsetMultiplier;
event.scroll_delta_y = delta_y * kScrollOffsetMultiplier;
SendPointerEventWithData(event);
}
void Win32FlutterWindow::SendPointerEventWithData(
const FlutterPointerEvent& event_data) {
// If sending anything other than an add, and the pointer isn't already added,
// synthesize an add to satisfy Flutter's expectations about events.
if (!pointer_currently_added_ &&
event_data.phase != FlutterPointerPhase::kAdd) {
FlutterPointerEvent event = {};
event.phase = FlutterPointerPhase::kAdd;
event.x = event_data.x;
event.y = event_data.y;
SendPointerEventWithData(event);
}
// Don't double-add (e.g., if events are delivered out of order, so an add has
// already been synthesized).
if (pointer_currently_added_ &&
event_data.phase == FlutterPointerPhase::kAdd) {
return;
}
FlutterPointerEvent event = event_data;
// Set metadata that's always the same regardless of the event.
event.struct_size = sizeof(event);
event.timestamp =
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
// Windows passes all input in either physical pixels (Per-monitor, System
// DPI) or pre-scaled to match bitmap scaling of output where process is
// running in DPI unaware more. In either case, no need to manually scale
// input here. For more information see DPIHelper.
event.scroll_delta_x;
event.scroll_delta_y;
FlutterEngineSendPointerEvent(engine_, &event, 1);
if (event_data.phase == FlutterPointerPhase::kAdd) {
pointer_currently_added_ = true;
} else if (event_data.phase == FlutterPointerPhase::kRemove) {
pointer_currently_added_ = false;
}
}
bool Win32FlutterWindow::MakeCurrent() {
return surface_manager->MakeCurrent(render_surface);
}
bool Win32FlutterWindow::ClearContext() {
return surface_manager->MakeCurrent(nullptr);
}
bool Win32FlutterWindow::SwapBuffers() {
return surface_manager->SwapBuffers(render_surface);
}
void Win32FlutterWindow::CreateRenderSurface() {
if (surface_manager && render_surface == EGL_NO_SURFACE) {
render_surface = surface_manager->CreateSurface(GetWindowHandle());
}
}
void Win32FlutterWindow::DestroyRenderSurface() {
if (surface_manager) {
surface_manager->DestroySurface(render_surface);
}
render_surface = EGL_NO_SURFACE;
}
} // namespace flutter