mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[windows] Allow delegation of top-level WindowProc (flutter/engine#20613)
Adds APIs for runners to delegate WindowProc handlers into the Flutter engine, and for plugins to register as possible delegates. This allows for plugins to alter top-level window behavior in ways that can only be done from the WindowProc, such as resize control. This functionality remains entirely on the native side, so is synchronous. Part of https://github.com/flutter/flutter/issues/53168
This commit is contained in:
parent
5c8d00a819
commit
bb04632eeb
@ -1376,6 +1376,9 @@ FILE: ../../../flutter/shell/platform/windows/win32_task_runner.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/win32_task_runner.h
|
||||
FILE: ../../../flutter/shell/platform/windows/win32_window.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/win32_window.h
|
||||
FILE: ../../../flutter/shell/platform/windows/win32_window_proc_delegate_manager.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/win32_window_proc_delegate_manager.h
|
||||
FILE: ../../../flutter/shell/platform/windows/win32_window_proc_delegate_manager_unittests.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/win32_window_unittests.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/window_binding_handler.h
|
||||
FILE: ../../../flutter/shell/platform/windows/window_binding_handler_delegate.h
|
||||
|
||||
@ -68,6 +68,8 @@ source_set("flutter_windows_source") {
|
||||
"win32_task_runner.h",
|
||||
"win32_window.cc",
|
||||
"win32_window.h",
|
||||
"win32_window_proc_delegate_manager.cc",
|
||||
"win32_window_proc_delegate_manager.h",
|
||||
"window_binding_handler.h",
|
||||
"window_binding_handler_delegate.h",
|
||||
"window_state.h",
|
||||
@ -127,6 +129,7 @@ executable("flutter_windows_unittests") {
|
||||
"testing/win32_window_test.h",
|
||||
"win32_dpi_utils_unittests.cc",
|
||||
"win32_flutter_window_unittests.cc",
|
||||
"win32_window_proc_delegate_manager_unittests.cc",
|
||||
"win32_window_unittests.cc",
|
||||
]
|
||||
|
||||
|
||||
@ -29,6 +29,17 @@ FlutterViewController::~FlutterViewController() {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<LRESULT> FlutterViewController::HandleTopLevelWindowProc(
|
||||
HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam) {
|
||||
LRESULT result;
|
||||
bool handled = FlutterDesktopViewControllerHandleTopLevelWindowProc(
|
||||
controller_, hwnd, message, wparam, lparam, &result);
|
||||
return handled ? result : std::optional<LRESULT>(std::nullopt);
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds FlutterViewController::ProcessMessages() {
|
||||
return engine_->ProcessMessages();
|
||||
}
|
||||
|
||||
@ -5,8 +5,12 @@
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_VIEW_CONTROLLER_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_VIEW_CONTROLLER_H_
|
||||
|
||||
#include <Windows.h>
|
||||
#include <flutter_windows.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "dart_project.h"
|
||||
#include "flutter_engine.h"
|
||||
#include "flutter_view.h"
|
||||
@ -42,6 +46,16 @@ class FlutterViewController : public PluginRegistry {
|
||||
// Returns the view managed by this controller.
|
||||
FlutterView* view() { return view_.get(); }
|
||||
|
||||
// Allows the Flutter engine and any interested plugins an opportunity to
|
||||
// handle the given message.
|
||||
//
|
||||
// If a result is returned, then the message was handled in such a way that
|
||||
// further handling should not be done.
|
||||
std::optional<LRESULT> HandleTopLevelWindowProc(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
|
||||
// DEPRECATED. Call engine()->ProcessMessages() instead.
|
||||
std::chrono::nanoseconds ProcessMessages();
|
||||
|
||||
|
||||
@ -5,15 +5,24 @@
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_WINDOWS_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_WINDOWS_H_
|
||||
|
||||
#include <Windows.h>
|
||||
#include <flutter_windows.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "flutter_view.h"
|
||||
#include "plugin_registrar.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
// A delegate callback for WindowProc delegation.
|
||||
//
|
||||
// Implementations should return a value only if they have handled the message
|
||||
// and want to stop all further handling.
|
||||
using WindowProcDelegate = std::function<std::optional<
|
||||
LRESULT>(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)>;
|
||||
|
||||
// An extension to PluginRegistrar providing access to Windows-specific
|
||||
// functionality.
|
||||
class PluginRegistrarWindows : public PluginRegistrar {
|
||||
@ -35,9 +44,78 @@ class PluginRegistrarWindows : public PluginRegistrar {
|
||||
|
||||
FlutterView* GetView() { return view_.get(); }
|
||||
|
||||
// Registers |delegate| to recieve WindowProc callbacks for the top-level
|
||||
// window containing this Flutter instance. Returns an ID that can be used to
|
||||
// unregister the handler.
|
||||
//
|
||||
// Delegates are not guaranteed to be called:
|
||||
// - The application may choose not to delegate WindowProc calls.
|
||||
// - If multiple plugins are registered, the first one that returns a value
|
||||
// from the delegate message will "win", and others will not be called.
|
||||
// The order of delegate calls is not defined.
|
||||
//
|
||||
// Delegates should be implemented as narrowly as possible, only returning
|
||||
// a value in cases where it's important that other delegates not run, to
|
||||
// minimize the chances of conflicts between plugins.
|
||||
int RegisterTopLevelWindowProcDelegate(WindowProcDelegate delegate) {
|
||||
if (window_proc_delegates_.empty()) {
|
||||
FlutterDesktopPluginRegistrarRegisterTopLevelWindowProcDelegate(
|
||||
registrar(), PluginRegistrarWindows::OnTopLevelWindowProc, this);
|
||||
}
|
||||
int delegate_id = next_window_proc_delegate_id_++;
|
||||
window_proc_delegates_.emplace(delegate_id, std::move(delegate));
|
||||
return delegate_id;
|
||||
}
|
||||
|
||||
// Unregisters a previously registered delegate.
|
||||
void UnregisterTopLevelWindowProcDelegate(int proc_id) {
|
||||
window_proc_delegates_.erase(proc_id);
|
||||
if (window_proc_delegates_.empty()) {
|
||||
FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate(
|
||||
registrar(), PluginRegistrarWindows::OnTopLevelWindowProc);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// A FlutterDesktopWindowProcCallback implementation that forwards back to
|
||||
// a PluginRegistarWindows instance provided as |user_data|.
|
||||
static bool OnTopLevelWindowProc(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
void* user_data,
|
||||
LRESULT* result) {
|
||||
const auto* registrar = static_cast<PluginRegistrarWindows*>(user_data);
|
||||
std::optional optional_result = registrar->CallTopLevelWindowProcDelegates(
|
||||
hwnd, message, wparam, lparam);
|
||||
if (optional_result) {
|
||||
*result = *optional_result;
|
||||
}
|
||||
return optional_result.has_value();
|
||||
}
|
||||
|
||||
std::optional<LRESULT> CallTopLevelWindowProcDelegates(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam) const {
|
||||
std::optional<LRESULT> result;
|
||||
for (const auto& pair : window_proc_delegates_) {
|
||||
result = pair.second(hwnd, message, wparam, lparam);
|
||||
// Stop as soon as any delegate indicates that it has handled the message.
|
||||
if (result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// The associated FlutterView, if any.
|
||||
std::unique_ptr<FlutterView> view_;
|
||||
|
||||
// The next ID to return from RegisterWindowProcDelegate.
|
||||
int next_window_proc_delegate_id_ = 1;
|
||||
|
||||
std::map<int, WindowProcDelegate> window_proc_delegates_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -14,7 +14,34 @@ namespace flutter {
|
||||
namespace {
|
||||
|
||||
// Stub implementation to validate calls to the API.
|
||||
class TestWindowsApi : public testing::StubFlutterWindowsApi {};
|
||||
class TestWindowsApi : public testing::StubFlutterWindowsApi {
|
||||
public:
|
||||
void PluginRegistrarRegisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopWindowProcCallback delegate,
|
||||
void* user_data) override {
|
||||
++registered_delegate_count_;
|
||||
last_registered_delegate_ = delegate;
|
||||
last_registered_user_data_ = user_data;
|
||||
}
|
||||
|
||||
void PluginRegistrarUnregisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopWindowProcCallback delegate) override {
|
||||
--registered_delegate_count_;
|
||||
}
|
||||
|
||||
int registered_delegate_count() { return registered_delegate_count_; }
|
||||
|
||||
FlutterDesktopWindowProcCallback last_registered_delegate() {
|
||||
return last_registered_delegate_;
|
||||
}
|
||||
|
||||
void* last_registered_user_data() { return last_registered_user_data_; }
|
||||
|
||||
private:
|
||||
int registered_delegate_count_ = 0;
|
||||
FlutterDesktopWindowProcCallback last_registered_delegate_ = nullptr;
|
||||
void* last_registered_user_data_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -27,4 +54,104 @@ TEST(PluginRegistrarWindowsTest, GetView) {
|
||||
EXPECT_NE(registrar.GetView(), nullptr);
|
||||
}
|
||||
|
||||
TEST(PluginRegistrarWindowsTest, RegisterUnregister) {
|
||||
testing::ScopedStubFlutterWindowsApi scoped_api_stub(
|
||||
std::make_unique<TestWindowsApi>());
|
||||
auto test_api = static_cast<TestWindowsApi*>(scoped_api_stub.stub());
|
||||
PluginRegistrarWindows registrar(
|
||||
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1));
|
||||
|
||||
WindowProcDelegate delegate = [](HWND hwnd, UINT message, WPARAM wparam,
|
||||
LPARAM lparam) {
|
||||
return std::optional<LRESULT>();
|
||||
};
|
||||
int id_a = registrar.RegisterTopLevelWindowProcDelegate(delegate);
|
||||
EXPECT_EQ(test_api->registered_delegate_count(), 1);
|
||||
int id_b = registrar.RegisterTopLevelWindowProcDelegate(delegate);
|
||||
// All the C++-level delegates are driven by a since C callback, so the
|
||||
// registration count should stay the same.
|
||||
EXPECT_EQ(test_api->registered_delegate_count(), 1);
|
||||
|
||||
// Unregistering one of the two delegates shouldn't cause the underlying C
|
||||
// callback to be unregistered.
|
||||
registrar.UnregisterTopLevelWindowProcDelegate(id_a);
|
||||
EXPECT_EQ(test_api->registered_delegate_count(), 1);
|
||||
// Unregistering both should unregister it.
|
||||
registrar.UnregisterTopLevelWindowProcDelegate(id_b);
|
||||
EXPECT_EQ(test_api->registered_delegate_count(), 0);
|
||||
|
||||
EXPECT_NE(id_a, id_b);
|
||||
}
|
||||
|
||||
TEST(PluginRegistrarWindowsTest, CallsRegisteredDelegates) {
|
||||
testing::ScopedStubFlutterWindowsApi scoped_api_stub(
|
||||
std::make_unique<TestWindowsApi>());
|
||||
auto test_api = static_cast<TestWindowsApi*>(scoped_api_stub.stub());
|
||||
PluginRegistrarWindows registrar(
|
||||
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1));
|
||||
|
||||
HWND dummy_hwnd;
|
||||
bool called_a = false;
|
||||
WindowProcDelegate delegate_a = [&called_a, &dummy_hwnd](
|
||||
HWND hwnd, UINT message, WPARAM wparam,
|
||||
LPARAM lparam) {
|
||||
called_a = true;
|
||||
EXPECT_EQ(hwnd, dummy_hwnd);
|
||||
EXPECT_EQ(message, 2);
|
||||
EXPECT_EQ(wparam, 3);
|
||||
EXPECT_EQ(lparam, 4);
|
||||
return std::optional<LRESULT>();
|
||||
};
|
||||
bool called_b = false;
|
||||
WindowProcDelegate delegate_b = [&called_b](HWND hwnd, UINT message,
|
||||
WPARAM wparam, LPARAM lparam) {
|
||||
called_b = true;
|
||||
return std::optional<LRESULT>();
|
||||
};
|
||||
int id_a = registrar.RegisterTopLevelWindowProcDelegate(delegate_a);
|
||||
int id_b = registrar.RegisterTopLevelWindowProcDelegate(delegate_b);
|
||||
|
||||
LRESULT result = 0;
|
||||
bool handled = test_api->last_registered_delegate()(
|
||||
dummy_hwnd, 2, 3, 4, test_api->last_registered_user_data(), &result);
|
||||
EXPECT_TRUE(called_a);
|
||||
EXPECT_TRUE(called_b);
|
||||
EXPECT_FALSE(handled);
|
||||
}
|
||||
|
||||
TEST(PluginRegistrarWindowsTest, StopsOnceHandled) {
|
||||
testing::ScopedStubFlutterWindowsApi scoped_api_stub(
|
||||
std::make_unique<TestWindowsApi>());
|
||||
auto test_api = static_cast<TestWindowsApi*>(scoped_api_stub.stub());
|
||||
PluginRegistrarWindows registrar(
|
||||
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1));
|
||||
|
||||
bool called_a = false;
|
||||
WindowProcDelegate delegate_a = [&called_a](HWND hwnd, UINT message,
|
||||
WPARAM wparam, LPARAM lparam) {
|
||||
called_a = true;
|
||||
return std::optional<LRESULT>(7);
|
||||
};
|
||||
bool called_b = false;
|
||||
WindowProcDelegate delegate_b = [&called_b](HWND hwnd, UINT message,
|
||||
WPARAM wparam, LPARAM lparam) {
|
||||
called_b = true;
|
||||
return std::optional<LRESULT>(7);
|
||||
};
|
||||
int id_a = registrar.RegisterTopLevelWindowProcDelegate(delegate_a);
|
||||
int id_b = registrar.RegisterTopLevelWindowProcDelegate(delegate_b);
|
||||
|
||||
HWND dummy_hwnd;
|
||||
LRESULT result = 0;
|
||||
bool handled = test_api->last_registered_delegate()(
|
||||
dummy_hwnd, 2, 3, 4, test_api->last_registered_user_data(), &result);
|
||||
// Only one of the delegates should have been called, since each claims to
|
||||
// have fully handled the message.
|
||||
EXPECT_TRUE(called_a || called_b);
|
||||
EXPECT_NE(called_a, called_b);
|
||||
// The return value should propagate through.
|
||||
EXPECT_TRUE(handled);
|
||||
EXPECT_EQ(result, 7);
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -64,6 +64,20 @@ FlutterDesktopViewRef FlutterDesktopViewControllerGetView(
|
||||
return reinterpret_cast<FlutterDesktopViewRef>(1);
|
||||
}
|
||||
|
||||
bool FlutterDesktopViewControllerHandleTopLevelWindowProc(
|
||||
FlutterDesktopViewControllerRef controller,
|
||||
HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
LRESULT* result) {
|
||||
if (s_stub_implementation) {
|
||||
return s_stub_implementation->ViewControllerHandleTopLevelWindowProc(
|
||||
hwnd, message, wparam, lparam, result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FlutterDesktopEngineRef FlutterDesktopEngineCreate(
|
||||
const FlutterDesktopEngineProperties& engine_properties) {
|
||||
if (s_stub_implementation) {
|
||||
@ -119,3 +133,23 @@ FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView(
|
||||
// The stub ignores this, so just return an arbitrary non-zero value.
|
||||
return reinterpret_cast<FlutterDesktopViewRef>(1);
|
||||
}
|
||||
|
||||
void FlutterDesktopPluginRegistrarRegisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopPluginRegistrarRef registrar,
|
||||
FlutterDesktopWindowProcCallback delegate,
|
||||
void* user_data) {
|
||||
if (s_stub_implementation) {
|
||||
return s_stub_implementation
|
||||
->PluginRegistrarRegisterTopLevelWindowProcDelegate(delegate,
|
||||
user_data);
|
||||
}
|
||||
}
|
||||
|
||||
void FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopPluginRegistrarRef registrar,
|
||||
FlutterDesktopWindowProcCallback delegate) {
|
||||
if (s_stub_implementation) {
|
||||
return s_stub_implementation
|
||||
->PluginRegistrarUnregisterTopLevelWindowProcDelegate(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,6 +38,15 @@ class StubFlutterWindowsApi {
|
||||
// Called for FlutterDesktopViewControllerDestroy.
|
||||
virtual void ViewControllerDestroy() {}
|
||||
|
||||
// Called for FlutterDesktopViewControllerHandleTopLevelWindowProc.
|
||||
virtual bool ViewControllerHandleTopLevelWindowProc(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
LRESULT* result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Called for FlutterDesktopEngineCreate.
|
||||
virtual FlutterDesktopEngineRef EngineCreate(
|
||||
const FlutterDesktopEngineProperties& engine_properties) {
|
||||
@ -55,6 +64,16 @@ class StubFlutterWindowsApi {
|
||||
|
||||
// Called for FlutterDesktopViewGetHWND.
|
||||
virtual HWND ViewGetHWND() { return reinterpret_cast<HWND>(1); }
|
||||
|
||||
// Called for FlutterDesktopPluginRegistrarRegisterTopLevelWindowProcDelegate.
|
||||
virtual void PluginRegistrarRegisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopWindowProcCallback delegate,
|
||||
void* user_data) {}
|
||||
|
||||
// Called for
|
||||
// FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate.
|
||||
virtual void PluginRegistrarUnregisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopWindowProcCallback delegate) {}
|
||||
};
|
||||
|
||||
// A test helper that owns a stub implementation, making it the test stub for
|
||||
|
||||
@ -83,6 +83,23 @@ FlutterDesktopViewRef FlutterDesktopViewControllerGetView(
|
||||
return controller->view_wrapper.get();
|
||||
}
|
||||
|
||||
bool FlutterDesktopViewControllerHandleTopLevelWindowProc(
|
||||
FlutterDesktopViewControllerRef controller,
|
||||
HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
LRESULT* result) {
|
||||
std::optional<LRESULT> delegate_result =
|
||||
controller->view->GetEngine()
|
||||
->window_proc_delegate_manager()
|
||||
->OnTopLevelWindowProc(hwnd, message, wparam, lparam);
|
||||
if (delegate_result) {
|
||||
*result = *delegate_result;
|
||||
}
|
||||
return delegate_result.has_value();
|
||||
}
|
||||
|
||||
FlutterDesktopEngineRef FlutterDesktopEngineCreate(
|
||||
const FlutterDesktopEngineProperties& engine_properties) {
|
||||
flutter::FlutterProjectBundle project(engine_properties);
|
||||
@ -133,6 +150,21 @@ FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView(
|
||||
return registrar->view.get();
|
||||
}
|
||||
|
||||
void FlutterDesktopPluginRegistrarRegisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopPluginRegistrarRef registrar,
|
||||
FlutterDesktopWindowProcCallback delegate,
|
||||
void* user_data) {
|
||||
registrar->engine->window_proc_delegate_manager()
|
||||
->RegisterTopLevelWindowProcDelegate(delegate, user_data);
|
||||
}
|
||||
|
||||
void FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopPluginRegistrarRef registrar,
|
||||
FlutterDesktopWindowProcCallback delegate) {
|
||||
registrar->engine->window_proc_delegate_manager()
|
||||
->UnregisterTopLevelWindowProcDelegate(delegate);
|
||||
}
|
||||
|
||||
UINT FlutterDesktopGetDpiForHWND(HWND hwnd) {
|
||||
return flutter::GetDpiForHWND(hwnd);
|
||||
}
|
||||
@ -156,7 +188,7 @@ void FlutterDesktopResyncOutputStreams() {
|
||||
|
||||
FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(
|
||||
FlutterDesktopPluginRegistrarRef registrar) {
|
||||
return registrar->messenger;
|
||||
return registrar->engine->messenger();
|
||||
}
|
||||
|
||||
void FlutterDesktopRegistrarSetDestructionHandler(
|
||||
|
||||
@ -111,8 +111,11 @@ FlutterWindowsEngine::FlutterWindowsEngine(const FlutterProjectBundle& project)
|
||||
messenger_->dispatcher = message_dispatcher_.get();
|
||||
|
||||
plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
|
||||
plugin_registrar_->messenger = messenger_.get();
|
||||
plugin_registrar_->engine = this;
|
||||
plugin_registrar_->view = std::make_unique<FlutterDesktopView>();
|
||||
|
||||
window_proc_delegate_manager_ =
|
||||
std::make_unique<Win32WindowProcDelegateManager>();
|
||||
}
|
||||
|
||||
FlutterWindowsEngine::~FlutterWindowsEngine() {
|
||||
@ -190,7 +193,6 @@ bool FlutterWindowsEngine::RunWithEntrypoint(const char* entrypoint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
plugin_registrar_->messenger->engine = engine_;
|
||||
SendSystemSettings();
|
||||
|
||||
return true;
|
||||
|
||||
@ -5,13 +5,16 @@
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOWS_ENGINE_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOWS_ENGINE_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h"
|
||||
#include "flutter/shell/platform/windows/flutter_project_bundle.h"
|
||||
#include "flutter/shell/platform/windows/public/flutter_windows.h"
|
||||
#include "flutter/shell/platform/windows/win32_task_runner.h"
|
||||
#include "flutter/shell/platform/windows/win32_window_proc_delegate_manager.h"
|
||||
#include "flutter/shell/platform/windows/window_state.h"
|
||||
|
||||
namespace flutter {
|
||||
@ -64,6 +67,10 @@ class FlutterWindowsEngine {
|
||||
|
||||
Win32TaskRunner* task_runner() { return task_runner_.get(); }
|
||||
|
||||
Win32WindowProcDelegateManager* window_proc_delegate_manager() {
|
||||
return window_proc_delegate_manager_.get();
|
||||
}
|
||||
|
||||
// Callback passed to Flutter engine for notifying window of platform
|
||||
// messages.
|
||||
void HandlePlatformMessage(const FlutterPlatformMessage*);
|
||||
@ -97,6 +104,9 @@ class FlutterWindowsEngine {
|
||||
|
||||
// The plugin registrar handle given to API clients.
|
||||
std::unique_ptr<FlutterDesktopPluginRegistrar> plugin_registrar_;
|
||||
|
||||
// The manager for WindowProc delegate registration and callbacks.
|
||||
std::unique_ptr<Win32WindowProcDelegateManager> window_proc_delegate_manager_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -93,6 +93,19 @@ FLUTTER_EXPORT FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(
|
||||
FLUTTER_EXPORT FlutterDesktopViewRef
|
||||
FlutterDesktopViewControllerGetView(FlutterDesktopViewControllerRef controller);
|
||||
|
||||
// Allows the Flutter engine and any interested plugins an opportunity to
|
||||
// handle the given message.
|
||||
//
|
||||
// If the WindowProc was handled and further handling should stop, this returns
|
||||
// true and |result| will be populated. |result| is not set if returning false.
|
||||
FLUTTER_EXPORT bool FlutterDesktopViewControllerHandleTopLevelWindowProc(
|
||||
FlutterDesktopViewControllerRef controller,
|
||||
HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
LRESULT* result);
|
||||
|
||||
// ========== Engine ==========
|
||||
|
||||
// Creates a Flutter engine with the given properties.
|
||||
@ -146,12 +159,38 @@ FlutterDesktopEngineGetMessenger(FlutterDesktopEngineRef engine);
|
||||
FLUTTER_EXPORT HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view);
|
||||
|
||||
// ========== Plugin Registrar (extensions) ==========
|
||||
// These are Windows-specific extensions to flutter_plugin_registrar.h
|
||||
|
||||
// Returns the view associated with this registrar's engine instance
|
||||
// This is a Windows-specific extension to flutter_plugin_registrar.h.
|
||||
// Function pointer type for top level WindowProc delegate registration.
|
||||
//
|
||||
// The user data will be whatever was passed to
|
||||
// FlutterDesktopRegisterTopLevelWindowProcHandler.
|
||||
//
|
||||
// Implementations should populate |result| and return true if the WindowProc
|
||||
// was handled and further handling should stop. |result| is ignored if the
|
||||
// function returns false.
|
||||
typedef bool (*FlutterDesktopWindowProcCallback)(HWND /* hwnd */,
|
||||
UINT /* uMsg */,
|
||||
WPARAM /*wParam*/,
|
||||
LPARAM /* lParam*/,
|
||||
void* /* user data */,
|
||||
LRESULT* result);
|
||||
|
||||
// Returns the view associated with this registrar's engine instance.
|
||||
FLUTTER_EXPORT FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView(
|
||||
FlutterDesktopPluginRegistrarRef registrar);
|
||||
|
||||
FLUTTER_EXPORT void
|
||||
FlutterDesktopPluginRegistrarRegisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopPluginRegistrarRef registrar,
|
||||
FlutterDesktopWindowProcCallback delegate,
|
||||
void* user_data);
|
||||
|
||||
FLUTTER_EXPORT void
|
||||
FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopPluginRegistrarRef registrar,
|
||||
FlutterDesktopWindowProcCallback delegate);
|
||||
|
||||
// ========== Freestanding Utilities ==========
|
||||
|
||||
// Gets the DPI for a given |hwnd|, depending on the supported APIs per
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
// 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 <windows.h>
|
||||
|
||||
#include "flutter/shell/platform/windows/win32_dpi_utils.h"
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
// 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/testing/win32_flutter_window_test.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
// 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/win32_window_proc_delegate_manager.h"
|
||||
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
Win32WindowProcDelegateManager::Win32WindowProcDelegateManager() = default;
|
||||
Win32WindowProcDelegateManager::~Win32WindowProcDelegateManager() = default;
|
||||
|
||||
void Win32WindowProcDelegateManager::RegisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopWindowProcCallback delegate,
|
||||
void* user_data) {
|
||||
top_level_window_proc_handlers_[delegate] = user_data;
|
||||
}
|
||||
|
||||
void Win32WindowProcDelegateManager::UnregisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopWindowProcCallback delegate) {
|
||||
top_level_window_proc_handlers_.erase(delegate);
|
||||
}
|
||||
|
||||
std::optional<LRESULT> Win32WindowProcDelegateManager::OnTopLevelWindowProc(
|
||||
HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam) {
|
||||
std::optional<LRESULT> result;
|
||||
for (const auto& [handler, user_data] : top_level_window_proc_handlers_) {
|
||||
LPARAM handler_result;
|
||||
// Stop as soon as any delegate indicates that it has handled the message.
|
||||
if (handler(hwnd, message, wparam, lparam, user_data, &handler_result)) {
|
||||
result = handler_result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
@ -0,0 +1,58 @@
|
||||
// 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_WINDOWS_WIN32_WINDOW_PROC_DELEGATE_MANAGER_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_WINDOWS_WIN32_WINDOW_PROC_DELEGATE_MANAGER_H_
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/shell/platform/windows/public/flutter_windows.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
// Handles registration, unregistration, and dispatching for WindowProc
|
||||
// delegation.
|
||||
class Win32WindowProcDelegateManager {
|
||||
public:
|
||||
explicit Win32WindowProcDelegateManager();
|
||||
~Win32WindowProcDelegateManager();
|
||||
|
||||
// Prevent copying.
|
||||
Win32WindowProcDelegateManager(Win32WindowProcDelegateManager const&) =
|
||||
delete;
|
||||
Win32WindowProcDelegateManager& operator=(
|
||||
Win32WindowProcDelegateManager const&) = delete;
|
||||
|
||||
// Adds |delegate| as a delegate to be called for |OnTopLevelWindowProc|.
|
||||
//
|
||||
// Multiple calls with the same |delegate| will replace the previous
|
||||
// registration, even if |user_data| is different.
|
||||
void RegisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopWindowProcCallback delegate,
|
||||
void* user_data);
|
||||
|
||||
// Unregisters |delegate| as a delate for |OnTopLevelWindowProc|.
|
||||
void UnregisterTopLevelWindowProcDelegate(
|
||||
FlutterDesktopWindowProcCallback delegate);
|
||||
|
||||
// Calls any registered WindowProc delegates.
|
||||
//
|
||||
// If a result is returned, then the message was handled in such a way that
|
||||
// further handling should not be done.
|
||||
std::optional<LRESULT> OnTopLevelWindowProc(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
|
||||
private:
|
||||
std::map<FlutterDesktopWindowProcCallback, void*>
|
||||
top_level_window_proc_handlers_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_WIN32_WINDOW_PROC_DELEGATE_MANAGER_H_
|
||||
@ -0,0 +1,168 @@
|
||||
// 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/win32_window_proc_delegate_manager.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
namespace {
|
||||
|
||||
using TestWindowProcDelegate = std::function<std::optional<
|
||||
LRESULT>(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)>;
|
||||
|
||||
// A FlutterDesktopWindowProcCallback that forwards to a std::function provided
|
||||
// as user_data.
|
||||
bool TestWindowProcCallback(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
void* user_data,
|
||||
LRESULT* result) {
|
||||
TestWindowProcDelegate& delegate =
|
||||
*static_cast<TestWindowProcDelegate*>(user_data);
|
||||
auto delegate_result = delegate(hwnd, message, wparam, lparam);
|
||||
if (delegate_result) {
|
||||
*result = *delegate_result;
|
||||
}
|
||||
return delegate_result.has_value();
|
||||
}
|
||||
|
||||
// Same as the above, but with a different address, to test multiple
|
||||
// registration.
|
||||
bool TestWindowProcCallback2(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
void* user_data,
|
||||
LRESULT* result) {
|
||||
return TestWindowProcCallback(hwnd, message, wparam, lparam, user_data,
|
||||
result);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(Win32WindowProcDelegateManagerTest, CallsCorrectly) {
|
||||
Win32WindowProcDelegateManager manager;
|
||||
HWND dummy_hwnd;
|
||||
|
||||
bool called = false;
|
||||
TestWindowProcDelegate delegate = [&called, &dummy_hwnd](
|
||||
HWND hwnd, UINT message, WPARAM wparam,
|
||||
LPARAM lparam) {
|
||||
called = true;
|
||||
EXPECT_EQ(hwnd, dummy_hwnd);
|
||||
EXPECT_EQ(message, 2);
|
||||
EXPECT_EQ(wparam, 3);
|
||||
EXPECT_EQ(lparam, 4);
|
||||
return std::optional<LRESULT>();
|
||||
};
|
||||
manager.RegisterTopLevelWindowProcDelegate(TestWindowProcCallback, &delegate);
|
||||
auto result = manager.OnTopLevelWindowProc(dummy_hwnd, 2, 3, 4);
|
||||
|
||||
EXPECT_TRUE(called);
|
||||
EXPECT_FALSE(result);
|
||||
}
|
||||
|
||||
TEST(Win32WindowProcDelegateManagerTest, ReplacementRegister) {
|
||||
Win32WindowProcDelegateManager manager;
|
||||
|
||||
bool called_a = false;
|
||||
TestWindowProcDelegate delegate_a =
|
||||
[&called_a](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
called_a = true;
|
||||
return std::optional<LRESULT>();
|
||||
};
|
||||
bool called_b = false;
|
||||
TestWindowProcDelegate delegate_b =
|
||||
[&called_b](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
called_b = true;
|
||||
return std::optional<LRESULT>();
|
||||
};
|
||||
manager.RegisterTopLevelWindowProcDelegate(TestWindowProcCallback,
|
||||
&delegate_a);
|
||||
// The function pointer is the same, so this should replace, not add.
|
||||
manager.RegisterTopLevelWindowProcDelegate(TestWindowProcCallback,
|
||||
&delegate_b);
|
||||
manager.OnTopLevelWindowProc(nullptr, 0, 0, 0);
|
||||
|
||||
EXPECT_FALSE(called_a);
|
||||
EXPECT_TRUE(called_b);
|
||||
}
|
||||
|
||||
TEST(Win32WindowProcDelegateManagerTest, RegisterMultiple) {
|
||||
Win32WindowProcDelegateManager manager;
|
||||
|
||||
bool called_a = false;
|
||||
TestWindowProcDelegate delegate_a =
|
||||
[&called_a](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
called_a = true;
|
||||
return std::optional<LRESULT>();
|
||||
};
|
||||
bool called_b = false;
|
||||
TestWindowProcDelegate delegate_b =
|
||||
[&called_b](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
called_b = true;
|
||||
return std::optional<LRESULT>();
|
||||
};
|
||||
manager.RegisterTopLevelWindowProcDelegate(TestWindowProcCallback,
|
||||
&delegate_a);
|
||||
// Function pointer is different, so both should be called.
|
||||
manager.RegisterTopLevelWindowProcDelegate(TestWindowProcCallback2,
|
||||
&delegate_b);
|
||||
manager.OnTopLevelWindowProc(nullptr, 0, 0, 0);
|
||||
|
||||
EXPECT_TRUE(called_a);
|
||||
EXPECT_TRUE(called_b);
|
||||
}
|
||||
|
||||
TEST(Win32WindowProcDelegateManagerTest, ConflictingDelegates) {
|
||||
Win32WindowProcDelegateManager manager;
|
||||
|
||||
bool called_a = false;
|
||||
TestWindowProcDelegate delegate_a =
|
||||
[&called_a](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
called_a = true;
|
||||
return std::optional<LRESULT>(1);
|
||||
};
|
||||
bool called_b = false;
|
||||
TestWindowProcDelegate delegate_b =
|
||||
[&called_b](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
called_b = true;
|
||||
return std::optional<LRESULT>(1);
|
||||
};
|
||||
manager.RegisterTopLevelWindowProcDelegate(TestWindowProcCallback,
|
||||
&delegate_a);
|
||||
manager.RegisterTopLevelWindowProcDelegate(TestWindowProcCallback2,
|
||||
&delegate_b);
|
||||
auto result = manager.OnTopLevelWindowProc(nullptr, 0, 0, 0);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
// Exactly one of the handlers should be called since each will claim to have
|
||||
// handled the message. Which one is unspecified, since the calling order is
|
||||
// unspecified.
|
||||
EXPECT_TRUE(called_a || called_b);
|
||||
EXPECT_NE(called_a, called_b);
|
||||
}
|
||||
|
||||
TEST(Win32WindowProcDelegateManagerTest, Unregister) {
|
||||
Win32WindowProcDelegateManager manager;
|
||||
|
||||
bool called = false;
|
||||
TestWindowProcDelegate delegate = [&called](HWND hwnd, UINT message,
|
||||
WPARAM wparam, LPARAM lparam) {
|
||||
called = true;
|
||||
return std::optional<LRESULT>();
|
||||
};
|
||||
manager.RegisterTopLevelWindowProcDelegate(TestWindowProcCallback, &delegate);
|
||||
manager.UnregisterTopLevelWindowProcDelegate(TestWindowProcCallback);
|
||||
auto result = manager.OnTopLevelWindowProc(nullptr, 0, 0, 0);
|
||||
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_FALSE(called);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
@ -1,3 +1,7 @@
|
||||
// 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/testing/win32_window_test.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
|
||||
@ -34,8 +34,8 @@ struct FlutterDesktopView {
|
||||
|
||||
// State associated with the plugin registrar.
|
||||
struct FlutterDesktopPluginRegistrar {
|
||||
// The plugin messenger handle given to API clients.
|
||||
FlutterDesktopMessenger* messenger;
|
||||
// The engine that owns this state object.
|
||||
flutter::FlutterWindowsEngine* engine = nullptr;
|
||||
|
||||
// The handle for the view associated with this registrar.
|
||||
std::unique_ptr<FlutterDesktopView> view;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user