mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Adds a task runner, and exposes API to allow application-level runloops to know when they need to next call the API to process engine events. Internally, sends null events to wake up the app runloop when new events are scheduled to ensure the wait time is updated accordingly. Fixes #36420
370 lines
12 KiB
C++
370 lines
12 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(int width, int height) {
|
|
surface_manager = std::make_unique<AngleSurfaceManager>();
|
|
Win32Window::InitializeChild("FLUTTERVIEW", width, height);
|
|
}
|
|
|
|
Win32FlutterWindow::~Win32FlutterWindow() {
|
|
DestroyRenderSurface();
|
|
Win32Window::Destroy();
|
|
}
|
|
|
|
FlutterDesktopViewControllerRef Win32FlutterWindow::CreateWin32FlutterWindow(
|
|
const int width,
|
|
const int height) {
|
|
auto state = std::make_unique<FlutterDesktopViewControllerState>();
|
|
state->view = std::make_unique<flutter::Win32FlutterWindow>(width, height);
|
|
|
|
// a window wrapper for the state block, distinct from the
|
|
// window_wrapper handed to plugin_registrar.
|
|
state->view_wrapper = std::make_unique<FlutterDesktopView>();
|
|
state->view_wrapper->window = state->view.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<FlutterDesktopView>();
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
// Translates button codes from Win32 API to FlutterPointerMouseButtons.
|
|
static uint64_t ConvertWinButtonToFlutterButton(UINT button) {
|
|
switch (button) {
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
return kFlutterPointerButtonMousePrimary;
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
return kFlutterPointerButtonMouseSecondary;
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONUP:
|
|
return kFlutterPointerButtonMouseMiddle;
|
|
case XBUTTON1:
|
|
return kFlutterPointerButtonMouseBack;
|
|
case XBUTTON2:
|
|
return kFlutterPointerButtonMouseForward;
|
|
}
|
|
std::cerr << "Mouse button not recognized: " << button << std::endl;
|
|
return 0;
|
|
}
|
|
|
|
// 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, UINT button) {
|
|
if (process_events_) {
|
|
uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
|
|
if (flutter_button != 0) {
|
|
uint64_t mouse_buttons = GetMouseState().buttons | flutter_button;
|
|
SetMouseButtons(mouse_buttons);
|
|
SendPointerDown(x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Win32FlutterWindow::OnPointerUp(double x, double y, UINT button) {
|
|
if (process_events_) {
|
|
uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
|
|
if (flutter_button != 0) {
|
|
uint64_t mouse_buttons = GetMouseState().buttons & ~flutter_button;
|
|
SetMouseButtons(mouse_buttons);
|
|
SendPointerUp(x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Win32FlutterWindow::OnPointerLeave() {
|
|
if (process_events_) {
|
|
SendPointerLeave();
|
|
}
|
|
}
|
|
|
|
void Win32FlutterWindow::OnChar(char32_t 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::OnFontChange() {
|
|
if (engine_ == nullptr) {
|
|
return;
|
|
}
|
|
FlutterEngineReloadSystemFonts(engine_);
|
|
}
|
|
|
|
// 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) {
|
|
MouseState state = GetMouseState();
|
|
// For details about this logic, see FlutterPointerPhase in the embedder.h
|
|
// file.
|
|
event_data->phase = state.buttons == 0 ? state.flutter_state_is_down
|
|
? FlutterPointerPhase::kUp
|
|
: FlutterPointerPhase::kHover
|
|
: state.flutter_state_is_down
|
|
? FlutterPointerPhase::kMove
|
|
: FlutterPointerPhase::kDown;
|
|
}
|
|
|
|
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) {
|
|
FlutterPointerEvent event = {};
|
|
SetEventPhaseFromCursorButtonState(&event);
|
|
event.x = x;
|
|
event.y = y;
|
|
SendPointerEventWithData(event);
|
|
SetMouseFlutterStateDown(true);
|
|
}
|
|
|
|
void Win32FlutterWindow::SendPointerUp(double x, double y) {
|
|
FlutterPointerEvent event = {};
|
|
SetEventPhaseFromCursorButtonState(&event);
|
|
event.x = x;
|
|
event.y = y;
|
|
SendPointerEventWithData(event);
|
|
if (event.phase == FlutterPointerPhase::kUp) {
|
|
SetMouseFlutterStateDown(false);
|
|
}
|
|
}
|
|
|
|
void Win32FlutterWindow::SendPointerLeave() {
|
|
FlutterPointerEvent event = {};
|
|
event.phase = FlutterPointerPhase::kRemove;
|
|
SendPointerEventWithData(event);
|
|
}
|
|
|
|
void Win32FlutterWindow::SendChar(char32_t 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) {
|
|
MouseState mouse_state = GetMouseState();
|
|
// 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 (!mouse_state.flutter_state_is_added &&
|
|
event_data.phase != FlutterPointerPhase::kAdd) {
|
|
FlutterPointerEvent event = {};
|
|
event.phase = FlutterPointerPhase::kAdd;
|
|
event.x = event_data.x;
|
|
event.y = event_data.y;
|
|
event.buttons = 0;
|
|
SendPointerEventWithData(event);
|
|
}
|
|
// Don't double-add (e.g., if events are delivered out of order, so an add has
|
|
// already been synthesized).
|
|
if (mouse_state.flutter_state_is_added &&
|
|
event_data.phase == FlutterPointerPhase::kAdd) {
|
|
return;
|
|
}
|
|
|
|
FlutterPointerEvent event = event_data;
|
|
event.device_kind = kFlutterPointerDeviceKindMouse;
|
|
event.buttons = mouse_state.buttons;
|
|
|
|
// 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) {
|
|
SetMouseFlutterStateAdded(true);
|
|
} else if (event_data.phase == FlutterPointerPhase::kRemove) {
|
|
SetMouseFlutterStateAdded(false);
|
|
ResetMouseState();
|
|
}
|
|
}
|
|
|
|
bool Win32FlutterWindow::MakeCurrent() {
|
|
return surface_manager->MakeCurrent(render_surface);
|
|
}
|
|
|
|
bool Win32FlutterWindow::MakeResourceCurrent() {
|
|
return surface_manager->MakeResourceCurrent();
|
|
}
|
|
|
|
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
|